Skip to content

Commit 8bab077

Browse files
committed
(catsrc) allow catalogsource to be run as root
This PR exposes a spec.runAsRoot field, so that cluster admins can indicate that they want to run the catalogsource container as root user. This, along with the Pod Admission Controller enforce level `privileged` 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 7826bcc commit 8bab077

File tree

2 files changed

+160
-16
lines changed

2 files changed

+160
-16
lines changed

pkg/controller/registry/reconciler/reconciler.go

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -181,32 +181,38 @@ func Pod(source *operatorsv1alpha1.CatalogSource, name string, image string, saN
181181
corev1.ResourceMemory: resource.MustParse("50Mi"),
182182
},
183183
},
184-
SecurityContext: &corev1.SecurityContext{
185-
ReadOnlyRootFilesystem: pointer.Bool(false),
186-
AllowPrivilegeEscalation: pointer.Bool(false),
187-
Capabilities: &corev1.Capabilities{
188-
Drop: []corev1.Capability{"ALL"},
189-
},
190-
},
191184
ImagePullPolicy: pullPolicy,
192185
TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
193186
},
194187
},
195188
NodeSelector: map[string]string{
196189
"kubernetes.io/os": "linux",
197190
},
198-
SecurityContext: &corev1.PodSecurityContext{
199-
SeccompProfile: &corev1.SeccompProfile{
200-
Type: corev1.SeccompProfileTypeRuntimeDefault,
201-
},
202-
},
203191
ServiceAccountName: saName,
204192
},
205193
}
206-
207-
if runAsUser > 0 {
208-
pod.Spec.SecurityContext.RunAsUser = &runAsUser
209-
pod.Spec.SecurityContext.RunAsNonRoot = pointer.Bool(true)
194+
if source.Spec.RunAsRoot {
195+
pod.Spec.SecurityContext = &corev1.PodSecurityContext{
196+
RunAsUser: pointer.Int64(0),
197+
RunAsNonRoot: pointer.Bool(false),
198+
}
199+
} else {
200+
pod.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
201+
ReadOnlyRootFilesystem: pointer.Bool(false),
202+
AllowPrivilegeEscalation: pointer.Bool(false),
203+
Capabilities: &corev1.Capabilities{
204+
Drop: []corev1.Capability{"ALL"},
205+
},
206+
}
207+
pod.Spec.SecurityContext = &corev1.PodSecurityContext{
208+
SeccompProfile: &corev1.SeccompProfile{
209+
Type: corev1.SeccompProfileTypeRuntimeDefault,
210+
},
211+
}
212+
if runAsUser > 0 {
213+
pod.Spec.SecurityContext.RunAsUser = &runAsUser
214+
pod.Spec.SecurityContext.RunAsNonRoot = pointer.Bool(true)
215+
}
210216
}
211217
// Override scheduling options if specified
212218
if source.Spec.GrpcPodConfig != nil {

test/e2e/catalog_e2e_test.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,7 +1396,145 @@ var _ = Describe("Starting CatalogSource e2e tests", func() {
13961396
})
13971397
})
13981398
})
1399+
When("The namespace is labled as Pod Security Admission policy enforce:restricted", func() {
1400+
BeforeEach(func() {
1401+
var err error
1402+
testNS := &corev1.Namespace{}
1403+
Eventually(func() error {
1404+
testNS, err = c.KubernetesInterface().CoreV1().Namespaces().Get(context.TODO(), ns.GetName(), metav1.GetOptions{})
1405+
if err != nil {
1406+
return err
1407+
}
1408+
return nil
1409+
}).Should(BeNil())
1410+
1411+
testNS.ObjectMeta.Labels = map[string]string{
1412+
"pod-security.kubernetes.io/enforce": "restricted",
1413+
"pod-security.kubernetes.io/enforce-version": "latest",
1414+
}
13991415

1416+
Eventually(func() error {
1417+
_, err := c.KubernetesInterface().CoreV1().Namespaces().Update(context.TODO(), testNS, metav1.UpdateOptions{})
1418+
if err != nil {
1419+
return err
1420+
}
1421+
return nil
1422+
}).Should(BeNil())
1423+
})
1424+
When("A CatalogSource built with opm v1.21.0 (<v1.23.2)is created without spec.RunAsRoot set to true", func() {
1425+
var sourceName string
1426+
BeforeEach(func() {
1427+
sourceName = genName("catalog-")
1428+
source := &v1alpha1.CatalogSource{
1429+
TypeMeta: metav1.TypeMeta{
1430+
Kind: v1alpha1.CatalogSourceKind,
1431+
APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
1432+
},
1433+
ObjectMeta: metav1.ObjectMeta{
1434+
Name: sourceName,
1435+
Namespace: ns.GetName(),
1436+
Labels: map[string]string{"olm.catalogSource": sourceName},
1437+
},
1438+
Spec: v1alpha1.CatalogSourceSpec{
1439+
SourceType: v1alpha1.SourceTypeGrpc,
1440+
Image: "docker.io/anik120/ditto-index-opm:v1.21.0",
1441+
},
1442+
}
1443+
1444+
Eventually(func() error {
1445+
_, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
1446+
return err
1447+
}).Should(Succeed())
1448+
})
1449+
It("The registry pod fails to become come up because of lack of permission", func() {
1450+
Eventually(func() (bool, error) {
1451+
podList, err := c.KubernetesInterface().CoreV1().Pods(ns.GetName()).List(context.TODO(), metav1.ListOptions{})
1452+
if err != nil {
1453+
return false, err
1454+
}
1455+
for _, pod := range podList.Items {
1456+
if pod.ObjectMeta.OwnerReferences != nil && pod.ObjectMeta.OwnerReferences[0].Name == sourceName {
1457+
if pod.Status.ContainerStatuses != nil && pod.Status.ContainerStatuses[0].State.Terminated != nil {
1458+
return true, nil
1459+
}
1460+
}
1461+
}
1462+
return false, nil
1463+
}).Should(BeTrue())
1464+
})
1465+
})
1466+
})
1467+
When("The namespace is labled as Pod Security Admission policy enforce:privileged", func() {
1468+
BeforeEach(func() {
1469+
var err error
1470+
testNS := &corev1.Namespace{}
1471+
Eventually(func() error {
1472+
testNS, err = c.KubernetesInterface().CoreV1().Namespaces().Get(context.TODO(), ns.GetName(), metav1.GetOptions{})
1473+
if err != nil {
1474+
return err
1475+
}
1476+
return nil
1477+
}).Should(BeNil())
1478+
1479+
testNS.ObjectMeta.Labels = map[string]string{
1480+
"pod-security.kubernetes.io/enforce": "privileged",
1481+
"pod-security.kubernetes.io/enforce-version": "latest",
1482+
}
1483+
1484+
Eventually(func() error {
1485+
_, err := c.KubernetesInterface().CoreV1().Namespaces().Update(context.TODO(), testNS, metav1.UpdateOptions{})
1486+
if err != nil {
1487+
return err
1488+
}
1489+
return nil
1490+
}).Should(BeNil())
1491+
})
1492+
When("A CatalogSource built with opm v1.21.0 (<v1.23.2)is created with spec.RunAsRoot set to true", func() {
1493+
var sourceName string
1494+
BeforeEach(func() {
1495+
sourceName = genName("catalog-")
1496+
source := &v1alpha1.CatalogSource{
1497+
TypeMeta: metav1.TypeMeta{
1498+
Kind: v1alpha1.CatalogSourceKind,
1499+
APIVersion: v1alpha1.CatalogSourceCRDAPIVersion,
1500+
},
1501+
ObjectMeta: metav1.ObjectMeta{
1502+
Name: sourceName,
1503+
Namespace: ns.GetName(),
1504+
Labels: map[string]string{"olm.catalogSource": sourceName},
1505+
},
1506+
Spec: v1alpha1.CatalogSourceSpec{
1507+
RunAsRoot: true,
1508+
SourceType: v1alpha1.SourceTypeGrpc,
1509+
Image: "docker.io/anik120/ditto-index-opm:v1.21.0",
1510+
},
1511+
}
1512+
1513+
Eventually(func() error {
1514+
_, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
1515+
return err
1516+
}).Should(Succeed())
1517+
})
1518+
It("The registry pod comes up successfully", func() {
1519+
Eventually(func() (bool, error) {
1520+
podList, err := c.KubernetesInterface().CoreV1().Pods(ns.GetName()).List(context.TODO(), metav1.ListOptions{})
1521+
if err != nil {
1522+
return false, err
1523+
}
1524+
for _, pod := range podList.Items {
1525+
if pod.ObjectMeta.OwnerReferences != nil && pod.ObjectMeta.OwnerReferences[0].Name == sourceName {
1526+
if pod.Status.ContainerStatuses != nil {
1527+
if *pod.Status.ContainerStatuses[0].Started == true {
1528+
return true, nil
1529+
}
1530+
}
1531+
}
1532+
}
1533+
return false, nil
1534+
}).Should(BeTrue())
1535+
})
1536+
})
1537+
})
14001538
})
14011539

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

0 commit comments

Comments
 (0)