Skip to content

Set securityContext for operator and spilo #341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
13b06e9
Set securityContext for operator and spilo
eberlep Mar 16, 2022
181860d
Unexport variables
eberlep Mar 17, 2022
2cc4782
Merge remote-tracking branch 'origin/main' into securityContext
eberlep Apr 21, 2022
e4f2557
Fix kubebuilder url
eberlep Apr 22, 2022
be4bc1c
Add debug output
eberlep Apr 22, 2022
381d772
Debug output for make
eberlep Apr 22, 2022
20e1673
Remove debug output
eberlep Apr 22, 2022
62e4276
go install instead of go get
eberlep Apr 22, 2022
934b5e5
Revert "Debug output for make"
eberlep Apr 22, 2022
600e03e
Revert "Add debug output"
eberlep Apr 22, 2022
fec15f4
Make runAsNonRoot configurable
eberlep May 12, 2022
1002022
Merge remote-tracking branch 'origin/main' into securityContext
eberlep May 13, 2022
28cf662
Try runAsNonRoot, but with privilege escalation (for cron)
eberlep May 13, 2022
51f0b3e
Setting to true instead of not setting it and hoping for the default
eberlep Jun 28, 2022
f0d020e
Merge remote-tracking branch 'origin' into securityContext
eberlep Jun 28, 2022
2cee9fe
Merge remote-tracking branch 'origin/main' into securityContext
eberlep Aug 4, 2022
a0376a6
Make RunAsNonRoot revertable
eberlep Aug 9, 2022
6837490
Make RunAsNonRoot revertable
eberlep Aug 9, 2022
b4f6d02
Pin minor go version for PRs (aka tests, image itself is build via a …
eberlep Aug 9, 2022
9392e85
Even better, get the go version from the go.mod file
eberlep Aug 9, 2022
1d9196a
Update setup-go
eberlep Aug 9, 2022
4ccd9b9
Update linter to prevent it from fucking up the go build env
eberlep Aug 9, 2022
68e34d6
Merge remote-tracking branch 'origin/main' into securityContext
eberlep Aug 10, 2022
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
6 changes: 3 additions & 3 deletions .github/workflows/pull_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ jobs:
uses: actions/checkout@v2

- name: Ensure Go Version
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: '^1.18'
go-version-file: 'go.mod'

- name: Lint
uses: golangci/golangci-lint-action@v2
uses: golangci/golangci-lint-action@v3
with:
args: -p bugs -p unused --timeout=5m

Expand Down
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,7 @@ ifeq (,$(wildcard ~/.kubebuilder/${KUBEBUILDER_VERSION}))
{ \
os=$$(go env GOOS) ;\
arch=$$(go env GOARCH) ;\
curl -L https://go.kubebuilder.io/dl/${KUBEBUILDER_VERSION}/$${os}/$${arch} | tar -xz -C /tmp/ ;\
mv /tmp/kubebuilder_${KUBEBUILDER_VERSION}_$${os}_$${arch}/bin/* ${GOBIN} ;\
curl -L https://github.com/kubernetes-sigs/kubebuilder/releases/download/v${KUBEBUILDER_VERSION}/kubebuilder_$${os}_$${arch} -o ${GOBIN}/kubebuilder ;\
mkdir -p ~/.kubebuilder ;\
touch ~/.kubebuilder/${KUBEBUILDER_VERSION} ;\
}
Expand Down
17 changes: 16 additions & 1 deletion api/v1/postgres_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ const (
defaultPostgresParamValueSSLMinProtocolVersion = "TLSv1.2"
defaultPostgresParamValueSSLPreferServerCiphers = "on"
defaultPostgresParamValueSSLCiphers = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"

// spiloRunAsUser the uid to use when running the spilo (postgres) image
spiloRunAsUser int64 = 101
// spiloRunAsGroup the gid to use when running the spilo (postgres) image
spiloRunAsGroup int64 = 101
)

var (
Expand Down Expand Up @@ -495,7 +500,7 @@ func (p *Postgres) ToPeripheralResourceLookupKey() types.NamespacedName {
}
}

func (p *Postgres) ToUnstructuredZalandoPostgresql(z *zalando.Postgresql, c *corev1.ConfigMap, sc string, pgParamBlockList map[string]bool, rbs *BackupConfig, srcDB *Postgres, patroniTTL, patroniLoopWait, patroniRetryTimeout uint32) (*unstructured.Unstructured, error) {
func (p *Postgres) ToUnstructuredZalandoPostgresql(z *zalando.Postgresql, c *corev1.ConfigMap, sc string, pgParamBlockList map[string]bool, rbs *BackupConfig, srcDB *Postgres, runAsNonRoot bool, patroniTTL, patroniLoopWait, patroniRetryTimeout uint32) (*unstructured.Unstructured, error) {
if z == nil {
z = &zalando.Postgresql{}
}
Expand Down Expand Up @@ -612,6 +617,16 @@ func (p *Postgres) ToUnstructuredZalandoPostgresql(z *zalando.Postgresql, c *cor
}
}

if runAsNonRoot {
// Fix uid/gid for the spilo user
z.Spec.SpiloRunAsUser = pointer.Int64(spiloRunAsUser)
z.Spec.SpiloRunAsGroup = pointer.Int64(spiloRunAsGroup)
} else {
// Unset
z.Spec.SpiloRunAsUser = nil
z.Spec.SpiloRunAsGroup = nil
}

jsonZ, err := runtime.DefaultUnstructuredConverter.ToUnstructured(z)
if err != nil {
return nil, fmt.Errorf("failed to convert to unstructured zalando postgresql: %w", err)
Expand Down
23 changes: 14 additions & 9 deletions api/v1/postgres_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ func TestPostgresRestoreTimestamp_ToUnstructuredZalandoPostgresql(t *testing.T)
pgParamBlockList map[string]bool
rbs *BackupConfig
srcDB *Postgres
runAsNonRoot bool
want string
wantErr bool
}{
Expand Down Expand Up @@ -257,8 +258,9 @@ func TestPostgresRestoreTimestamp_ToUnstructuredZalandoPostgresql(t *testing.T)
Description: "description",
},
},
want: time.Now().Format(time.RFC3339), // I know this is not perfect, let's just hope we always finish within the same second...
wantErr: false,
runAsNonRoot: false,
want: time.Now().Format(time.RFC3339), // I know this is not perfect, let's just hope we always finish within the same second...
wantErr: false,
},
{
name: "undefined timestamp initialized with current time",
Expand All @@ -283,8 +285,9 @@ func TestPostgresRestoreTimestamp_ToUnstructuredZalandoPostgresql(t *testing.T)
Description: "description",
},
},
want: time.Now().Format(time.RFC3339), // I know this is not perfect, let's just hope we always finish within the same second...
wantErr: false,
runAsNonRoot: false,
want: time.Now().Format(time.RFC3339), // I know this is not perfect, let's just hope we always finish within the same second...
wantErr: false,
},
{
name: "given timestamp is passed along",
Expand All @@ -311,8 +314,9 @@ func TestPostgresRestoreTimestamp_ToUnstructuredZalandoPostgresql(t *testing.T)
Description: "description",
},
},
want: "invalid but whatever",
wantErr: false,
runAsNonRoot: false,
want: "invalid but whatever",
wantErr: false,
},
{
name: "fail on purpose",
Expand All @@ -339,8 +343,9 @@ func TestPostgresRestoreTimestamp_ToUnstructuredZalandoPostgresql(t *testing.T)
Description: "description",
},
},
want: "oranges",
wantErr: true,
runAsNonRoot: false,
want: "oranges",
wantErr: true,
},
}
for _, tt := range tests {
Expand All @@ -349,7 +354,7 @@ func TestPostgresRestoreTimestamp_ToUnstructuredZalandoPostgresql(t *testing.T)
p := &Postgres{
Spec: tt.spec,
}
got, _ := p.ToUnstructuredZalandoPostgresql(nil, tt.c, tt.sc, tt.pgParamBlockList, tt.rbs, tt.srcDB, 130, 10, 60)
got, _ := p.ToUnstructuredZalandoPostgresql(nil, tt.c, tt.sc, tt.pgParamBlockList, tt.rbs, tt.srcDB, tt.runAsNonRoot, 130, 10, 60)

jsonZ, err := runtime.DefaultUnstructuredConverter.ToUnstructured(got)
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions controllers/postgres_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type PostgresReconciler struct {
SidecarsConfigMapName string
EnableNetPol bool
EtcdHost string
RunAsNonRoot bool
PatroniTTL uint32
PatroniLoopWait uint32
PatroniRetryTimeout uint32
Expand Down Expand Up @@ -326,7 +327,7 @@ func (r *PostgresReconciler) createOrUpdateZalandoPostgresql(ctx context.Context
return fmt.Errorf("failed to fetch zalando postgresql: %w", err)
}

u, err := instance.ToUnstructuredZalandoPostgresql(nil, sidecarsCM, r.StorageClass, r.PgParamBlockList, restoreBackupConfig, restoreSouceInstance, patroniTTL, patroniLoopWait, patroniRetryTimout)
u, err := instance.ToUnstructuredZalandoPostgresql(nil, sidecarsCM, r.StorageClass, r.PgParamBlockList, restoreBackupConfig, restoreSouceInstance, r.RunAsNonRoot, patroniTTL, patroniLoopWait, patroniRetryTimout)
if err != nil {
return fmt.Errorf("failed to convert to unstructured zalando postgresql: %w", err)
}
Expand All @@ -342,7 +343,7 @@ func (r *PostgresReconciler) createOrUpdateZalandoPostgresql(ctx context.Context
// Update zalando postgresql
mergeFrom := client.MergeFrom(rawZ.DeepCopy())

u, err := instance.ToUnstructuredZalandoPostgresql(rawZ, sidecarsCM, r.StorageClass, r.PgParamBlockList, restoreBackupConfig, restoreSouceInstance, patroniTTL, patroniLoopWait, patroniRetryTimout)
u, err := instance.ToUnstructuredZalandoPostgresql(rawZ, sidecarsCM, r.StorageClass, r.PgParamBlockList, restoreBackupConfig, restoreSouceInstance, r.RunAsNonRoot, patroniTTL, patroniLoopWait, patroniRetryTimout)
if err != nil {
return fmt.Errorf("failed to convert to unstructured zalando postgresql: %w", err)
}
Expand Down
9 changes: 8 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const (
postgresletNamespaceFlg = "postgreslet-namespace"
sidecarsCMNameFlg = "sidecars-configmap-name"
enableNetPolFlg = "enable-netpol"
runAsNonRootFlg = "run-as-non-root"
enablePodAntiaffinityFlg = "enable-pod-antiaffinity"
patroniRetryTimeoutFlg = "patroni-retry-timeout"
enableStandbyLeaderSelectorFlg = "enable-standby-leader-selector"
Expand All @@ -78,7 +79,7 @@ func init() {

func main() {
var metricsAddrCtrlMgr, metricsAddrSvcMgr, partitionID, tenant, ctrlClusterKubeconfig, pspName, lbIP, storageClass, postgresImage, etcdHost, operatorImage, majorVersionUpgradeMode, postgresletNamespace, sidecarsCMName string
var enableLeaderElection, enableCRDValidation, enableNetPol, enablePodAntiaffinity, enableStandbyLeaderSelector bool
var enableLeaderElection, enableCRDValidation, enableNetPol, runAsNonRoot, enablePodAntiaffinity, enableStandbyLeaderSelector bool
var portRangeStart, portRangeSize int
var patroniTTL, patroniLoopWait, patroniRetryTimeout uint32
var pgParamBlockList map[string]bool
Expand Down Expand Up @@ -159,6 +160,9 @@ func main() {
viper.SetDefault(enableNetPolFlg, false)
enableNetPol = viper.GetBool(enableNetPolFlg)

viper.SetDefault(runAsNonRootFlg, false)
runAsNonRoot = viper.GetBool(runAsNonRootFlg)

viper.SetDefault(enablePodAntiaffinityFlg, false)
enablePodAntiaffinity = viper.GetBool(enablePodAntiaffinityFlg)

Expand Down Expand Up @@ -199,6 +203,7 @@ func main() {
postgresletNamespaceFlg, postgresletNamespace,
sidecarsCMNameFlg, sidecarsCMName,
enableNetPolFlg, enableNetPol,
runAsNonRootFlg, runAsNonRoot,
enablePodAntiaffinityFlg, enablePodAntiaffinity,
patroniRetryTimeoutFlg, patroniRetryTimeout,
enableStandbyLeaderSelectorFlg, enableStandbyLeaderSelector,
Expand Down Expand Up @@ -243,6 +248,7 @@ func main() {
MajorVersionUpgradeMode: majorVersionUpgradeMode,
PostgresletNamespace: postgresletNamespace,
SidecarsConfigMapName: sidecarsCMName,
RunAsNonRoot: runAsNonRoot,
PodAntiaffinity: enablePodAntiaffinity,
}
opMgr, err := operatormanager.New(svcClusterConf, "external/svc-postgres-operator.yaml", scheme, ctrl.Log.WithName("OperatorManager"), opMgrOpts)
Expand Down Expand Up @@ -273,6 +279,7 @@ func main() {
SidecarsConfigMapName: sidecarsCMName,
EnableNetPol: enableNetPol,
EtcdHost: etcdHost,
RunAsNonRoot: runAsNonRoot,
PatroniTTL: patroniTTL,
PatroniLoopWait: patroniLoopWait,
PatroniRetryTimeout: patroniRetryTimeout,
Expand Down
30 changes: 27 additions & 3 deletions pkg/operatormanager/operatormanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand All @@ -47,6 +48,9 @@ const (
SidecarsCMExporterQueriesKey string = "queries.yaml"

localSidecarsCMName = "postgres-sidecars-configmap"

// operatorRunAsUser the uid to use when running the operator image
operatorRunAsUser int64 = 1000
)

// operatorPodMatchingLabels is for listing operator pods
Expand All @@ -62,6 +66,7 @@ type Options struct {
MajorVersionUpgradeMode string
PostgresletNamespace string
SidecarsConfigMapName string
RunAsNonRoot bool
PodAntiaffinity bool
}

Expand Down Expand Up @@ -356,9 +361,23 @@ func (m *OperatorManager) createNewClientObject(ctx context.Context, obj client.
m.log.Info("handling Deployment")
if len(v.Spec.Template.Spec.Containers) != 1 {
m.log.Info("Unexpected number of containers in deployment, ignoring.")
} else if m.options.OperatorImage != "" {
m.log.Info("Patching operator image", "image", m.options.OperatorImage)
v.Spec.Template.Spec.Containers[0].Image = m.options.OperatorImage
} else {
if m.options.OperatorImage != "" {
m.log.Info("Patching operator image", "image", m.options.OperatorImage)
v.Spec.Template.Spec.Containers[0].Image = m.options.OperatorImage
}

if m.options.RunAsNonRoot {
v.Spec.Template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
RunAsUser: pointer.Int64(operatorRunAsUser),
RunAsNonRoot: pointer.Bool(true),
ReadOnlyRootFilesystem: pointer.Bool(true),
AllowPrivilegeEscalation: pointer.Bool(false),
}
} else {
// Unset
v.Spec.Template.Spec.Containers[0].SecurityContext = nil
}
}
err = m.Get(ctx, key, &appsv1.Deployment{})
default:
Expand Down Expand Up @@ -414,6 +433,11 @@ func (m *OperatorManager) editConfigMap(cm *corev1.ConfigMap, namespace string,
cm.Data["super_username"] = "postgres"
cm.Data["replication_username"] = "standby"

// From the docs (https://postgres-operator.readthedocs.io/en/latest/reference/operator_parameters/):
// Required by cron which needs setuid. Without this parameter, certification rotation & backups will not be done
cm.Data["spilo_allow_privilege_escalation"] = "true"
cm.Data["spilo_privileged"] = "false"

cm.Data["enable_pod_antiaffinity"] = strconv.FormatBool(options.PodAntiaffinity)
}

Expand Down