Skip to content

Commit 6568409

Browse files
authored
Merge pull request #88 from cybertec-postgresql/securityContext
Security context
2 parents 4dd6f62 + 04f8519 commit 6568409

File tree

10 files changed

+153
-11
lines changed

10 files changed

+153
-11
lines changed

pkg/apis/cpo.opensource.cybertec.at/v1/operator_configuration_type.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ type KubernetesMetaConfiguration struct {
6262
PodTerminateGracePeriod Duration `json:"pod_terminate_grace_period,omitempty"`
6363
SpiloPrivileged bool `json:"spilo_privileged,omitempty"`
6464
SpiloAllowPrivilegeEscalation *bool `json:"spilo_allow_privilege_escalation,omitempty"`
65+
ReadOnlyRootFilesystem *bool `json:"container_readonly_root_filesystem" default:"false"`
6566
SpiloRunAsUser *int64 `json:"spilo_runasuser,omitempty"`
6667
SpiloRunAsGroup *int64 `json:"spilo_runasgroup,omitempty"`
6768
SpiloFSGroup *int64 `json:"spilo_fsgroup,omitempty"`
@@ -102,6 +103,7 @@ type KubernetesMetaConfiguration struct {
102103
PodManagementPolicy string `json:"pod_management_policy,omitempty"`
103104
PersistentVolumeClaimRetentionPolicy map[string]string `json:"persistent_volume_claim_retention_policy,omitempty"`
104105
EnableReadinessProbe bool `json:"enable_readiness_probe,omitempty"`
106+
EnableLivenessProbe bool `json:"enable_liveness_probe,omitempty"`
105107
EnableCrossNamespaceSecret bool `json:"enable_cross_namespace_secret,omitempty"`
106108
}
107109

pkg/apis/cpo.opensource.cybertec.at/v1/postgresql_type.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,13 @@ type CloneDescription struct {
216216

217217
// Sidecar defines a container to be run in the same pod as the Postgres container.
218218
type Sidecar struct {
219-
*Resources `json:"resources,omitempty"`
220-
Name string `json:"name,omitempty"`
221-
DockerImage string `json:"image,omitempty"`
222-
Ports []v1.ContainerPort `json:"ports,omitempty"`
223-
Env []v1.EnvVar `json:"env,omitempty"`
219+
*Resources `json:"resources,omitempty"`
220+
Name string `json:"name,omitempty"`
221+
DockerImage string `json:"image,omitempty"`
222+
Ports []v1.ContainerPort `json:"ports,omitempty"`
223+
Env []v1.EnvVar `json:"env,omitempty"`
224+
SecurityContext *v1.SecurityContext `json:"securityContext,omitempty"`
225+
VolumeMounts []v1.VolumeMount `json:"volumeMounts,omitempty"`
224226
}
225227

226228
// UserFlags defines flags (such as superuser, nologin) that could be assigned to individual users

pkg/apis/cpo.opensource.cybertec.at/v1/zz_generated.deepcopy.go

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cluster/cluster.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,8 @@ func (c *Cluster) compareContainers(description string, setA, setB []v1.Containe
677677
func(a, b v1.Container) bool { return a.Name != b.Name }),
678678
newCheck("new statefulset %s's %s (index %d) readiness probe does not match the current one",
679679
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.ReadinessProbe, b.ReadinessProbe) }),
680+
newCheck("new statefulset %s's %s (index %d) liveness probe does not match the current one",
681+
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.LivenessProbe, b.LivenessProbe) }),
680682
newCheck("new statefulset %s's %s (index %d) ports do not match the current one",
681683
func(a, b v1.Container) bool { return !comparePorts(a.Ports, b.Ports) }),
682684
newCheck("new statefulset %s's %s (index %d) resources do not match the current ones",

pkg/cluster/k8sres.go

Lines changed: 108 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,7 @@ func generateContainer(
679679
volumeMounts []v1.VolumeMount,
680680
privilegedMode bool,
681681
privilegeEscalationMode *bool,
682+
readOnlyRootFilesystem *bool,
682683
additionalPodCapabilities *v1.Capabilities,
683684
) *v1.Container {
684685
return &v1.Container{
@@ -705,7 +706,7 @@ func generateContainer(
705706
SecurityContext: &v1.SecurityContext{
706707
AllowPrivilegeEscalation: privilegeEscalationMode,
707708
Privileged: &privilegedMode,
708-
ReadOnlyRootFilesystem: util.False(),
709+
ReadOnlyRootFilesystem: readOnlyRootFilesystem,
709710
Capabilities: additionalPodCapabilities,
710711
},
711712
}
@@ -738,7 +739,7 @@ func (c *Cluster) generateSidecarContainers(sidecars []cpov1.Sidecar,
738739
}
739740

740741
// adds common fields to sidecars
741-
func patchSidecarContainers(in []v1.Container, volumeMounts []v1.VolumeMount, superUserName string, credentialsSecretName string, logger *logrus.Entry) []v1.Container {
742+
func patchSidecarContainers(in []v1.Container, volumeMounts []v1.VolumeMount, superUserName string, credentialsSecretName string, logger *logrus.Entry, privilegedMode bool, privilegeEscalationMode *bool, additionalPodCapabilities *v1.Capabilities) []v1.Container {
742743
result := []v1.Container{}
743744

744745
for _, container := range in {
@@ -779,6 +780,7 @@ func patchSidecarContainers(in []v1.Container, volumeMounts []v1.VolumeMount, su
779780
},
780781
}
781782
container.Env = appendEnvVars(env, container.Env...)
783+
782784
result = append(result, container)
783785
}
784786

@@ -875,6 +877,15 @@ func (c *Cluster) generatePodTemplate(
875877
podSpec.PriorityClassName = priorityClassName
876878
}
877879

880+
if c.Postgresql.Spec.Monitoring != nil {
881+
addEmptyDirVolume(&podSpec, "exporter-tmp", "postgres-exporter", "/tmp")
882+
}
883+
884+
if c.OpConfig.ReadOnlyRootFilesystem != nil && *c.OpConfig.ReadOnlyRootFilesystem {
885+
addRunVolume(&podSpec, "postgres-run", "postgres", "/run")
886+
addEmptyDirVolume(&podSpec, "postgres-tmp", "postgres", "/tmp")
887+
}
888+
878889
if sharePgSocketWithSidecars != nil && *sharePgSocketWithSidecars {
879890
addVarRunVolume(&podSpec)
880891
}
@@ -990,6 +1001,19 @@ func (c *Cluster) generateSpiloPodEnvVars(
9901001
Name: "HUMAN_ROLE",
9911002
Value: c.OpConfig.PamRoleName,
9921003
},
1004+
// NSS WRAPPER
1005+
{
1006+
Name: "LD_PRELOAD",
1007+
Value: "/usr/lib64/libnss_wrapper.so",
1008+
},
1009+
{
1010+
Name: "NSS_WRAPPER_PASSWD",
1011+
Value: "/tmp/nss_wrapper/passwd",
1012+
},
1013+
{
1014+
Name: "NSS_WRAPPER_GROUP",
1015+
Value: "/tmp/nss_wrapper/group",
1016+
},
9931017
}
9941018

9951019
if c.OpConfig.EnableSpiloWalPathCompat {
@@ -1245,6 +1269,8 @@ func getSidecarContainer(sidecar cpov1.Sidecar, index int, resources *v1.Resourc
12451269
Resources: *resources,
12461270
Env: sidecar.Env,
12471271
Ports: sidecar.Ports,
1272+
SecurityContext: sidecar.SecurityContext,
1273+
VolumeMounts: sidecar.VolumeMounts,
12481274
}
12491275
}
12501276

@@ -1294,6 +1320,23 @@ func generateSpiloReadinessProbe() *v1.Probe {
12941320
}
12951321
}
12961322

1323+
func generatePatroniLivenessProbe() *v1.Probe {
1324+
return &v1.Probe{
1325+
FailureThreshold: 6,
1326+
ProbeHandler: v1.ProbeHandler{
1327+
HTTPGet: &v1.HTTPGetAction{
1328+
Path: "/liveness",
1329+
Port: intstr.IntOrString{IntVal: patroni.ApiPort},
1330+
Scheme: v1.URISchemeHTTP,
1331+
},
1332+
},
1333+
InitialDelaySeconds: 30,
1334+
PeriodSeconds: 10,
1335+
TimeoutSeconds: 5,
1336+
SuccessThreshold: 1,
1337+
}
1338+
}
1339+
12971340
func (c *Cluster) generateStatefulSet(spec *cpov1.PostgresSpec) (*appsv1.StatefulSet, error) {
12981341

12991342
var (
@@ -1424,6 +1467,7 @@ func (c *Cluster) generateStatefulSet(spec *cpov1.PostgresSpec) (*appsv1.Statefu
14241467
}
14251468
additionalVolumes = append(additionalVolumes, tlsVolumes...)
14261469
}
1470+
14271471
repo_host_mode := false
14281472
// Add this envVar so that it is not added to the pgbackrest initcontainer
14291473
if specHasPgbackrestPVCRepo(spec) {
@@ -1448,13 +1492,18 @@ func (c *Cluster) generateStatefulSet(spec *cpov1.PostgresSpec) (*appsv1.Statefu
14481492
volumeMounts,
14491493
c.OpConfig.Resources.SpiloPrivileged,
14501494
c.OpConfig.Resources.SpiloAllowPrivilegeEscalation,
1495+
c.OpConfig.Resources.ReadOnlyRootFilesystem,
14511496
generateCapabilities(c.OpConfig.AdditionalPodCapabilities),
14521497
)
14531498

14541499
// Patroni responds 200 to probe only if it either owns the leader lock or postgres is running and DCS is accessible
14551500
if c.OpConfig.EnableReadinessProbe {
14561501
spiloContainer.ReadinessProbe = generateSpiloReadinessProbe()
14571502
}
1503+
//
1504+
if c.OpConfig.EnableLivenessProbe {
1505+
spiloContainer.LivenessProbe = generatePatroniLivenessProbe()
1506+
}
14581507

14591508
// generate container specs for sidecars specified in the cluster manifest
14601509
clusterSpecificSidecars := []v1.Container{}
@@ -1510,7 +1559,7 @@ func (c *Cluster) generateStatefulSet(spec *cpov1.PostgresSpec) (*appsv1.Statefu
15101559
containerName, containerName)
15111560
}
15121561

1513-
sidecarContainers = patchSidecarContainers(sidecarContainers, volumeMounts, c.OpConfig.SuperUsername, c.credentialSecretName(c.OpConfig.SuperUsername), c.logger)
1562+
sidecarContainers = patchSidecarContainers(sidecarContainers, volumeMounts, c.OpConfig.SuperUsername, c.credentialSecretName(c.OpConfig.SuperUsername), c.logger, c.OpConfig.Resources.SpiloPrivileged, c.OpConfig.Resources.SpiloAllowPrivilegeEscalation, generateCapabilities(c.OpConfig.AdditionalPodCapabilities))
15141563

15151564
tolerationSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration)
15161565
topologySpreadConstraintsSpec := topologySpreadConstraints(&spec.TopologySpreadConstraints)
@@ -1519,7 +1568,7 @@ func (c *Cluster) generateStatefulSet(spec *cpov1.PostgresSpec) (*appsv1.Statefu
15191568
podAnnotations := c.generatePodAnnotations(spec)
15201569

15211570
if spec.GetBackup().Pgbackrest != nil {
1522-
initContainers = append(initContainers, c.generatePgbackrestRestoreContainer(spec, repo_host_mode, volumeMounts, resourceRequirements))
1571+
initContainers = append(initContainers, c.generatePgbackrestRestoreContainer(spec, repo_host_mode, volumeMounts, resourceRequirements, c.OpConfig.Resources.SpiloPrivileged, c.OpConfig.Resources.SpiloAllowPrivilegeEscalation, generateCapabilities(c.OpConfig.AdditionalPodCapabilities)))
15231572

15241573
additionalVolumes = append(additionalVolumes, c.generatePgbackrestConfigVolume(spec.Backup.Pgbackrest, false))
15251574

@@ -1622,7 +1671,7 @@ func (c *Cluster) generateStatefulSet(spec *cpov1.PostgresSpec) (*appsv1.Statefu
16221671
return statefulSet, nil
16231672
}
16241673

1625-
func (c *Cluster) generatePgbackrestRestoreContainer(spec *cpov1.PostgresSpec, repo_host_mode bool, volumeMounts []v1.VolumeMount, resourceRequirements *v1.ResourceRequirements) v1.Container {
1674+
func (c *Cluster) generatePgbackrestRestoreContainer(spec *cpov1.PostgresSpec, repo_host_mode bool, volumeMounts []v1.VolumeMount, resourceRequirements *v1.ResourceRequirements, privilegedMode bool, privilegeEscalationMode *bool, additionalPodCapabilities *v1.Capabilities) v1.Container {
16261675
isOptional := true
16271676
pgbackrestRestoreEnvVars := []v1.EnvVar{
16281677
{
@@ -1702,6 +1751,12 @@ func (c *Cluster) generatePgbackrestRestoreContainer(spec *cpov1.PostgresSpec, r
17021751
Env: pgbackrestRestoreEnvVars,
17031752
VolumeMounts: volumeMounts,
17041753
Resources: *resourceRequirements,
1754+
SecurityContext: &v1.SecurityContext{
1755+
AllowPrivilegeEscalation: privilegeEscalationMode,
1756+
Privileged: &privilegedMode,
1757+
ReadOnlyRootFilesystem: util.True(),
1758+
Capabilities: additionalPodCapabilities,
1759+
},
17051760
}
17061761
}
17071762

@@ -1760,6 +1815,7 @@ func (c *Cluster) generateRepoHostStatefulSet(spec *cpov1.PostgresSpec) (*appsv1
17601815
volumeMounts,
17611816
c.OpConfig.Resources.SpiloPrivileged,
17621817
c.OpConfig.Resources.SpiloAllowPrivilegeEscalation,
1818+
c.OpConfig.Resources.ReadOnlyRootFilesystem,
17631819
generateCapabilities(c.OpConfig.AdditionalPodCapabilities),
17641820
)
17651821

@@ -2163,6 +2219,48 @@ func addShmVolume(podSpec *v1.PodSpec) {
21632219
podSpec.Volumes = volumes
21642220
}
21652221

2222+
func addEmptyDirVolume(podSpec *v1.PodSpec, volumeName string, containerName string, path string) {
2223+
vol := v1.Volume{
2224+
Name: volumeName,
2225+
VolumeSource: v1.VolumeSource{
2226+
EmptyDir: &v1.EmptyDirVolumeSource{},
2227+
},
2228+
}
2229+
podSpec.Volumes = append(podSpec.Volumes, vol)
2230+
2231+
mount := v1.VolumeMount{
2232+
Name: vol.Name,
2233+
MountPath: path,
2234+
}
2235+
2236+
for i := range podSpec.Containers {
2237+
if podSpec.Containers[i].Name == containerName {
2238+
podSpec.Containers[i].VolumeMounts = append(podSpec.Containers[i].VolumeMounts, mount)
2239+
}
2240+
}
2241+
}
2242+
2243+
func addRunVolume(podSpec *v1.PodSpec, volumeName string, containerName string, path string) {
2244+
vol := v1.Volume{
2245+
Name: volumeName,
2246+
VolumeSource: v1.VolumeSource{
2247+
EmptyDir: &v1.EmptyDirVolumeSource{},
2248+
},
2249+
}
2250+
podSpec.Volumes = append(podSpec.Volumes, vol)
2251+
2252+
mount := v1.VolumeMount{
2253+
Name: vol.Name,
2254+
MountPath: path,
2255+
}
2256+
2257+
for i := range podSpec.Containers {
2258+
if podSpec.Containers[i].Name == containerName {
2259+
podSpec.Containers[i].VolumeMounts = append(podSpec.Containers[i].VolumeMounts, mount)
2260+
}
2261+
}
2262+
}
2263+
21662264
func addVarRunVolume(podSpec *v1.PodSpec) {
21672265
volumes := append(podSpec.Volumes, v1.Volume{
21682266
Name: "postgresql-run",
@@ -2730,6 +2828,7 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) {
27302828
[]v1.VolumeMount{},
27312829
c.OpConfig.SpiloPrivileged, // use same value as for normal DB pods
27322830
c.OpConfig.SpiloAllowPrivilegeEscalation,
2831+
util.False(),
27332832
nil,
27342833
)
27352834

@@ -3256,9 +3355,13 @@ func (c *Cluster) generatePgbackrestJob(backup *cpov1.Pgbackrest, repo *cpov1.Re
32563355
[]v1.VolumeMount{},
32573356
c.OpConfig.SpiloPrivileged, // use same value as for normal DB pods
32583357
c.OpConfig.SpiloAllowPrivilegeEscalation,
3358+
c.OpConfig.Resources.ReadOnlyRootFilesystem,
32593359
nil,
32603360
)
32613361

3362+
// Patch securityContext - readOnlyRootFilesystem
3363+
pgbackrestContainer.SecurityContext.ReadOnlyRootFilesystem = util.True()
3364+
32623365
podAffinityTerm := v1.PodAffinityTerm{
32633366
LabelSelector: c.roleLabelsSelector(Master),
32643367
TopologyKey: "kubernetes.io/hostname",

pkg/cluster/resources.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ func (c *Cluster) createStatefulSet() (*appsv1.StatefulSet, error) {
9292
},
9393
},
9494
Env: c.generateMonitoringEnvVars(),
95+
SecurityContext: &v1.SecurityContext{
96+
AllowPrivilegeEscalation: c.OpConfig.Resources.SpiloAllowPrivilegeEscalation,
97+
Privileged: &c.OpConfig.Resources.SpiloPrivileged,
98+
ReadOnlyRootFilesystem: util.True(),
99+
Capabilities: generateCapabilities(c.OpConfig.AdditionalPodCapabilities),
100+
},
95101
}
96102
c.Spec.Sidecars = append(c.Spec.Sidecars, *sidecar) //populate the sidecar spec so that the sidecar is automatically created
97103
}

pkg/cluster/sync.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,12 @@ func (c *Cluster) syncStatefulSet() error {
514514
},
515515
},
516516
Env: c.generateMonitoringEnvVars(),
517+
SecurityContext: &v1.SecurityContext{
518+
AllowPrivilegeEscalation: c.OpConfig.Resources.SpiloAllowPrivilegeEscalation,
519+
Privileged: &c.OpConfig.Resources.SpiloPrivileged,
520+
ReadOnlyRootFilesystem: util.True(),
521+
Capabilities: generateCapabilities(c.OpConfig.AdditionalPodCapabilities),
522+
},
517523
}
518524
c.Spec.Sidecars = append(c.Spec.Sidecars, *sidecar) //populate the sidecar spec so that the sidecar is automatically created
519525
}

pkg/controller/controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"sync"
1111
"time"
1212

13-
"github.com/sirupsen/logrus"
1413
cpov1 "github.com/cybertec-postgresql/cybertec-pg-operator/pkg/apis/cpo.opensource.cybertec.at/v1"
1514
"github.com/cybertec-postgresql/cybertec-pg-operator/pkg/apiserver"
1615
"github.com/cybertec-postgresql/cybertec-pg-operator/pkg/cluster"
@@ -22,6 +21,7 @@ import (
2221
"github.com/cybertec-postgresql/cybertec-pg-operator/pkg/util/constants"
2322
"github.com/cybertec-postgresql/cybertec-pg-operator/pkg/util/k8sutil"
2423
"github.com/cybertec-postgresql/cybertec-pg-operator/pkg/util/ringlog"
24+
"github.com/sirupsen/logrus"
2525
v1 "k8s.io/api/core/v1"
2626
rbacv1 "k8s.io/api/rbac/v1"
2727
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

pkg/controller/operator_config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *cpov1.OperatorConfigura
7575
result.PodTerminateGracePeriod = util.CoalesceDuration(time.Duration(fromCRD.Kubernetes.PodTerminateGracePeriod), "5m")
7676
result.SpiloPrivileged = fromCRD.Kubernetes.SpiloPrivileged
7777
result.SpiloAllowPrivilegeEscalation = util.CoalesceBool(fromCRD.Kubernetes.SpiloAllowPrivilegeEscalation, util.True())
78+
result.ReadOnlyRootFilesystem = util.CoalesceBool(fromCRD.Kubernetes.ReadOnlyRootFilesystem, util.False())
7879
result.SpiloRunAsUser = fromCRD.Kubernetes.SpiloRunAsUser
7980
result.SpiloRunAsGroup = fromCRD.Kubernetes.SpiloRunAsGroup
8081
result.SpiloFSGroup = fromCRD.Kubernetes.SpiloFSGroup
@@ -121,6 +122,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *cpov1.OperatorConfigura
121122
result.PodManagementPolicy = util.Coalesce(fromCRD.Kubernetes.PodManagementPolicy, "ordered_ready")
122123
result.PersistentVolumeClaimRetentionPolicy = fromCRD.Kubernetes.PersistentVolumeClaimRetentionPolicy
123124
result.EnableReadinessProbe = fromCRD.Kubernetes.EnableReadinessProbe
125+
result.EnableLivenessProbe = fromCRD.Kubernetes.EnableLivenessProbe
124126
result.MasterPodMoveTimeout = util.CoalesceDuration(time.Duration(fromCRD.Kubernetes.MasterPodMoveTimeout), "10m")
125127
result.EnablePodAntiAffinity = fromCRD.Kubernetes.EnablePodAntiAffinity
126128
result.PodAntiAffinityTopologyKey = util.Coalesce(fromCRD.Kubernetes.PodAntiAffinityTopologyKey, "kubernetes.io/hostname")

pkg/util/config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type Resources struct {
3838
SpiloPrivileged bool `name:"spilo_privileged" default:"false"`
3939
SpiloAllowPrivilegeEscalation *bool `name:"spilo_allow_privilege_escalation" default:"true"`
4040
AdditionalPodCapabilities []string `name:"additional_pod_capabilities" default:""`
41+
ReadOnlyRootFilesystem *bool `name:"container_readonly_root_filesystem" default:"false"`
4142
ClusterLabels map[string]string `name:"cluster_labels" default:"application:cpo"`
4243
InheritedLabels []string `name:"inherited_labels" default:""`
4344
InheritedAnnotations []string `name:"inherited_annotations" default:""`
@@ -249,6 +250,7 @@ type Config struct {
249250
PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"`
250251
PodManagementPolicy string `name:"pod_management_policy" default:"ordered_ready"`
251252
EnableReadinessProbe bool `name:"enable_readiness_probe" default:"false"`
253+
EnableLivenessProbe bool `name:"enable_liveness_probe" default:"true"`
252254
ProtectedRoles []string `name:"protected_role_names" default:"admin,cron_admin"`
253255
PostgresSuperuserTeams []string `name:"postgres_superuser_teams" default:""`
254256
SetMemoryRequestToLimit bool `name:"set_memory_request_to_limit" default:"false"`

0 commit comments

Comments
 (0)