diff --git a/config/crd/bases/multigres.com_multigresclusters.yaml b/config/crd/bases/multigres.com_multigresclusters.yaml index d87b9ac7..03149e88 100644 --- a/config/crd/bases/multigres.com_multigresclusters.yaml +++ b/config/crd/bases/multigres.com_multigresclusters.yaml @@ -5139,8 +5139,7 @@ spec: fsGroup: description: |- FSGroup sets the pod-level fsGroup for shared volume and socket access across - pool containers. When unset, the operator does not set pod fsGroup and runtime - defaults apply. + pool containers. When unset, runtime defaults apply. format: int64 minimum: 1 type: integer @@ -7586,8 +7585,7 @@ spec: fsGroup: description: |- FSGroup sets the pod-level fsGroup for shared volume and socket access across - pool containers. When unset, the operator does not set pod fsGroup and runtime - defaults apply. + pool containers. When unset, runtime defaults apply. format: int64 minimum: 1 type: integer diff --git a/config/crd/bases/multigres.com_shards.yaml b/config/crd/bases/multigres.com_shards.yaml index 888ca2f4..27cc2af7 100644 --- a/config/crd/bases/multigres.com_shards.yaml +++ b/config/crd/bases/multigres.com_shards.yaml @@ -2428,8 +2428,7 @@ spec: fsGroup: description: |- FSGroup sets the pod-level fsGroup for shared volume and socket access across - pool containers. When unset, the operator does not set pod fsGroup and runtime - defaults apply. + pool containers. When unset, runtime defaults apply. format: int64 minimum: 1 type: integer diff --git a/config/crd/bases/multigres.com_shardtemplates.yaml b/config/crd/bases/multigres.com_shardtemplates.yaml index 99431855..23947c86 100644 --- a/config/crd/bases/multigres.com_shardtemplates.yaml +++ b/config/crd/bases/multigres.com_shardtemplates.yaml @@ -2062,8 +2062,7 @@ spec: fsGroup: description: |- FSGroup sets the pod-level fsGroup for shared volume and socket access across - pool containers. When unset, the operator does not set pod fsGroup and runtime - defaults apply. + pool containers. When unset, runtime defaults apply. format: int64 minimum: 1 type: integer diff --git a/config/crd/bases/multigres.com_tablegroups.yaml b/config/crd/bases/multigres.com_tablegroups.yaml index db5924e0..52bd5646 100644 --- a/config/crd/bases/multigres.com_tablegroups.yaml +++ b/config/crd/bases/multigres.com_tablegroups.yaml @@ -2592,8 +2592,7 @@ spec: fsGroup: description: |- FSGroup sets the pod-level fsGroup for shared volume and socket access across - pool containers. When unset, the operator does not set pod fsGroup and runtime - defaults apply. + pool containers. When unset, runtime defaults apply. format: int64 minimum: 1 type: integer diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 49df1d2d..e8da7151 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -143,5 +143,6 @@ rules: resources: - storageclasses verbs: + - get - list - watch diff --git a/pkg/resource-handler/controller/shard/containers.go b/pkg/resource-handler/controller/shard/containers.go index 5487fe27..d95b4e71 100644 --- a/pkg/resource-handler/controller/shard/containers.go +++ b/pkg/resource-handler/controller/shard/containers.go @@ -208,14 +208,17 @@ func buildPgctldContainer( } return corev1.Container{ - Name: "postgres", - Image: image, - Command: []string{"/usr/local/bin/pgctld"}, - Args: args, - Resources: pool.Postgres.Resources, - Env: env, - SecurityContext: buildContainerSecurityContext(pool.FSGroup), - VolumeMounts: volumeMounts, + Name: "postgres", + Image: image, + Command: []string{"/usr/local/bin/pgctld"}, + Args: args, + Resources: pool.Postgres.Resources, + Env: env, + SecurityContext: buildContainerSecurityContext( + pool.FSGroup, + defaultPostgresRunAsUserUID, + ), + VolumeMounts: volumeMounts, StartupProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ @@ -280,7 +283,10 @@ func buildPostgresExporterContainer( }, }, }, - SecurityContext: buildContainerSecurityContext(pool.FSGroup), + SecurityContext: buildContainerSecurityContext( + pool.FSGroup, + defaultPostgresExporterRunAsUserUID, + ), } } @@ -337,13 +343,16 @@ func buildMultiPoolerSidecar( } c := corev1.Container{ - Name: "multipooler", - Image: image, - Args: args, - Ports: buildMultiPoolerContainerPorts(), - Resources: pool.Multipooler.Resources, - RestartPolicy: &sidecarRestartPolicy, - SecurityContext: buildContainerSecurityContext(pool.FSGroup), + Name: "multipooler", + Image: image, + Args: args, + Ports: buildMultiPoolerContainerPorts(), + Resources: pool.Multipooler.Resources, + RestartPolicy: &sidecarRestartPolicy, + SecurityContext: buildContainerSecurityContext( + pool.FSGroup, + defaultMultiPoolerRunAsUserUID, + ), StartupProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ diff --git a/pkg/resource-handler/controller/shard/containers_test.go b/pkg/resource-handler/controller/shard/containers_test.go index 7b8e8d3c..36c55737 100644 --- a/pkg/resource-handler/controller/shard/containers_test.go +++ b/pkg/resource-handler/controller/shard/containers_test.go @@ -74,6 +74,8 @@ func TestBuildMultiPoolerSidecar(t *testing.T) { RestartPolicy: &sidecarRestartPolicy, SecurityContext: &corev1.SecurityContext{ RunAsNonRoot: ptr.To(true), + RunAsUser: ptr.To(defaultMultiPoolerRunAsUserUID), + RunAsGroup: ptr.To(defaultMultiPoolerRunAsUserUID), }, StartupProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ @@ -177,6 +179,8 @@ func TestBuildMultiPoolerSidecar(t *testing.T) { RestartPolicy: &sidecarRestartPolicy, SecurityContext: &corev1.SecurityContext{ RunAsNonRoot: ptr.To(true), + RunAsUser: ptr.To(defaultMultiPoolerRunAsUserUID), + RunAsGroup: ptr.To(defaultMultiPoolerRunAsUserUID), }, StartupProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ @@ -298,6 +302,8 @@ func TestBuildMultiPoolerSidecar(t *testing.T) { RestartPolicy: &sidecarRestartPolicy, SecurityContext: &corev1.SecurityContext{ RunAsNonRoot: ptr.To(true), + RunAsUser: ptr.To(defaultMultiPoolerRunAsUserUID), + RunAsGroup: ptr.To(defaultMultiPoolerRunAsUserUID), }, StartupProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ @@ -403,6 +409,8 @@ func TestBuildPostgresExporterContainer(t *testing.T) { }, SecurityContext: &corev1.SecurityContext{ RunAsNonRoot: ptr.To(true), + RunAsUser: ptr.To(defaultPostgresExporterRunAsUserUID), + RunAsGroup: ptr.To(defaultPostgresExporterRunAsUserUID), }, } diff --git a/pkg/resource-handler/controller/shard/pool_pod.go b/pkg/resource-handler/controller/shard/pool_pod.go index a5539b7a..dc2f7b02 100644 --- a/pkg/resource-handler/controller/shard/pool_pod.go +++ b/pkg/resource-handler/controller/shard/pool_pod.go @@ -24,6 +24,11 @@ const ( // connections and set NOT_SERVING in etcd before SIGKILL. defaultTerminationGracePeriod int64 = 30 + // Default container UIDs used when fsGroup is not provided. + defaultPostgresRunAsUserUID int64 = 999 + defaultPostgresExporterRunAsUserUID int64 = 65534 + defaultMultiPoolerRunAsUserUID int64 = 999 + // DefaultPoolReplicas is the default number of replicas for a pool cell if not specified. DefaultPoolReplicas int32 = 1 ) @@ -134,17 +139,24 @@ func buildPoolPodSecurityContext(poolSpec multigresv1alpha1.PoolSpec) *corev1.Po } } -// buildContainerSecurityContext returns a non-root SecurityContext. When fsGroup -// is set, RunAsUser and RunAsGroup are pinned to that value so all containers -// in the pod share the same filesystem identity on shared volumes. -func buildContainerSecurityContext(fsGroup *int64) *corev1.SecurityContext { +// buildContainerSecurityContext returns a non-root SecurityContext. +// +// If fsGroup is set, RunAsUser and RunAsGroup are pinned to that value so all +// containers in the pod share the same filesystem identity on shared volumes. +// Otherwise, a container-specific fallback UID is used to ensure kubelet can +// verify runAsNonRoot against a numeric user. +func buildContainerSecurityContext(fsGroup *int64, fallbackUID int64) *corev1.SecurityContext { sc := &corev1.SecurityContext{ RunAsNonRoot: ptr.To(true), } if fsGroup != nil { sc.RunAsUser = fsGroup sc.RunAsGroup = fsGroup + return sc } + + sc.RunAsUser = ptr.To(fallbackUID) + sc.RunAsGroup = ptr.To(fallbackUID) return sc } diff --git a/pkg/resource-handler/controller/shard/pool_pod_test.go b/pkg/resource-handler/controller/shard/pool_pod_test.go index 2d3eadd8..273cbec1 100644 --- a/pkg/resource-handler/controller/shard/pool_pod_test.go +++ b/pkg/resource-handler/controller/shard/pool_pod_test.go @@ -198,21 +198,21 @@ func TestBuildPoolPod_SecurityContextWithFSGroup(t *testing.T) { func TestBuildContainerSecurityContext(t *testing.T) { t.Run("nil fsGroup", func(t *testing.T) { - sc := buildContainerSecurityContext(nil) + sc := buildContainerSecurityContext(nil, defaultPostgresRunAsUserUID) assert.True(t, *sc.RunAsNonRoot) - assert.Nil(t, sc.RunAsUser) - assert.Nil(t, sc.RunAsGroup) + assert.Equal(t, defaultPostgresRunAsUserUID, *sc.RunAsUser) + assert.Equal(t, defaultPostgresRunAsUserUID, *sc.RunAsGroup) }) t.Run("with fsGroup", func(t *testing.T) { - sc := buildContainerSecurityContext(ptr.To(int64(999))) + sc := buildContainerSecurityContext(ptr.To(int64(999)), defaultPostgresRunAsUserUID) assert.True(t, *sc.RunAsNonRoot) assert.Equal(t, int64(999), *sc.RunAsUser) assert.Equal(t, int64(999), *sc.RunAsGroup) }) t.Run("alpine fsGroup", func(t *testing.T) { - sc := buildContainerSecurityContext(ptr.To(int64(70))) + sc := buildContainerSecurityContext(ptr.To(int64(70)), defaultPostgresRunAsUserUID) assert.True(t, *sc.RunAsNonRoot) assert.Equal(t, int64(70), *sc.RunAsUser) assert.Equal(t, int64(70), *sc.RunAsGroup)