RBAC posture¶
Portal's ServiceAccount asks for the minimum verbs needed to do its job, on the GVKs it has been told to watch. This page captures the philosophy; for the concrete ClusterRole rules and per-action toggles see ../operator/rbac-scoping.md.
Read scope is broad by design¶
To audit a GVK, Portal needs get,list,watch on it. That set spans:
- Every GVK in the
watchedGvksHelm value. - Every GVK referenced by a
cluster.<gvk>.*lookup in any loaded rule.
For most production deployments that ends up looking like a fair chunk of the core/apps/networking APIs. This is by design — Portal cannot reason about what it cannot see. The mitigation is:
- Read-only verbs only. Portal never asks for
create,update,patch,deleteon GVKs it merely audits. - Verbs are scoped to a known list; there is no
resources: ["*"]rule. - Minimum-viable installs can disable layers they don't use (no
--auditmeans no informers means nowatchpermissions on audited workloads).
Write scope is opt-in per action¶
Action RBAC is gated by Helm values. Defaults from deploy/helm/portal/values.yaml:
Each toggle adds exactly one rule: block to the ClusterRole. With all toggles off, Portal has zero write capability on cluster workloads — only on its own status subresources, on PolicyReports, and on Leases.
The AlertManager action does not need cluster RBAC (it's a webhook call to AlertManager), so it's enabled by default.
Portal does not ask for cluster-admin¶
Specifically, Portal never asks for:
*on any apiGroup or resource.update,patch,deleteonroles,clusterroles,rolebindings,clusterrolebindings.escalateonclusterroles.impersonateon users or groups.- Write access to its own ServiceAccount or Deployment.
If a Portal upgrade ever introduces a new ClusterRole rule, it is reviewed against the principle above. The reviewer's question: "Is this verb on this resource strictly required to ship the documented feature?"
Audit Portal itself¶
The cluster operator should audit Portal's effective permissions periodically:
The output should match what operator/rbac-scoping.md says it should. Deviations are either:
- A new action toggle you turned on and forgot. Audit your Helm values.
- Drift from a manual
kubectl apply -fof a stale role. Re-runhelm upgrade portal -n portal-systemto reconcile.
Hardening checklist¶
- Set
rbac.create: falseif you bring your own ClusterRole (e.g. via Argo CD with a separately-templated role). Portal still works; the chart just doesn't render the role. - Pin Portal's image (
image.tag: vX.Y.Z, notlatest) and verify the digest. RBAC is only as strong as the binary using it. - Restrict who can patch
validatingwebhookconfigurations— that's the off-switch. - Restrict who can patch
Namespaceannotations — that's how theportal.io/bypass=trueannotation gets set. - Monitor
portal_admission_bypass_totalin Prometheus. Any non-zero rate is a break-glass that should be reviewed.
For the threat-model context see threat-model.md.