diff --git a/cmd/operator-controller/main.go b/cmd/operator-controller/main.go index f026c0222..248e07baa 100644 --- a/cmd/operator-controller/main.go +++ b/cmd/operator-controller/main.go @@ -724,7 +724,12 @@ func (c *helmReconcilerConfigurator) Configure(ceReconciler *controllers.Cluster // 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(), + // Additional verbs / bundle manifest that are expected by the content manager to watch those resources + authorization.WithClusterCollectionVerbs("list", "watch"), + authorization.WithNamespacedCollectionVerbs("create"), + ) } cm := contentmanager.NewManager(clientRestConfigMapper, c.mgr.GetConfig(), c.mgr.GetRESTMapper()) diff --git a/internal/operator-controller/authorization/rbac.go b/internal/operator-controller/authorization/rbac.go index d85e532f8..e2b87cb20 100644 --- a/internal/operator-controller/authorization/rbac.go +++ b/internal/operator-controller/authorization/rbac.go @@ -29,7 +29,7 @@ import ( rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1" rbacregistry "k8s.io/kubernetes/pkg/registry/rbac" "k8s.io/kubernetes/pkg/registry/rbac/validation" - rbac "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac" + "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -59,26 +59,42 @@ type ScopedPolicyRules struct { var objectVerbs = []string{"get", "patch", "update", "delete"} -// Here we are splitting collection verbs based on required scope -// NB: this split is tightly coupled to the requirements of the contentmanager, specifically -// its need for cluster-scoped list/watch permissions. -// TODO: We are accepting this coupling for now, but plan to decouple -// TODO: link for above https://github.com/operator-framework/operator-controller/issues/1911 -var namespacedCollectionVerbs = []string{"create"} -var clusterCollectionVerbs = []string{"list", "watch"} +type RBACPreAuthorizerOption func(*rbacPreAuthorizer) + +// WithClusterCollectionVerbs configures cluster-scoped collection verbs (e.g. list, watch) +// that are checked in addition to object and namespaced collection verbs. +func WithClusterCollectionVerbs(verbs ...string) RBACPreAuthorizerOption { + return func(a *rbacPreAuthorizer) { + a.clusterCollectionVerbs = slices.Clone(verbs) + } +} + +// WithNamespacedCollectionVerbs configures namespaced collection verbs (e.g. create) +// that are checked for each unique namespace across all objects in a GVR. +func WithNamespacedCollectionVerbs(verbs ...string) RBACPreAuthorizerOption { + return func(a *rbacPreAuthorizer) { + a.namespacedCollectionVerbs = slices.Clone(verbs) + } +} type rbacPreAuthorizer struct { - authorizer authorizer.Authorizer - ruleResolver validation.AuthorizationRuleResolver - restMapper meta.RESTMapper + authorizer authorizer.Authorizer + ruleResolver validation.AuthorizationRuleResolver + restMapper meta.RESTMapper + clusterCollectionVerbs []string + namespacedCollectionVerbs []string } -func NewRBACPreAuthorizer(cl client.Client) PreAuthorizer { - return &rbacPreAuthorizer{ +func NewRBACPreAuthorizer(cl client.Client, opts ...RBACPreAuthorizerOption) PreAuthorizer { + a := &rbacPreAuthorizer{ authorizer: newRBACAuthorizer(cl), ruleResolver: newRBACRulesResolver(cl), restMapper: cl.RESTMapper(), } + for _, opt := range opts { + opt(a) + } + return a } func (a *rbacPreAuthorizer) PreAuthorize(ctx context.Context, user user.Info, manifestReader io.Reader, additionalRequiredPerms ...UserAuthorizerAttributesFactory) ([]ScopedPolicyRules, error) { @@ -88,7 +104,7 @@ func (a *rbacPreAuthorizer) PreAuthorize(ctx context.Context, user user.Info, ma } // derive manifest related attributes records - attributesRecords := dm.asAuthorizationAttributesRecordsForUser(user) + attributesRecords := dm.asAuthorizationAttributesRecordsForUser(user, a.clusterCollectionVerbs, a.namespacedCollectionVerbs) // append additional required perms for _, fn := range additionalRequiredPerms { @@ -324,7 +340,7 @@ func (dm *decodedManifest) rbacObjects() []client.Object { return objects } -func (dm *decodedManifest) asAuthorizationAttributesRecordsForUser(manifestManager user.Info) []authorizer.AttributesRecord { +func (dm *decodedManifest) asAuthorizationAttributesRecordsForUser(manifestManager user.Info, clusterCollectionVerbs, namespacedCollectionVerbs []string) []authorizer.AttributesRecord { // Calculate initial capacity as an upper-bound estimate: // - For each key: len(objectVerbs) records (4) // - For unique namespaces: len(namespacedCollectionVerbs) records (1 per unique namespace across all keys in a GVR) @@ -369,7 +385,7 @@ func (dm *decodedManifest) asAuthorizationAttributesRecordsForUser(manifestManag }) } } - // generate records for cluster-scoped collection verbs (list, watch) required by contentmanager + // generate records for cluster-scoped collection verbs (e.g. list, watch) for _, v := range clusterCollectionVerbs { attributeRecords = append(attributeRecords, authorizer.AttributesRecord{ User: manifestManager, diff --git a/internal/operator-controller/authorization/rbac_test.go b/internal/operator-controller/authorization/rbac_test.go index 9d80cc52b..16d5449e4 100644 --- a/internal/operator-controller/authorization/rbac_test.go +++ b/internal/operator-controller/authorization/rbac_test.go @@ -396,7 +396,7 @@ func setupFakeClient(role client.Object) client.Client { func TestPreAuthorize_Success(t *testing.T) { t.Run("preauthorize succeeds with no missing rbac rules", func(t *testing.T) { fakeClient := setupFakeClient(privilegedClusterRole) - preAuth := NewRBACPreAuthorizer(fakeClient) + preAuth := NewRBACPreAuthorizer(fakeClient, WithClusterCollectionVerbs("list", "watch"), WithNamespacedCollectionVerbs("create")) missingRules, err := preAuth.PreAuthorize(context.TODO(), testUser, strings.NewReader(testManifest)) require.NoError(t, err) require.Equal(t, []ScopedPolicyRules{}, missingRules) @@ -406,7 +406,7 @@ func TestPreAuthorize_Success(t *testing.T) { func TestPreAuthorize_MissingRBAC(t *testing.T) { t.Run("preauthorize fails and finds missing rbac rules", func(t *testing.T) { fakeClient := setupFakeClient(limitedClusterRole) - preAuth := NewRBACPreAuthorizer(fakeClient) + preAuth := NewRBACPreAuthorizer(fakeClient, WithClusterCollectionVerbs("list", "watch"), WithNamespacedCollectionVerbs("create")) missingRules, err := preAuth.PreAuthorize(context.TODO(), testUser, strings.NewReader(testManifest)) require.NoError(t, err) require.Equal(t, expectedSingleNamespaceMissingRules, missingRules) @@ -416,7 +416,7 @@ func TestPreAuthorize_MissingRBAC(t *testing.T) { func TestPreAuthorizeMultiNamespace_MissingRBAC(t *testing.T) { t.Run("preauthorize fails and finds missing rbac rules in multiple namespaces", func(t *testing.T) { fakeClient := setupFakeClient(limitedClusterRole) - preAuth := NewRBACPreAuthorizer(fakeClient) + preAuth := NewRBACPreAuthorizer(fakeClient, WithClusterCollectionVerbs("list", "watch"), WithNamespacedCollectionVerbs("create")) missingRules, err := preAuth.PreAuthorize(context.TODO(), testUser, strings.NewReader(testManifestMultiNamespace)) require.NoError(t, err) require.Equal(t, expectedMultiNamespaceMissingRules, missingRules) @@ -426,7 +426,7 @@ func TestPreAuthorizeMultiNamespace_MissingRBAC(t *testing.T) { func TestPreAuthorize_CheckEscalation(t *testing.T) { t.Run("preauthorize succeeds with no missing rbac rules", func(t *testing.T) { fakeClient := setupFakeClient(escalatingClusterRole) - preAuth := NewRBACPreAuthorizer(fakeClient) + preAuth := NewRBACPreAuthorizer(fakeClient, WithClusterCollectionVerbs("list", "watch"), WithNamespacedCollectionVerbs("create")) missingRules, err := preAuth.PreAuthorize(context.TODO(), testUser, strings.NewReader(testManifest)) require.NoError(t, err) require.Equal(t, []ScopedPolicyRules{}, missingRules) @@ -436,7 +436,7 @@ func TestPreAuthorize_CheckEscalation(t *testing.T) { func TestPreAuthorize_AdditionalRequiredPerms_MissingRBAC(t *testing.T) { t.Run("preauthorize fails and finds missing rbac rules coming from the additional required permissions", func(t *testing.T) { fakeClient := setupFakeClient(escalatingClusterRole) - preAuth := NewRBACPreAuthorizer(fakeClient) + preAuth := NewRBACPreAuthorizer(fakeClient, WithClusterCollectionVerbs("list", "watch"), WithNamespacedCollectionVerbs("create")) missingRules, err := preAuth.PreAuthorize(context.TODO(), testUser, strings.NewReader(testManifest), func(user user.Info) []authorizer.AttributesRecord { return []authorizer.AttributesRecord{ { @@ -465,6 +465,237 @@ func TestPreAuthorize_AdditionalRequiredPerms_MissingRBAC(t *testing.T) { }) } +func TestPreAuthorize_WithClusterCollectionVerbs(t *testing.T) { + // expectedNamespacedMissingRules are the missing rules expected in the "test-namespace" + // namespace regardless of cluster collection verb configuration. These come from object + // verbs (get, patch, update, delete), namespaced collection verbs (create), and the + // escalation check for the role/rolebinding in the manifest. + expectedNamespacedMissingRules := ScopedPolicyRules{ + Namespace: "test-namespace", + MissingRules: []rbacv1.PolicyRule{ + { + Verbs: []string{"create"}, + APIGroups: []string{"*"}, + Resources: []string{"certificates"}}, + { + Verbs: []string{"create"}, + APIGroups: []string{""}, + Resources: []string{"services"}}, + { + Verbs: []string{"create"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"rolebindings"}}, + { + Verbs: []string{"create"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"roles"}}, + { + Verbs: []string{"delete", "get", "patch", "update"}, + APIGroups: []string{""}, + Resources: []string{"services"}, + ResourceNames: []string{"test-service"}}, + { + Verbs: []string{"delete", "get", "patch", "update"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"rolebindings"}, + ResourceNames: []string{"test-extension-binding"}}, + { + Verbs: []string{"delete", "get", "patch", "update"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"roles"}, + ResourceNames: []string{"test-extension-role"}}, + { + Verbs: []string{"watch"}, + APIGroups: []string{"*"}, + Resources: []string{"serviceaccounts"}, + }, + }, + } + + t.Run("no cluster collection verbs option omits cluster-scoped collection rules", func(t *testing.T) { + fakeClient := setupFakeClient(limitedClusterRole) + preAuth := NewRBACPreAuthorizer(fakeClient, WithNamespacedCollectionVerbs("create")) + missingRules, err := preAuth.PreAuthorize(context.TODO(), testUser, strings.NewReader(testManifest)) + require.NoError(t, err) + // With no cluster collection verbs, there should be no cluster-scoped (namespace="") missing rules + require.Equal(t, []ScopedPolicyRules{expectedNamespacedMissingRules}, missingRules) + }) + + t.Run("cluster verbs option only checks those verbs at cluster scope", func(t *testing.T) { + fakeClient := setupFakeClient(limitedClusterRole) + preAuth := NewRBACPreAuthorizer(fakeClient, WithClusterCollectionVerbs("get", "patch", "update"), WithNamespacedCollectionVerbs("create")) + missingRules, err := preAuth.PreAuthorize(context.TODO(), testUser, strings.NewReader(testManifest)) + require.NoError(t, err) + require.Equal(t, []ScopedPolicyRules{ + { + Namespace: "", + MissingRules: []rbacv1.PolicyRule{ + { + Verbs: []string{"get", "patch", "update"}, + APIGroups: []string{""}, + Resources: []string{"services"}, + ResourceNames: []string(nil), + NonResourceURLs: []string(nil)}, + { + Verbs: []string{"get", "patch", "update"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"rolebindings"}, + ResourceNames: []string(nil), + NonResourceURLs: []string(nil)}, + { + Verbs: []string{"get", "patch", "update"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"roles"}, + ResourceNames: []string(nil), + NonResourceURLs: []string(nil), + }, + }, + }, + expectedNamespacedMissingRules, + }, missingRules) + }) + + t.Run("privileged user with no cluster collection verbs succeeds", func(t *testing.T) { + fakeClient := setupFakeClient(privilegedClusterRole) + preAuth := NewRBACPreAuthorizer(fakeClient, WithNamespacedCollectionVerbs("create")) + missingRules, err := preAuth.PreAuthorize(context.TODO(), testUser, strings.NewReader(testManifest)) + require.NoError(t, err) + require.Equal(t, []ScopedPolicyRules{}, missingRules) + }) +} + +func TestPreAuthorize_WithNamespacedCollectionVerbs(t *testing.T) { + // expectedClusterMissingRules are the missing rules expected at cluster scope + // when cluster collection verbs are configured as "list", "watch". + expectedClusterMissingRules := ScopedPolicyRules{ + Namespace: "", + MissingRules: []rbacv1.PolicyRule{ + { + Verbs: []string{"list", "watch"}, + APIGroups: []string{""}, + Resources: []string{"services"}, + ResourceNames: []string(nil), + NonResourceURLs: []string(nil)}, + { + Verbs: []string{"list", "watch"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"rolebindings"}, + ResourceNames: []string(nil), + NonResourceURLs: []string(nil)}, + { + Verbs: []string{"list", "watch"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"roles"}, + ResourceNames: []string(nil), + NonResourceURLs: []string(nil), + }, + }, + } + + t.Run("no namespaced collection verbs option omits namespaced collection rules", func(t *testing.T) { + fakeClient := setupFakeClient(limitedClusterRole) + preAuth := NewRBACPreAuthorizer(fakeClient, WithClusterCollectionVerbs("list", "watch")) + missingRules, err := preAuth.PreAuthorize(context.TODO(), testUser, strings.NewReader(testManifest)) + require.NoError(t, err) + // Without namespaced collection verbs, no "create" rules from collection verbs should appear, + // but object verbs (get, patch, update, delete) and escalation checks still apply + require.Equal(t, []ScopedPolicyRules{ + expectedClusterMissingRules, + { + Namespace: "test-namespace", + MissingRules: []rbacv1.PolicyRule{ + { + Verbs: []string{"create"}, + APIGroups: []string{"*"}, + Resources: []string{"certificates"}}, + { + Verbs: []string{"delete", "get", "patch", "update"}, + APIGroups: []string{""}, + Resources: []string{"services"}, + ResourceNames: []string{"test-service"}}, + { + Verbs: []string{"delete", "get", "patch", "update"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"rolebindings"}, + ResourceNames: []string{"test-extension-binding"}}, + { + Verbs: []string{"delete", "get", "patch", "update"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"roles"}, + ResourceNames: []string{"test-extension-role"}}, + { + Verbs: []string{"watch"}, + APIGroups: []string{"*"}, + Resources: []string{"serviceaccounts"}, + }, + }, + }, + }, missingRules) + }) + + t.Run("namespaced collection verbs option checks those verbs per namespace", func(t *testing.T) { + fakeClient := setupFakeClient(limitedClusterRole) + preAuth := NewRBACPreAuthorizer(fakeClient, WithClusterCollectionVerbs("list", "watch"), WithNamespacedCollectionVerbs("create", "deletecollection")) + missingRules, err := preAuth.PreAuthorize(context.TODO(), testUser, strings.NewReader(testManifest)) + require.NoError(t, err) + // Should have cluster-scoped missing rules plus namespaced rules with both create and deletecollection. + // Note: "certificates" with apiGroup "*" comes from the escalation check on the Role, not + // from namespaced collection verbs, so it only has "create". + require.Equal(t, []ScopedPolicyRules{ + expectedClusterMissingRules, + { + Namespace: "test-namespace", + MissingRules: []rbacv1.PolicyRule{ + { + Verbs: []string{"create", "deletecollection"}, + APIGroups: []string{""}, + Resources: []string{"services"}}, + { + Verbs: []string{"create", "deletecollection"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"rolebindings"}}, + { + Verbs: []string{"create", "deletecollection"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"roles"}}, + { + Verbs: []string{"create"}, + APIGroups: []string{"*"}, + Resources: []string{"certificates"}}, + { + Verbs: []string{"delete", "get", "patch", "update"}, + APIGroups: []string{""}, + Resources: []string{"services"}, + ResourceNames: []string{"test-service"}}, + { + Verbs: []string{"delete", "get", "patch", "update"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"rolebindings"}, + ResourceNames: []string{"test-extension-binding"}}, + { + Verbs: []string{"delete", "get", "patch", "update"}, + APIGroups: []string{"rbac.authorization.k8s.io"}, + Resources: []string{"roles"}, + ResourceNames: []string{"test-extension-role"}}, + { + Verbs: []string{"watch"}, + APIGroups: []string{"*"}, + Resources: []string{"serviceaccounts"}, + }, + }, + }, + }, missingRules) + }) + + t.Run("privileged user with custom namespaced collection verbs succeeds", func(t *testing.T) { + fakeClient := setupFakeClient(privilegedClusterRole) + preAuth := NewRBACPreAuthorizer(fakeClient, WithNamespacedCollectionVerbs("create", "deletecollection")) + missingRules, err := preAuth.PreAuthorize(context.TODO(), testUser, strings.NewReader(testManifest)) + require.NoError(t, err) + require.Equal(t, []ScopedPolicyRules{}, missingRules) + }) +} + // TestParseEscalationErrorForMissingRules Are tests with respect to https://github.com/kubernetes/api/blob/e8d4d542f6a9a16a694bfc8e3b8cd1557eecfc9d/rbac/v1/types.go#L49-L74 // Goal is: prove the regex works as planned AND that if the error messages ever change we'll learn about it with these tests func TestParseEscalationErrorForMissingRules_ParsingLogic(t *testing.T) { diff --git a/internal/operator-controller/rukpak/render/registryv1/generators/generators.go b/internal/operator-controller/rukpak/render/registryv1/generators/generators.go index 8f45bb762..9815c4dba 100644 --- a/internal/operator-controller/rukpak/render/registryv1/generators/generators.go +++ b/internal/operator-controller/rukpak/render/registryv1/generators/generators.go @@ -148,6 +148,14 @@ func BundleCSVPermissionsGenerator(rv1 *bundle.RegistryV1, opts render.Options) // (opts.TargetNamespaces = [”]), the CSV's permission spec will be promoted to ClusterRole and ClusterRoleBinding // resources. To keep parity with OLMv0, these will also include an extra rule to get, list, watch namespaces // (see https://github.com/operator-framework/operator-lifecycle-manager/blob/dfd0b2bea85038d3c0d65348bc812d297f16b8d2/pkg/controller/operators/olm/operatorgroup.go#L539) +// The reasoning for this added rule is: +// - An operator author designing for both SingleNamespace and AllNamespaces install modes should +// only declare the minimum permissions needed — i.e., no cluster-scoped permissions in its CSV. +// - When OLM places that operator into a global OperatorGroup, it lifts the Roles to ClusterRoles. +// But some operators may need to discover namespaces to function globally, which they didn't need +// (and shouldn't have requested) in single-namespace mode. +// - So OLM automatically appends get/list/watch on namespaces during the lift, bridging the gap +// without requiring the operator author to over-request permissions upfront. func BundleCSVClusterPermissionsGenerator(rv1 *bundle.RegistryV1, opts render.Options) ([]client.Object, error) { if rv1 == nil { return nil, fmt.Errorf("bundle cannot be nil") diff --git a/test/e2e/steps/steps.go b/test/e2e/steps/steps.go index abea3fcb2..4697adbf9 100644 --- a/test/e2e/steps/steps.go +++ b/test/e2e/steps/steps.go @@ -44,6 +44,9 @@ const ( olmDeploymentName = "operator-controller-controller-manager" timeout = 5 * time.Minute tick = 1 * time.Second + + helmRBACTemplate = "rbac-template.yaml" + boxcutterRBACTemplate = "boxcutter-rbac-template.yaml" ) var ( @@ -815,8 +818,14 @@ func ServiceAccountIsAvailableInNamespace(ctx context.Context, serviceAccount st } // ServiceAccountWithNeededPermissionsIsAvailableInNamespace creates a ServiceAccount and applies standard RBAC permissions. +// The RBAC template is selected based on the BoxcutterRuntime feature gate: the boxcutter applier does not require +// cluster-scoped list/watch permissions, so a narrower template is used when BoxcutterRuntime is enabled. func ServiceAccountWithNeededPermissionsIsAvailableInNamespace(ctx context.Context, serviceAccount string) error { - return applyPermissionsToServiceAccount(ctx, serviceAccount, "rbac-template.yaml") + rbacTemplate := helmRBACTemplate + if enabled, found := featureGates[features.BoxcutterRuntime]; found && enabled { + rbacTemplate = boxcutterRBACTemplate + } + return applyPermissionsToServiceAccount(ctx, serviceAccount, rbacTemplate) } // ServiceAccountWithClusterAdminPermissionsIsAvailableInNamespace creates a ServiceAccount and applies cluster-admin RBAC. diff --git a/test/e2e/steps/testdata/boxcutter-rbac-template.yaml b/test/e2e/steps/testdata/boxcutter-rbac-template.yaml new file mode 100644 index 000000000..7c35f997e --- /dev/null +++ b/test/e2e/steps/testdata/boxcutter-rbac-template.yaml @@ -0,0 +1,71 @@ +--- +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 + - apiGroups: [apiextensions.k8s.io] + resources: [customresourcedefinitions] + verbs: [update, create, get, delete, patch] + - apiGroups: [""] + resources: + - configmaps + - serviceaccounts + verbs: [update, create, list, watch, get, delete, patch] + - apiGroups: [ "" ] + resources: + - events + verbs: [ create, patch ] + - apiGroups: ["apps"] + resources: + - deployments + verbs: [ update, create, get, delete, patch ] + - apiGroups: ["networking.k8s.io"] + resources: + - networkpolicies + verbs: [update, create, list, get, delete, patch] + - apiGroups: ["rbac.authorization.k8s.io"] + resources: + - clusterroles + - roles + - clusterrolebindings + - rolebindings + verbs: [ update, create, get, delete, patch ] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: [update, create, 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/rbac-template.yaml b/test/e2e/steps/testdata/rbac-template.yaml index 90138b9c6..6eb9be42a 100644 --- a/test/e2e/steps/testdata/rbac-template.yaml +++ b/test/e2e/steps/testdata/rbac-template.yaml @@ -13,10 +13,6 @@ rules: resources: [clusterextensions, clusterextensions/finalizers] resourceNames: ["${CLUSTEREXTENSION_NAME}"] verbs: [update] - # Allow ClusterExtensionRevisions to set blockOwnerDeletion ownerReferences - - apiGroups: [olm.operatorframework.io] - resources: [clusterextensionrevisions, clusterextensionrevisions/finalizers] - verbs: [update, create, list, watch, get, delete, patch] - apiGroups: [apiextensions.k8s.io] resources: [customresourcedefinitions] verbs: [update, create, list, watch, get, delete, patch]