ADR-1592: the controller runs under its own service account, the only one that may read VmafxTenants¶
- Status: Accepted
- Date: 2026-10-04
- Deciders: maintainer, agent
- Tags: helm, security, controller, operator, rbac, phase4b, fork-local
Context¶
With a tenant registry (ADR-1519) the chart bound the VmafxTenant reader Role to the chart's single service account, which the server, job and node pods use as well, so every one of those pods could list the tenants' identity-provider settings. The operator's ClusterRole also carried get/list/watch (and status updates) on vmafxtenants cluster-wide, added by ADR-1058 for a tenant reconciler that does not exist: the operator registers no VmafxTenant type and watches no such resource. The follow-up list of 2026-10-04 asks for separate service accounts so that only the controller reads VmafxTenants.
Decision¶
- The chart creates
<serviceAccount name>-controllerwhenevercontroller.enabled(as it creates<name>-operatorfor the operator), and only the controller pods run under it. - The tenant-reader Role (namespace-scoped) is bound to that account and to no other. The server, job and node pods keep the shared account, which now holds no RBAC.
- The operator's
ClusterRoleloses itsvmafxtenantsandvmafxtenants/statusrules. This replaces ADR-1058's tenant rule; the rest of ADR-1058 stands. test_helm_service_accounts.pyresolves every Role and ClusterRole through its bindings and requires the accounts reachingvmafxtenantsto be exactly the controller's.
Alternatives considered¶
| Option | Pros | Cons | Why not chosen |
|---|---|---|---|
| Controller-only account, operator rule removed (chosen) | Least privilege: one account reads tenants; matches the code (no operator tenant reconciler) | A second account to name when serviceAccount.create: false users pre-create accounts | The account is created by the chart, as the operator's already is |
| One account per workload (server, node, controller) | Every pod's identity distinct | More objects; the server and node accounts would still hold no RBAC | No extra protection beyond the controller split |
| Keep the shared account, rely on the Role's namespace scope | No change | Every node and server pod reads tenant settings | Not least privilege |
| Keep the operator's tenant rule for a future reconciler | No churn | Cluster-wide read of tenant settings for a component that does not use it | Add it with the reconciler if one is ever written |
Consequences¶
- Positive: tenant settings are readable only by the controller; the operator's cluster-wide grant shrinks to the resources it reconciles.
- Negative: deployments that pre-create service accounts must also provide
<name>-controllerRBAC-free names; the chart creates it. - Neutral / follow-ups: none.