Skip to content

Commit 3e03339

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 bd97e32 commit 3e03339

File tree

5 files changed

+211
-25
lines changed

5 files changed

+211
-25
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ 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 so that the pod can be run in PSA `privileged` namespace. By default, SecurityContextConfig is set to `restricted`. If a value other than `legacy` or `restricted` is used as input, the default value is set instead of using the incorrect value When using older catalog images, which could not be run in restricted mode, the SecurityContextConfig should be set to `legacy`. More info about PSA can be found here: https://kubernetes.io/docs/concepts/security/pod-security-admission/'
84+
type: string
8285
tolerations:
8386
description: Tolerations are the catalog source's pod's tolerations.
8487
type: array
@@ -120,9 +123,6 @@ spec:
120123
type: integer
121124
publisher:
122125
type: string
123-
runAsRoot:
124-
description: RunAsRoot allows admins to indicate that they wish to run the CatalogSource pod in a privileged pod as root. This should only be enabled when running older catalog images which could not be run as non-root.
125-
type: boolean
126126
secrets:
127127
description: Secrets represent set of secrets that can be used to access the contents of the catalog. It is best to keep this list small, since each will need to be tried for every catalog entry.
128128
type: array

pkg/controller/registry/reconciler/reconciler.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package reconciler
44
import (
55
"fmt"
66
"hash/fnv"
7+
"log"
78
"strings"
89

910
corev1 "k8s.io/api/core/v1"
@@ -182,11 +183,7 @@ func Pod(source *operatorsv1alpha1.CatalogSource, name string, image string, saN
182183
},
183184
},
184185
SecurityContext: &corev1.SecurityContext{
185-
ReadOnlyRootFilesystem: pointer.Bool(false),
186-
AllowPrivilegeEscalation: pointer.Bool(false),
187-
Capabilities: &corev1.Capabilities{
188-
Drop: []corev1.Capability{"ALL"},
189-
},
186+
ReadOnlyRootFilesystem: pointer.Bool(false),
190187
},
191188
ImagePullPolicy: pullPolicy,
192189
TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
@@ -195,19 +192,29 @@ func Pod(source *operatorsv1alpha1.CatalogSource, name string, image string, saN
195192
NodeSelector: map[string]string{
196193
"kubernetes.io/os": "linux",
197194
},
198-
SecurityContext: &corev1.PodSecurityContext{
199-
SeccompProfile: &corev1.SeccompProfile{
200-
Type: corev1.SeccompProfileTypeRuntimeDefault,
201-
},
202-
},
203195
ServiceAccountName: saName,
204196
},
205197
}
206198

207-
if runAsUser > 0 {
208-
pod.Spec.SecurityContext.RunAsUser = &runAsUser
209-
pod.Spec.SecurityContext.RunAsNonRoot = pointer.Bool(true)
199+
if source.Spec.GrpcPodConfig != nil {
200+
log.Printf("anik120: SecurityContextConfig: %s", source.Spec.GrpcPodConfig.SecurityContextConfig)
201+
}
202+
if source.Spec.GrpcPodConfig == nil || source.Spec.GrpcPodConfig.SecurityContextConfig != operatorsv1alpha1.Legacy {
203+
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = pointer.Bool(false)
204+
pod.Spec.Containers[0].SecurityContext.Capabilities = &corev1.Capabilities{
205+
Drop: []corev1.Capability{"ALL"},
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
}
217+
211218
// Override scheduling options if specified
212219
if source.Spec.GrpcPodConfig != nil {
213220
grpcPodConfig := source.Spec.GrpcPodConfig

test/e2e/catalog_e2e_test.go

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,7 +1396,147 @@ 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+
}
1415+
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.GrpcPodConfig.RequirePrivilegedAccess 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: "quay.io/olmtest/old-opm-catsrc: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+
}
13991483

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.GrpcPodConfig.RequirePrivilegedAccess 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+
GrpcPodConfig: &v1alpha1.GrpcPodConfig{
1508+
SecurityContextConfig: operatorsv1alpha1.Restricted,
1509+
},
1510+
SourceType: v1alpha1.SourceTypeGrpc,
1511+
Image: "quay.io/olmtest/old-opm-catsrc:v1.21.0",
1512+
},
1513+
}
1514+
1515+
Eventually(func() error {
1516+
_, err := crc.OperatorsV1alpha1().CatalogSources(source.GetNamespace()).Create(context.Background(), source, metav1.CreateOptions{})
1517+
return err
1518+
}).Should(Succeed())
1519+
})
1520+
It("The registry pod comes up successfully", func() {
1521+
Eventually(func() (bool, error) {
1522+
podList, err := c.KubernetesInterface().CoreV1().Pods(ns.GetName()).List(context.TODO(), metav1.ListOptions{})
1523+
if err != nil {
1524+
return false, err
1525+
}
1526+
for _, pod := range podList.Items {
1527+
if pod.ObjectMeta.OwnerReferences != nil && pod.ObjectMeta.OwnerReferences[0].Name == sourceName {
1528+
if pod.Status.ContainerStatuses != nil {
1529+
if *pod.Status.ContainerStatuses[0].Started == true {
1530+
return true, nil
1531+
}
1532+
}
1533+
}
1534+
}
1535+
return false, nil
1536+
}).Should(BeTrue())
1537+
})
1538+
})
1539+
})
14001540
})
14011541

14021542
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

Lines changed: 3 additions & 3 deletions
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

Lines changed: 45 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)