TL;DR
Reaching burrowd is the whole grant. burrowd acts as itself, not as the caller — so a person whose kubeconfig is read-only in an app namespace can write it through burrowd.
- Contained, and deliberately: burrowd's write is namespaced, its ClusterRole is read-only, and it holds zero
rbac.authorization.k8s.io grants, so it cannot escalate itself or grant anyone anything.
- Still a confused deputy. Kubernetes RBAC stops being the ceiling the moment a caller goes through burrowd.
- A security review would flag this. Not the current ICP — filed so it is a decision rather than a discovery.
What I need from you
Nothing. Recorded for when enterprise becomes real.
What burrowd actually holds
Every Role and ClusterRole in cmd/burrow/manifests/install.yaml.tmpl:
| Grant |
Scope |
Notable |
burrowd-builds |
namespaced |
jobs create/delete, pods + logs read, secrets get/create/delete |
burrowd-addons |
namespaced |
deployments/services/PVCs/configmaps full write, secrets get/create/update/delete, CNPG clusters and backups |
burrowd-credentials |
namespaced |
get/update on one named Secret |
burrowd-cluster-capabilities |
cluster |
get/list only — nodes, pods, storageclasses, ingressclasses, deployments |
Two properties worth stating because they are better than they need to be:
- No
rbac.authorization.k8s.io anywhere. No roles, no clusterroles, no bindings, at any scope. burrowd cannot widen its own access and cannot grant access to anyone. Whatever else is true, privilege escalation through burrowd's own RBAC is not available.
- The cluster-scoped grant is genuinely read-only, with no secrets in it.
So the blast radius is the four namespaces Burrow owns, which is the containment the design intends.
The problem an auditor will name
The caller's identity is used to reach burrowd and then discarded.
services/proxy RBAC decides whether you may reach the burrowd Service. That is binary. Once through, every action runs under burrowd's ServiceAccount — so the effective permission of any caller is burrowd's, not their own.
Concretely: a person granted read-only access to an app namespace, who can also reach burrowd, can deploy to that namespace, read its Secrets, and delete its workloads. Their kubeconfig forbids all three. Nothing in Kubernetes' authorization model is consulted for the operation itself.
Guardrails are the only thing that narrows it, and they narrow burrowd's ceiling rather than raising a floor under each caller. That is a different guarantee from the one an enterprise expects, which is:
whatever Burrow lets me do is a subset of what my Kubernetes RBAC already lets me do
That statement is false today, and it is the statement a reviewer will test.
Why it is like this
The API-server proxy authenticates the caller and then makes a plain request to burrowd, forwarding no identity. burrowd cannot learn who was just authenticated even if it wanted to. So acting as the caller is not a matter of reading a header — it requires burrowd to impersonate, which means holding impersonate on users and groups.
That grant is close to become anyone, and it is exactly what a platform team refuses. Trading a namespaced ServiceAccount for cluster-wide impersonation makes the audit worse, not better, even though it makes the model more honest.
Options, none of them free
Impersonation. burrowd acts as the caller, so RBAC becomes the real ceiling and the enterprise statement above becomes true. Costs a very powerful grant and an argument with every platform team. Also breaks the parts of Burrow that legitimately act on their own behalf — reconciliation, the auto-deploy poller, anything unattended has no caller to impersonate.
A per-principal ceiling recorded in Burrow. With ADR-0084's principal table, Burrow could record what each principal may do and refuse beyond it. Honest about what it is — Burrow's own authorization, not Kubernetes' — and does not pretend to inherit RBAC. Cheaper, and it is a second thing to keep in agreement with the cluster.
Accept and document. State plainly that reaching burrowd grants burrowd's permissions within the namespaces Burrow owns, and that Burrow's RBAC is the boundary rather than the caller's. Correct, and it is the answer that gets written down rather than discovered.
A SubjectAccessReview pre-check. Before acting, burrowd asks the API server whether the caller would be allowed. Needs the caller's identity, which the proxy does not forward — so it only works if identity arrives another way, and it adds a round trip per operation.
Not now
The current ICP is a self-hoster who owns their cluster and holds admin on it. For them this is not a finding at all: burrowd's permissions and theirs are the same set.
It becomes real the first time somebody wants Burrow in an organisation where the person using it is deliberately not a cluster admin — which is the same population ADR-0084 exists to serve. Worth revisiting when that stops being hypothetical, and worth not claiming otherwise in the meantime.
Related
- ADR-0038 — the agent's scoped credential, which solves the adjacent and more urgent problem: an agent inheriting a high-privilege human's reach
- ADR-0019 — where the namespaced-write posture is described
- #450 — principals and admin, the seam a per-principal ceiling would attach to
TL;DR
Reaching burrowd is the whole grant. burrowd acts as itself, not as the caller — so a person whose kubeconfig is read-only in an app namespace can write it through burrowd.
rbac.authorization.k8s.iogrants, so it cannot escalate itself or grant anyone anything.What I need from you
Nothing. Recorded for when enterprise becomes real.
What burrowd actually holds
Every Role and ClusterRole in
cmd/burrow/manifests/install.yaml.tmpl:burrowd-buildsburrowd-addonsburrowd-credentialsget/updateon one named Secretburrowd-cluster-capabilitiesget/listonly — nodes, pods, storageclasses, ingressclasses, deploymentsTwo properties worth stating because they are better than they need to be:
rbac.authorization.k8s.ioanywhere. No roles, no clusterroles, no bindings, at any scope. burrowd cannot widen its own access and cannot grant access to anyone. Whatever else is true, privilege escalation through burrowd's own RBAC is not available.So the blast radius is the four namespaces Burrow owns, which is the containment the design intends.
The problem an auditor will name
The caller's identity is used to reach burrowd and then discarded.
services/proxyRBAC decides whether you may reach the burrowd Service. That is binary. Once through, every action runs under burrowd's ServiceAccount — so the effective permission of any caller is burrowd's, not their own.Concretely: a person granted read-only access to an app namespace, who can also reach burrowd, can deploy to that namespace, read its Secrets, and delete its workloads. Their kubeconfig forbids all three. Nothing in Kubernetes' authorization model is consulted for the operation itself.
Guardrails are the only thing that narrows it, and they narrow burrowd's ceiling rather than raising a floor under each caller. That is a different guarantee from the one an enterprise expects, which is:
That statement is false today, and it is the statement a reviewer will test.
Why it is like this
The API-server proxy authenticates the caller and then makes a plain request to burrowd, forwarding no identity. burrowd cannot learn who was just authenticated even if it wanted to. So acting as the caller is not a matter of reading a header — it requires burrowd to impersonate, which means holding
impersonateon users and groups.That grant is close to become anyone, and it is exactly what a platform team refuses. Trading a namespaced ServiceAccount for cluster-wide impersonation makes the audit worse, not better, even though it makes the model more honest.
Options, none of them free
Impersonation. burrowd acts as the caller, so RBAC becomes the real ceiling and the enterprise statement above becomes true. Costs a very powerful grant and an argument with every platform team. Also breaks the parts of Burrow that legitimately act on their own behalf — reconciliation, the auto-deploy poller, anything unattended has no caller to impersonate.
A per-principal ceiling recorded in Burrow. With ADR-0084's principal table, Burrow could record what each principal may do and refuse beyond it. Honest about what it is — Burrow's own authorization, not Kubernetes' — and does not pretend to inherit RBAC. Cheaper, and it is a second thing to keep in agreement with the cluster.
Accept and document. State plainly that reaching burrowd grants burrowd's permissions within the namespaces Burrow owns, and that Burrow's RBAC is the boundary rather than the caller's. Correct, and it is the answer that gets written down rather than discovered.
A
SubjectAccessReviewpre-check. Before acting, burrowd asks the API server whether the caller would be allowed. Needs the caller's identity, which the proxy does not forward — so it only works if identity arrives another way, and it adds a round trip per operation.Not now
The current ICP is a self-hoster who owns their cluster and holds admin on it. For them this is not a finding at all: burrowd's permissions and theirs are the same set.
It becomes real the first time somebody wants Burrow in an organisation where the person using it is deliberately not a cluster admin — which is the same population ADR-0084 exists to serve. Worth revisiting when that stops being hypothetical, and worth not claiming otherwise in the meantime.
Related