Skip to content

Commit 2711e76

Browse files
committed
(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]>
1 parent 1e69145 commit 2711e76

File tree

2 files changed

+170
-14
lines changed

2 files changed

+170
-14
lines changed

pkg/controller/registry/reconciler/reconciler.go

Lines changed: 25 additions & 13 deletions
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,19 @@ 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+
switch source.Spec.GrpcPodConfig.SecurityContextConfig {
200+
case operatorsv1alpha1.Restricted:
201+
addSecurityContext(pod, runAsUser)
202+
}
203+
} else {
204+
addSecurityContext(pod, runAsUser)
210205
}
206+
211207
// Override scheduling options if specified
212208
if source.Spec.GrpcPodConfig != nil {
213209
grpcPodConfig := source.Spec.GrpcPodConfig
@@ -256,3 +252,19 @@ func hashPodSpec(spec corev1.PodSpec) string {
256252
hashutil.DeepHashObject(hasher, &spec)
257253
return rand.SafeEncodeString(fmt.Sprint(hasher.Sum32()))
258254
}
255+
256+
func addSecurityContext(pod *corev1.Pod, runAsUser int64) {
257+
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = pointer.Bool(false)
258+
pod.Spec.Containers[0].SecurityContext.Capabilities = &corev1.Capabilities{
259+
Drop: []corev1.Capability{"ALL"},
260+
}
261+
pod.Spec.SecurityContext = &corev1.PodSecurityContext{
262+
SeccompProfile: &corev1.SeccompProfile{
263+
Type: corev1.SeccompProfileTypeRuntimeDefault,
264+
},
265+
}
266+
if runAsUser > 0 {
267+
pod.Spec.SecurityContext.RunAsUser = &runAsUser
268+
pod.Spec.SecurityContext.RunAsNonRoot = pointer.Bool(true)
269+
}
270+
}

test/e2e/catalog_e2e_test.go

Lines changed: 145 additions & 1 deletion
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) {

0 commit comments

Comments
 (0)