RBAC for AI agents in Kubernetes

Give each AI agent its own Kubernetes service account, bind that account to a Role that lists only the resources and verbs the agent needs, and set spec.serviceAccountName on its pod. RBAC only adds permissions and has no deny rules, so the grants you bind are the whole limit.
This post covers the RBAC side only: the bindings, the pod settings and the tokens an agent uses.
Checked against the Kubernetes RBAC, service account and Pod Security Standards pages as fetched on 2026-11-05.
Give each agent its own service account
Kubernetes creates a default service account in every namespace. The docs say it gets no permissions beyond API discovery, and a pod that does not name a service account uses it. The docs also say that permissions given to default are available to every pod in the namespace that does not set serviceAccountName.
So a grant made to default for one workload reaches every other pod that falls back to it. Create a named service account for each agent, and never bind a role to default for an agent’s sake. The account names below (triage-agent) are examples.
Write a Role for the job
RBAC rules have three parts: the API group, the resource and the verbs. The RBAC docs give this example of a namespaced Role that reads pods:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
Keep the verbs list as short as the agent’s job allows. The docs’ examples also use create, update, patch and delete on Deployments and Jobs, so a write-capable agent needs those verbs listed explicitly. The docs also say that deletecollection and top-level create cannot be restricted by resource name, so if an agent needs either verb, the grant covers every object of that type in its scope.
Bind the account in one namespace
The RBAC docs show a RoleBinding that refers to a ClusterRole and grants it only inside one namespace. That shape lets one ClusterRole serve agents in several namespaces, with a separate binding in each. The sample below uses a ClusterRole with the same rules as the Role above, bound in the agents namespace.
- Create the ClusterRole. It has no namespace, because ClusterRole is a non-namespaced resource.
- Create a RoleBinding in the agent’s namespace. Its
subjectsentry names the service account, including its namespace. - Check that no ClusterRoleBinding also grants the agent’s account the same rules.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: triage-agent-pod-reader
namespace: agents
subjects:
- kind: ServiceAccount
name: triage-agent
namespace: agents
roleRef:
kind: ClusterRole
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Lock down the agent’s pod and its account
The pod names its service account in the Pod specification, and Kubernetes then provides that account’s credentials to the pod. The docs also describe automountServiceAccountToken, which controls whether credentials are injected. Set it to false for an agent that never calls the Kubernetes API. An agent that does call the API keeps the default or sets it explicitly. It gets the grants bound to its own account, plus any grants bound to groups it belongs to, such as system:serviceaccounts, system:serviceaccounts:agents or system:authenticated. A broad group grant reaches the agent even when its direct binding is narrow, so audit the group bindings too.
spec:
serviceAccountName: triage-agent
automountServiceAccountToken: false
RBAC controls what the pod can do with the API. It does not control what a process inside the container can do on the node, and the same pod spec has to cover that. The Pod Security Standards Restricted profile sets those limits. Its container fields apply to containers, initContainers and ephemeralContainers, and a single failing container fails the whole pod.
| Control | Field to set | Allowed value |
|---|---|---|
| Privilege escalation | securityContext.allowPrivilegeEscalation |
false |
| Non-root user | securityContext.runAsNonRoot |
true |
| Root UID | securityContext.runAsUser |
Any non-zero value, or unset |
| Seccomp | securityContext.seccompProfile.type |
RuntimeDefault or Localhost |
| Capabilities | securityContext.capabilities.drop |
Must include ALL |
| Added capabilities | securityContext.capabilities.add |
Unset, or NET_BIND_SERVICE only |
| Volume types | spec.volumes[*] |
configMap, csi, downwardAPI, emptyDir, ephemeral, persistentVolumeClaim, projected or secret |
Keep tokens short-lived
Since v1.22, Kubernetes gives a pod a short-lived token from the TokenRequest API. It mounts the token as a projected volume and rotates it automatically. That is the token an agent pod should use.
Older behaviour is different. The docs say that before v1.24 a permanent token was created automatically for each service account, and that these tokens do not expire or rotate. The docs advise against them, especially at scale. Application code that calls the API can request a short-lived token itself through the TokenRequest API, so avoid mounting a static Secret token into an agent pod.
Compare the three ways to grant an agent access
Permissions reach an agent through the bindings that name its account and through the groups it belongs to, and the three setups below differ mainly in how far those bindings reach. The Kubernetes docs define each mechanism, and the fallback case is the one most often left in place by accident. A pod on default with no bindings has nothing to lose, but an agent that is later bound to default inherits that grant everywhere.
The table compares the three setups. The “Use it for” column is our recommendation.
| Setup | What the agent gets | Use it for |
|---|---|---|
Pod on default, no bindings |
API discovery only | Nothing; do not run agents this way |
| Named service account, RoleBinding to a ClusterRole | The ClusterRole’s rules, in one namespace | Most agents |
| ClusterRoleBinding | The ClusterRole’s rules in every namespace | Only agents that need cluster-wide reach |
Enforcing this with Kyverno
The settings in this post can also be enforced at admission. Kyverno’s policy library has rules for some of them, such as disallowing privilege escalation and requiring drop ALL capabilities, so a manifest that skips review can still be checked before it runs. Start the rule in audit mode, read the results, and only then switch it to deny, so that workloads already in production are not blocked by surprise.
The Kyverno project page covers installation and the project’s documentation. For the wider governance picture, see governing AI agents.
Sources
- Kubernetes RBAC: Roles, ClusterRoles, bindings, the additive model and the verb notes.
- Kubernetes service accounts: the default account,
automountServiceAccountToken, and projected tokens. - Kubernetes Pod Security Standards: the Restricted profile fields and volume types.
Frequently asked questions
Can an AI agent run under the namespace default service account?
It can, but you should not let it. Kubernetes gives the default account no permissions beyond API discovery outside kube-system. Any grant you later bind to default applies to every pod in the namespace that does not name a service account. An agent on default inherits whatever another workload was given, so create a named account for each agent.
Does a RoleBinding limit a ClusterRole to one namespace?
Yes. The RBAC docs say a RoleBinding grants permissions within its own namespace even when it refers to a ClusterRole. The docs' example binds the ClusterRole secret-reader in the development namespace, and says that only grants permissions within that namespace. Use this pattern when one ClusterRole serves several agents in different namespaces.
Can an RBAC rule stop an agent from deleting objects?
Only by leaving out the delete verb. RBAC permissions are purely additive, with no deny rules, so a rule cannot subtract access that another binding grants. Check every binding that reaches the agent's service account, because one forgotten grant is enough for a delete to succeed.
Should an agent pod mount a service account token if it never calls the API?
No. Set automountServiceAccountToken to false in the Pod specification, which stops Kubernetes injecting credentials for that account. Leave it on only for agents that call the Kubernetes API, and give those agents the narrowest Role they need.