diff --git a/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go b/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go index a4370df5f4..d30104a9c0 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go +++ b/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go @@ -356,6 +356,12 @@ func Pod(source *operatorsv1alpha1.CatalogSource, name, opmImg, utilImage, img s if securityContextConfig == operatorsv1alpha1.Restricted { // Apply 'restricted' security settings addSecurityContext(pod, runAsUser) + + // Pin SCC selection to prevent custom SCCs from preempting catalog pods on + // HCP, where the creating user has broader SCC access than the local SA. + if _, alreadySet := podAnnotations["openshift.io/required-scc"]; !alreadySet { + podAnnotations["openshift.io/required-scc"] = "restricted-v2" + } } // Set priorityclass if its annotation exists diff --git a/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler_test.go b/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler_test.go index cfcf065dbe..738bb04487 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler_test.go @@ -523,7 +523,7 @@ func TestPodExtractContent(t *testing.T) { GenerateName: "test-", Namespace: "testns", Labels: map[string]string{"olm.pod-spec-hash": "8qB6OcFt60v8HdhXnPkB1cjF39t7RkFx9K0JxW", "olm.managed": "true"}, - Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}, + Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true", "openshift.io/required-scc": "restricted-v2"}, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -606,7 +606,7 @@ func TestPodExtractContent(t *testing.T) { GenerateName: "test-", Namespace: "testns", Labels: map[string]string{"olm.pod-spec-hash": "3xuLPXGJ2pzekw21PFU68XUKOYc7PTuW45M521", "olm.managed": "true"}, - Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}, + Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true", "openshift.io/required-scc": "restricted-v2"}, }, Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ @@ -738,7 +738,7 @@ func TestPodExtractContent(t *testing.T) { GenerateName: "test-", Namespace: "testns", Labels: map[string]string{"olm.pod-spec-hash": "7noQSgGmkI4BD1MPKe0pEFFfOE3jJtN2DUyZuD", "olm.managed": "true"}, - Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}, + Annotations: map[string]string{"cluster-autoscaler.kubernetes.io/safe-to-evict": "true", "openshift.io/required-scc": "restricted-v2"}, }, Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ @@ -1159,6 +1159,58 @@ func TestPodContainerSecurityContext(t *testing.T) { } } +func TestPodRequiredSCCAnnotation(t *testing.T) { + testcases := []struct { + title string + securityConfig v1alpha1.SecurityConfig + inputAnnotations map[string]string + expectAnnotation bool + expectedSCCValue string + }{ + { + title: "Restricted config adds required-scc annotation", + securityConfig: v1alpha1.Restricted, + inputAnnotations: map[string]string{}, + expectAnnotation: true, + expectedSCCValue: "restricted-v2", + }, + { + title: "Legacy config does not add required-scc annotation", + securityConfig: v1alpha1.Legacy, + inputAnnotations: map[string]string{}, + expectAnnotation: false, + }, + { + title: "User-provided required-scc annotation is preserved", + securityConfig: v1alpha1.Restricted, + inputAnnotations: map[string]string{"openshift.io/required-scc": "nonroot-v2"}, + expectAnnotation: true, + expectedSCCValue: "nonroot-v2", + }, + } + + for _, tc := range testcases { + t.Run(tc.title, func(t *testing.T) { + catsrc := &v1alpha1.CatalogSource{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: testNamespace, + }, + } + pod, err := Pod(catsrc, "catalog", "opmImage", "utilImage", "busybox", serviceAccount("", "service-account"), map[string]string{}, tc.inputAnnotations, int32(0), int32(0), workloadUserID, tc.securityConfig) + require.NoError(t, err) + + val, exists := pod.Annotations["openshift.io/required-scc"] + if tc.expectAnnotation { + require.True(t, exists, "expected openshift.io/required-scc annotation to be present") + require.Equal(t, tc.expectedSCCValue, val) + } else { + require.False(t, exists, "expected openshift.io/required-scc annotation to be absent") + } + }) + } +} + // TestPodAvoidsConcurrentWrite is a regression test for // https://bugzilla.redhat.com/show_bug.cgi?id=2101357 // we were mutating the input annotations and labels parameters causing diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go index a4370df5f4..d30104a9c0 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler/reconciler.go @@ -356,6 +356,12 @@ func Pod(source *operatorsv1alpha1.CatalogSource, name, opmImg, utilImage, img s if securityContextConfig == operatorsv1alpha1.Restricted { // Apply 'restricted' security settings addSecurityContext(pod, runAsUser) + + // Pin SCC selection to prevent custom SCCs from preempting catalog pods on + // HCP, where the creating user has broader SCC access than the local SA. + if _, alreadySet := podAnnotations["openshift.io/required-scc"]; !alreadySet { + podAnnotations["openshift.io/required-scc"] = "restricted-v2" + } } // Set priorityclass if its annotation exists