buzz-backend-kubernetes: service_account grants RBAC the pod cannot exercise
Problem
The kubernetes backend hardcodes automount_service_account_token: Some(false)
on the agent pod (crates/buzz-backend-kubernetes/src/pod.rs), and the
service_account config field documents the consequence: "Scheduling/RBAC
identity only. No API token is mounted."
That's sound hardening for most agents — a pod holding the agent's private key
shouldn't also carry an API-stealable cluster credential it never uses. But it
half-disconnects the service_account feature: an operator can bind RBAC to
the ServiceAccount the field names, and the pod can never exercise it, because
Kubernetes only delivers a pod's API identity through the projected token
volume that this flag suppresses. There is no environment-variable or
deploy-payload equivalent; the volume is the only door.
For agents whose job is the cluster — introspection, diagnostics, answering
"why is this pod crashlooping" — this is a hard capability wall. Concretely,
deploying such an agent today gets you:
$ kubectl get pods
The connection to the server localhost:8080 was refused
(in-cluster config requires the mounted token; without it kubectl falls back
to its localhost default), while the RBAC carefully granted to the configured
ServiceAccount sits unused.
Proposed solution
A provider_config boolean, cluster_api_access, default false:
false (default): current behavior exactly — the hardening stance is
preserved and existing deployments are untouched.
true: the pod spec sets automountServiceAccountToken: true, giving the
pod whatever RBAC its service_account grants — which the operator already
controls, and which defaults to effectively nothing for an unbound account.
Schema entry alongside service_account, with a description that states the
trade honestly ("the token is an API-stealable credential in the pod; leave
off unless the agent's job involves the cluster itself").
Naming note: the natural name (automount_service_account_token, mirroring
the PodSpec field) is unavailable — the I2 key lint bans the substring
"token" in provider_config keys — so the field is named for the intent rather
than the mechanism. Happy to bikeshed.
Alternatives considered and rejected:
- Shipping a static long-lived SA token via
env_vars works today but
inverts the security rationale: it replaces the platform's audience-bound,
~1h-rotating projected token with a never-expiring credential riding the
deploy payload — a strictly more stealable credential adopted to route
around a flag whose purpose is limiting credential theft.
- TokenRequest at runtime requires an authenticated client to call it.
Status
I have a working patch (config field + schema + pod wiring + tests, including
the schema-guard and I2-lint updates) against relay-v0.2.1, currently
soak-testing on my own cluster: a read-only diagnostics agent (view
ClusterRole + a named-Secret Role) deployed through Buzz Desktop, using
kubectl/talosctl against the cluster it runs in. Before/after is crisp — the
identical agent config crashes on the Forbidden above without the flag and
runs with it. Glad to port it onto main and open a PR if the direction looks
right.
Related work
I found no prior issues or PRs proposing this (searched: automount, service
account token, in-cluster kubectl, RBAC + pod). #5548 is adjacent as active
provider-protocol design discussion but addresses launch semantics, not pod
capabilities.
🤖 Investigated and drafted with Claude Code;
diagnosed, reviewed, and submitted by a human.
buzz-backend-kubernetes:
service_accountgrants RBAC the pod cannot exerciseProblem
The kubernetes backend hardcodes
automount_service_account_token: Some(false)on the agent pod (
crates/buzz-backend-kubernetes/src/pod.rs), and theservice_accountconfig field documents the consequence: "Scheduling/RBACidentity only. No API token is mounted."
That's sound hardening for most agents — a pod holding the agent's private key
shouldn't also carry an API-stealable cluster credential it never uses. But it
half-disconnects the
service_accountfeature: an operator can bind RBAC tothe ServiceAccount the field names, and the pod can never exercise it, because
Kubernetes only delivers a pod's API identity through the projected token
volume that this flag suppresses. There is no environment-variable or
deploy-payload equivalent; the volume is the only door.
For agents whose job is the cluster — introspection, diagnostics, answering
"why is this pod crashlooping" — this is a hard capability wall. Concretely,
deploying such an agent today gets you:
(in-cluster config requires the mounted token; without it kubectl falls back
to its localhost default), while the RBAC carefully granted to the configured
ServiceAccount sits unused.
Proposed solution
A provider_config boolean,
cluster_api_access, defaultfalse:false(default): current behavior exactly — the hardening stance ispreserved and existing deployments are untouched.
true: the pod spec setsautomountServiceAccountToken: true, giving thepod whatever RBAC its
service_accountgrants — which the operator alreadycontrols, and which defaults to effectively nothing for an unbound account.
Schema entry alongside
service_account, with a description that states thetrade honestly ("the token is an API-stealable credential in the pod; leave
off unless the agent's job involves the cluster itself").
Naming note: the natural name (
automount_service_account_token, mirroringthe PodSpec field) is unavailable — the I2 key lint bans the substring
"token" in provider_config keys — so the field is named for the intent rather
than the mechanism. Happy to bikeshed.
Alternatives considered and rejected:
env_varsworks today butinverts the security rationale: it replaces the platform's audience-bound,
~1h-rotating projected token with a never-expiring credential riding the
deploy payload — a strictly more stealable credential adopted to route
around a flag whose purpose is limiting credential theft.
Status
I have a working patch (config field + schema + pod wiring + tests, including
the schema-guard and I2-lint updates) against
relay-v0.2.1, currentlysoak-testing on my own cluster: a read-only diagnostics agent (view
ClusterRole + a named-Secret Role) deployed through Buzz Desktop, using
kubectl/talosctl against the cluster it runs in. Before/after is crisp — the
identical agent config crashes on the Forbidden above without the flag and
runs with it. Glad to port it onto
mainand open a PR if the direction looksright.
Related work
I found no prior issues or PRs proposing this (searched: automount, service
account token, in-cluster kubectl, RBAC + pod). #5548 is adjacent as active
provider-protocol design discussion but addresses launch semantics, not pod
capabilities.
🤖 Investigated and drafted with Claude Code;
diagnosed, reviewed, and submitted by a human.