Skip to content

Commit edffd9c

Browse files
authored
(psa) allow legacy Catalogsources to run in non-restrcted namespaces (#2845)
* (chore) vendor in o-f/api v0.17.1 Signed-off-by: Anik Bhattacharjee <[email protected]> * (psa) allow legacy Catalogsources to run in non-restrcted namespaces This PR configures the Catalogsource reconciler to use the spec.GrpcPodConfig.SecurityContextConfig field to determine if the pod.spec.securityContext and container[*].spec.SecurityContext for the registry pod should be configured to be runnable in a PSA restrcited namespace or not, so that cluster admins can indicate that they want to run legacy catalogsources in a non-resctricted (baseline/privileged) namespace. This allows cluster admins to run catalogsources that are built with a version of opm that is less than v1.23.2 (i.e a version of opm that does not contain [this commit](operator-framework/operator-registry#974) Signed-off-by: Anik Bhattacharjee <[email protected]> Signed-off-by: Anik Bhattacharjee <[email protected]>
1 parent bfeb1e4 commit edffd9c

File tree

10 files changed

+214
-20
lines changed

10 files changed

+214
-20
lines changed

deploy/chart/crds/0000_50_olm_00-catalogsources.crd.yaml

+7
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ spec:
7979
priorityClassName:
8080
description: If specified, indicates the pod's priority. If not specified, the pod priority will be default or zero if there is no default.
8181
type: string
82+
securityContextConfig:
83+
description: 'SecurityContextConfig can be one of `legacy` or `restricted`. The CatalogSource''s pod is either injected with the right pod.spec.securityContext and pod.spec.container[*].securityContext values to allow the pod to run in Pod Security Admission(PSA) controller''s `restricted` mode, or doesn''t set these values at all, in which case the pod can only be run in PSA `baseline` or `privileged` namespaces. By default, SecurityContextConfig is set to `restricted`. If the value is unspecified, the default value of `restricted` is used. Specifying any other value will result in a validation error. When using older catalog images, which could not be run in `restricted` mode, the SecurityContextConfig should be set to `legacy`. More information about PSA can be found here: https://kubernetes.io/docs/concepts/security/pod-security-admission/'''
84+
type: string
85+
default: restricted
86+
enum:
87+
- legacy
88+
- restricted
8289
tolerations:
8390
description: Tolerations are the catalog source's pod's tolerations.
8491
type: array

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ require (
2424
github.com/onsi/gomega v1.18.1
2525
github.com/openshift/api v0.0.0-20200331152225-585af27e34fd
2626
github.com/openshift/client-go v0.0.0-20200326155132-2a6cd50aedd0
27-
github.com/operator-framework/api v0.16.0
27+
github.com/operator-framework/api v0.17.1
2828
github.com/operator-framework/operator-registry v1.17.5
2929
github.com/otiai10/copy v1.2.0
3030
github.com/pkg/errors v0.9.1

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -1078,8 +1078,8 @@ github.com/openshift/client-go v0.0.0-20200326155132-2a6cd50aedd0/go.mod h1:uUQ4
10781078
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
10791079
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
10801080
github.com/operator-framework/api v0.7.1/go.mod h1:L7IvLd/ckxJEJg/t4oTTlnHKAJIP/p51AvEslW3wYdY=
1081-
github.com/operator-framework/api v0.16.0 h1:swUOhVv7QDszxBTwYM8QAtyeqI4EQHNVAiKMS+xjakY=
1082-
github.com/operator-framework/api v0.16.0/go.mod h1:kk8xJahHJR3bKqrA+A+1VIrhOTmyV76k+ARv+iV+u1Q=
1081+
github.com/operator-framework/api v0.17.1 h1:J/6+Xj4IEV8C7hcirqUFwOiZAU3PbnJhWvB0/bB51c4=
1082+
github.com/operator-framework/api v0.17.1/go.mod h1:kk8xJahHJR3bKqrA+A+1VIrhOTmyV76k+ARv+iV+u1Q=
10831083
github.com/operator-framework/operator-registry v1.17.5 h1:LR8m1rFz5Gcyje8WK6iYt+gIhtzqo52zMRALdmTYHT0=
10841084
github.com/operator-framework/operator-registry v1.17.5/go.mod h1:sRQIgDMZZdUcmHltzyCnM6RUoDF+WS8Arj1BQIARDS8=
10851085
github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k=

pkg/controller/registry/reconciler/reconciler.go

+24-13
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,7 @@ func Pod(source *operatorsv1alpha1.CatalogSource, name string, image string, saN
182182
},
183183
},
184184
SecurityContext: &corev1.SecurityContext{
185-
ReadOnlyRootFilesystem: pointer.Bool(false),
186-
AllowPrivilegeEscalation: pointer.Bool(false),
187-
Capabilities: &corev1.Capabilities{
188-
Drop: []corev1.Capability{"ALL"},
189-
},
185+
ReadOnlyRootFilesystem: pointer.Bool(false),
190186
},
191187
ImagePullPolicy: pullPolicy,
192188
TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
@@ -195,19 +191,18 @@ func Pod(source *operatorsv1alpha1.CatalogSource, name string, image string, saN
195191
NodeSelector: map[string]string{
196192
"kubernetes.io/os": "linux",
197193
},
198-
SecurityContext: &corev1.PodSecurityContext{
199-
SeccompProfile: &corev1.SeccompProfile{
200-
Type: corev1.SeccompProfileTypeRuntimeDefault,
201-
},
202-
},
203194
ServiceAccountName: saName,
204195
},
205196
}
206197

207-
if runAsUser > 0 {
208-
pod.Spec.SecurityContext.RunAsUser = &runAsUser
209-
pod.Spec.SecurityContext.RunAsNonRoot = pointer.Bool(true)
198+
if source.Spec.GrpcPodConfig != nil {
199+
if source.Spec.GrpcPodConfig.SecurityContextConfig == operatorsv1alpha1.Restricted {
200+
addSecurityContext(pod, runAsUser)
201+
}
202+
} else {
203+
addSecurityContext(pod, runAsUser)
210204
}
205+
211206
// Override scheduling options if specified
212207
if source.Spec.GrpcPodConfig != nil {
213208
grpcPodConfig := source.Spec.GrpcPodConfig
@@ -256,3 +251,19 @@ func hashPodSpec(spec corev1.PodSpec) string {
256251
hashutil.DeepHashObject(hasher, &spec)
257252
return rand.SafeEncodeString(fmt.Sprint(hasher.Sum32()))
258253
}
254+
255+
func addSecurityContext(pod *corev1.Pod, runAsUser int64) {
256+
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = pointer.Bool(false)
257+
pod.Spec.Containers[0].SecurityContext.Capabilities = &corev1.Capabilities{
258+
Drop: []corev1.Capability{"ALL"},
259+
}
260+
pod.Spec.SecurityContext = &corev1.PodSecurityContext{
261+
SeccompProfile: &corev1.SeccompProfile{
262+
Type: corev1.SeccompProfileTypeRuntimeDefault,
263+
},
264+
}
265+
if runAsUser > 0 {
266+
pod.Spec.SecurityContext.RunAsUser = &runAsUser
267+
pod.Spec.SecurityContext.RunAsNonRoot = pointer.Bool(true)
268+
}
269+
}

test/e2e/catalog_e2e_test.go

+145-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ var _ = Describe("Starting CatalogSource e2e tests", func() {
5353
)
5454

5555
BeforeEach(func() {
56-
namespaceName := genName("catsrc-e2e-")
56+
// In OPC, PSA labels for any namespace created that is not prefixed with "openshift-" is overriden to enforce
57+
// PSA restricted. This test namespace needs to prefixed with openshift- so that baseline/privileged enforcement
58+
// for the PSA specific tests are not overridden,
59+
// Change it only after https://github.com/operator-framework/operator-lifecycle-manager/issues/2859 is closed.
60+
namespaceName := genName("openshift-catsrc-e2e-")
5761
og := operatorsv1.OperatorGroup{
5862
ObjectMeta: metav1.ObjectMeta{
5963
Name: fmt.Sprintf("%s-operatorgroup", namespaceName),
@@ -1396,7 +1400,147 @@ var _ = Describe("Starting CatalogSource e2e tests", func() {
13961400
})
13971401
})
13981402
})
1403+
When("The namespace is labled as Pod Security Admission policy enforce:restricted", func() {
1404+
BeforeEach(func() {
1405+
var err error
1406+
testNS := &corev1.Namespace{}
1407+
Eventually(func() error {
1408+
testNS, err = c.KubernetesInterface().CoreV1().Namespaces().Get(context.TODO(), ns.GetName(), metav1.GetOptions{})
1409+
if err != nil {
1410+
return err
1411+
}
1412+
return nil
1413+
}).Should(BeNil())
1414+
1415+
testNS.ObjectMeta.Labels = map[string]string{
1416+
"pod-security.kubernetes.io/enforce": "restricted",
1417+
"pod-security.kubernetes.io/enforce-version": "latest",
1418+
}
1419+
1420+
Eventually(func() error {
1421+
_, err := c.KubernetesInterface().CoreV1().Namespaces().Update(context.TODO(), testNS, metav1.UpdateOptions{})
1422+
if err != nil {
1423+
return err
1424+
}
1425+
return nil
1426+
}).Should(BeNil())
1427+
})
1428+
When("A CatalogSource built with opm v1.21.0 (<v1.23.2)is created without spec.GrpcPodConfig.SecurityContextConfig set to legacy", func() {
1429+
var sourceName string
1430+
BeforeEach(func() {
1431+
sourceName = genName("catalog-")
1432+
source := &v1alpha1.CatalogSource{
1433+
TypeMeta: metav1.TypeMeta{
1434+
Kind: v1alpha1.CatalogSourceKind,
1435+
APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
1436+
},
1437+
ObjectMeta: metav1.ObjectMeta{
1438+
Name: sourceName,
1439+
Namespace: ns.GetName(),
1440+
Labels: map[string]string{"olm.catalogSource": sourceName},
1441+
},
1442+
Spec: v1alpha1.CatalogSourceSpec{
1443+
SourceType: v1alpha1.SourceTypeGrpc,
1444+
Image: "quay.io/olmtest/old-opm-catsrc:v1.21.0",
1445+
},
1446+
}
1447+
1448+
Eventually(func() error {
1449+
_, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
1450+
return err
1451+
}).Should(Succeed())
1452+
})
1453+
It("The registry pod fails to become come up because of lack of permission", func() {
1454+
Eventually(func() (bool, error) {
1455+
podList, err := c.KubernetesInterface().CoreV1().Pods(ns.GetName()).List(context.TODO(), metav1.ListOptions{})
1456+
if err != nil {
1457+
return false, err
1458+
}
1459+
for _, pod := range podList.Items {
1460+
if pod.ObjectMeta.OwnerReferences != nil && pod.ObjectMeta.OwnerReferences[0].Name == sourceName {
1461+
if pod.Status.ContainerStatuses != nil && pod.Status.ContainerStatuses[0].State.Terminated != nil {
1462+
return true, nil
1463+
}
1464+
}
1465+
}
1466+
return false, nil
1467+
}).Should(BeTrue())
1468+
})
1469+
})
1470+
})
1471+
When("The namespace is labled as Pod Security Admission policy enforce:baseline", func() {
1472+
BeforeEach(func() {
1473+
var err error
1474+
testNS := &corev1.Namespace{}
1475+
Eventually(func() error {
1476+
testNS, err = c.KubernetesInterface().CoreV1().Namespaces().Get(context.TODO(), ns.GetName(), metav1.GetOptions{})
1477+
if err != nil {
1478+
return err
1479+
}
1480+
return nil
1481+
}).Should(BeNil())
1482+
1483+
testNS.ObjectMeta.Labels = map[string]string{
1484+
"pod-security.kubernetes.io/enforce": "baseline",
1485+
"pod-security.kubernetes.io/enforce-version": "latest",
1486+
}
13991487

1488+
Eventually(func() error {
1489+
_, err := c.KubernetesInterface().CoreV1().Namespaces().Update(context.TODO(), testNS, metav1.UpdateOptions{})
1490+
if err != nil {
1491+
return err
1492+
}
1493+
return nil
1494+
}).Should(BeNil())
1495+
})
1496+
When("A CatalogSource built with opm v1.21.0 (<v1.23.2)is created with spec.GrpcPodConfig.SecurityContextConfig set to legacy", func() {
1497+
var sourceName string
1498+
BeforeEach(func() {
1499+
sourceName = genName("catalog-")
1500+
source := &v1alpha1.CatalogSource{
1501+
TypeMeta: metav1.TypeMeta{
1502+
Kind: v1alpha1.CatalogSourceKind,
1503+
APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
1504+
},
1505+
ObjectMeta: metav1.ObjectMeta{
1506+
Name: sourceName,
1507+
Namespace: ns.GetName(),
1508+
Labels: map[string]string{"olm.catalogSource": sourceName},
1509+
},
1510+
Spec: v1alpha1.CatalogSourceSpec{
1511+
GrpcPodConfig: &v1alpha1.GrpcPodConfig{
1512+
SecurityContextConfig: operatorsv1alpha1.Legacy,
1513+
},
1514+
SourceType: v1alpha1.SourceTypeGrpc,
1515+
Image: "quay.io/olmtest/old-opm-catsrc:v1.21.0",
1516+
},
1517+
}
1518+
1519+
Eventually(func() error {
1520+
_, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
1521+
return err
1522+
}).Should(Succeed())
1523+
})
1524+
It("The registry pod comes up successfully", func() {
1525+
Eventually(func() (bool, error) {
1526+
podList, err := c.KubernetesInterface().CoreV1().Pods(ns.GetName()).List(context.TODO(), metav1.ListOptions{})
1527+
if err != nil {
1528+
return false, err
1529+
}
1530+
for _, pod := range podList.Items {
1531+
if pod.ObjectMeta.OwnerReferences != nil && pod.ObjectMeta.OwnerReferences[0].Name == sourceName {
1532+
if pod.Status.ContainerStatuses != nil {
1533+
if *pod.Status.ContainerStatuses[0].Started == true {
1534+
return true, nil
1535+
}
1536+
}
1537+
}
1538+
}
1539+
return false, nil
1540+
}).Should(BeTrue())
1541+
})
1542+
})
1543+
})
14001544
})
14011545

14021546
func getOperatorDeployment(c operatorclient.ClientInterface, namespace string, operatorLabels labels.Set) (*appsv1.Deployment, error) {

vendor/github.com/operator-framework/api/crds/operators.coreos.com_catalogsources.yaml

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/operator-framework/api/crds/zz_defs.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/operator-framework/api/pkg/manifests/bundleloader.go

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/operator-framework/api/pkg/operators/v1alpha1/catalogsource_types.go

+22-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/modules.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ github.com/openshift/client-go/config/informers/externalversions/config
626626
github.com/openshift/client-go/config/informers/externalversions/config/v1
627627
github.com/openshift/client-go/config/informers/externalversions/internalinterfaces
628628
github.com/openshift/client-go/config/listers/config/v1
629-
# github.com/operator-framework/api v0.16.0
629+
# github.com/operator-framework/api v0.17.1
630630
## explicit; go 1.18
631631
github.com/operator-framework/api/crds
632632
github.com/operator-framework/api/pkg/constraints

0 commit comments

Comments
 (0)