{{- if .Values.rbac.create }} {{/* Hand-written, and deliberately short. Why ClusterRole and not Role: the worker's job is to `helm upgrade --install` a tenant release into a namespace it creates. `namespaces` is a cluster-scoped resource — a namespaced Role cannot grant `create` on it, and cannot grant anything inside the tenant namespaces either, because they do not exist when the chart is installed. What keeps this least-privilege is not the scope, it is the contents: every resource and verb is named, there is no `*`, no cluster-admin, and no rbac.authorization.k8s.io group. That last omission is the load-bearing one — the worker cannot escalate itself, because it cannot create a RoleBinding at all. The reconciler binds to the same role but only ever reads; it enqueues tasks, it does not provision, and it never deletes an orphan. */}} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: {{ include "svcforge.fullname" . }}-provisioner labels: {{- include "svcforge.labels" . | nindent 4 }} rules: - apiGroups: [""] resources: [namespaces] verbs: [get, list, create] - apiGroups: [""] resources: [secrets, services, configmaps, persistentvolumeclaims, serviceaccounts] verbs: [get, list, watch, create, update, patch, delete] - apiGroups: [apps] resources: [deployments, statefulsets] verbs: [get, list, watch, create, update, patch, delete] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: {{ include "svcforge.fullname" . }}-provisioner labels: {{- include "svcforge.labels" . | nindent 4 }} roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: {{ include "svcforge.fullname" . }}-provisioner subjects: - kind: ServiceAccount name: {{ include "svcforge.serviceAccountName" (dict "ctx" $ "component" "worker") }} namespace: {{ .Release.Namespace }} - kind: ServiceAccount name: {{ include "svcforge.serviceAccountName" (dict "ctx" $ "component" "reconciler") }} namespace: {{ .Release.Namespace }} {{- end }}