Skip to content

Commit f19d68a

Browse files
committed
Add checking of the namespace PSA restrictions
Signed-off-by: btofel <[email protected]>
1 parent f7d09c6 commit f19d68a

File tree

7 files changed

+201
-120
lines changed

7 files changed

+201
-120
lines changed

pkg/controller/operators/catalog/operator_test.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ import (
5959
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/solver"
6060
"github.com/operator-framework/operator-lifecycle-manager/pkg/fakes"
6161
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/clientfake"
62-
controllerclient "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/controller-runtime/client"
6362
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
6463
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister"
6564
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
@@ -2006,14 +2005,15 @@ func NewFakeOperator(ctx context.Context, namespace string, namespaces []string,
20062005
}
20072006
op.sources = grpc.NewSourceStore(config.logger, 1*time.Second, 5*time.Second, op.syncSourceState)
20082007
if op.reconciler == nil {
2009-
s := runtime.NewScheme()
2010-
err := k8sfake.AddToScheme(s)
2011-
if err != nil {
2012-
return nil, err
2008+
op.reconciler = &fakes.FakeRegistryReconcilerFactory{
2009+
ReconcilerForSourceStub: func(source *v1alpha1.CatalogSource) reconciler.RegistryReconciler {
2010+
return &fakes.FakeRegistryReconciler{
2011+
EnsureRegistryServerStub: func(logger *logrus.Entry, source *v1alpha1.CatalogSource) error {
2012+
return nil
2013+
},
2014+
}
2015+
},
20132016
}
2014-
applier := controllerclient.NewFakeApplier(s, "testowner")
2015-
2016-
op.reconciler = reconciler.NewRegistryReconcilerFactory(lister, op.opClient, "test:pod", op.now, applier, 1001, "", "")
20172017
}
20182018

20192019
op.RunInformers(ctx)
@@ -2028,7 +2028,6 @@ func NewFakeOperator(ctx context.Context, namespace string, namespaces []string,
20282028

20292029
return op, nil
20302030
}
2031-
20322031
func installPlan(name, namespace string, phase v1alpha1.InstallPlanPhase, names ...string) *v1alpha1.InstallPlan {
20332032
return &v1alpha1.InstallPlan{
20342033
ObjectMeta: metav1.ObjectMeta{
@@ -2175,7 +2174,7 @@ func pod(t *testing.T, s v1alpha1.CatalogSource) *corev1.Pod {
21752174
Name: s.GetName(),
21762175
},
21772176
}
2178-
pod, err := reconciler.Pod(&s, "registry-server", "central-opm", "central-util", s.Spec.Image, serviceAccount, s.GetLabels(), s.GetAnnotations(), 5, 10, 1001)
2177+
pod, err := reconciler.Pod(&s, "registry-server", "central-opm", "central-util", s.Spec.Image, serviceAccount, s.GetLabels(), s.GetAnnotations(), 5, 10, 1001, v1alpha1.Legacy)
21792178
if err != nil {
21802179
t.Fatal(err)
21812180
}

pkg/controller/registry/reconciler/configmap.go

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"fmt"
77

8+
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
89
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
910
hashutil "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/hash"
1011
"github.com/pkg/errors"
@@ -25,7 +26,8 @@ import (
2526
// configMapCatalogSourceDecorator wraps CatalogSource to add additional methods
2627
type configMapCatalogSourceDecorator struct {
2728
*v1alpha1.CatalogSource
28-
runAsUser int64
29+
Reconciler *ConfigMapRegistryReconciler
30+
runAsUser int64
2931
}
3032

3133
const (
@@ -109,8 +111,32 @@ func (s *configMapCatalogSourceDecorator) Service() (*corev1.Service, error) {
109111
return svc, nil
110112
}
111113

114+
func (s *configMapCatalogSourceDecorator) getNamespaceSecurityContextConfig() (operatorsv1alpha1.SecurityConfig, error) {
115+
namespace := s.GetNamespace()
116+
if config, ok := s.Reconciler.namespacePSAConfigCache[namespace]; ok {
117+
return config, nil
118+
}
119+
// Retrieve the client from the reconciler
120+
client := s.Reconciler.OpClient
121+
122+
ns, err := client.KubernetesInterface().CoreV1().Namespaces().Get(context.TODO(), s.GetNamespace(), metav1.GetOptions{})
123+
if err != nil {
124+
return "", fmt.Errorf("error fetching namespace: %v", err)
125+
}
126+
// 'pod-security.kubernetes.io/enforce' is the label used for enforcing namespace level security,
127+
// and 'restricted' is the value indicating a restricted security policy.
128+
if val, exists := ns.Labels["pod-security.kubernetes.io/enforce"]; exists && val == "restricted" {
129+
return operatorsv1alpha1.Restricted, nil
130+
}
131+
132+
return operatorsv1alpha1.Legacy, nil
133+
}
112134
func (s *configMapCatalogSourceDecorator) Pod(image string) (*corev1.Pod, error) {
113-
pod, err := Pod(s.CatalogSource, "configmap-registry-server", "", "", image, nil, s.Labels(), s.Annotations(), 5, 5, s.runAsUser)
135+
securityContextConfig, err := s.getNamespaceSecurityContextConfig()
136+
if err != nil {
137+
return nil, err
138+
}
139+
pod, err := Pod(s.CatalogSource, "configmap-registry-server", "", "", image, nil, s.Labels(), s.Annotations(), 5, 5, s.runAsUser, securityContextConfig)
114140
if err != nil {
115141
return nil, err
116142
}
@@ -183,11 +209,12 @@ func (s *configMapCatalogSourceDecorator) RoleBinding() *rbacv1.RoleBinding {
183209
}
184210

185211
type ConfigMapRegistryReconciler struct {
186-
now nowFunc
187-
Lister operatorlister.OperatorLister
188-
OpClient operatorclient.ClientInterface
189-
Image string
190-
createPodAsUser int64
212+
now nowFunc
213+
Lister operatorlister.OperatorLister
214+
OpClient operatorclient.ClientInterface
215+
Image string
216+
createPodAsUser int64
217+
namespacePSAConfigCache map[string]operatorsv1alpha1.SecurityConfig
191218
}
192219

193220
var _ RegistryEnsurer = &ConfigMapRegistryReconciler{}
@@ -274,7 +301,10 @@ func (c *ConfigMapRegistryReconciler) currentPodsWithCorrectResourceVersion(sour
274301

275302
// EnsureRegistryServer ensures that all components of registry server are up to date.
276303
func (c *ConfigMapRegistryReconciler) EnsureRegistryServer(logger *logrus.Entry, catalogSource *v1alpha1.CatalogSource) error {
277-
source := configMapCatalogSourceDecorator{catalogSource, c.createPodAsUser}
304+
if c.namespacePSAConfigCache == nil {
305+
c.namespacePSAConfigCache = make(map[string]operatorsv1alpha1.SecurityConfig)
306+
}
307+
source := configMapCatalogSourceDecorator{catalogSource, c, c.createPodAsUser}
278308

279309
image := c.Image
280310
if source.Spec.SourceType == "grpc" {
@@ -449,7 +479,7 @@ func (c *ConfigMapRegistryReconciler) ensureService(source configMapCatalogSourc
449479

450480
// CheckRegistryServer returns true if the given CatalogSource is considered healthy; false otherwise.
451481
func (c *ConfigMapRegistryReconciler) CheckRegistryServer(logger *logrus.Entry, catalogSource *v1alpha1.CatalogSource) (healthy bool, err error) {
452-
source := configMapCatalogSourceDecorator{catalogSource, c.createPodAsUser}
482+
source := configMapCatalogSourceDecorator{catalogSource, c, c.createPodAsUser}
453483

454484
image := c.Image
455485
if source.Spec.SourceType == "grpc" {

pkg/controller/registry/reconciler/configmap_test.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -185,29 +185,28 @@ func validConfigMapCatalogSource(configMap *corev1.ConfigMap) *v1alpha1.CatalogS
185185
}
186186
}
187187

188-
func objectsForCatalogSource(t *testing.T, catsrc *v1alpha1.CatalogSource) []runtime.Object {
188+
func objectsForCatalogSource(t *testing.T, catsrc *v1alpha1.CatalogSource, reconciler RegistryReconciler) []runtime.Object {
189189
var objs []runtime.Object
190190
switch catsrc.Spec.SourceType {
191191
case v1alpha1.SourceTypeInternal, v1alpha1.SourceTypeConfigmap:
192-
decorated := configMapCatalogSourceDecorator{catsrc, runAsUser}
192+
decorated := configMapCatalogSourceDecorator{catsrc, reconciler.(*ConfigMapRegistryReconciler), runAsUser}
193193
service, err := decorated.Service()
194194
if err != nil {
195195
t.Fatal(err)
196196
}
197-
pod, err := decorated.Pod(registryImageName)
197+
serviceAccount := decorated.ServiceAccount()
198+
pod, err := decorated.Pod(catsrc.Spec.Image)
198199
if err != nil {
199200
t.Fatal(err)
200201
}
201-
objs = clientfake.AddSimpleGeneratedNames(
202-
clientfake.AddSimpleGeneratedName(pod),
202+
objs = append(objs,
203+
pod,
203204
service,
204-
decorated.ServiceAccount(),
205-
decorated.Role(),
206-
decorated.RoleBinding(),
205+
serviceAccount,
207206
)
208207
case v1alpha1.SourceTypeGrpc:
209208
if catsrc.Spec.Image != "" {
210-
decorated := grpcCatalogSourceDecorator{CatalogSource: catsrc, createPodAsUser: runAsUser, opmImage: ""}
209+
decorated := grpcCatalogSourceDecorator{CatalogSource: catsrc, Reconciler: reconciler.(*GrpcRegistryReconciler), createPodAsUser: runAsUser, opmImage: ""}
211210
serviceAccount := decorated.ServiceAccount()
212211
service, err := decorated.Service()
213212
if err != nil {
@@ -217,7 +216,7 @@ func objectsForCatalogSource(t *testing.T, catsrc *v1alpha1.CatalogSource) []run
217216
if err != nil {
218217
t.Fatal(err)
219218
}
220-
objs = clientfake.AddSimpleGeneratedNames(
219+
objs = append(objs,
221220
pod,
222221
service,
223222
serviceAccount,
@@ -238,6 +237,7 @@ func objectsForCatalogSource(t *testing.T, catsrc *v1alpha1.CatalogSource) []run
238237
Controller: &isController,
239238
}})
240239
}
240+
241241
return objs
242242
}
243243

@@ -334,7 +334,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
334334
testName: "ExistingRegistry/BadServiceAccount",
335335
in: in{
336336
cluster: cluster{
337-
k8sObjs: append(modifyObjName(objectsForCatalogSource(t, validCatalogSource), &corev1.ServiceAccount{}, "badName"), validConfigMap),
337+
k8sObjs: append(modifyObjName(objectsForCatalogSource(t, validCatalogSource, nil), &corev1.ServiceAccount{}, "badName"), validConfigMap),
338338
},
339339
catsrc: validCatalogSource,
340340
},
@@ -352,7 +352,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
352352
testName: "ExistingRegistry/BadService",
353353
in: in{
354354
cluster: cluster{
355-
k8sObjs: append(modifyObjName(objectsForCatalogSource(t, validCatalogSource), &corev1.Service{}, "badName"), validConfigMap),
355+
k8sObjs: append(modifyObjName(objectsForCatalogSource(t, validCatalogSource, nil), &corev1.Service{}, "badName"), validConfigMap),
356356
},
357357
catsrc: validCatalogSource,
358358
},
@@ -370,7 +370,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
370370
testName: "ExistingRegistry/BadServiceWithWrongHash",
371371
in: in{
372372
cluster: cluster{
373-
k8sObjs: append(setLabel(objectsForCatalogSource(t, validCatalogSource), &corev1.Service{}, ServiceHashLabelKey, "wrongHash"), validConfigMap),
373+
k8sObjs: append(setLabel(objectsForCatalogSource(t, validCatalogSource, nil), &corev1.Service{}, ServiceHashLabelKey, "wrongHash"), validConfigMap),
374374
},
375375
catsrc: validCatalogSource,
376376
},
@@ -388,7 +388,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
388388
testName: "ExistingRegistry/BadPod",
389389
in: in{
390390
cluster: cluster{
391-
k8sObjs: append(setLabel(objectsForCatalogSource(t, validCatalogSource), &corev1.Pod{}, CatalogSourceLabelKey, "badValue"), validConfigMap),
391+
k8sObjs: append(setLabel(objectsForCatalogSource(t, validCatalogSource, nil), &corev1.Pod{}, CatalogSourceLabelKey, "badValue"), validConfigMap),
392392
},
393393
catsrc: validCatalogSource,
394394
},
@@ -406,7 +406,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
406406
testName: "ExistingRegistry/BadRole",
407407
in: in{
408408
cluster: cluster{
409-
k8sObjs: append(modifyObjName(objectsForCatalogSource(t, validCatalogSource), &rbacv1.Role{}, "badName"), validConfigMap),
409+
k8sObjs: append(modifyObjName(objectsForCatalogSource(t, validCatalogSource, nil), &rbacv1.Role{}, "badName"), validConfigMap),
410410
},
411411
catsrc: validCatalogSource,
412412
},
@@ -424,7 +424,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
424424
testName: "ExistingRegistry/BadRoleBinding",
425425
in: in{
426426
cluster: cluster{
427-
k8sObjs: append(modifyObjName(objectsForCatalogSource(t, validCatalogSource), &rbacv1.RoleBinding{}, "badName"), validConfigMap),
427+
k8sObjs: append(modifyObjName(objectsForCatalogSource(t, validCatalogSource, nil), &rbacv1.RoleBinding{}, "badName"), validConfigMap),
428428
},
429429
catsrc: validCatalogSource,
430430
},
@@ -442,7 +442,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
442442
testName: "ExistingRegistry/OldPod",
443443
in: in{
444444
cluster: cluster{
445-
k8sObjs: append(objectsForCatalogSource(t, validCatalogSource), validConfigMap),
445+
k8sObjs: append(objectsForCatalogSource(t, validCatalogSource, nil), validConfigMap),
446446
},
447447
catsrc: outdatedCatalogSource,
448448
},
@@ -479,7 +479,7 @@ func TestConfigMapRegistryReconciler(t *testing.T) {
479479
}
480480

481481
// if no error, the reconciler should create the same set of kube objects every time
482-
decorated := configMapCatalogSourceDecorator{tt.in.catsrc, runAsUser}
482+
decorated := configMapCatalogSourceDecorator{tt.in.catsrc, rec.(*ConfigMapRegistryReconciler), runAsUser}
483483

484484
pod, err := decorated.Pod(registryImageName)
485485
require.NoError(t, err)

pkg/controller/registry/reconciler/grpc.go

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/google/go-cmp/cmp"
1010
"github.com/operator-framework/api/pkg/operators/v1alpha1"
11+
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
1112
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
1213
controllerclient "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/controller-runtime/client"
1314
hashutil "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/hash"
@@ -32,6 +33,7 @@ const (
3233
// grpcCatalogSourceDecorator wraps CatalogSource to add additional methods
3334
type grpcCatalogSourceDecorator struct {
3435
*v1alpha1.CatalogSource
36+
Reconciler *GrpcRegistryReconciler
3537
createPodAsUser int64
3638
opmImage string
3739
utilImage string
@@ -65,6 +67,27 @@ func (s *grpcCatalogSourceDecorator) Labels() map[string]string {
6567
}
6668
}
6769

70+
func (s *grpcCatalogSourceDecorator) getNamespaceSecurityContextConfig() (operatorsv1alpha1.SecurityConfig, error) {
71+
namespace := s.GetNamespace()
72+
if config, ok := s.Reconciler.namespacePSAConfigCache[namespace]; ok {
73+
return config, nil
74+
}
75+
// Retrieve the client from the reconciler
76+
client := s.Reconciler.OpClient
77+
78+
ns, err := client.KubernetesInterface().CoreV1().Namespaces().Get(context.TODO(), s.GetNamespace(), metav1.GetOptions{})
79+
if err != nil {
80+
return "", fmt.Errorf("error fetching namespace: %v", err)
81+
}
82+
// 'pod-security.kubernetes.io/enforce' is the label used for enforcing namespace level security,
83+
// and 'restricted' is the value indicating a restricted security policy.
84+
if val, exists := ns.Labels["pod-security.kubernetes.io/enforce"]; exists && val == "restricted" {
85+
return operatorsv1alpha1.Restricted, nil
86+
}
87+
88+
return operatorsv1alpha1.Legacy, nil
89+
}
90+
6891
func (s *grpcCatalogSourceDecorator) Annotations() map[string]string {
6992
// TODO: Maybe something better than just a copy of all annotations would be to have a specific 'podMetadata' section in the CatalogSource?
7093
return s.GetAnnotations()
@@ -131,7 +154,11 @@ func (s *grpcCatalogSourceDecorator) ServiceAccount() *corev1.ServiceAccount {
131154
}
132155

133156
func (s *grpcCatalogSourceDecorator) Pod(serviceAccount *corev1.ServiceAccount) (*corev1.Pod, error) {
134-
pod, err := Pod(s.CatalogSource, "registry-server", s.opmImage, s.utilImage, s.Spec.Image, serviceAccount, s.Labels(), s.Annotations(), 5, 10, s.createPodAsUser)
157+
securityContextConfig, err := s.getNamespaceSecurityContextConfig()
158+
if err != nil {
159+
return nil, err
160+
}
161+
pod, err := Pod(s.CatalogSource, "registry-server", s.opmImage, s.utilImage, s.Spec.Image, serviceAccount, s.Labels(), s.Annotations(), 5, 10, s.createPodAsUser, securityContextConfig)
135162
if err != nil {
136163
return nil, err
137164
}
@@ -140,13 +167,14 @@ func (s *grpcCatalogSourceDecorator) Pod(serviceAccount *corev1.ServiceAccount)
140167
}
141168

142169
type GrpcRegistryReconciler struct {
143-
now nowFunc
144-
Lister operatorlister.OperatorLister
145-
OpClient operatorclient.ClientInterface
146-
SSAClient *controllerclient.ServerSideApplier
147-
createPodAsUser int64
148-
opmImage string
149-
utilImage string
170+
now nowFunc
171+
Lister operatorlister.OperatorLister
172+
OpClient operatorclient.ClientInterface
173+
SSAClient *controllerclient.ServerSideApplier
174+
createPodAsUser int64
175+
opmImage string
176+
utilImage string
177+
namespacePSAConfigCache map[string]operatorsv1alpha1.SecurityConfig
150178
}
151179

152180
var _ RegistryReconciler = &GrpcRegistryReconciler{}
@@ -246,6 +274,9 @@ func correctImages(source grpcCatalogSourceDecorator, pod *corev1.Pod) bool {
246274

247275
// EnsureRegistryServer ensures that all components of registry server are up to date.
248276
func (c *GrpcRegistryReconciler) EnsureRegistryServer(logger *logrus.Entry, catalogSource *v1alpha1.CatalogSource) error {
277+
if c.namespacePSAConfigCache == nil {
278+
c.namespacePSAConfigCache = make(map[string]operatorsv1alpha1.SecurityConfig)
279+
}
249280
source := grpcCatalogSourceDecorator{CatalogSource: catalogSource, createPodAsUser: c.createPodAsUser, opmImage: c.opmImage, utilImage: c.utilImage}
250281

251282
// if service status is nil, we force create every object to ensure they're created the first time

0 commit comments

Comments
 (0)