Skip to content

Commit e4a9a66

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
1 parent 6c4e779 commit e4a9a66

23 files changed

+193
-112
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", 0, "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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ apiVersion: v1
22
kind: Namespace
33
metadata:
44
name: {{ .Values.namespace }}
5+
labels:
6+
pod-security.kubernetes.io/enforce: restricted
57

68
---
79
apiVersion: v1
810
kind: Namespace
911
metadata:
1012
name: {{ .Values.operator_namespace }}
13+
labels:
14+
pod-security.kubernetes.io/enforce: restricted

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+
- "1001"
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/operators/catalog/operator.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,13 @@ type Operator struct {
120120
clientFactory clients.Factory
121121
muInstallPlan sync.Mutex
122122
sourceInvalidator *resolver.RegistrySourceProvider
123+
workloadUserID int64
123124
}
124125

125126
type CatalogSourceSyncFunc func(logger *logrus.Entry, in *v1alpha1.CatalogSource) (out *v1alpha1.CatalogSource, continueSync bool, syncError error)
126127

127128
// NewOperator creates a new Catalog Operator.
128-
func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clock, logger *logrus.Logger, resync time.Duration, configmapRegistryImage, opmImage, utilImage string, operatorNamespace string, scheme *runtime.Scheme, installPlanTimeout time.Duration, bundleUnpackTimeout time.Duration) (*Operator, error) {
129+
func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clock, logger *logrus.Logger, resync time.Duration, configmapRegistryImage, opmImage, utilImage string, operatorNamespace string, scheme *runtime.Scheme, installPlanTimeout time.Duration, bundleUnpackTimeout time.Duration, workloadUserID int64) (*Operator, error) {
129130
resyncPeriod := queueinformer.ResyncWithJitter(resync, 0.2)
130131
config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath)
131132
if err != nil {
@@ -179,6 +180,7 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo
179180
client: crClient,
180181
lister: lister,
181182
namespace: operatorNamespace,
183+
workloadUserID: workloadUserID,
182184
recorder: eventRecorder,
183185
catsrcQueueSet: queueinformer.NewEmptyResourceQueueSet(),
184186
subQueueSet: queueinformer.NewEmptyResourceQueueSet(),
@@ -320,6 +322,7 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo
320322
subscription.WithAppendedReconcilers(subscription.ReconcilerFromLegacySyncHandler(op.syncSubscriptions, nil)),
321323
subscription.WithRegistryReconcilerFactory(op.reconciler),
322324
subscription.WithGlobalCatalogNamespace(op.namespace),
325+
subscription.WithWorkloadUserID(workloadUserID),
323326
)
324327
if err != nil {
325328
return nil, err
@@ -704,15 +707,15 @@ func (o *Operator) syncRegistryServer(logger *logrus.Entry, in *v1alpha1.Catalog
704707
out = in.DeepCopy()
705708

706709
sourceKey := registry.CatalogKey{Name: in.GetName(), Namespace: in.GetNamespace()}
707-
srcReconciler := o.reconciler.ReconcilerForSource(in)
710+
srcReconciler := o.reconciler.ReconcilerForSource(in, o.workloadUserID)
708711
if srcReconciler == nil {
709712
// TODO: Add failure status on catalogsource and remove from sources
710713
syncError = fmt.Errorf("no reconciler for source type %s", in.Spec.SourceType)
711714
out.SetError(v1alpha1.CatalogSourceRegistryServerError, syncError)
712715
return
713716
}
714717

715-
healthy, err := srcReconciler.CheckRegistryServer(in)
718+
healthy, err := srcReconciler.CheckRegistryServer(in, o.workloadUserID)
716719
if err != nil {
717720
syncError = err
718721
out.SetError(v1alpha1.CatalogSourceRegistryServerError, syncError)
@@ -733,7 +736,7 @@ func (o *Operator) syncRegistryServer(logger *logrus.Entry, in *v1alpha1.Catalog
733736
// Registry pod hasn't been created or hasn't been updated since the last configmap update, recreate it
734737
logger.Debug("ensuring registry server")
735738

736-
err = srcReconciler.EnsureRegistryServer(out)
739+
err = srcReconciler.EnsureRegistryServer(out, o.workloadUserID)
737740
if err != nil {
738741
if _, ok := err.(reconciler.UpdateNotReadyErr); ok {
739742
logger.Debug("requeueing registry server for catalog update check: update pod not yet ready")

pkg/controller/operators/catalog/operator_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,9 +1245,9 @@ func TestSyncResolvingNamespace(t *testing.T) {
12451245
require.NoError(t, err)
12461246

12471247
o.reconciler = &fakes.FakeRegistryReconcilerFactory{
1248-
ReconcilerForSourceStub: func(source *v1alpha1.CatalogSource) reconciler.RegistryReconciler {
1248+
ReconcilerForSourceStub: func(source *v1alpha1.CatalogSource, runAsUser int64) reconciler.RegistryReconciler {
12491249
return &fakes.FakeRegistryReconciler{
1250-
EnsureRegistryServerStub: func(source *v1alpha1.CatalogSource) error {
1250+
EnsureRegistryServerStub: func(source *v1alpha1.CatalogSource, runAsUser int64) error {
12511251
return nil
12521252
},
12531253
}
@@ -1439,6 +1439,7 @@ type fakeOperatorConfig struct {
14391439
recorder record.EventRecorder
14401440
reconciler reconciler.RegistryReconcilerFactory
14411441
sources []sourceAddress
1442+
// workloadUserID int64
14421443
}
14431444

14441445
// fakeOperatorOption applies an option to the given fake operator configuration.
@@ -1596,6 +1597,7 @@ func NewFakeOperator(ctx context.Context, namespace string, namespaces []string,
15961597
kubernetesClient: clientFake,
15971598
dynamicClient: dynamicClientFake,
15981599
},
1600+
workloadUserID: 1001,
15991601
}
16001602
op.sources = grpc.NewSourceStore(config.logger, 1*time.Second, 5*time.Second, op.syncSourceState)
16011603
if op.reconciler == nil {
@@ -1758,7 +1760,7 @@ func toManifest(t *testing.T, obj runtime.Object) string {
17581760
}
17591761

17601762
func pod(s v1alpha1.CatalogSource) *corev1.Pod {
1761-
pod := reconciler.Pod(&s, "registry-server", s.Spec.Image, s.GetName(), s.GetLabels(), s.GetAnnotations(), 5, 10)
1763+
pod := reconciler.Pod(&s, "registry-server", s.Spec.Image, s.GetName(), s.GetLabels(), s.GetAnnotations(), 5, 10, 1001)
17621764
ownerutil.AddOwner(pod, &s, false, false)
17631765
return pod
17641766
}

pkg/controller/operators/catalog/subscription/config.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type syncerConfig struct {
2626
reconcilers kubestate.ReconcilerChain
2727
registryReconcilerFactory reconciler.RegistryReconcilerFactory
2828
globalCatalogNamespace string
29+
workloadUserID int64
2930
}
3031

3132
// SyncerOption is a configuration option for a subscription syncer.
@@ -128,6 +129,12 @@ func WithGlobalCatalogNamespace(namespace string) SyncerOption {
128129
}
129130
}
130131

132+
func WithWorkloadUserID(userID int64) SyncerOption {
133+
return func(config *syncerConfig) {
134+
config.workloadUserID = userID
135+
}
136+
}
137+
131138
func newInvalidConfigError(msg string) error {
132139
return errors.Errorf("invalid subscription syncer config: %s", msg)
133140
}

pkg/controller/operators/catalog/subscription/reconciler.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type catalogHealthReconciler struct {
5656
catalogLister listers.CatalogSourceLister
5757
registryReconcilerFactory reconciler.RegistryReconcilerFactory
5858
globalCatalogNamespace string
59+
workloadUserID int64
5960
}
6061

6162
// Reconcile reconciles subscription catalog health conditions.
@@ -79,7 +80,7 @@ func (c *catalogHealthReconciler) Reconcile(ctx context.Context, in kubestate.St
7980
// Gather catalog health and transition state
8081
ns := s.Subscription().GetNamespace()
8182
var catalogHealth []v1alpha1.SubscriptionCatalogHealth
82-
if catalogHealth, err = c.catalogHealth(ns); err != nil {
83+
if catalogHealth, err = c.catalogHealth(ns, c.workloadUserID); err != nil {
8384
break
8485
}
8586

@@ -110,7 +111,7 @@ func (c *catalogHealthReconciler) Reconcile(ctx context.Context, in kubestate.St
110111

111112
// catalogHealth gets the health of catalogs that can affect Susbcriptions in the given namespace.
112113
// This means all catalogs in the given namespace, as well as any catalogs in the operator's global catalog namespace.
113-
func (c *catalogHealthReconciler) catalogHealth(namespace string) ([]v1alpha1.SubscriptionCatalogHealth, error) {
114+
func (c *catalogHealthReconciler) catalogHealth(namespace string, userID int64) ([]v1alpha1.SubscriptionCatalogHealth, error) {
114115
catalogs, err := c.catalogLister.CatalogSources(namespace).List(labels.Everything())
115116
if err != nil {
116117
return nil, err
@@ -134,7 +135,7 @@ func (c *catalogHealthReconciler) catalogHealth(namespace string) ([]v1alpha1.Su
134135
now := c.now()
135136
var errs []error
136137
for i, catalog := range catalogs {
137-
h, err := c.health(now, catalog)
138+
h, err := c.health(now, catalog, userID)
138139
if err != nil {
139140
errs = append(errs, err)
140141
continue
@@ -155,8 +156,8 @@ func (c *catalogHealthReconciler) catalogHealth(namespace string) ([]v1alpha1.Su
155156
}
156157

157158
// health returns a SusbcriptionCatalogHealth for the given catalog with the given now.
158-
func (c *catalogHealthReconciler) health(now *metav1.Time, catalog *v1alpha1.CatalogSource) (*v1alpha1.SubscriptionCatalogHealth, error) {
159-
healthy, err := c.healthy(catalog)
159+
func (c *catalogHealthReconciler) health(now *metav1.Time, catalog *v1alpha1.CatalogSource, userID int64) (*v1alpha1.SubscriptionCatalogHealth, error) {
160+
healthy, err := c.healthy(catalog, userID)
160161
if err != nil {
161162
return nil, err
162163
}
@@ -181,19 +182,19 @@ func (c *catalogHealthReconciler) health(now *metav1.Time, catalog *v1alpha1.Cat
181182

182183
// healthy returns true if the given catalog is healthy, false otherwise, and any error encountered
183184
// while checking the catalog's registry server.
184-
func (c *catalogHealthReconciler) healthy(catalog *v1alpha1.CatalogSource) (bool, error) {
185+
func (c *catalogHealthReconciler) healthy(catalog *v1alpha1.CatalogSource, userID int64) (bool, error) {
185186
if catalog.Status.Reason == v1alpha1.CatalogSourceSpecInvalidError {
186187
// The catalog's spec is bad, mark unhealthy
187188
return false, nil
188189
}
189190

190191
// Check connection health
191-
rec := c.registryReconcilerFactory.ReconcilerForSource(catalog)
192+
rec := c.registryReconcilerFactory.ReconcilerForSource(catalog, userID)
192193
if rec == nil {
193194
return false, fmt.Errorf("could not get reconciler for catalog: %#v", catalog)
194195
}
195196

196-
return rec.CheckRegistryServer(catalog)
197+
return rec.CheckRegistryServer(catalog, userID)
197198
}
198199

199200
// installPlanReconciler reconciles InstallPlan status for Subscriptions.

pkg/controller/operators/catalog/subscription/reconciler_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,9 +1674,9 @@ func TestInstallPlanReconcile(t *testing.T) {
16741674

16751675
func fakeRegistryReconcilerFactory(healthy bool, err error) *olmfakes.FakeRegistryReconcilerFactory {
16761676
return &olmfakes.FakeRegistryReconcilerFactory{
1677-
ReconcilerForSourceStub: func(*v1alpha1.CatalogSource) registryreconciler.RegistryReconciler {
1677+
ReconcilerForSourceStub: func(*v1alpha1.CatalogSource, int64) registryreconciler.RegistryReconciler {
16781678
return &olmfakes.FakeRegistryReconciler{
1679-
CheckRegistryServerStub: func(*v1alpha1.CatalogSource) (bool, error) {
1679+
CheckRegistryServerStub: func(*v1alpha1.CatalogSource, int64) (bool, error) {
16801680
return healthy, err
16811681
},
16821682
}

pkg/controller/operators/catalog/subscription/syncer.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ func newSyncerWithConfig(ctx context.Context, config *syncerConfig) (kubestate.S
234234
catalogLister: config.lister.OperatorsV1alpha1().CatalogSourceLister(),
235235
registryReconcilerFactory: config.registryReconcilerFactory,
236236
globalCatalogNamespace: config.globalCatalogNamespace,
237+
workloadUserID: config.workloadUserID,
237238
},
238239
}
239240
s.reconcilers = append(defaultReconcilers, s.reconcilers...)

pkg/controller/operators/catalog/subscriptions_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,9 +1020,9 @@ func TestSyncSubscriptions(t *testing.T) {
10201020
require.NoError(t, err)
10211021

10221022
o.reconciler = &fakes.FakeRegistryReconcilerFactory{
1023-
ReconcilerForSourceStub: func(source *v1alpha1.CatalogSource) reconciler.RegistryReconciler {
1023+
ReconcilerForSourceStub: func(source *v1alpha1.CatalogSource, runAsUser int64) reconciler.RegistryReconciler {
10241024
return &fakes.FakeRegistryReconciler{
1025-
EnsureRegistryServerStub: func(source *v1alpha1.CatalogSource) error {
1025+
EnsureRegistryServerStub: func(source *v1alpha1.CatalogSource, runAsUser int64) error {
10261026
return nil
10271027
},
10281028
}
@@ -1159,9 +1159,9 @@ func BenchmarkSyncResolvingNamespace(b *testing.B) {
11591159
},
11601160
},
11611161
reconciler: &fakes.FakeRegistryReconcilerFactory{
1162-
ReconcilerForSourceStub: func(*v1alpha1.CatalogSource) reconciler.RegistryReconciler {
1162+
ReconcilerForSourceStub: func(*v1alpha1.CatalogSource, int64) reconciler.RegistryReconciler {
11631163
return &fakes.FakeRegistryReconciler{
1164-
CheckRegistryServerStub: func(*v1alpha1.CatalogSource) (bool, error) {
1164+
CheckRegistryServerStub: func(*v1alpha1.CatalogSource, int64) (bool, error) {
11651165
return true, nil
11661166
},
11671167
}

0 commit comments

Comments
 (0)