diff --git a/cmd/flux/install_test.go b/cmd/flux/install_test.go index 2406fc6dbe..8055dc8b78 100644 --- a/cmd/flux/install_test.go +++ b/cmd/flux/install_test.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "slices" "strings" "testing" @@ -85,6 +86,8 @@ func TestInstall_ComponentsExtra(t *testing.T) { foundImageReflector := false foundSourceWatcher := false foundExternalArtifact := false + foundImpersonationRole := false + foundImpersonationBinding := false for _, obj := range manifests { if obj.GetKind() == "Deployment" && obj.GetName() == "image-automation-controller" { foundImageAutomation = true @@ -103,9 +106,32 @@ func TestInstall_ComponentsExtra(t *testing.T) { g.Expect(args).To(ContainElement("--feature-gates=ExternalArtifact=true")) foundExternalArtifact = true } + if obj.GetKind() == "ClusterRole" && + strings.HasPrefix(obj.GetName(), "crd-controller-impersonator") { + rules, _, _ := unstructured.NestedSlice(obj.Object, "rules") + for _, r := range rules { + resources, _, _ := unstructured.NestedStringSlice(r.(map[string]any), "resources") + verbs, _, _ := unstructured.NestedStringSlice(r.(map[string]any), "verbs") + if slices.Contains(resources, "serviceaccounts") && slices.Contains(verbs, "impersonate") { + foundImpersonationRole = true + } + } + } + if obj.GetKind() == "ClusterRoleBinding" && + strings.HasPrefix(obj.GetName(), "crd-controller-impersonator") { + subjects, _, _ := unstructured.NestedSlice(obj.Object, "subjects") + for _, s := range subjects { + name, _, _ := unstructured.NestedString(s.(map[string]any), "name") + if name == "source-watcher" || name == "kustomize-controller" || name == "helm-controller" { + foundImpersonationBinding = true + } + } + } } g.Expect(foundImageAutomation).To(BeTrue(), "image-automation-controller deployment not found") g.Expect(foundImageReflector).To(BeTrue(), "image-reflector-controller deployment not found") g.Expect(foundSourceWatcher).To(BeTrue(), "source-watcher deployment not found") g.Expect(foundExternalArtifact).To(BeTrue(), "ExternalArtifact feature gate not found") + g.Expect(foundImpersonationRole).To(BeTrue(), "ServiceAccount impersonation ClusterRole not found") + g.Expect(foundImpersonationBinding).To(BeTrue(), "ServiceAccount impersonation ClusterRoleBinding not found") } diff --git a/manifests/rbac/impersonator.yaml b/manifests/rbac/impersonator.yaml new file mode 100644 index 0000000000..c926226fe3 --- /dev/null +++ b/manifests/rbac/impersonator.yaml @@ -0,0 +1,31 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: crd-controller-impersonator +rules: +# required for ServiceAccount impersonation +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - impersonate +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: crd-controller-impersonator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: crd-controller-impersonator +subjects: + - kind: ServiceAccount + name: kustomize-controller + namespace: flux-system + - kind: ServiceAccount + name: helm-controller + namespace: flux-system + - kind: ServiceAccount + name: source-watcher + namespace: flux-system diff --git a/manifests/rbac/kustomization.yaml b/manifests/rbac/kustomization.yaml index 4e708ef2f4..8866185817 100644 --- a/manifests/rbac/kustomization.yaml +++ b/manifests/rbac/kustomization.yaml @@ -2,6 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - controller.yaml + - impersonator.yaml - reconciler.yaml - edit.yaml - view.yaml