You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pc...@apache.org on 2023/07/03 09:53:31 UTC
[camel-k] 01/02: fix(core): Permissions on operator and builder pods (S2I compatibility)
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 <ga...@gmail.com>
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
+}