From 423b87cd20319984d8fb734497d4c4a1cc57275d Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 25 Mar 2026 16:40:16 +0100 Subject: [PATCH 01/12] chore: Describe RBAC rules, remove unnecessary rules --- .../superset-operator/templates/roles.yaml | 72 +++++++++++++------ 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/deploy/helm/superset-operator/templates/roles.yaml b/deploy/helm/superset-operator/templates/roles.yaml index cf337e93..7a48831f 100644 --- a/deploy/helm/superset-operator/templates/roles.yaml +++ b/deploy/helm/superset-operator/templates/roles.yaml @@ -6,6 +6,7 @@ metadata: labels: {{- include "operator.labels" . | nindent 4 }} rules: + # For automatic cluster domain detection - apiGroups: - "" resources: @@ -13,31 +14,45 @@ rules: verbs: - list - watch - # For automatic cluster domain detection + # For automatic cluster domain detection (read node DNS details via kubelet proxy) - apiGroups: - "" resources: - nodes/proxy verbs: - get + # Manage core namespaced resources created per SupersetCluster. + # All resources are applied via Server-Side Apply (create + patch) and tracked for + # orphan cleanup (list + delete). The ReconciliationPaused strategy uses get instead + # of apply_patch, so get is also required. The update verb is not needed (SSA uses patch). + # - configmaps: store role group configuration and Vector log config; watched via .watches() + # - services: expose the Superset web UI (headless) and metrics endpoint; watched via .owns() - apiGroups: - "" resources: - - pods - configmaps - - secrets - services - - endpoints - - serviceaccounts - - secrets verbs: - create - delete - get - list - patch - - update - watch + # ServiceAccounts are created per SupersetCluster and per DruidConnection (for the import Job). + # Applied via SSA and tracked for orphan cleanup. Not watched by the controller. + - apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - create + - delete + - get + - list + - patch + # RoleBindings bind the product ClusterRole to per-cluster ServiceAccounts. + # Applied via SSA and tracked for orphan cleanup. Not watched by the controller. - apiGroups: - rbac.authorization.k8s.io resources: @@ -48,32 +63,34 @@ rules: - get - list - patch - - update - - watch + # StatefulSets run the Superset web server pods. + # Applied via SSA, tracked for orphan cleanup, and watched via .owns(). - apiGroups: - apps resources: - statefulsets verbs: - - get - create - delete + - get - list - patch - - update - watch + # Jobs run the Druid datasource import task (one Job per DruidConnection). + # Applied directly via SSA (not tracked by cluster_resources, so no orphan cleanup / no delete). + # The druid connection controller watches Jobs via .watches() and reads them via client.get(). - apiGroups: - batch resources: - jobs verbs: - create - - delete - get - list - patch - - update - watch + # PodDisruptionBudgets protect Superset pods from simultaneous voluntary eviction. + # Applied via SSA and tracked for orphan cleanup. Not watched by the controller. - apiGroups: - policy resources: @@ -84,8 +101,6 @@ rules: - get - list - patch - - update - - watch - apiGroups: - apiextensions.k8s.io resources: @@ -101,6 +116,7 @@ rules: - list - watch {{- end }} + # Required to emit Kubernetes events reporting reconciliation results and errors. - apiGroups: - events.k8s.io resources: @@ -108,23 +124,33 @@ rules: verbs: - create - patch + # Watch the operator's own CRDs. The superset controller is triggered by SupersetCluster changes; + # the druid connection controller is triggered by DruidConnection changes and also watches + # SupersetCluster (to react when the cluster becomes available). - apiGroups: - {{ include "operator.name" . }}.stackable.tech resources: - {{ include "operator.name" . }}clusters - druidconnections - - druidconnections/status verbs: - get - list - - patch - watch + # Patch status for SupersetCluster (reports conditions such as Available/Degraded). - apiGroups: - {{ include "operator.name" . }}.stackable.tech resources: - {{ include "operator.name" . }}clusters/status verbs: - patch + # Patch status for DruidConnection (tracks import job progress: Pending/Importing/Ready/Failed). + - apiGroups: + - {{ include "operator.name" . }}.stackable.tech + resources: + - druidconnections/status + verbs: + - patch + # Watch AuthenticationClass resources to react when authentication configuration changes. - apiGroups: - authentication.stackable.tech resources: @@ -133,6 +159,7 @@ rules: - get - list - watch + # Bind the product ClusterRole to per-cluster ServiceAccounts (creates RoleBindings). - apiGroups: - rbac.authorization.k8s.io resources: @@ -141,17 +168,18 @@ rules: - bind resourceNames: - {{ include "operator.name" . }}-clusterrole + # Listeners expose Superset externally via the Stackable Listener Operator. + # Applied via SSA and tracked for orphan cleanup. Not watched by the controller. - apiGroups: - listeners.stackable.tech resources: - listeners verbs: + - create + - delete - get - list - - watch - patch - - create - - delete --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -160,6 +188,8 @@ metadata: labels: {{- include "operator.labels" . | nindent 4 }} rules: + # The Superset pod reads its own ServiceAccount token and ConfigMaps (e.g. for OPA role mapping). + # It also reads the credentials Secret to obtain database URI, secret key, and admin credentials. - apiGroups: - "" resources: @@ -168,6 +198,7 @@ rules: - serviceaccounts verbs: - get + # Required to emit Kubernetes events from within the Superset pod. - apiGroups: - events.k8s.io resources: @@ -176,6 +207,7 @@ rules: - create - patch {{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }} + # Required on OpenShift to allow Superset pods to run as a non-root user (nonroot-v2 SCC). - apiGroups: - security.openshift.io resources: From 01c8cc904e786a26c4b77a8090344cfe5280cc2a Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 25 Mar 2026 16:42:07 +0100 Subject: [PATCH 02/12] chore: Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69b5cdea..d971bde5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +- Helm deployed RBAC permissions documented, with unnecessary permissions removed ([#717]). + +[#717]: https://github.com/stackabletech/superset-operator/pull/717 + ## [26.3.0] - 2026-03-16 ## [26.3.0-rc1] - 2026-03-16 From e624eb4de8c96e64f2d5f214a1f4c850c17ec892 Mon Sep 17 00:00:00 2001 From: Nick <10092581+NickLarsenNZ@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:06:19 +0200 Subject: [PATCH 03/12] Apply suggestions from code review Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d971bde5..81f342d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## [Unreleased] -- Helm deployed RBAC permissions documented, with unnecessary permissions removed ([#717]). +- Document Helm deployed RBAC permissions and remove unnecessary permissions ([#717]). [#717]: https://github.com/stackabletech/superset-operator/pull/717 From 9420a0a73ab9e67c793cc4a5d27707746eb1e95d Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 8 Apr 2026 14:40:08 +0200 Subject: [PATCH 04/12] chore: Add missing comment on rule --- deploy/helm/superset-operator/templates/roles.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deploy/helm/superset-operator/templates/roles.yaml b/deploy/helm/superset-operator/templates/roles.yaml index 7a48831f..bcb949ce 100644 --- a/deploy/helm/superset-operator/templates/roles.yaml +++ b/deploy/helm/superset-operator/templates/roles.yaml @@ -101,6 +101,8 @@ rules: - get - list - patch + # Required for maintaining the CRDs within the operator (including the conversion webhook info). + # Also for the startup condition check before the controller can run. - apiGroups: - apiextensions.k8s.io resources: From 7119bbcaa1e0e2dc5f058d594e41b038c97a10b7 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 8 Apr 2026 14:41:06 +0200 Subject: [PATCH 05/12] chore: Remove the get for customresourcedefinitions for the operator clusterrole Not needed for crd maintenance --- deploy/helm/superset-operator/templates/roles.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/deploy/helm/superset-operator/templates/roles.yaml b/deploy/helm/superset-operator/templates/roles.yaml index bcb949ce..ed5ed874 100644 --- a/deploy/helm/superset-operator/templates/roles.yaml +++ b/deploy/helm/superset-operator/templates/roles.yaml @@ -108,7 +108,6 @@ rules: resources: - customresourcedefinitions verbs: - - get # Required to maintain the CRD. The operator needs to do this, as it needs to enter e.g. it's # generated certificate in the conversion webhook. {{- if .Values.maintenance.customResourceDefinitions.maintain }} From a92afa1898547a9449f0a2f588f3ccde8db574fb Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 8 Apr 2026 14:41:58 +0200 Subject: [PATCH 06/12] chore: Remove the nodes list/watch rule for the operator clusterrole Not needed for clusterDomain detection --- deploy/helm/superset-operator/templates/roles.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/deploy/helm/superset-operator/templates/roles.yaml b/deploy/helm/superset-operator/templates/roles.yaml index ed5ed874..f0385de1 100644 --- a/deploy/helm/superset-operator/templates/roles.yaml +++ b/deploy/helm/superset-operator/templates/roles.yaml @@ -6,14 +6,6 @@ metadata: labels: {{- include "operator.labels" . | nindent 4 }} rules: - # For automatic cluster domain detection - - apiGroups: - - "" - resources: - - nodes - verbs: - - list - - watch # For automatic cluster domain detection (read node DNS details via kubelet proxy) - apiGroups: - "" From 744a0175fb9417dca37675345a2f78d5163db6d3 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 8 Apr 2026 14:51:30 +0200 Subject: [PATCH 07/12] chore: Remove the configmaps/secrets/serviceaccounts get rule for the product clusterrole All secrets/configmaps are mounted, not accessed via the superset app --- deploy/helm/superset-operator/templates/roles.yaml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/deploy/helm/superset-operator/templates/roles.yaml b/deploy/helm/superset-operator/templates/roles.yaml index f0385de1..91b00540 100644 --- a/deploy/helm/superset-operator/templates/roles.yaml +++ b/deploy/helm/superset-operator/templates/roles.yaml @@ -181,16 +181,6 @@ metadata: labels: {{- include "operator.labels" . | nindent 4 }} rules: - # The Superset pod reads its own ServiceAccount token and ConfigMaps (e.g. for OPA role mapping). - # It also reads the credentials Secret to obtain database URI, secret key, and admin credentials. - - apiGroups: - - "" - resources: - - configmaps - - secrets - - serviceaccounts - verbs: - - get # Required to emit Kubernetes events from within the Superset pod. - apiGroups: - events.k8s.io From c934e584231a7e59c16ae0465940b2d014be22b2 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 8 Apr 2026 14:51:56 +0200 Subject: [PATCH 08/12] fix: customresourcedefinitions is always required for the startup condition --- deploy/helm/superset-operator/templates/roles.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/helm/superset-operator/templates/roles.yaml b/deploy/helm/superset-operator/templates/roles.yaml index 91b00540..81cab347 100644 --- a/deploy/helm/superset-operator/templates/roles.yaml +++ b/deploy/helm/superset-operator/templates/roles.yaml @@ -105,10 +105,10 @@ rules: {{- if .Values.maintenance.customResourceDefinitions.maintain }} - create - patch + {{- end }} # Required for startup condition - list - watch - {{- end }} # Required to emit Kubernetes events reporting reconciliation results and errors. - apiGroups: - events.k8s.io From 19b74abeb54909d2572374d379cfe047db3b03f0 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 8 Apr 2026 15:00:41 +0200 Subject: [PATCH 09/12] chore: Simplify rule comments --- .../superset-operator/templates/roles.yaml | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/deploy/helm/superset-operator/templates/roles.yaml b/deploy/helm/superset-operator/templates/roles.yaml index 81cab347..5d9be267 100644 --- a/deploy/helm/superset-operator/templates/roles.yaml +++ b/deploy/helm/superset-operator/templates/roles.yaml @@ -6,7 +6,7 @@ metadata: labels: {{- include "operator.labels" . | nindent 4 }} rules: - # For automatic cluster domain detection (read node DNS details via kubelet proxy) + # For automatic cluster domain detection - apiGroups: - "" resources: @@ -15,10 +15,7 @@ rules: - get # Manage core namespaced resources created per SupersetCluster. # All resources are applied via Server-Side Apply (create + patch) and tracked for - # orphan cleanup (list + delete). The ReconciliationPaused strategy uses get instead - # of apply_patch, so get is also required. The update verb is not needed (SSA uses patch). - # - configmaps: store role group configuration and Vector log config; watched via .watches() - # - services: expose the Superset web UI (headless) and metrics endpoint; watched via .owns() + # orphan cleanup (list + delete). ReconciliationPaused uses get. - apiGroups: - "" resources: @@ -31,8 +28,8 @@ rules: - list - patch - watch - # ServiceAccounts are created per SupersetCluster and per DruidConnection (for the import Job). - # Applied via SSA and tracked for orphan cleanup. Not watched by the controller. + # ServiceAccount created per SupersetCluster and per DruidConnection. + # Applied via SSA and tracked for orphan cleanup. - apiGroups: - "" resources: @@ -43,8 +40,8 @@ rules: - get - list - patch - # RoleBindings bind the product ClusterRole to per-cluster ServiceAccounts. - # Applied via SSA and tracked for orphan cleanup. Not watched by the controller. + # RoleBinding created per SupersetCluster to bind the product ClusterRole to the workload + # ServiceAccount. Applied via SSA and tracked for orphan cleanup. - apiGroups: - rbac.authorization.k8s.io resources: @@ -55,8 +52,7 @@ rules: - get - list - patch - # StatefulSets run the Superset web server pods. - # Applied via SSA, tracked for orphan cleanup, and watched via .owns(). + # StatefulSet created per role group. Applied via SSA and tracked for orphan cleanup. - apiGroups: - apps resources: @@ -68,9 +64,7 @@ rules: - list - patch - watch - # Jobs run the Druid datasource import task (one Job per DruidConnection). - # Applied directly via SSA (not tracked by cluster_resources, so no orphan cleanup / no delete). - # The druid connection controller watches Jobs via .watches() and reads them via client.get(). + # Job created per DruidConnection to run the datasource import task. - apiGroups: - batch resources: @@ -81,8 +75,7 @@ rules: - list - patch - watch - # PodDisruptionBudgets protect Superset pods from simultaneous voluntary eviction. - # Applied via SSA and tracked for orphan cleanup. Not watched by the controller. + # PodDisruptionBudget created per role. Applied via SSA and tracked for orphan cleanup. - apiGroups: - policy resources: @@ -109,7 +102,7 @@ rules: # Required for startup condition - list - watch - # Required to emit Kubernetes events reporting reconciliation results and errors. + # Required to report reconciliation results and errors back to the SupersetCluster object. - apiGroups: - events.k8s.io resources: @@ -117,9 +110,7 @@ rules: verbs: - create - patch - # Watch the operator's own CRDs. The superset controller is triggered by SupersetCluster changes; - # the druid connection controller is triggered by DruidConnection changes and also watches - # SupersetCluster (to react when the cluster becomes available). + # Primary CRDs: SupersetCluster and DruidConnection. - apiGroups: - {{ include "operator.name" . }}.stackable.tech resources: @@ -152,7 +143,7 @@ rules: - get - list - watch - # Bind the product ClusterRole to per-cluster ServiceAccounts (creates RoleBindings). + # Required to bind the product ClusterRole to the per-cluster ServiceAccount. - apiGroups: - rbac.authorization.k8s.io resources: @@ -161,8 +152,8 @@ rules: - bind resourceNames: - {{ include "operator.name" . }}-clusterrole - # Listeners expose Superset externally via the Stackable Listener Operator. - # Applied via SSA and tracked for orphan cleanup. Not watched by the controller. + # Listener created per role group for external access. Applied via SSA and tracked for orphan + # cleanup. - apiGroups: - listeners.stackable.tech resources: @@ -190,7 +181,7 @@ rules: - create - patch {{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }} - # Required on OpenShift to allow Superset pods to run as a non-root user (nonroot-v2 SCC). + # Required on OpenShift to allow Superset pods to run as a non-root user. - apiGroups: - security.openshift.io resources: From 78a7e0135e9f789e7ffd5bc2d4270393bd97aca4 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 8 Apr 2026 15:01:32 +0200 Subject: [PATCH 10/12] chore: Remove the events.k8s.io rule from the product ClusterRole The operator manages events --- deploy/helm/superset-operator/templates/roles.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/deploy/helm/superset-operator/templates/roles.yaml b/deploy/helm/superset-operator/templates/roles.yaml index 5d9be267..bbb7dd5c 100644 --- a/deploy/helm/superset-operator/templates/roles.yaml +++ b/deploy/helm/superset-operator/templates/roles.yaml @@ -172,14 +172,6 @@ metadata: labels: {{- include "operator.labels" . | nindent 4 }} rules: - # Required to emit Kubernetes events from within the Superset pod. - - apiGroups: - - events.k8s.io - resources: - - events - verbs: - - create - - patch {{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }} # Required on OpenShift to allow Superset pods to run as a non-root user. - apiGroups: From d34841c73f4a6f486375f72a695126ccce088cd2 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 8 Apr 2026 15:02:21 +0200 Subject: [PATCH 11/12] chore: Keep the rbac.authorization.k8s.io rules within a ClusterRole close to each other --- .../superset-operator/templates/roles.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/deploy/helm/superset-operator/templates/roles.yaml b/deploy/helm/superset-operator/templates/roles.yaml index bbb7dd5c..6d39e143 100644 --- a/deploy/helm/superset-operator/templates/roles.yaml +++ b/deploy/helm/superset-operator/templates/roles.yaml @@ -52,6 +52,15 @@ rules: - get - list - patch + # Required to bind the product ClusterRole to the per-cluster ServiceAccount. + - apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + verbs: + - bind + resourceNames: + - {{ include "operator.name" . }}-clusterrole # StatefulSet created per role group. Applied via SSA and tracked for orphan cleanup. - apiGroups: - apps @@ -143,15 +152,6 @@ rules: - get - list - watch - # Required to bind the product ClusterRole to the per-cluster ServiceAccount. - - apiGroups: - - rbac.authorization.k8s.io - resources: - - clusterroles - verbs: - - bind - resourceNames: - - {{ include "operator.name" . }}-clusterrole # Listener created per role group for external access. Applied via SSA and tracked for orphan # cleanup. - apiGroups: From 408510463fab83b9cac776666dae262161384ef4 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 8 Apr 2026 15:04:31 +0200 Subject: [PATCH 12/12] chore: Split the roles.yaml into separate files for clusterrole-operator.yaml and clusterrole-product.yaml --- .../{roles.yaml => clusterrole-operator.yaml} | 19 ----------------- .../templates/clusterrole-product.yaml | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 19 deletions(-) rename deploy/helm/superset-operator/templates/{roles.yaml => clusterrole-operator.yaml} (89%) create mode 100644 deploy/helm/superset-operator/templates/clusterrole-product.yaml diff --git a/deploy/helm/superset-operator/templates/roles.yaml b/deploy/helm/superset-operator/templates/clusterrole-operator.yaml similarity index 89% rename from deploy/helm/superset-operator/templates/roles.yaml rename to deploy/helm/superset-operator/templates/clusterrole-operator.yaml index 6d39e143..a81c1a2a 100644 --- a/deploy/helm/superset-operator/templates/roles.yaml +++ b/deploy/helm/superset-operator/templates/clusterrole-operator.yaml @@ -164,22 +164,3 @@ rules: - get - list - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "operator.name" . }}-clusterrole - labels: - {{- include "operator.labels" . | nindent 4 }} -rules: -{{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }} - # Required on OpenShift to allow Superset pods to run as a non-root user. - - apiGroups: - - security.openshift.io - resources: - - securitycontextconstraints - resourceNames: - - nonroot-v2 - verbs: - - use -{{ end }} diff --git a/deploy/helm/superset-operator/templates/clusterrole-product.yaml b/deploy/helm/superset-operator/templates/clusterrole-product.yaml new file mode 100644 index 00000000..ebabcd19 --- /dev/null +++ b/deploy/helm/superset-operator/templates/clusterrole-product.yaml @@ -0,0 +1,21 @@ +--- +# Product ClusterRole: bound (via per SupersetCluster RoleBinding) to the ServiceAccount that +# Superset workload pods run as. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "operator.name" . }}-clusterrole + labels: + {{- include "operator.labels" . | nindent 4 }} +rules: +{{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }} + # Required on OpenShift to allow Superset pods to run as a non-root user. + - apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - nonroot-v2 + verbs: + - use +{{ end }}