From 55b6193e00f5b9c280edf38c222a398d4ceb0b7a Mon Sep 17 00:00:00 2001 From: Kui Wang Date: Tue, 24 Mar 2026 13:23:33 +0800 Subject: [PATCH] add create verb to boxcutter preflight --- cmd/operator-controller/main.go | 5 +- test/e2e/features/install.feature | 35 +++++++++ test/e2e/steps/steps.go | 13 ++++ ...-sa-boxcutter-no-create-rbac-template.yaml | 72 +++++++++++++++++++ .../olm-sa-helm-no-create-rbac-template.yaml | 68 ++++++++++++++++++ 5 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 test/e2e/steps/testdata/olm-sa-boxcutter-no-create-rbac-template.yaml create mode 100644 test/e2e/steps/testdata/olm-sa-helm-no-create-rbac-template.yaml diff --git a/cmd/operator-controller/main.go b/cmd/operator-controller/main.go index f905d105d..5f83e1fb1 100644 --- a/cmd/operator-controller/main.go +++ b/cmd/operator-controller/main.go @@ -617,7 +617,10 @@ func (c *boxcutterReconcilerConfigurator) Configure(ceReconciler *controllers.Cl // determine if PreAuthorizer should be enabled based on feature gate var preAuth authorization.PreAuthorizer if features.OperatorControllerFeatureGate.Enabled(features.PreflightPermissions) { - preAuth = authorization.NewRBACPreAuthorizer(c.mgr.GetClient()) + preAuth = authorization.NewRBACPreAuthorizer( + c.mgr.GetClient(), + authorization.WithNamespacedCollectionVerbs("create"), + ) } // TODO: better scheme handling - which types do we want to support? diff --git a/test/e2e/features/install.feature b/test/e2e/features/install.feature index ce3fb3430..0ee3b70ef 100644 --- a/test/e2e/features/install.feature +++ b/test/e2e/features/install.feature @@ -535,3 +535,38 @@ Feature: Install ClusterExtension nodeSelector: kubernetes.io/os: linux """ + + @BoxcutterRuntime + @PreflightPermissions + Scenario: Boxcutter preflight check detects missing CREATE permissions + Given ServiceAccount "olm-sa" without create permissions is available in ${TEST_NAMESPACE} + And ClusterExtension is applied + """ + apiVersion: olm.operatorframework.io/v1 + kind: ClusterExtension + metadata: + name: ${NAME} + spec: + namespace: ${TEST_NAMESPACE} + serviceAccount: + name: olm-sa + source: + sourceType: Catalog + catalog: + packageName: test + selector: + matchLabels: + "olm.operatorframework.io/metadata.name": test-catalog + """ + And ClusterExtension reports Progressing as True with Reason Retrying and Message includes: + """ + pre-authorization failed: service account requires the following permissions to manage cluster extension + """ + And ClusterExtension reports Progressing as True with Reason Retrying and Message includes: + """ + Verbs:[create] + """ + When ServiceAccount "olm-sa" with needed permissions is available in ${TEST_NAMESPACE} + Then ClusterExtension is available + And ClusterExtension reports Progressing as True with Reason Succeeded + And ClusterExtension reports Installed as True diff --git a/test/e2e/steps/steps.go b/test/e2e/steps/steps.go index 57cfc864e..90a444fbb 100644 --- a/test/e2e/steps/steps.go +++ b/test/e2e/steps/steps.go @@ -120,6 +120,7 @@ func RegisterSteps(sc *godog.ScenarioContext) { sc.Step(`^(?i)ServiceAccount "([^"]*)" with permissions to install extensions is available in "([^"]*)" namespace$`, ServiceAccountWithNeededPermissionsIsAvailableInGivenNamespace) sc.Step(`^(?i)ServiceAccount "([^"]*)" with needed permissions is available in test namespace$`, ServiceAccountWithNeededPermissionsIsAvailableInTestNamespace) sc.Step(`^(?i)ServiceAccount "([^"]*)" with needed permissions is available in \${TEST_NAMESPACE}$`, ServiceAccountWithNeededPermissionsIsAvailableInTestNamespace) + sc.Step(`^(?i)ServiceAccount "([^"]*)" without create permissions is available in \${TEST_NAMESPACE}$`, ServiceAccountWithoutCreatePermissionsIsAvailableInTestNamespace) sc.Step(`^(?i)ServiceAccount "([^"]*)" is available in \${TEST_NAMESPACE}$`, ServiceAccountIsAvailableInNamespace) sc.Step(`^(?i)ServiceAccount "([^"]*)" in test namespace is cluster admin$`, ServiceAccountWithClusterAdminPermissionsIsAvailableInNamespace) sc.Step(`^(?i)ServiceAccount "([^"]+)" in test namespace has permissions to fetch "([^"]+)" metrics$`, ServiceAccountWithFetchMetricsPermissions) @@ -877,6 +878,18 @@ func ServiceAccountWithNeededPermissionsIsAvailableInTestNamespace(ctx context.C return applyPermissionsToServiceAccount(ctx, serviceAccount, rbacTemplate) } +// ServiceAccountWithoutCreatePermissionsIsAvailableInTestNamespace creates a ServiceAccount with permissions that +// intentionally exclude the "create" verb to test preflight permission validation for Boxcutter applier. +// This is used to verify that the preflight check properly detects missing CREATE permissions. +func ServiceAccountWithoutCreatePermissionsIsAvailableInTestNamespace(ctx context.Context, serviceAccount string) error { + kernel := "helm" + if enabled, found := featureGates[features.BoxcutterRuntime]; found && enabled { + kernel = "boxcutter" + } + rbacTemplate := fmt.Sprintf("%s-%s-no-create-rbac-template.yaml", serviceAccount, kernel) + return applyPermissionsToServiceAccount(ctx, serviceAccount, rbacTemplate) +} + // ServiceAccountWithNeededPermissionsIsAvailableInGivenNamespace creates a ServiceAccount and enables creation of any cluster extension on behalf of this account. func ServiceAccountWithNeededPermissionsIsAvailableInGivenNamespace(ctx context.Context, serviceAccount string, ns string) error { sc := scenarioCtx(ctx) diff --git a/test/e2e/steps/testdata/olm-sa-boxcutter-no-create-rbac-template.yaml b/test/e2e/steps/testdata/olm-sa-boxcutter-no-create-rbac-template.yaml new file mode 100644 index 000000000..8581d69cd --- /dev/null +++ b/test/e2e/steps/testdata/olm-sa-boxcutter-no-create-rbac-template.yaml @@ -0,0 +1,72 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: ${TEST_NAMESPACE} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: ${TEST_NAMESPACE}-${SERVICEACCOUNT_NAME}-olm-admin-clusterrole +rules: + # Allow management of ClusterExtensionRevision finalizers (e.g. by the Boxcutter applier) + - apiGroups: [olm.operatorframework.io] + resources: [clusterextensionrevisions/finalizers] + verbs: [update, patch] + # OLMv0 compatibility requirement for AllNamespaces install + # https://github.com/operator-framework/operator-lifecycle-manager/blob/dfd0b2bea85038d3c0d65348bc812d297f16b8d2/pkg/controller/operators/olm/operatorgroup.go#L530 + - apiGroups: [ "" ] + resources: + - namespaces + verbs: [ get, list, watch ] + # Bundle resource management RBAC derived from bundle resource and permissions described in the ClusterServiceVersion + # NOTE: Intentionally MISSING "create" verb to test preflight check + - apiGroups: [apiextensions.k8s.io] + resources: [customresourcedefinitions] + verbs: [update, get, delete, patch] + - apiGroups: [""] + resources: + - configmaps + - serviceaccounts + verbs: [update, list, watch, get, delete, patch] + - apiGroups: [ "" ] + resources: + - events + verbs: [ patch ] + - apiGroups: ["apps"] + resources: + - deployments + verbs: [ update, get, delete, patch ] + - apiGroups: ["networking.k8s.io"] + resources: + - networkpolicies + verbs: [update, list, get, delete, patch] + - apiGroups: ["rbac.authorization.k8s.io"] + resources: + - clusterroles + - roles + - clusterrolebindings + - rolebindings + verbs: [ update, get, delete, patch ] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: [update, list, watch, get, delete, patch] + - apiGroups: ["authorization.k8s.io"] + resources: ["subjectaccessreviews"] + verbs: [create] + - apiGroups: ["authentication.k8s.io"] + resources: ["tokenreviews"] + verbs: [create] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: ${TEST_NAMESPACE}-${SERVICEACCOUNT_NAME}-install-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: ${TEST_NAMESPACE}-${SERVICEACCOUNT_NAME}-olm-admin-clusterrole +subjects: + - kind: ServiceAccount + name: ${SERVICEACCOUNT_NAME} + namespace: ${TEST_NAMESPACE} diff --git a/test/e2e/steps/testdata/olm-sa-helm-no-create-rbac-template.yaml b/test/e2e/steps/testdata/olm-sa-helm-no-create-rbac-template.yaml new file mode 100644 index 000000000..a7d25e798 --- /dev/null +++ b/test/e2e/steps/testdata/olm-sa-helm-no-create-rbac-template.yaml @@ -0,0 +1,68 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: ${TEST_NAMESPACE} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: ${TEST_NAMESPACE}-${SERVICEACCOUNT_NAME}-olm-admin-clusterrole +rules: + - apiGroups: [olm.operatorframework.io] + resources: [clusterextensions, clusterextensions/finalizers] + resourceNames: ["${CLUSTEREXTENSION_NAME}"] + verbs: [update] + # Bundle resource management RBAC derived from bundle resource and permissions described in the ClusterServiceVersion + # NOTE: Intentionally MISSING "create" verb to test preflight check + - apiGroups: [apiextensions.k8s.io] + resources: [customresourcedefinitions] + verbs: [update, list, watch, get, delete, patch] + - apiGroups: [""] + resources: + - configmaps + - secrets + - services + - serviceaccounts + - events + - namespaces + - persistentvolumes + - persistentvolumeclaims + verbs: [update, list, watch, get, delete, patch] + - apiGroups: ["apps"] + resources: + - deployments + verbs: [ update, list, watch, get, delete, patch ] + - apiGroups: ["networking.k8s.io"] + resources: + - networkpolicies + verbs: [ update, list, watch, get, delete, patch ] + - apiGroups: ["rbac.authorization.k8s.io"] + resources: + - clusterroles + - roles + - clusterrolebindings + - rolebindings + verbs: [ update, list, watch, get, delete, patch ] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: [ update, list, watch, get, delete, patch ] + - apiGroups: ["authorization.k8s.io"] + resources: ["subjectaccessreviews"] + verbs: [create] + - apiGroups: ["authentication.k8s.io"] + resources: ["tokenreviews"] + verbs: [create] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: ${TEST_NAMESPACE}-${SERVICEACCOUNT_NAME}-install-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: ${TEST_NAMESPACE}-${SERVICEACCOUNT_NAME}-olm-admin-clusterrole +subjects: + - kind: ServiceAccount + name: ${SERVICEACCOUNT_NAME} + namespace: ${TEST_NAMESPACE}