diff --git a/pkg/render/apiserver.go b/pkg/render/apiserver.go index b08f0c3a13..c1a6f38a6b 100644 --- a/pkg/render/apiserver.go +++ b/pkg/render/apiserver.go @@ -2047,6 +2047,21 @@ func (c *apiServerComponent) tigeraNetworkAdminClusterRole() *rbacv1.ClusterRole }) } + // In v3 CRD / webhooks mode there is no aggregated apiserver, and the + // calico-uisettings-passthrough ClusterRole that normally grants the broad + // uisettings permission isn't deployed. Grant write verbs here so the + // calico-webhooks UISettings handler (which narrows access via a SAR on + // uisettingsgroups/data) gets invoked instead of being short-circuited by + // kube-apiserver RBAC. + if !c.cfg.RequiresAggregationServer { + rules = append(rules, rbacv1.PolicyRule{ + APIGroups: []string{"projectcalico.org"}, + Resources: []string{"uisettings"}, + Verbs: []string{"create", "update", "delete", "patch"}, + ResourceNames: []string{"cluster-settings", "user-settings"}, + }) + } + return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/render/apiserver_test.go b/pkg/render/apiserver_test.go index f82c94b79b..80be288386 100644 --- a/pkg/render/apiserver_test.go +++ b/pkg/render/apiserver_test.go @@ -411,6 +411,18 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { } rtest.ExpectResources(resources, expectedResources) + + // In CRD/webhooks mode the calico-uisettings-passthrough ClusterRole is not deployed, so + // tigera-network-admin needs to grant write access to uisettings itself for the + // calico-webhooks UISettings handler to do the narrowing instead of being short-circuited + // by kube-apiserver RBAC. + networkAdmin := rtest.GetResource(resources, "tigera-network-admin", "", "rbac.authorization.k8s.io", "v1", "ClusterRole").(*rbacv1.ClusterRole) + Expect(networkAdmin.Rules).To(ContainElement(rbacv1.PolicyRule{ + APIGroups: []string{"projectcalico.org"}, + Resources: []string{"uisettings"}, + Verbs: []string{"create", "update", "delete", "patch"}, + ResourceNames: []string{"cluster-settings", "user-settings"}, + })) }) It("should render L7 Admission Controller with default config when SidecarInjection is Enabled", func() {