diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e0c466f..a5b2b200 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Changed + +- Helm deployed RBAC permissions documented, with unnecessary permissions removed ([#770]). + +[#770]: https://github.com/stackabletech/hdfs-operator/pull/770 + ## [26.3.0] - 2026-03-16 ## [26.3.0-rc1] - 2026-03-16 diff --git a/Cargo.nix b/Cargo.nix index ae8f95fe..06b24d1d 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -4813,7 +4813,7 @@ rec { src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; rev = "7486017f60827d1d769d7bf17bf56adb21f8bb02"; - sha256 = "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17"; + sha256 = "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2"; }; libName = "k8s_version"; authors = [ @@ -9422,7 +9422,7 @@ rec { src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; rev = "7486017f60827d1d769d7bf17bf56adb21f8bb02"; - sha256 = "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17"; + sha256 = "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2"; }; libName = "stackable_certs"; authors = [ @@ -9623,7 +9623,7 @@ rec { src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; rev = "7486017f60827d1d769d7bf17bf56adb21f8bb02"; - sha256 = "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17"; + sha256 = "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2"; }; libName = "stackable_operator"; authors = [ @@ -9795,7 +9795,7 @@ rec { src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; rev = "7486017f60827d1d769d7bf17bf56adb21f8bb02"; - sha256 = "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17"; + sha256 = "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2"; }; procMacro = true; libName = "stackable_operator_derive"; @@ -9830,7 +9830,7 @@ rec { src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; rev = "7486017f60827d1d769d7bf17bf56adb21f8bb02"; - sha256 = "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17"; + sha256 = "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2"; }; libName = "stackable_shared"; authors = [ @@ -9911,7 +9911,7 @@ rec { src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; rev = "7486017f60827d1d769d7bf17bf56adb21f8bb02"; - sha256 = "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17"; + sha256 = "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2"; }; libName = "stackable_telemetry"; authors = [ @@ -10021,7 +10021,7 @@ rec { src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; rev = "7486017f60827d1d769d7bf17bf56adb21f8bb02"; - sha256 = "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17"; + sha256 = "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2"; }; libName = "stackable_versioned"; authors = [ @@ -10065,7 +10065,7 @@ rec { src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; rev = "7486017f60827d1d769d7bf17bf56adb21f8bb02"; - sha256 = "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17"; + sha256 = "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2"; }; procMacro = true; libName = "stackable_versioned_macros"; @@ -10133,7 +10133,7 @@ rec { src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; rev = "7486017f60827d1d769d7bf17bf56adb21f8bb02"; - sha256 = "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17"; + sha256 = "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2"; }; libName = "stackable_webhook"; authors = [ diff --git a/crate-hashes.json b/crate-hashes.json index 3714ebda..2148b36f 100644 --- a/crate-hashes.json +++ b/crate-hashes.json @@ -4,14 +4,14 @@ "git+https://github.com/kube-rs/kube-rs?rev=fe69cc486ff8e62a7da61d64ec3ebbd9e64c43b5#kube-derive@3.0.1": "1irm4g79crlxjm3iqrgvx0f6wxdcj394ky84q89pk9i36y2mlw3n", "git+https://github.com/kube-rs/kube-rs?rev=fe69cc486ff8e62a7da61d64ec3ebbd9e64c43b5#kube-runtime@3.0.1": "1irm4g79crlxjm3iqrgvx0f6wxdcj394ky84q89pk9i36y2mlw3n", "git+https://github.com/kube-rs/kube-rs?rev=fe69cc486ff8e62a7da61d64ec3ebbd9e64c43b5#kube@3.0.1": "1irm4g79crlxjm3iqrgvx0f6wxdcj394ky84q89pk9i36y2mlw3n", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#k8s-version@0.1.3": "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-certs@0.4.0": "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-operator-derive@0.3.1": "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-operator@0.108.0": "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-shared@0.1.0": "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-telemetry@0.6.2": "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-versioned-macros@0.8.3": "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-versioned@0.8.3": "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-webhook@0.9.0": "0yxp9d7x3xzlc7i67mjkizf587hvx8kwjly9p10x320hvp91qf17", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#k8s-version@0.1.3": "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-certs@0.4.0": "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-operator-derive@0.3.1": "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-operator@0.108.0": "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-shared@0.1.0": "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-telemetry@0.6.2": "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-versioned-macros@0.8.3": "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-versioned@0.8.3": "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.108.0#stackable-webhook@0.9.0": "1fgc7i8rhq1nl9m4s69sbfiywy2jx4narpynvm3g54vd5yd4c6m2", "git+https://github.com/stackabletech/product-config.git?tag=0.8.0#product-config@0.8.0": "1dz70kapm2wdqcr7ndyjji0lhsl98bsq95gnb2lw487wf6yr7987" } \ No newline at end of file diff --git a/deploy/helm/hdfs-operator/templates/roles.yaml b/deploy/helm/hdfs-operator/templates/clusterrole-operator.yaml similarity index 55% rename from deploy/helm/hdfs-operator/templates/roles.yaml rename to deploy/helm/hdfs-operator/templates/clusterrole-operator.yaml index 347f4fef..5eb0b66b 100644 --- a/deploy/helm/hdfs-operator/templates/roles.yaml +++ b/deploy/helm/hdfs-operator/templates/clusterrole-operator.yaml @@ -6,74 +6,63 @@ metadata: labels: {{- include "operator.labels" . | nindent 4 }} rules: - - apiGroups: - - "" - resources: - - nodes - verbs: - - list - - watch - - get - # For automatic cluster domain detection + # For automatic cluster domain detection (reads kubelet config via the nodes/proxy API). - apiGroups: - "" resources: - nodes/proxy verbs: - get + # Manage core workload resources created per HdfsCluster. + # All resources are applied via Server-Side Apply (create + patch) and tracked for + # orphan cleanup (list + delete). - apiGroups: - "" resources: - - pods - configmaps - - secrets - services - - endpoints - - serviceaccounts verbs: - create - delete - get - list - patch - - update - watch + # serviceaccounts are applied via SSA and tracked for orphan cleanup. - apiGroups: - - rbac.authorization.k8s.io + - "" resources: - - rolebindings + - serviceaccounts verbs: - create - delete - get - list - patch - - update - - watch + # rolebindings are applied via SSA and tracked for orphan cleanup. - apiGroups: - - apps + - rbac.authorization.k8s.io resources: - - statefulsets + - rolebindings verbs: - - get - create - delete + - get - list - patch - - update - - watch + # statefulsets are applied via SSA, tracked for orphan cleanup. - apiGroups: - - batch + - apps resources: - - jobs + - statefulsets verbs: - create - delete - get - list - patch - - update - watch + # poddisruptionbudgets are applied via SSA and tracked for orphan cleanup. - apiGroups: - policy resources: @@ -84,23 +73,23 @@ rules: - get - list - patch - - update - - watch + # 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: - - 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 }} - create - patch + {{- end }} # Required for startup condition - list - watch - {{- end }} + # Required to report reconciliation results and warnings back to the HdfsCluster object. - apiGroups: - events.k8s.io resources: @@ -108,20 +97,15 @@ rules: verbs: - create - patch + # Read listener addresses to build the discovery ConfigMap for downstream clients. + # Listeners are managed by the listener-operator; this operator only reads them. - apiGroups: - listeners.stackable.tech resources: - listeners verbs: - get - - list - - apiGroups: - - "" - resources: - - endpoints - verbs: - - get - - list + # Watch HdfsClusters for reconciliation - apiGroups: - {{ include "operator.name" . }}.stackable.tech resources: @@ -129,14 +113,17 @@ rules: verbs: - get - list - - patch - watch + # Status subresource: updated at the end of every reconciliation. - apiGroups: - {{ include "operator.name" . }}.stackable.tech resources: - {{ include "operator.name" . }}clusters/status verbs: - patch + # Manage the hdfs-clusterrolebinding-nodes ClusterRoleBinding via Server-Side Apply. + # This binding grants the HDFS product pods (topology provider) access to node and pod + # information for rack awareness. Scoped to the specific ClusterRoleBinding by name. - apiGroups: - rbac.authorization.k8s.io resources: @@ -144,12 +131,15 @@ rules: resourceNames: - {{ include "operator.name" . }}-clusterrolebinding-nodes verbs: - - patch - - get - - update - - list - - watch - create + - patch + # Allow binding the product ClusterRoles: + # - hdfs-clusterrole: referenced by per-cluster RoleBindings created by the operator + # - hdfs-clusterrole-nodes: referenced by the shared hdfs-clusterrolebinding-nodes + # ClusterRoleBinding managed by the operator. The bind verb is required because the + # operator itself does not hold all permissions that hdfs-clusterrole-nodes grants + # (nodes, endpoints, pods, listeners), so Kubernetes would otherwise reject the + # ClusterRoleBinding patch as a privilege escalation. - apiGroups: - rbac.authorization.k8s.io resources: @@ -158,76 +148,4 @@ rules: - bind resourceNames: - {{ include "operator.name" . }}-clusterrole ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "operator.name" . }}-clusterrole - labels: - {{- include "operator.labels" . | nindent 4 }} -rules: - - apiGroups: - - "" - resources: - - configmaps - - secrets - - serviceaccounts - - pods - verbs: - - get - - apiGroups: - - "" - resources: - - pods - verbs: - - list - - apiGroups: - - events.k8s.io - resources: - - events - verbs: - - create - - patch -{{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }} - - apiGroups: - - security.openshift.io - resources: - - securitycontextconstraints - resourceNames: - - nonroot-v2 - verbs: - - use -{{ end }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "operator.name" . }}-clusterrole-nodes - labels: - {{- include "operator.labels" . | nindent 4 }} -rules: - - apiGroups: - - "" - resources: - - pods - - nodes - - endpoints - verbs: - - get - - list - # needed for pod informer - - watch - - apiGroups: - - listeners.stackable.tech - resources: - - listeners - verbs: - - get - - list - # needed to query the crd version (v1alpha1 etc.) before fetching listeners - - apiGroups: - - apiextensions.k8s.io - resources: - - customresourcedefinitions - verbs: - - get + - {{ include "operator.name" . }}-clusterrole-nodes diff --git a/deploy/helm/hdfs-operator/templates/clusterrole-product.yaml b/deploy/helm/hdfs-operator/templates/clusterrole-product.yaml new file mode 100644 index 00000000..dad2330f --- /dev/null +++ b/deploy/helm/hdfs-operator/templates/clusterrole-product.yaml @@ -0,0 +1,75 @@ +--- +# Product ClusterRole: bound (via per-HdfsCluster RoleBinding) to the ServiceAccount that HDFS +# workload pods (namenodes, datanodes, journalnodes) run as. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "operator.name" . }}-clusterrole + labels: + {{- include "operator.labels" . | nindent 4 }} +rules: + # List and get pods for peer discovery (e.g. format-namenodes discovering namenode peers). + # These must live here in the product ClusterRole (not just in the nodes ClusterRole) because + # the nodes ClusterRoleBinding is updated asynchronously and may not yet include this service + # account when format-namenodes runs for the first time. + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - list + - watch +{{ if .Capabilities.APIVersions.Has "security.openshift.io/v1" }} + # Required on OpenShift to allow HDFS pods to run as a non-root user. + - apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + resourceNames: + - nonroot-v2 + verbs: + - use +{{ end }} +--- +# Nodes ClusterRole: bound (via a single cluster-scoped ClusterRoleBinding) to the same +# ServiceAccount. Grants the additional permissions needed by the HDFS topology provider +# for rack awareness, which requires cluster-wide pod, node, and endpoint access. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "operator.name" . }}-clusterrole-nodes + labels: + {{- include "operator.labels" . | nindent 4 }} +rules: + # The topology provider (rack awareness) running inside HDFS pods queries pod, node, and + # endpoint information to compute rack labels for DataNodes. + # - pods: discover pod-to-node assignment for topology calculation; watched by pod informer + # - nodes: read rack labels (e.g. topology.kubernetes.io/zone) from the node the pod runs on + # - endpoints: map service endpoints to pod IPs for topology resolution + - apiGroups: + - "" + resources: + - pods + - nodes + - endpoints + verbs: + - get + - list + # needed for pod informer + - watch + # Read listener addresses so HDFS pods can resolve external access endpoints. + - apiGroups: + - listeners.stackable.tech + resources: + - listeners + verbs: + - get + - list + # Required to discover the installed CRD API version before querying listeners. + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - get