diff --git a/config/manager/operator-deployment.yaml b/config/manager/operator-deployment.yaml index f8e93a6872..26898da2d1 100644 --- a/config/manager/operator-deployment.yaml +++ b/config/manager/operator-deployment.yaml @@ -78,4 +78,12 @@ spec: port: 8081 initialDelaySeconds: 20 periodSeconds: 10 + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL diff --git a/e2e/install/cli/install_test.go b/e2e/install/cli/install_test.go index d39d104c69..b90f0116dc 100644 --- a/e2e/install/cli/install_test.go +++ b/e2e/install/cli/install_test.go @@ -55,6 +55,16 @@ func TestBasicInstallation(t *testing.T) { Eventually(PlatformConditionStatus(ns, v1.IntegrationPlatformConditionReady), TestTimeoutShort). Should(Equal(corev1.ConditionTrue)) + // Check if default security context has been applyed + Eventually(OperatorPodHas(ns, func(pod *corev1.Pod) bool { + if pod.Spec.Containers == nil || len(pod.Spec.Containers) == 0 { + return false + } + // exclude user for openshift + pod.Spec.Containers[0].SecurityContext.RunAsUser = nil + return reflect.DeepEqual(pod.Spec.Containers[0].SecurityContext, kubernetes.DefaultOperatorSecurityContext()) + }), TestTimeoutShort).Should(BeTrue()) + t.Run("run yaml", func(t *testing.T) { Expect(KamelRunWithID(operatorID, ns, "files/yaml.yaml").Execute()).To(Succeed()) Eventually(IntegrationPodPhase(ns, "yaml"), TestTimeoutLong).Should(Equal(corev1.PodRunning)) diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go index 05fc4fb466..83a8935a77 100644 --- a/e2e/support/test_support.go +++ b/e2e/support/test_support.go @@ -1341,6 +1341,16 @@ func OperatorImage(ns string) func() string { } } +func OperatorPodHas(ns string, predicate func(pod *corev1.Pod) bool) func() bool { + return func() bool { + pod := OperatorPod(ns)() + if pod == nil { + return false + } + return predicate(pod) + } +} + func OperatorPodPhase(ns string) func() corev1.PodPhase { return func() corev1.PodPhase { pod := OperatorPod(ns)() diff --git a/helm/camel-k/templates/operator.yaml b/helm/camel-k/templates/operator.yaml index 6b2b274a81..ba80263b1d 100644 --- a/helm/camel-k/templates/operator.yaml +++ b/helm/camel-k/templates/operator.yaml @@ -89,10 +89,21 @@ spec: resources: {{- toYaml . | nindent 12 }} {{- end }} + {{- if .Values.operator.securityContext }} {{- with .Values.operator.securityContext }} securityContext: {{- toYaml . | nindent 12 }} {{- end }} + {{- else }} + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + {{- end }} serviceAccountName: camel-k-operator {{- with .Values.operator.tolerations }} tolerations: diff --git a/pkg/install/operator.go b/pkg/install/operator.go index 5c5a2e134b..9daf61ee0c 100644 --- a/pkg/install/operator.go +++ b/pkg/install/operator.go @@ -44,6 +44,7 @@ import ( "github.com/apache/camel-k/v2/pkg/util/knative" "github.com/apache/camel-k/v2/pkg/util/kubernetes" "github.com/apache/camel-k/v2/pkg/util/minikube" + "github.com/apache/camel-k/v2/pkg/util/openshift" "github.com/apache/camel-k/v2/pkg/util/patch" image "github.com/apache/camel-k/v2/pkg/util/registry" ) @@ -243,6 +244,16 @@ func OperatorOrCollect(ctx context.Context, cmd *cobra.Command, c client.Client, // Remove Ingress permissions as it's not needed on OpenShift // This should ideally be removed from the common RBAC manifest. RemoveIngressRoleCustomizer(o) + + if d, ok := o.(*appsv1.Deployment); ok { + securityContext, _ := openshift.GetOpenshiftSecurityContextRestricted(ctx, c, cfg.Namespace) + if securityContext != nil { + d.Spec.Template.Spec.Containers[0].SecurityContext = securityContext + + } else { + d.Spec.Template.Spec.Containers[0].SecurityContext = kubernetes.DefaultOperatorSecurityContext() + } + } } return o diff --git a/pkg/trait/container.go b/pkg/trait/container.go index 141b4f58ef..d2409c43e2 100644 --- a/pkg/trait/container.go +++ b/pkg/trait/container.go @@ -39,6 +39,7 @@ import ( "github.com/apache/camel-k/v2/pkg/util/envvar" "github.com/apache/camel-k/v2/pkg/util/knative" "github.com/apache/camel-k/v2/pkg/util/kubernetes" + "github.com/apache/camel-k/v2/pkg/util/openshift" ) const ( @@ -200,6 +201,8 @@ func (t *containerTrait) configureContainer(e *Environment) error { } t.configureCapabilities(e) + t.configureSecurityContext(e, &container) + var containers *[]corev1.Container visited := false @@ -339,3 +342,14 @@ func (t *containerTrait) configureCapabilities(e *Environment) { e.ApplicationProperties["camel.context.rest-configuration.component"] = "platform-http" } } + +func (t *containerTrait) configureSecurityContext(e *Environment, container *corev1.Container) { + // get security context from security context constraint configuration in namespace + isOpenShift, _ := openshift.IsOpenShift(e.Client) + if isOpenShift { + securityContext, _ := openshift.GetOpenshiftSecurityContextRestricted(e.Ctx, e.Client, e.Platform.Namespace) + if securityContext != nil { + container.SecurityContext = securityContext + } + } +} diff --git a/pkg/util/kubernetes/security.go b/pkg/util/kubernetes/security.go new file mode 100644 index 0000000000..b4a4d7692d --- /dev/null +++ b/pkg/util/kubernetes/security.go @@ -0,0 +1,38 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubernetes + +import ( + corev1 "k8s.io/api/core/v1" +) + +// DefaultOperatorSecurityContext to ensure a container with low privilege and limited permissions. +func DefaultOperatorSecurityContext() *corev1.SecurityContext { + runAsNonRoot := true + allowPrivilegeEscalation := false + sc := corev1.SecurityContext{ + RunAsNonRoot: &runAsNonRoot, + SeccompProfile: &corev1.SeccompProfile{ + Type: corev1.SeccompProfileTypeRuntimeDefault, + }, + AllowPrivilegeEscalation: &allowPrivilegeEscalation, + Capabilities: &corev1.Capabilities{Drop: []corev1.Capability{"ALL"}}, + } + + return &sc +}