This is an automated email from the ASF dual-hosted git repository.
squakez pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git
The following commit(s) were added to refs/heads/main by this push:
new 45a2dc6b5 Fix #6678: restrict toleration.taints keys via operator
allow list (#6695)
45a2dc6b5 is described below
commit 45a2dc6b51828f08f4ca05dcd45567f25e3e627f
Author: Harsh Mehta <[email protected]>
AuthorDate: Tue Jun 30 14:54:19 2026 +0530
Fix #6678: restrict toleration.taints keys via operator allow list (#6695)
* feat(toleration): add support for allowed taint keys in tolerations
Signed-off-by: Harsh Mehta <[email protected]>
* fix(toleration): streamline taint filtering and improve taint key
extraction
Signed-off-by: Harsh Mehta <[email protected]>
---------
Signed-off-by: Harsh Mehta <[email protected]>
---
docs/modules/ROOT/pages/installation/builds.adoc | 4 ++
docs/modules/traits/pages/toleration.adoc | 3 ++
pkg/platform/env_platform.go | 21 ++++++++
pkg/platform/env_platform_test.go | 26 ++++++++++
pkg/trait/toleration.go | 34 +++++++++++++
pkg/trait/toleration_test.go | 65 ++++++++++++++++++++++++
6 files changed, 153 insertions(+)
diff --git a/docs/modules/ROOT/pages/installation/builds.adoc
b/docs/modules/ROOT/pages/installation/builds.adoc
index b9b93ba5d..38f88f508 100644
--- a/docs/modules/ROOT/pages/installation/builds.adoc
+++ b/docs/modules/ROOT/pages/installation/builds.adoc
@@ -47,6 +47,10 @@ Here a quick resume of the parameters you can configure as
environment variables
| Maximum number of builds that can run concurrently.
| `3` if build strategy is `routine`, `10` if `pod`
+| TOLERATION_TAINTS_ALLOWED_KEYS
+| Comma-separated list of taint keys that CR authors are permitted to use in
`toleration.taints`. When unset or empty all keys are accepted. Taints whose
key is not in the list are dropped and an info message is logged. Example:
`node-role.kubernetes.io/master,disktype`.
+|
+
| AFFINITY_NODE_LABELS_ALLOWED_KEYS
| Comma-separated list of label keys that CR authors are permitted to use in
`affinity.nodeAffinityLabels`. When unset or empty all keys are accepted.
Expressions whose key is not in the list are dropped and an info message is
logged. Example: `kubernetes.io/hostname,topology.kubernetes.io/zone`.
|
diff --git a/docs/modules/traits/pages/toleration.adoc
b/docs/modules/traits/pages/toleration.adoc
index b6f1eb38f..01711fe56 100755
--- a/docs/modules/traits/pages/toleration.adoc
+++ b/docs/modules/traits/pages/toleration.adoc
@@ -65,3 +65,6 @@ $ kamel run -t
toleration.taints="node.kubernetes.io/network-unavailable:NoExecu
+
[source,console]
$ kamel run -t toleration.taints="disktype=ssd:PreferNoSchedule" ...
+
+
+NOTE: Operators can restrict which taint keys CR authors are permitted to use
in `toleration.taints` by setting the `TOLERATION_TAINTS_ALLOWED_KEYS`
environment variable on the operator deployment to a comma-separated list of
allowed keys (e.g. `node-role.kubernetes.io/master,disktype`). Taints whose key
is not in the list are dropped and an info message is logged. When the variable
is unset or empty, all keys are accepted (default behavior). See build
environment variables documentation [...]
diff --git a/pkg/platform/env_platform.go b/pkg/platform/env_platform.go
index 24527ed31..c9ef96d94 100644
--- a/pkg/platform/env_platform.go
+++ b/pkg/platform/env_platform.go
@@ -173,6 +173,27 @@ func publishStrategy()
v1.IntegrationPlatformBuildPublishStrategy {
return DefaultPublishStrategy
}
+// TolerationTaintsAllowList returns the list of taint keys that are allowed
to be used in
+// toleration.taints. When the list is empty (TOLERATION_TAINTS_ALLOWED_KEYS
is unset or blank),
+// any key is permitted. When the list is non-empty only taints whose key is
in the list are
+// accepted; others are dropped and an info message is logged by the trait.
+func TolerationTaintsAllowList() []string {
+ raw := GetEnvOrDefault("TOLERATION_TAINTS_ALLOWED_KEYS", "")
+ if raw == "" {
+ return nil
+ }
+ parts := strings.Split(raw, ",")
+ result := make([]string, 0, len(parts))
+ for _, p := range parts {
+ p = strings.TrimSpace(p)
+ if p != "" {
+ result = append(result, p)
+ }
+ }
+
+ return result
+}
+
// AffinityNodeLabelsAllowList returns the list of label keys that are allowed
to be used in
// affinity.nodeAffinityLabels. When the list is empty
(AFFINITY_NODE_LABELS_ALLOWED_KEYS is
// unset or blank), any key is permitted. When the list is non-empty only
expressions whose
diff --git a/pkg/platform/env_platform_test.go
b/pkg/platform/env_platform_test.go
index 797b7517c..468dd0ecf 100644
--- a/pkg/platform/env_platform_test.go
+++ b/pkg/platform/env_platform_test.go
@@ -168,6 +168,32 @@ func TestBuilderNodeSelectorAllowList_MultipleKeys(t
*testing.T) {
assert.Equal(t, []string{"kubernetes.io/hostname",
"node-role.kubernetes.io/worker", "topology.kubernetes.io/zone"}, allowList)
}
+func TestTolerationTaintsAllowList_NotSet(t *testing.T) {
+ allowList := TolerationTaintsAllowList()
+ assert.Nil(t, allowList)
+}
+
+func TestTolerationTaintsAllowList_Empty(t *testing.T) {
+ t.Setenv("TOLERATION_TAINTS_ALLOWED_KEYS", "")
+
+ allowList := TolerationTaintsAllowList()
+ assert.Empty(t, allowList)
+}
+
+func TestTolerationTaintsAllowList_SingleKey(t *testing.T) {
+ t.Setenv("TOLERATION_TAINTS_ALLOWED_KEYS",
"node-role.kubernetes.io/master")
+
+ allowList := TolerationTaintsAllowList()
+ assert.Equal(t, []string{"node-role.kubernetes.io/master"}, allowList)
+}
+
+func TestTolerationTaintsAllowList_MultipleKeys(t *testing.T) {
+ t.Setenv("TOLERATION_TAINTS_ALLOWED_KEYS",
"node-role.kubernetes.io/master, disktype ")
+
+ allowList := TolerationTaintsAllowList()
+ assert.Equal(t, []string{"node-role.kubernetes.io/master", "disktype"},
allowList)
+}
+
func TestAffinityNodeLabelsAllowList_NotSet(t *testing.T) {
allowList := AffinityNodeLabelsAllowList()
assert.Nil(t, allowList)
diff --git a/pkg/trait/toleration.go b/pkg/trait/toleration.go
index 78e2c6a7f..0b0ab1c44 100644
--- a/pkg/trait/toleration.go
+++ b/pkg/trait/toleration.go
@@ -20,11 +20,14 @@ package trait
import (
"errors"
"fmt"
+ "slices"
+ "strings"
corev1 "k8s.io/api/core/v1"
"k8s.io/utils/ptr"
traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait"
+ "github.com/apache/camel-k/v2/pkg/platform"
"github.com/apache/camel-k/v2/pkg/util/kubernetes"
)
@@ -53,6 +56,8 @@ func (t *tolerationTrait) Configure(e *Environment) (bool,
*TraitCondition, erro
return false, nil, errors.New("no taint was provided")
}
+ t.filterTaints()
+
return e.IntegrationInRunningPhases(), nil, nil
}
@@ -73,3 +78,32 @@ func (t *tolerationTrait) Apply(e *Environment) error {
return nil
}
+
+// filterTaints removes taint entries whose key is not in the
operator-configured allow list.
+// When TOLERATION_TAINTS_ALLOWED_KEYS is unset or empty all taints are kept.
+func (t *tolerationTrait) filterTaints() {
+ allowList := platform.TolerationTaintsAllowList()
+ if len(allowList) == 0 || len(t.Taints) == 0 {
+ return
+ }
+ kept := make([]string, 0, len(t.Taints))
+ for _, taint := range t.Taints {
+ key := taintKey(taint)
+ if slices.Contains(allowList, key) {
+ kept = append(kept, taint)
+ } else {
+ t.L.Info("toleration.taints key is not in the allowed
list and will be ignored",
+ "key", key, "allowedKeys", allowList)
+ }
+ }
+ t.Taints = kept
+}
+
+// taintKey extracts the key from a taint string of the form
Key[=Value]:Effect[:Seconds].
+func taintKey(taint string) string {
+ if parts := strings.SplitN(taint, "=", 2); len(parts) > 1 {
+ return parts[0]
+ }
+
+ return strings.SplitN(taint, ":", 2)[0]
+}
diff --git a/pkg/trait/toleration_test.go b/pkg/trait/toleration_test.go
index ea4538902..7f311ae28 100644
--- a/pkg/trait/toleration_test.go
+++ b/pkg/trait/toleration_test.go
@@ -131,6 +131,71 @@ func TestTolerationValidTaints(t *testing.T) {
require.NoError(t, err)
}
+func TestFilterTaints_NoAllowList(t *testing.T) {
+ trait := createNominalTolerationTrait()
+ trait.Taints = []string{"disktype:NoSchedule",
"node-role.kubernetes.io/master:NoExecute"}
+
+ trait.filterTaints()
+ assert.Len(t, trait.Taints, 2)
+}
+
+func TestFilterTaints_AllowListFilters(t *testing.T) {
+ t.Setenv("TOLERATION_TAINTS_ALLOWED_KEYS", "disktype")
+
+ trait := createNominalTolerationTrait()
+ trait.Taints = []string{"disktype:NoSchedule",
"node-role.kubernetes.io/master:NoExecute"}
+
+ trait.filterTaints()
+ assert.Equal(t, []string{"disktype:NoSchedule"}, trait.Taints)
+}
+
+func TestFilterTaints_AllAllowed(t *testing.T) {
+ t.Setenv("TOLERATION_TAINTS_ALLOWED_KEYS",
"disktype,node-role.kubernetes.io/master")
+
+ trait := createNominalTolerationTrait()
+ trait.Taints = []string{"disktype:NoSchedule",
"node-role.kubernetes.io/master:NoExecute"}
+
+ trait.filterTaints()
+ assert.Len(t, trait.Taints, 2)
+}
+
+func TestFilterTaints_AllDropped(t *testing.T) {
+ t.Setenv("TOLERATION_TAINTS_ALLOWED_KEYS", "disktype")
+
+ trait := createNominalTolerationTrait()
+ trait.Taints = []string{"node-role.kubernetes.io/master:NoExecute"}
+
+ trait.filterTaints()
+ assert.Empty(t, trait.Taints)
+}
+
+func TestFilterTaints_KeyWithValue(t *testing.T) {
+ t.Setenv("TOLERATION_TAINTS_ALLOWED_KEYS", "my-toleration")
+
+ trait := createNominalTolerationTrait()
+ trait.Taints = []string{"my-toleration=my-value:NoExecute",
"other=val:NoSchedule"}
+
+ trait.filterTaints()
+ assert.Equal(t, []string{"my-toleration=my-value:NoExecute"},
trait.Taints)
+}
+
+func TestApplyTolerationWithAllowList(t *testing.T) {
+ t.Setenv("TOLERATION_TAINTS_ALLOWED_KEYS", "disktype")
+
+ tolerationTrait := createNominalTolerationTrait()
+ tolerationTrait.Taints = []string{"disktype:NoSchedule",
"node-role.kubernetes.io/master:NoExecute"}
+
+ environment, deployment := createNominalDeploymentTraitTest()
+ _, _, err := tolerationTrait.Configure(environment)
+ require.NoError(t, err)
+ err = tolerationTrait.Apply(environment)
+
+ require.NoError(t, err)
+ tolerations := deployment.Spec.Template.Spec.Tolerations
+ assert.Len(t, tolerations, 1)
+ assert.Equal(t, "disktype", tolerations[0].Key)
+}
+
func createNominalTolerationTrait() *tolerationTrait {
tolerationTrait, _ := newTolerationTrait().(*tolerationTrait)
tolerationTrait.Enabled = ptr.To(true)