From f32da0c62bfdfeeee3dee454ae61f1d46895a51d Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 25 Mar 2026 15:54:33 +0100 Subject: [PATCH 1/6] chore: Describe RBAC rules, remove unnecessary rules --- .../listener-operator/templates/roles.yaml | 68 ++++++++++++++++--- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/deploy/helm/listener-operator/templates/roles.yaml b/deploy/helm/listener-operator/templates/roles.yaml index 03b75dad..5a8a1309 100644 --- a/deploy/helm/listener-operator/templates/roles.yaml +++ b/deploy/helm/listener-operator/templates/roles.yaml @@ -50,18 +50,27 @@ metadata: labels: {{- include "operator.labels" . | nindent 4 }} rules: + # Services are created per Listener via Server-Side Apply (create + patch). The + # ReconciliationPaused strategy fetches existing Services instead of re-applying (get). + # The controller watches owned Services to retrigger reconciliation (list + watch). + # Orphaned Services are cleaned up by ClusterResources (delete). - apiGroups: - "" resources: - - events - services verbs: - get - list - watch - create - - delete # Needed to set an ownerRef on already existing Services - patch + - delete + # PersistentVolumes are watched by the controller to retrigger Listener reconciliation + # when PV node affinity changes (list + watch). They are listed by label selector to + # find which nodes back a NodePort Listener, and fetched by the CSI node driver during + # volume publish (get). The CSI node driver patches PV labels via Server-Side Apply to + # associate PVs with their Listener (patch + create for SSA). The external-provisioner + # sidecar creates and deletes PVs in response to PVC lifecycle events (create + delete). - apiGroups: - "" resources: @@ -70,9 +79,18 @@ rules: - get - list - watch - - patch - create + - patch - delete + # Nodes are fetched to resolve external addresses for NodePort Listeners (get). The + # external-provisioner sidecar lists and watches Nodes to resolve CSI volume topology + # (required by --feature-gates=Topology=true). + # PersistentVolumeClaims are fetched by the CSI controller and node driver to determine + # the Listener selector for a volume (get). The external-provisioner sidecar watches + # PVCs to trigger provisioning (list + watch). + # Endpoints are watched to identify which nodes host the pods backing a NodePort + # Listener, as a fallback for older volumes that predate PV-label-based node discovery + # (get + list + watch). - apiGroups: - "" resources: @@ -83,13 +101,16 @@ rules: - get - list - watch - # For automatic cluster domain detection + # For automatic cluster domain detection via the local kubelet's configz API. - apiGroups: - "" resources: - nodes/proxy verbs: - get + # The external-provisioner sidecar reads CSINode objects to discover the topology keys + # supported by this driver, and reads StorageClasses to determine the provisioner name + # and volume binding mode. - apiGroups: - storage.k8s.io resources: @@ -99,6 +120,8 @@ rules: - get - list - watch + # The CSI node driver reads the Pod to discover container ports and node assignment + # (get), and labels the Pod so the Listener's Service selector can target it (patch). - apiGroups: - "" resources: @@ -106,6 +129,8 @@ rules: verbs: - get - patch + # Publish reconciliation errors as Kubernetes Events. The kube-rs Recorder uses the + # events.k8s.io/v1 API: create for new events, merge-patch to increment repeated ones. - apiGroups: - events.k8s.io resources: @@ -113,31 +138,54 @@ rules: verbs: - create - patch + # ListenerClasses define how Listeners are exposed. They are watched to trigger + # re-reconciliation of all Listeners using a changed class (list + watch + get). + # The operator creates preset ListenerClasses at startup via create-if-missing (create). - apiGroups: - listeners.stackable.tech resources: - listenerclasses - - listeners verbs: {{- if .Values.maintenance.customResourceDefinitions.maintain }} - - create - patch {{- end }} - get - list - watch + - create + # Listeners are the primary reconciled resource: the controller watches all of them + # (list + watch + get). The CSI node driver creates or updates Listeners via + # Server-Side Apply for volumes that reference a ListenerClass directly (create + patch). + # Orphaned Listeners created by the CSI node driver are removed by ClusterResources + # (delete). - apiGroups: - listeners.stackable.tech resources: - listeners + verbs: + - get + - list + - watch + - create + - patch + - delete + # Update the Listener's status with resolved ingress addresses after each reconciliation. + - apiGroups: + - listeners.stackable.tech + resources: - listeners/status - - listeners/finalizers - - podlisteners verbs: - patch + # PodListeners record the resolved listener addresses for each volume mounted in a Pod. + # The CSI node driver creates a PodListeners object when a Pod first mounts a Listener + # volume (create), then merge-patches it to add entries for additional volumes (patch). + - apiGroups: + - listeners.stackable.tech + resources: + - podlisteners + verbs: - create - - delete - - update + - patch {{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }} - apiGroups: - security.openshift.io From a8f7ce2b6c32487c06c95db7351b074e83187d97 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Wed, 25 Mar 2026 16:10:05 +0100 Subject: [PATCH 2/6] chore: Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b939b35..7997bc58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- Helm deployed RBAC permissions documented, with unnecessary permissions removed ([#380]). + +[#380]: https://github.com/stackabletech/listener-operator/pull/380 + ## [26.3.0] - 2026-03-16 ## [26.3.0-rc1] - 2026-03-16 From 9d65f5321a7beeeda63c1e293800ff9deb2dc7a0 Mon Sep 17 00:00:00 2001 From: Nick <10092581+NickLarsenNZ@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:04:47 +0200 Subject: [PATCH 3/6] 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 7997bc58..1e5593ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] -- Helm deployed RBAC permissions documented, with unnecessary permissions removed ([#380]). +- Document Helm deployed RBAC permissions and remove unnecessary permissions ([#380]). [#380]: https://github.com/stackabletech/listener-operator/pull/380 From 8b225d9aa50086b61550e32d2991e0a746d1c67a Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Thu, 9 Apr 2026 09:20:08 +0200 Subject: [PATCH 4/6] chore: Ensure all rules have a comment --- deploy/helm/listener-operator/templates/roles.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/deploy/helm/listener-operator/templates/roles.yaml b/deploy/helm/listener-operator/templates/roles.yaml index 5a8a1309..d3c3e317 100644 --- a/deploy/helm/listener-operator/templates/roles.yaml +++ b/deploy/helm/listener-operator/templates/roles.yaml @@ -187,6 +187,8 @@ rules: - create - patch {{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }} + # Required on OpenShift to allow the listener-operator pods to run with the listener-scc + # security context constraints. - apiGroups: - security.openshift.io resourceNames: @@ -196,17 +198,19 @@ rules: verbs: - use {{ end }} -# 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 }} + # 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: - customresourcedefinitions verbs: + {{ if .Values.maintenance.customResourceDefinitions.maintain }} + # 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. - create - patch + {{ end }} # Required for startup condition - list - watch -{{ end }} From b3066c84977281741135987fbd20f81fa59cf3e5 Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Thu, 9 Apr 2026 10:09:32 +0200 Subject: [PATCH 5/6] chore: Simplify comments --- .../listener-operator/templates/roles.yaml | 67 ++++++++----------- 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/deploy/helm/listener-operator/templates/roles.yaml b/deploy/helm/listener-operator/templates/roles.yaml index d3c3e317..49b2061e 100644 --- a/deploy/helm/listener-operator/templates/roles.yaml +++ b/deploy/helm/listener-operator/templates/roles.yaml @@ -50,10 +50,7 @@ metadata: labels: {{- include "operator.labels" . | nindent 4 }} rules: - # Services are created per Listener via Server-Side Apply (create + patch). The - # ReconciliationPaused strategy fetches existing Services instead of re-applying (get). - # The controller watches owned Services to retrigger reconciliation (list + watch). - # Orphaned Services are cleaned up by ClusterResources (delete). + # Service created per Listener. Applied via SSA and tracked for orphan cleanup. - apiGroups: - "" resources: @@ -65,12 +62,10 @@ rules: - create - patch - delete - # PersistentVolumes are watched by the controller to retrigger Listener reconciliation - # when PV node affinity changes (list + watch). They are listed by label selector to - # find which nodes back a NodePort Listener, and fetched by the CSI node driver during - # volume publish (get). The CSI node driver patches PV labels via Server-Side Apply to - # associate PVs with their Listener (patch + create for SSA). The external-provisioner - # sidecar creates and deletes PVs in response to PVC lifecycle events (create + delete). + # PersistentVolumes are watched to retrigger Listener reconciliation on node affinity + # changes. The CSI node driver patches PV labels via Server-Side Apply to associate PVs + # with their Listener. The external-provisioner sidecar creates and deletes PVs for PVC + # lifecycle events. - apiGroups: - "" resources: @@ -82,15 +77,13 @@ rules: - create - patch - delete - # Nodes are fetched to resolve external addresses for NodePort Listeners (get). The - # external-provisioner sidecar lists and watches Nodes to resolve CSI volume topology - # (required by --feature-gates=Topology=true). - # PersistentVolumeClaims are fetched by the CSI controller and node driver to determine - # the Listener selector for a volume (get). The external-provisioner sidecar watches - # PVCs to trigger provisioning (list + watch). - # Endpoints are watched to identify which nodes host the pods backing a NodePort - # Listener, as a fallback for older volumes that predate PV-label-based node discovery - # (get + list + watch). + # Nodes are fetched to resolve external addresses for NodePort Listeners. The + # external-provisioner sidecar lists and watches Nodes for CSI volume topology + # (--feature-gates=Topology=true). + # PersistentVolumeClaims are read by the CSI controller and node driver for Listener + # selector annotations. The external-provisioner sidecar watches PVCs to trigger PV provisioning. + # Endpoints are watched to discover which nodes back a NodePort Listener, as a fallback + # for older volumes that predate PV-label-based node discovery. - apiGroups: - "" resources: @@ -108,9 +101,8 @@ rules: - nodes/proxy verbs: - get - # The external-provisioner sidecar reads CSINode objects to discover the topology keys - # supported by this driver, and reads StorageClasses to determine the provisioner name - # and volume binding mode. + # Required by the external-provisioner sidecar to discover driver topology keys (CSINodes) + # and determine volume binding mode (StorageClasses). - apiGroups: - storage.k8s.io resources: @@ -120,8 +112,8 @@ rules: - get - list - watch - # The CSI node driver reads the Pod to discover container ports and node assignment - # (get), and labels the Pod so the Listener's Service selector can target it (patch). + # The CSI node driver reads the Pod to discover container ports and node assignment, + # and labels the Pod so the Listener's Service selector can target it. - apiGroups: - "" resources: @@ -129,8 +121,7 @@ rules: verbs: - get - patch - # Publish reconciliation errors as Kubernetes Events. The kube-rs Recorder uses the - # events.k8s.io/v1 API: create for new events, merge-patch to increment repeated ones. + # Publish reconciliation errors as Kubernetes Events. - apiGroups: - events.k8s.io resources: @@ -138,9 +129,8 @@ rules: verbs: - create - patch - # ListenerClasses define how Listeners are exposed. They are watched to trigger - # re-reconciliation of all Listeners using a changed class (list + watch + get). - # The operator creates preset ListenerClasses at startup via create-if-missing (create). + # ListenerClasses define how Listeners are exposed. Watched to retrigger reconciliation + # when a ListenerClass changes. The operator creates preset ListenerClasses at startup. - apiGroups: - listeners.stackable.tech resources: @@ -153,11 +143,9 @@ rules: - list - watch - create - # Listeners are the primary reconciled resource: the controller watches all of them - # (list + watch + get). The CSI node driver creates or updates Listeners via - # Server-Side Apply for volumes that reference a ListenerClass directly (create + patch). - # Orphaned Listeners created by the CSI node driver are removed by ClusterResources - # (delete). + # Listeners are the primary reconciled resource. The CSI node driver creates or updates + # Listeners via Server-Side Apply for volumes that reference a ListenerClass directly. + # Orphaned Listeners are cleaned up. - apiGroups: - listeners.stackable.tech resources: @@ -177,8 +165,8 @@ rules: verbs: - patch # PodListeners record the resolved listener addresses for each volume mounted in a Pod. - # The CSI node driver creates a PodListeners object when a Pod first mounts a Listener - # volume (create), then merge-patches it to add entries for additional volumes (patch). + # Created by the CSI node driver when a Pod first mounts a Listener volume, then patched + # to add entries for additional volumes. - apiGroups: - listeners.stackable.tech resources: @@ -198,16 +186,15 @@ rules: verbs: - use {{ end }} - # Required for maintaining the CRDs within the operator (including the conversion webhook info). - # Also for the startup condition check before the controller can run. + # Required for maintaining the CRDs (including the conversion webhook configuration) and + # for the startup condition check. - apiGroups: - apiextensions.k8s.io resources: - customresourcedefinitions verbs: {{ if .Values.maintenance.customResourceDefinitions.maintain }} - # 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. + # Required to maintain the CRD (e.g. conversion webhook certificate). - create - patch {{ end }} From 8182051e652ee2f6893bef41e5fdb417cc52882e Mon Sep 17 00:00:00 2001 From: Nick Larsen Date: Thu, 9 Apr 2026 13:13:19 +0200 Subject: [PATCH 6/6] chore: Restore rule for legacy events --- deploy/helm/listener-operator/templates/roles.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/deploy/helm/listener-operator/templates/roles.yaml b/deploy/helm/listener-operator/templates/roles.yaml index 49b2061e..d10f2796 100644 --- a/deploy/helm/listener-operator/templates/roles.yaml +++ b/deploy/helm/listener-operator/templates/roles.yaml @@ -50,6 +50,17 @@ metadata: labels: {{- include "operator.labels" . | nindent 4 }} rules: + # Required by the external-provisioner sidecar, which still uses the legacy core/v1 events + # API (not events.k8s.io). See upstream RBAC: + # https://github.com/kubernetes-csi/external-provisioner/blob/v5.3.0/deploy/kubernetes/rbac.yaml + - apiGroups: + - "" + resources: + - events + verbs: + - get + - list + - watch # Service created per Listener. Applied via SSA and tracked for orphan cleanup. - apiGroups: - ""