This is an automated email from the ASF dual-hosted git repository.

pcongiusti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 11e989fd6c8a2244955c217221132f3524c2982e
Author: Gaelle Fournier <[email protected]>
AuthorDate: Wed Jun 28 09:51:49 2023 +0200

    fix(core): Permissions on operator and builder pods (S2I compatibility)
    
    * Change default Dockerfile user from 1001 to 1001:0
    * Add builder pod security context compatible with OCP 
SecurityContextConstraint restricted-v2 
(https://docs.openshift.com/container-platform/4.12/authentication/managing-security-context-constraints.html)
    * Change Dockerfile S2I to user compatible with SecurityContextConstraint
    * Add permission to get a namespace to operator to ensure 
SecurityContextConstraint labels in namespace are accessible
    * Remove root FsGroup on operator as it is no longer needed
---
 build/Dockerfile                     | 12 +++--
 config/rbac/operator-role.yaml       |  6 +++
 pkg/controller/build/build_pod.go    | 40 +++++++++++----
 pkg/controller/build/monitor_pod.go  |  2 +-
 pkg/controller/catalog/initialize.go | 24 +++++++--
 pkg/install/operator.go              |  4 --
 pkg/util/openshift/openshift.go      | 99 ++++++++++++++++++++++++++++++++++++
 7 files changed, 163 insertions(+), 24 deletions(-)

diff --git a/build/Dockerfile b/build/Dockerfile
index 92efe413b..5f5251637 100644
--- a/build/Dockerfile
+++ b/build/Dockerfile
@@ -45,14 +45,16 @@ ENV MAVEN_OPTS="${MAVEN_OPTS} 
-Dlogback.configurationFile=${MAVEN_HOME}/conf/log
 ADD build/_maven_output ${MVN_REPO}
 ADD build/_kamelets /kamelets
 
-RUN chgrp -R 1001 ${MVN_REPO} \
-    && chown -R 1001 ${MVN_REPO} \
+RUN chgrp -R 0 ${MVN_REPO} \
+    && chown -R 1001:0 ${MVN_REPO} \
+    && chmod -R 775 ${MVN_REPO} \
     && chgrp -R 0 /kamelets \
     && chmod -R g=u /kamelets \
-    && chgrp -R 1001 ${MAVEN_HOME} \
-    && chown -R 1001 ${MAVEN_HOME}
+    && chgrp -R 0 ${MAVEN_HOME} \
+    && chown -R 1001:0 ${MAVEN_HOME} \
+    && chmod -R 775 ${MAVEN_HOME}
 
-USER 1001
+USER 1001:0
 
 ADD build/_output/bin/kamel /usr/local/bin/kamel
 
diff --git a/config/rbac/operator-role.yaml b/config/rbac/operator-role.yaml
index 975028317..5c01d853d 100644
--- a/config/rbac/operator-role.yaml
+++ b/config/rbac/operator-role.yaml
@@ -179,3 +179,9 @@ rules:
   verbs:
   - get
   - list
+- apiGroups:
+  - ""
+  resources:
+  - namespaces
+  verbs:
+  - get
\ No newline at end of file
diff --git a/pkg/controller/build/build_pod.go 
b/pkg/controller/build/build_pod.go
index 4627e8806..bb5fe31a7 100644
--- a/pkg/controller/build/build_pod.go
+++ b/pkg/controller/build/build_pod.go
@@ -33,9 +33,11 @@ import (
 
        v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
        "github.com/apache/camel-k/v2/pkg/builder"
+       "github.com/apache/camel-k/v2/pkg/client"
        "github.com/apache/camel-k/v2/pkg/platform"
        "github.com/apache/camel-k/v2/pkg/util/defaults"
        "github.com/apache/camel-k/v2/pkg/util/kubernetes"
+       "github.com/apache/camel-k/v2/pkg/util/openshift"
 )
 
 const (
@@ -112,8 +114,22 @@ var (
        }
 )
 
-func newBuildPod(ctx context.Context, c ctrl.Reader, build *v1.Build) 
(*corev1.Pod, error) {
+func newBuildPod(ctx context.Context, c ctrl.Reader, client client.Client, 
build *v1.Build) (*corev1.Pod, error) {
        var ugfid int64 = 1001
+       podSecurityContext := &corev1.PodSecurityContext{
+               RunAsUser:  &ugfid,
+               RunAsGroup: &ugfid,
+               FSGroup:    &ugfid,
+       }
+       for _, task := range build.Spec.Tasks {
+               // get pod security context from security context constraint 
configuration in namespace
+               if task.S2i != nil {
+                       podSecurityContextConstrained, _ := 
openshift.GetOpenshiftPodSecurityContextRestricted(ctx, client, 
build.BuilderPodNamespace())
+                       if podSecurityContextConstrained != nil {
+                               podSecurityContext = 
podSecurityContextConstrained
+                       }
+               }
+       }
        pod := &corev1.Pod{
                TypeMeta: metav1.TypeMeta{
                        APIVersion: corev1.SchemeGroupVersion.String(),
@@ -130,11 +146,7 @@ func newBuildPod(ctx context.Context, c ctrl.Reader, build 
*v1.Build) (*corev1.P
                Spec: corev1.PodSpec{
                        ServiceAccountName: platform.BuilderServiceAccount,
                        RestartPolicy:      corev1.RestartPolicyNever,
-                       SecurityContext: &corev1.PodSecurityContext{
-                               RunAsUser:  &ugfid,
-                               RunAsGroup: &ugfid,
-                               FSGroup:    &ugfid,
-                       },
+                       SecurityContext:    podSecurityContext,
                },
        }
 
@@ -143,7 +155,7 @@ func newBuildPod(ctx context.Context, c ctrl.Reader, build 
*v1.Build) (*corev1.P
        for _, task := range build.Spec.Tasks {
                switch {
                case task.Builder != nil:
-                       addBuildTaskToPod(build, task.Builder.Name, pod)
+                       addBuildTaskToPod(ctx, client, build, 
task.Builder.Name, pod)
                case task.Buildah != nil:
                        err := addBuildahTaskToPod(ctx, c, build, task.Buildah, 
pod)
                        if err != nil {
@@ -155,9 +167,9 @@ func newBuildPod(ctx context.Context, c ctrl.Reader, build 
*v1.Build) (*corev1.P
                                return nil, err
                        }
                case task.S2i != nil:
-                       addBuildTaskToPod(build, task.S2i.Name, pod)
+                       addBuildTaskToPod(ctx, client, build, task.S2i.Name, 
pod)
                case task.Spectrum != nil:
-                       addBuildTaskToPod(build, task.Spectrum.Name, pod)
+                       addBuildTaskToPod(ctx, client, build, 
task.Spectrum.Name, pod)
                case task.Custom != nil:
                        addCustomTaskToPod(build, task.Custom, pod)
                }
@@ -244,7 +256,7 @@ func buildPodName(build *v1.Build) string {
        return "camel-k-" + build.Name + "-builder"
 }
 
-func addBuildTaskToPod(build *v1.Build, taskName string, pod *corev1.Pod) {
+func addBuildTaskToPod(ctx context.Context, client client.Client, build 
*v1.Build, taskName string, pod *corev1.Pod) {
        if !hasVolume(pod, builderVolume) {
                pod.Spec.Volumes = append(pod.Spec.Volumes,
                        // EmptyDir volume used to share the build state across 
tasks
@@ -283,6 +295,14 @@ func addBuildTaskToPod(build *v1.Build, taskName string, 
pod *corev1.Pod) {
                Env:        envVars,
        }
 
+       // get security context from security context constraint configuration 
in namespace
+       if taskName == "s2i" {
+               securityContextConstrained, _ := 
openshift.GetOpenshiftSecurityContextRestricted(ctx, client, 
build.BuilderPodNamespace())
+               if securityContextConstrained != nil {
+                       container.SecurityContext = securityContextConstrained
+               }
+       }
+
        configureResources(build, &container)
        addContainerToPod(build, container, pod)
 }
diff --git a/pkg/controller/build/monitor_pod.go 
b/pkg/controller/build/monitor_pod.go
index d16928e00..89255b639 100644
--- a/pkg/controller/build/monitor_pod.go
+++ b/pkg/controller/build/monitor_pod.go
@@ -72,7 +72,7 @@ func (action *monitorPodAction) Handle(ctx context.Context, 
build *v1.Build) (*v
                switch build.Status.Phase {
 
                case v1.BuildPhasePending:
-                       if pod, err = newBuildPod(ctx, action.reader, build); 
err != nil {
+                       if pod, err = newBuildPod(ctx, action.reader, 
action.client, build); err != nil {
                                return nil, err
                        }
 
diff --git a/pkg/controller/catalog/initialize.go 
b/pkg/controller/catalog/initialize.go
index 79935bcf8..0ab547243 100644
--- a/pkg/controller/catalog/initialize.go
+++ b/pkg/controller/catalog/initialize.go
@@ -39,6 +39,7 @@ import (
        "github.com/apache/camel-k/v2/pkg/util"
        "github.com/apache/camel-k/v2/pkg/util/defaults"
        "github.com/apache/camel-k/v2/pkg/util/kubernetes"
+       "github.com/apache/camel-k/v2/pkg/util/openshift"
        "github.com/apache/camel-k/v2/pkg/util/s2i"
 
        spectrum "github.com/container-tools/spectrum/pkg/builder"
@@ -172,13 +173,15 @@ func initializeS2i(ctx context.Context, c client.Client, 
ip *v1.IntegrationPlatf
        )
        imageTag := strings.ToLower(catalog.Spec.Runtime.Version)
 
+       uidStr := getS2iUserID(ctx, c, ip, catalog)
+
        // Dockfile
        dockerfile := string([]byte(`
                FROM ` + catalog.Spec.GetQuarkusToolingImage() + `
-               USER 1001
-               ADD /usr/local/bin/kamel /usr/local/bin/kamel
-               ADD /usr/share/maven/mvnw/ /usr/share/maven/mvnw/
-               ADD ` + defaults.LocalRepository + ` ` + 
defaults.LocalRepository + `
+               USER ` + uidStr + `:0
+               ADD --chown=` + uidStr + `:0 /usr/local/bin/kamel 
/usr/local/bin/kamel
+               ADD --chown=` + uidStr + `:0 /usr/share/maven/mvnw/ 
/usr/share/maven/mvnw/
+               ADD --chown=` + uidStr + `:0 ` + defaults.LocalRepository + ` ` 
+ defaults.LocalRepository + `
        `))
 
        owner := catalogReference(catalog)
@@ -557,3 +560,16 @@ func catalogReference(catalog *v1.CamelCatalog) 
*unstructured.Unstructured {
        owner.SetKind(catalog.Kind)
        return owner
 }
+
+// get user id from security context constraint configuration in namespace if 
present.
+func getS2iUserID(ctx context.Context, c client.Client, ip 
*v1.IntegrationPlatform, catalog *v1.CamelCatalog) string {
+       ugfidStr := "1001"
+       if ip.Status.Cluster == v1.IntegrationPlatformClusterOpenShift {
+               uidStr, err := openshift.GetOpenshiftUser(ctx, c, 
catalog.GetNamespace())
+               if err != nil {
+                       Log.Error(err, "Unable to retieve an Openshift user and 
group Ids.")
+               }
+               return uidStr
+       }
+       return ugfidStr
+}
diff --git a/pkg/install/operator.go b/pkg/install/operator.go
index 4f3d1081f..5c5a2e134 100644
--- a/pkg/install/operator.go
+++ b/pkg/install/operator.go
@@ -174,10 +174,6 @@ func OperatorOrCollect(ctx context.Context, cmd 
*cobra.Command, c client.Client,
                                        fmt.Sprintf("--health-port=%d", 
cfg.Health.Port))
                                
d.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Port = 
intstr.FromInt(int(cfg.Health.Port))
                        }
-                       var ugfid int64 = 0
-                       d.Spec.Template.Spec.SecurityContext = 
&corev1.PodSecurityContext{
-                               FSGroup: &ugfid,
-                       }
                }
                if cfg.Debugging.Enabled {
                        if d, ok := o.(*appsv1.Deployment); ok {
diff --git a/pkg/util/openshift/openshift.go b/pkg/util/openshift/openshift.go
index da3f78f6f..21b932102 100644
--- a/pkg/util/openshift/openshift.go
+++ b/pkg/util/openshift/openshift.go
@@ -18,7 +18,15 @@ limitations under the License.
 package openshift
 
 import (
+       "context"
+       "errors"
+       "fmt"
+       "strconv"
+       "strings"
+
+       corev1 "k8s.io/api/core/v1"
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/client-go/kubernetes"
 )
 
@@ -33,3 +41,94 @@ func IsOpenShift(client kubernetes.Interface) (bool, error) {
 
        return true, nil
 }
+
+// GetOpenshiftPodSecurityContextRestricted return the PodSecurityContext 
(https://docs.openshift.com/container-platform/4.12/authentication/managing-security-context-constraints.html):
+// FsGroup set to the minimum value in the 
"openshift.io/sa.scc.supplemental-groups" annotation if exists, else falls back 
to minimum value "openshift.io/sa.scc.uid-range" annotation.
+func GetOpenshiftPodSecurityContextRestricted(ctx context.Context, client 
kubernetes.Interface, namespace string) (*corev1.PodSecurityContext, error) {
+
+       ns, err := client.CoreV1().Namespaces().Get(ctx, namespace, 
metav1.GetOptions{})
+       if err != nil {
+               return nil, fmt.Errorf("failed to get namespace %q: %w", 
namespace, err)
+       }
+
+       uidRange, ok := 
ns.ObjectMeta.Annotations["openshift.io/sa.scc.uid-range"]
+       if !ok {
+               return nil, errors.New("annotation 
'openshift.io/sa.scc.uid-range' not found")
+       }
+
+       supplementalGroups, ok := 
ns.ObjectMeta.Annotations["openshift.io/sa.scc.supplemental-groups"]
+       if !ok {
+               supplementalGroups = uidRange
+       }
+
+       supplementalGroups = strings.Split(supplementalGroups, ",")[0]
+       fsGroupStr := strings.Split(supplementalGroups, "/")[0]
+       fsGroup, err := strconv.ParseInt(fsGroupStr, 10, 64)
+       if err != nil {
+               return nil, fmt.Errorf("failed to convert fsgroup to integer 
%q: %w", fsGroupStr, err)
+       }
+
+       psc := corev1.PodSecurityContext{
+               FSGroup: &fsGroup,
+               SeccompProfile: &corev1.SeccompProfile{
+                       Type: corev1.SeccompProfileTypeRuntimeDefault,
+               },
+       }
+
+       return &psc, nil
+
+}
+
+// GetOpenshiftSecurityContextRestricted return the PodSecurityContext 
(https://docs.openshift.com/container-platform/4.12/authentication/managing-security-context-constraints.html):
+// User set to the minimum value in the "openshift.io/sa.scc.uid-range" 
annotation.
+func GetOpenshiftSecurityContextRestricted(ctx context.Context, client 
kubernetes.Interface, namespace string) (*corev1.SecurityContext, error) {
+
+       ns, err := client.CoreV1().Namespaces().Get(ctx, namespace, 
metav1.GetOptions{})
+       if err != nil {
+               return nil, fmt.Errorf("failed to get namespace %q: %w", 
namespace, err)
+       }
+
+       uidRange, ok := 
ns.ObjectMeta.Annotations["openshift.io/sa.scc.uid-range"]
+       if !ok {
+               return nil, errors.New("annotation 
'openshift.io/sa.scc.uid-range' not found")
+       }
+
+       uidStr := strings.Split(uidRange, "/")[0]
+       uid, err := strconv.ParseInt(uidStr, 10, 64)
+       if err != nil {
+               return nil, fmt.Errorf("failed to convert uid to integer %q: 
%w", uidStr, err)
+       }
+
+       runAsNonRoot := true
+       allowPrivilegeEscalation := false
+       sc := corev1.SecurityContext{
+               RunAsUser:    &uid,
+               RunAsNonRoot: &runAsNonRoot,
+               SeccompProfile: &corev1.SeccompProfile{
+                       Type: corev1.SeccompProfileTypeRuntimeDefault,
+               },
+               AllowPrivilegeEscalation: &allowPrivilegeEscalation,
+               Capabilities:             &corev1.Capabilities{Drop: 
[]corev1.Capability{"ALL"}},
+       }
+
+       return &sc, nil
+
+}
+
+// GetOpenshiftUser return the UserId 
(https://docs.openshift.com/container-platform/4.12/authentication/managing-security-context-constraints.html):
+// User set to the minimum value in the "openshift.io/sa.scc.uid-range" 
annotation.
+func GetOpenshiftUser(ctx context.Context, client kubernetes.Interface, 
namespace string) (string, error) {
+
+       ns, err := client.CoreV1().Namespaces().Get(ctx, namespace, 
metav1.GetOptions{})
+       if err != nil {
+               return "", fmt.Errorf("failed to get namespace %q: %w", 
namespace, err)
+       }
+
+       uidRange, ok := 
ns.ObjectMeta.Annotations["openshift.io/sa.scc.uid-range"]
+       if !ok {
+               return "", errors.New("annotation 
'openshift.io/sa.scc.uid-range' not found")
+       }
+
+       uidStr := strings.Split(uidRange, "/")[0]
+       return uidStr, nil
+}

Reply via email to