Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions charts/postgres-operator/crds/operatorconfigurations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,15 @@ spec:
type: integer
spilo_fsgroup:
type: integer
spilo_runasnonroot:
type: boolean
spilo_seccompprofile:
type: object
properties:
localhostProfile:
type: string
type:
type: string
spilo_privileged:
type: boolean
default: false
Expand Down
9 changes: 9 additions & 0 deletions charts/postgres-operator/crds/postgresqls.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,15 @@ spec:
type: integer
spiloFSGroup:
type: integer
spiloRunAsNonRoot:
type: boolean
spiloSeccompProfile:
type: object
properties:
localhostProfile:
type: string
type:
type: string
standby:
type: object
properties:
Expand Down
8 changes: 8 additions & 0 deletions charts/postgres-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,14 @@ configKubernetes:
# group ID with write-access to volumes (required to run Spilo as non-root process)
# spilo_fsgroup: 103

# sets runAsNonRoot in the security context. If this is set you also must set spilo_runasuser.
# spilo_runasnonroot: true

# sets seccompProfile in the security context
# spilo_seccompprofile:
# type: Localhost
# localhostProfile: profiles/audit.json

# whether the Spilo container should run in privileged mode
spilo_privileged: false
# whether the Spilo container should run with additional permissions other than parent.
Expand Down
7 changes: 7 additions & 0 deletions docs/reference/cluster_manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ These parameters are grouped directly under the `spec` key in the manifest.
requires a custom Spilo image. Note the FSGroup of a Pod cannot be changed
without recreating a new Pod. Optional.

* **spiloRunAsNonRoot**
boolean flag to set `runAsNonRoot` in the pod security context. If this is set
then `spiloRunAsUser` must also be set. Optional.

* **spiloSeccompProfile**
sets the `seccompProfile` in the pod security context. Optional.

* **enableMasterLoadBalancer**
boolean flag to override the operator defaults (set by the
`enable_master_load_balancer` parameter) to define whether to enable the load
Expand Down
7 changes: 7 additions & 0 deletions docs/reference/operator_parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,13 @@ configuration they are grouped under the `kubernetes` key.
non-root process, but requires a custom Spilo image. Note the FSGroup of a Pod
cannot be changed without recreating a new Pod.

* **spilo_runasnonroot**
boolean flag to set `runAsNonRoot` in the Spilo pod security context. If this
is set then `spilo_runasuser` must also be set. Optional.

* **spilo_seccompprofile**
sets the `seccompProfile` in the Spilo pod security context. Optional.

* **spilo_privileged**
whether the Spilo container should run in privileged mode. Privileged mode is
used for AWS volume resizing and not required if you don't need that
Expand Down
4 changes: 4 additions & 0 deletions manifests/postgresql-operator-default-configuration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ configuration:
# spilo_runasuser: 101
# spilo_runasgroup: 103
# spilo_fsgroup: 103
# spilo_runasnonroot: true
# spilo_seccompprofile:
# type: Localhost
# localhostProfile: profiles/audit.json
spilo_privileged: false
storage_resize_mode: pvc
# toleration:
Expand Down
9 changes: 9 additions & 0 deletions manifests/postgresql.crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,15 @@ spec:
type: integer
spiloFSGroup:
type: integer
spiloRunAsNonRoot:
type: boolean
spiloSeccompProfile:
type: object
properties:
localhostProfile:
type: string
type:
type: string
standby:
type: object
properties:
Expand Down
28 changes: 28 additions & 0 deletions pkg/apis/acid.zalan.do/v1/crds.go
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,20 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{
"spiloFSGroup": {
Type: "integer",
},
"spiloRunAsNonRoot": {
Type: "boolean",
},
"spiloSeccompProfile": {
Type: "object",
Properties: map[string]apiextv1.JSONSchemaProps{
"localhostProfile": {
Type: "string",
},
"type": {
Type: "string",
},
},
},
"standby": {
Type: "object",
Properties: map[string]apiextv1.JSONSchemaProps{
Expand Down Expand Up @@ -1525,6 +1539,20 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
"spilo_fsgroup": {
Type: "integer",
},
"spilo_runasnonroot": {
Type: "boolean",
},
"spilo_seccompprofile": {
Type: "object",
Properties: map[string]apiextv1.JSONSchemaProps{
"localhostProfile": {
Type: "string",
},
"type": {
Type: "string",
},
},
},
"spilo_privileged": {
Type: "boolean",
},
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/acid.zalan.do/v1/operator_configuration_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ type KubernetesMetaConfiguration struct {
SpiloRunAsUser *int64 `json:"spilo_runasuser,omitempty"`
SpiloRunAsGroup *int64 `json:"spilo_runasgroup,omitempty"`
SpiloFSGroup *int64 `json:"spilo_fsgroup,omitempty"`
SpiloRunAsNonRoot *bool `json:"spilo_runasnonroot,omitempty"`
SpiloSeccompProfile *v1.SeccompProfile `json:"spilo_seccompprofile,omitempty"`
AdditionalPodCapabilities []string `json:"additional_pod_capabilities,omitempty"`
WatchedNamespace string `json:"watched_namespace,omitempty"`
PDBNameFormat config.StringTemplate `json:"pdb_name_format,omitempty"`
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/acid.zalan.do/v1/postgresql_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ type PostgresSpec struct {
SpiloRunAsGroup *int64 `json:"spiloRunAsGroup,omitempty"`
SpiloFSGroup *int64 `json:"spiloFSGroup,omitempty"`

SpiloRunAsNonRoot *bool `json:"spiloRunAsNonRoot,omitempty"`
SpiloSeccompProfile *v1.SeccompProfile `json:"spiloSeccompProfile,omitempty"`

// vars that enable load balancers are pointers because it is important to know if any of them is omitted from the Postgres manifest
// in that case the var evaluates to nil and the value is taken from the operator config
EnableMasterLoadBalancer *bool `json:"enableMasterLoadBalancer,omitempty"`
Expand Down
20 changes: 20 additions & 0 deletions pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions pkg/cluster/k8sres.go
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,8 @@ func (c *Cluster) generatePodTemplate(
spiloRunAsUser *int64,
spiloRunAsGroup *int64,
spiloFSGroup *int64,
spiloRunAsNonRoot *bool,
spiloSeccompProfile *v1.SeccompProfile,
nodeAffinity *v1.Affinity,
schedulerName *string,
terminateGracePeriod int64,
Expand Down Expand Up @@ -845,6 +847,14 @@ func (c *Cluster) generatePodTemplate(
securityContext.FSGroup = spiloFSGroup
}

if spiloRunAsNonRoot != nil {
securityContext.RunAsNonRoot = spiloRunAsNonRoot
}

if spiloSeccompProfile != nil {
securityContext.SeccompProfile = spiloSeccompProfile
}

podSpec := v1.PodSpec{
ServiceAccountName: podServiceAccountName,
TerminationGracePeriodSeconds: &terminateGracePeriodSeconds,
Expand Down Expand Up @@ -1357,6 +1367,16 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
effectiveFSGroup = spec.SpiloFSGroup
}

effectiveRunAsNonRoot := c.OpConfig.Resources.SpiloRunAsNonRoot
if spec.SpiloRunAsNonRoot != nil {
effectiveRunAsNonRoot = spec.SpiloRunAsNonRoot
}

effectiveSeccompProfile := c.OpConfig.Resources.SpiloSeccompProfile
if spec.SpiloSeccompProfile != nil {
effectiveSeccompProfile = spec.SpiloSeccompProfile
}

volumeMounts := generateVolumeMounts(spec.Volume)

// configure TLS with a custom secret volume
Expand Down Expand Up @@ -1473,6 +1493,8 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
effectiveRunAsUser,
effectiveRunAsGroup,
effectiveFSGroup,
effectiveRunAsNonRoot,
effectiveSeccompProfile,
c.nodeAffinity(c.OpConfig.NodeReadinessLabel, spec.NodeAffinity),
spec.SchedulerName,
int64(c.OpConfig.PodTerminateGracePeriod.Seconds()),
Expand Down Expand Up @@ -2361,6 +2383,8 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) {
nil,
nil,
nil,
nil,
nil,
c.nodeAffinity(c.OpConfig.NodeReadinessLabel, nil),
nil,
int64(c.OpConfig.PodTerminateGracePeriod.Seconds()),
Expand Down
120 changes: 120 additions & 0 deletions pkg/cluster/k8sres_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3984,3 +3984,123 @@ func TestGenerateCapabilities(t *testing.T) {
}
}
}

func TestGenerateSeccompProfile(t *testing.T) {
mockClient, _ := newFakeK8sTestClient()

spiloSeccompProfile := v1.SeccompProfile{
Type: "Localhost",
LocalhostProfile: k8sutil.StringToPointer("profiles/audit.json"),
}

postgresql := acidv1.Postgresql{
ObjectMeta: metav1.ObjectMeta{
Name: "acid-test-cluster",
Namespace: "default",
},
Spec: acidv1.PostgresSpec{
TeamID: "myapp",
NumberOfInstances: 1,
Resources: &acidv1.Resources{
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
},
Volume: acidv1.Volume{
Size: "1G",
},
},
}

testCluster := New(
Config{
OpConfig: config.Config{
PodManagementPolicy: "ordered_ready",
Resources: config.Resources{
SpiloSeccompProfile: &spiloSeccompProfile,
},
},
}, mockClient, postgresql, logger, eventRecorder)

// create a statefulset
sts, err := testCluster.createStatefulSet()
assert.NoError(t, err)

assert.Equal(t, spiloSeccompProfile.Type, sts.Spec.Template.Spec.SecurityContext.SeccompProfile.Type, "SeccompProfile.Type matches")
assert.Equal(t, *spiloSeccompProfile.LocalhostProfile, *sts.Spec.Template.Spec.SecurityContext.SeccompProfile.LocalhostProfile, "SeccompProfile.LocalhostProfile matches")
}

func TestGenerateEmptySeccompProfile(t *testing.T) {
mockClient, _ := newFakeK8sTestClient()

postgresql := acidv1.Postgresql{
ObjectMeta: metav1.ObjectMeta{
Name: "acid-test-cluster",
Namespace: "default",
},
Spec: acidv1.PostgresSpec{
TeamID: "myapp",
NumberOfInstances: 1,
Resources: &acidv1.Resources{
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
},
Volume: acidv1.Volume{
Size: "1G",
},
},
}

testCluster := New(
Config{
OpConfig: config.Config{
PodManagementPolicy: "ordered_ready",
Resources: config.Resources{},
},
}, mockClient, postgresql, logger, eventRecorder)

// create a statefulset
sts, err := testCluster.createStatefulSet()
assert.NoError(t, err)

assert.Nil(t, sts.Spec.Template.Spec.SecurityContext.SeccompProfile, "SeccompProfile not set")
}

func TestGenerateRunAsNonRoot(t *testing.T) {
mockClient, _ := newFakeK8sTestClient()

postgresql := acidv1.Postgresql{
ObjectMeta: metav1.ObjectMeta{
Name: "acid-test-cluster",
Namespace: "default",
},
Spec: acidv1.PostgresSpec{
TeamID: "myapp",
NumberOfInstances: 1,
Resources: &acidv1.Resources{
ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")},
},
Volume: acidv1.Volume{
Size: "1G",
},
},
}

runAsNonRoot := true

testCluster := New(
Config{
OpConfig: config.Config{
PodManagementPolicy: "ordered_ready",
Resources: config.Resources{
SpiloRunAsNonRoot: &runAsNonRoot,
},
},
}, mockClient, postgresql, logger, eventRecorder)

// create a statefulset
sts, err := testCluster.createStatefulSet()
assert.NoError(t, err)

assert.Equal(t, true, *sts.Spec.Template.Spec.SecurityContext.RunAsNonRoot, "RunAsNonRoot set")
}
2 changes: 2 additions & 0 deletions pkg/controller/operator_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.SpiloRunAsUser = fromCRD.Kubernetes.SpiloRunAsUser
result.SpiloRunAsGroup = fromCRD.Kubernetes.SpiloRunAsGroup
result.SpiloFSGroup = fromCRD.Kubernetes.SpiloFSGroup
result.SpiloRunAsNonRoot = fromCRD.Kubernetes.SpiloRunAsNonRoot
result.SpiloSeccompProfile = fromCRD.Kubernetes.SpiloSeccompProfile
result.AdditionalPodCapabilities = fromCRD.Kubernetes.AdditionalPodCapabilities
result.ClusterDomain = util.Coalesce(fromCRD.Kubernetes.ClusterDomain, "cluster.local")
result.WatchedNamespace = fromCRD.Kubernetes.WatchedNamespace
Expand Down
2 changes: 2 additions & 0 deletions pkg/util/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type Resources struct {
SpiloRunAsUser *int64 `name:"spilo_runasuser"`
SpiloRunAsGroup *int64 `name:"spilo_runasgroup"`
SpiloFSGroup *int64 `name:"spilo_fsgroup"`
SpiloRunAsNonRoot *bool `name:"spilo_runasnonroot"`
SpiloSeccompProfile *v1.SeccompProfile `name:"spilo_seccompprofile"`
PodPriorityClassName string `name:"pod_priority_class_name"`
ClusterDomain string `name:"cluster_domain" default:"cluster.local"`
SpiloPrivileged bool `name:"spilo_privileged" default:"false"`
Expand Down