{{- if .Values.backend.enabled -}} apiVersion: v1 kind: ServiceAccount metadata: name: tower labels: {{- include "tower.labels" . | nindent 4 }} --- # Tower needs cluster-wide read on Nodes + node metrics for the # capacity-bar feature; namespace get/list/create so it can ensure # the tenants ns exists before ArgoCD creates instance resources; # argoproj.io CRD CRUD so it can create + delete Applications. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: {{ .Release.Name }}-tower labels: {{- include "tower.labels" . | nindent 4 }} rules: - apiGroups: ['argoproj.io'] resources: ['applications', 'appprojects'] verbs: ['get', 'list', 'create', 'update', 'patch', 'delete'] - apiGroups: [''] resources: ['namespaces'] verbs: ['get', 'list', 'create'] - apiGroups: [''] resources: ['nodes'] verbs: ['get', 'list'] - apiGroups: ['metrics.k8s.io'] resources: ['nodes'] verbs: ['get', 'list'] # Tenant-control surface: when the destination cluster IS the # admin platform (in-cluster lab1), Tower acts on `tenants` ns # resources directly using its own ServiceAccount. When the # destination is a registered customer cluster (e.g. customer1), # Tower uses that cluster's admin cert and these in-cluster # rules don't apply. Without these, the in-cluster path of every # backup/restore/migrate flow returned 403 from the K8s API # service-proxy and `waitForOdooReady` looped to timeout. # # `services/proxy` is the specific subresource the probe needs; # the rest (jobs/pods/STSes/Deployments/svc/secret/cm/pvc) cover # spawn-Job + scaleDeployment + delete-cascade. - apiGroups: [''] resources: ['services/proxy'] verbs: ['get', 'create'] - apiGroups: [''] resources: ['pods', 'pods/log', 'pods/exec'] verbs: ['get', 'list', 'watch', 'create', 'delete'] - apiGroups: ['apps'] resources: ['deployments', 'deployments/scale', 'statefulsets', 'statefulsets/scale', 'replicasets'] verbs: ['get', 'list', 'watch', 'patch', 'update', 'delete'] - apiGroups: ['batch'] resources: ['jobs', 'cronjobs'] verbs: ['get', 'list', 'watch', 'create', 'patch', 'update', 'delete'] - apiGroups: [''] resources: ['services', 'secrets', 'configmaps', 'persistentvolumeclaims', 'events'] verbs: ['get', 'list', 'watch', 'patch', 'update', 'delete'] - apiGroups: ['traefik.io'] resources: ['ingressroutes'] verbs: ['get', 'list', 'delete'] # Read ArgoCD's cluster Secrets (the registered customer-cluster # creds) so Tower can build cross-cluster API clients for capacity # queries. Scoped to a single Role+RoleBinding in the argocd ns — # cluster-scoped Secret access would be over-broad. --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: {{ .Release.Name }}-tower-cluster-secrets namespace: argocd labels: {{- include "tower.labels" . | nindent 4 }} rules: - apiGroups: [''] resources: ['secrets'] verbs: ['get', 'list'] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: {{ .Release.Name }}-tower-cluster-secrets namespace: argocd labels: {{- include "tower.labels" . | nindent 4 }} subjects: - kind: ServiceAccount name: tower namespace: {{ .Release.Namespace }} roleRef: kind: Role name: {{ .Release.Name }}-tower-cluster-secrets apiGroup: rbac.authorization.k8s.io --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: {{ .Release.Name }}-tower labels: {{- include "tower.labels" . | nindent 4 }} subjects: - kind: ServiceAccount name: tower namespace: {{ .Release.Namespace }} roleRef: kind: ClusterRole name: {{ .Release.Name }}-tower apiGroup: rbac.authorization.k8s.io {{- end }}