diff --git a/config/user-rbac/dashboard_editor.yaml b/config/user-rbac/dashboard_editor.yaml index 8f15cb0..fab870e 100644 --- a/config/user-rbac/dashboard_editor.yaml +++ b/config/user-rbac/dashboard_editor.yaml @@ -1,8 +1,9 @@ # ClusterRole to bind to users who can also create, update and delete ClusterProfiles and # Profiles from the Sveltos dashboard. Write access to ClusterProfiles is effectively admin on # every cluster they select, because addon-controller deploys them with its own privileges. -# The ConfigMaps and Secrets a profile references are deliberately not covered here: grant them -# with a namespaced Role in the namespaces that hold the profile content. +# The dashboard stores the YAML of a profile in a ConfigMap, so the role also grants create, +# delete, get and update on ConfigMaps in every namespace. Secrets are deliberately not covered: +# grant them with a namespaced Role in the namespaces that hold the profile content. # Keep in sync with the SubjectAccessReviews issued in internal/server/k8s-utils.go and the # writes in internal/server/profile_writes.go. apiVersion: rbac.authorization.k8s.io/v1 @@ -39,6 +40,15 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - update - apiGroups: - lib.projectsveltos.io resources: diff --git a/internal/server/dashboard_roles_test.go b/internal/server/dashboard_roles_test.go index a5d5b60..2838783 100644 --- a/internal/server/dashboard_roles_test.go +++ b/internal/server/dashboard_roles_test.go @@ -168,17 +168,37 @@ var _ = Describe("Dashboard ClusterRoles", func() { Expect(isAllowed(user, verbUpdate, writableGroup, rbacClusterSummaries)).To(BeFalse()) }) - It("neither role grants access to Secrets or ConfigMaps", func() { + It("sveltos-dashboard-editor allows the ConfigMap writes the dashboard sends and no others", func() { + user := randomString() + bindToUser(user, createShippedRole(editorRoleFile, dashboardEditor)) + + // Creating a profile from YAML creates the ConfigMap holding the YAML, and deletes it if the + // profile cannot be created. Editing a profile gets and updates the ConfigMap. + for _, verb := range []string{verbGet, verbCreate, verbUpdate, verbDelete} { + Eventually(func() bool { + return isAllowed(user, verb, "", configMapResource) + }).Should(BeTrue(), "%s %s", verb, configMapResource) + } + + for _, verb := range []string{verbList, verbPatch} { + Expect(isAllowed(user, verb, "", configMapResource)).To(BeFalse(), "%s %s", verb, configMapResource) + } + }) + + It("neither role grants access to Secrets and the viewer does not grant access to ConfigMaps", func() { viewer := randomString() bindToUser(viewer, createShippedRole(viewerRoleFile, dashboardViewer)) editor := randomString() bindToUser(editor, createShippedRole(editorRoleFile, dashboardEditor)) + verbs := []string{verbGet, verbList, verbCreate, verbUpdate, verbDelete} + for _, verb := range verbs { + Expect(isAllowed(viewer, verb, "", configMapResource)).To(BeFalse(), "%s %s", verb, configMapResource) + } + for _, user := range []string{viewer, editor} { - for _, resource := range []string{secretsResource, configMapResource} { - for _, verb := range []string{verbGet, verbList, verbCreate, verbUpdate, verbDelete} { - Expect(isAllowed(user, verb, "", resource)).To(BeFalse(), "%s %s", verb, resource) - } + for _, verb := range verbs { + Expect(isAllowed(user, verb, "", secretsResource)).To(BeFalse(), "%s %s", verb, secretsResource) } } }) diff --git a/manifest/manifest.yaml b/manifest/manifest.yaml index eb61d7c..6f2be41 100644 --- a/manifest/manifest.yaml +++ b/manifest/manifest.yaml @@ -246,6 +246,15 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - update - apiGroups: - lib.projectsveltos.io resources: