Skip to content

Commit 231aa71

Browse files
committed
(psa) make workloads compatible with psa:restricted profile
With the introduction of [Pod Security Admission](https://kubernetes.io/docs/concepts/security/pod-security-admission/#pod-security-admission-labels-for-namespaces), the reccomeneded best practice is to enforce the Restricted policy of admission (see [1] for more details). This PR *) Lables the olm namespace as `enforce:restricted` *) updates the securityContext of olm workload pods(olm-operator, catalog-operator, and CatalogSource registry pods) to adhere to the `Restricted` policy. *) updates the bundle unpacking job to create a pod that adheres to the `Restricted` policy, so that bundles can be unpacked in the `Restricted` namespace. [1] https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted Signed-off-by: Anik Bhattacharjee <[email protected]>
1 parent 0fa6f29 commit 231aa71

26 files changed

+397
-138
lines changed

cmd/catalog/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ func (o *options) run(ctx context.Context, logger *logrus.Logger) error {
9898
k8sscheme.Scheme,
9999
o.installPlanTimeout,
100100
o.bundleUnpackTimeout,
101+
o.workloadUserID,
101102
)
102103
if err != nil {
103104
return fmt.Errorf("error configuring catalog operator: %s", err.Error())

cmd/catalog/start.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type options struct {
2525
tlsKeyPath string
2626
tlsCertPath string
2727
clientCAPath string
28+
workloadUserID int64
2829

2930
installPlanTimeout time.Duration
3031
bundleUnpackTimeout time.Duration
@@ -66,6 +67,7 @@ func newRootCmd() *cobra.Command {
6667
cmd.Flags().StringVar(&o.opmImage, "opmImage", defaultOPMImage, "the image to use for unpacking bundle content with opm")
6768
cmd.Flags().StringVar(&o.utilImage, "util-image", defaultUtilImage, "an image containing custom olm utilities")
6869
cmd.Flags().StringVar(&o.writeStatusName, "writeStatusName", defaultOperatorName, "ClusterOperator name in which to write status, set to \"\" to disable.")
70+
cmd.Flags().Int64Var(&o.workloadUserID, "workload-user-id", -1, "The non-root user ID that registry pods will run as.")
6971

7072
cmd.Flags().BoolVar(&o.debug, "debug", false, "use debug log level")
7173
cmd.Flags().BoolVar(&o.version, "version", false, "displays the olm version")

deploy/chart/templates/0000_50_olm_00-namespace.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,19 @@ apiVersion: v1
22
kind: Namespace
33
metadata:
44
name: {{ .Values.namespace }}
5+
labels:
6+
pod-security.kubernetes.io/enforce: restricted
7+
pod-security.kubernetes.io/enforce-version: latest
8+
pod-security.kubernetes.io/warn: restricted
9+
pod-security.kubernetes.io/warn-version: latest
510

611
---
712
apiVersion: v1
813
kind: Namespace
914
metadata:
1015
name: {{ .Values.operator_namespace }}
16+
labels:
17+
pod-security.kubernetes.io/enforce: baseline
18+
pod-security.kubernetes.io/enforce-version: latest
19+
pod-security.kubernetes.io/warn: baseline
20+
pod-security.kubernetes.io/warn-version: latest

deploy/chart/templates/0000_50_olm_07-olm-operator.deployment.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ spec:
1717
labels:
1818
app: olm-operator
1919
spec:
20+
securityContext:
21+
runAsNonRoot: true
22+
seccompProfile:
23+
type: RuntimeDefault
24+
{{- if eq .Values.installType "upstream" }}
25+
runAsUser: {{ .Values.package.securityContext.runAsUser }}
26+
{{- end }}
2027
serviceAccountName: olm-operator-serviceaccount
2128
{{- if or .Values.olm.tlsSecret .Values.olm.clientCASecret }}
2229
volumes:
@@ -33,6 +40,10 @@ spec:
3340
{{- end }}
3441
containers:
3542
- name: olm-operator
43+
securityContext:
44+
allowPrivilegeEscalation: false
45+
capabilities:
46+
drop: [ "ALL" ]
3647
{{- if or .Values.olm.tlsSecret .Values.olm.clientCASecret }}
3748
volumeMounts:
3849
{{- end }}

deploy/chart/templates/0000_50_olm_08-catalog-operator.deployment.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ spec:
1717
labels:
1818
app: catalog-operator
1919
spec:
20+
securityContext:
21+
runAsNonRoot: true
22+
seccompProfile:
23+
type: RuntimeDefault
24+
{{- if eq .Values.installType "upstream" }}
25+
runAsUser: {{ .Values.package.securityContext.runAsUser }}
26+
{{- end }}
2027
serviceAccountName: olm-operator-serviceaccount
2128
{{- if or .Values.catalog.tlsSecret .Values.catalog.clientCASecret }}
2229
volumes:
@@ -33,6 +40,10 @@ spec:
3340
{{- end }}
3441
containers:
3542
- name: catalog-operator
43+
securityContext:
44+
allowPrivilegeEscalation: false
45+
capabilities:
46+
drop: [ "ALL" ]
3647
{{- if or .Values.catalog.tlsSecret .Values.catalog.clientCASecret }}
3748
volumeMounts:
3849
{{- end }}
@@ -76,6 +87,8 @@ spec:
7687
- --client-ca
7788
- /profile-collector-cert/tls.crt
7889
{{- end }}
90+
- --workload-user-id
91+
- "{{ .Values.package.securityContext.runAsUser }}"
7992
image: {{ .Values.catalog.image.ref }}
8093
imagePullPolicy: {{ .Values.catalog.image.pullPolicy }}
8194
ports:

deploy/chart/templates/_packageserver.deployment-spec.yaml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ spec:
1414
labels:
1515
app: packageserver
1616
spec:
17+
securityContext:
18+
runAsNonRoot: true
19+
seccompProfile:
20+
type: RuntimeDefault
21+
{{- if eq .Values.installType "upstream" }}
22+
runAsUser: {{ .Values.package.securityContext.runAsUser }}
23+
{{- end }}
1724
serviceAccountName: olm-operator-serviceaccount
1825
{{- if .Values.package.nodeSelector }}
1926
nodeSelector:
@@ -25,6 +32,10 @@ spec:
2532
{{- end }}
2633
containers:
2734
- name: packageserver
35+
securityContext:
36+
allowPrivilegeEscalation: false
37+
capabilities:
38+
drop: [ "ALL" ]
2839
command:
2940
- /bin/package-server
3041
- -v=4
@@ -61,10 +72,6 @@ spec:
6172
resources:
6273
{{ toYaml .Values.package.resources | indent 10 }}
6374
{{- end }}
64-
{{- if .Values.package.securityContext }}
65-
securityContext:
66-
runAsUser: {{ .Values.package.securityContext.runAsUser }}
67-
{{- end }}
6875
volumeMounts:
6976
- name: tmpfs
7077
mountPath: /tmp

deploy/chart/values.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ package:
5353
internalPort: 5443
5454
nodeSelector:
5555
kubernetes.io/os: linux
56+
securityContext:
57+
runAsUser: 1001
5658
resources:
5759
requests:
5860
cpu: 10m

pkg/controller/bundle/bundle_unpacker.go

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
listersbatchv1 "k8s.io/client-go/listers/batch/v1"
2323
listerscorev1 "k8s.io/client-go/listers/core/v1"
2424
listersrbacv1 "k8s.io/client-go/listers/rbac/v1"
25+
"k8s.io/utils/pointer"
2526

2627
"github.com/operator-framework/api/pkg/operators/reference"
2728
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
@@ -85,7 +86,7 @@ func newBundleUnpackResult(lookup *operatorsv1alpha1.BundleLookup) *BundleUnpack
8586
}
8687
}
8788

88-
func (c *ConfigMapUnpacker) job(cmRef *corev1.ObjectReference, bundlePath string, secrets []corev1.LocalObjectReference, annotationUnpackTimeout time.Duration) *batchv1.Job {
89+
func (c *ConfigMapUnpacker) job(cmRef *corev1.ObjectReference, bundlePath string, secrets []corev1.LocalObjectReference, annotationUnpackTimeout time.Duration, runAsUser int64) *batchv1.Job {
8990
job := &batchv1.Job{
9091
Spec: batchv1.JobSpec{
9192
//ttlSecondsAfterFinished: 0 // can use in the future to not have to clean up job
@@ -101,6 +102,12 @@ func (c *ConfigMapUnpacker) job(cmRef *corev1.ObjectReference, bundlePath string
101102
// See: https://kubernetes.io/docs/concepts/workloads/controllers/job/#pod-backoff-failure-policy
102103
RestartPolicy: corev1.RestartPolicyNever,
103104
ImagePullSecrets: secrets,
105+
SecurityContext: &corev1.PodSecurityContext{
106+
RunAsNonRoot: pointer.Bool(true),
107+
SeccompProfile: &corev1.SeccompProfile{
108+
Type: corev1.SeccompProfileTypeRuntimeDefault,
109+
},
110+
},
104111
Containers: []corev1.Container{
105112
{
106113
Name: "extract",
@@ -129,6 +136,12 @@ func (c *ConfigMapUnpacker) job(cmRef *corev1.ObjectReference, bundlePath string
129136
corev1.ResourceMemory: resource.MustParse("50Mi"),
130137
},
131138
},
139+
SecurityContext: &corev1.SecurityContext{
140+
AllowPrivilegeEscalation: pointer.Bool(false),
141+
Capabilities: &corev1.Capabilities{
142+
Drop: []corev1.Capability{"ALL"},
143+
},
144+
},
132145
},
133146
},
134147
InitContainers: []corev1.Container{
@@ -148,6 +161,12 @@ func (c *ConfigMapUnpacker) job(cmRef *corev1.ObjectReference, bundlePath string
148161
corev1.ResourceMemory: resource.MustParse("50Mi"),
149162
},
150163
},
164+
SecurityContext: &corev1.SecurityContext{
165+
AllowPrivilegeEscalation: pointer.Bool(false),
166+
Capabilities: &corev1.Capabilities{
167+
Drop: []corev1.Capability{"ALL"},
168+
},
169+
},
151170
},
152171
{
153172
Name: "pull",
@@ -170,6 +189,12 @@ func (c *ConfigMapUnpacker) job(cmRef *corev1.ObjectReference, bundlePath string
170189
corev1.ResourceMemory: resource.MustParse("50Mi"),
171190
},
172191
},
192+
SecurityContext: &corev1.SecurityContext{
193+
AllowPrivilegeEscalation: pointer.Bool(false),
194+
Capabilities: &corev1.Capabilities{
195+
Drop: []corev1.Capability{"ALL"},
196+
},
197+
},
173198
},
174199
},
175200
Volumes: []corev1.Volume{
@@ -193,7 +218,9 @@ func (c *ConfigMapUnpacker) job(cmRef *corev1.ObjectReference, bundlePath string
193218
job.SetNamespace(cmRef.Namespace)
194219
job.SetName(cmRef.Name)
195220
job.SetOwnerReferences([]metav1.OwnerReference{ownerRef(cmRef)})
196-
221+
if runAsUser > 0 {
222+
job.Spec.Template.Spec.SecurityContext.RunAsUser = &runAsUser
223+
}
197224
// By default the BackoffLimit is set to 6 which with exponential backoff 10s + 20s + 40s ...
198225
// translates to ~10m of waiting time.
199226
// We want to fail faster than that when we have repeated failures from the bundle unpack pod
@@ -229,7 +256,7 @@ func (c *ConfigMapUnpacker) job(cmRef *corev1.ObjectReference, bundlePath string
229256
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . Unpacker
230257

231258
type Unpacker interface {
232-
UnpackBundle(lookup *operatorsv1alpha1.BundleLookup, timeout time.Duration) (result *BundleUnpackResult, err error)
259+
UnpackBundle(lookup *operatorsv1alpha1.BundleLookup, timeout time.Duration, runAsUser int64) (result *BundleUnpackResult, err error)
233260
}
234261

235262
type ConfigMapUnpacker struct {
@@ -383,7 +410,7 @@ const (
383410
NotUnpackedMessage = "bundle contents have not yet been persisted to installplan status"
384411
)
385412

386-
func (c *ConfigMapUnpacker) UnpackBundle(lookup *operatorsv1alpha1.BundleLookup, timeout time.Duration) (result *BundleUnpackResult, err error) {
413+
func (c *ConfigMapUnpacker) UnpackBundle(lookup *operatorsv1alpha1.BundleLookup, timeout time.Duration, runAsUser int64) (result *BundleUnpackResult, err error) {
387414
result = newBundleUnpackResult(lookup)
388415

389416
// if bundle lookup failed condition already present, then there is nothing more to do
@@ -445,7 +472,7 @@ func (c *ConfigMapUnpacker) UnpackBundle(lookup *operatorsv1alpha1.BundleLookup,
445472
secrets = append(secrets, corev1.LocalObjectReference{Name: secretName})
446473
}
447474
var job *batchv1.Job
448-
job, err = c.ensureJob(cmRef, result.Path, secrets, timeout)
475+
job, err = c.ensureJob(cmRef, result.Path, secrets, timeout, runAsUser)
449476
if err != nil || job == nil {
450477
// ensureJob can return nil if the job present does not match the expected job (spec and ownerefs)
451478
// The current job is deleted in that case so UnpackBundle needs to be retried
@@ -584,8 +611,8 @@ func (c *ConfigMapUnpacker) ensureConfigmap(csRef *corev1.ObjectReference, name
584611
return
585612
}
586613

587-
func (c *ConfigMapUnpacker) ensureJob(cmRef *corev1.ObjectReference, bundlePath string, secrets []corev1.LocalObjectReference, timeout time.Duration) (job *batchv1.Job, err error) {
588-
fresh := c.job(cmRef, bundlePath, secrets, timeout)
614+
func (c *ConfigMapUnpacker) ensureJob(cmRef *corev1.ObjectReference, bundlePath string, secrets []corev1.LocalObjectReference, timeout time.Duration, runAsUser int64) (job *batchv1.Job, err error) {
615+
fresh := c.job(cmRef, bundlePath, secrets, timeout, runAsUser)
589616
job, err = c.jobLister.Jobs(fresh.GetNamespace()).Get(fresh.GetName())
590617
if err != nil {
591618
if apierrors.IsNotFound(err) {

0 commit comments

Comments
 (0)