How to find over-privileged AI agents

An over-privileged AI agent is one whose identity can do more than its task needs. To find them, list the service account each agent runs as, test its permissions with kubectl auth can-i --as, compare the results with the task, and check the audit log for what the agent actually used.
Checked against the Kubernetes RBAC, authorization, service account and audit documentation, fetched 2026-11-09. Those pages do not state a version number.
What makes an agent over-privileged
Kubernetes RBAC grants are additive. The RBAC page says: “Permissions are purely additive (there are no “deny” rules).” An agent’s effective permissions are the union of every Role reachable through the bindings on its account. You cannot carve out an exception with a deny rule, so the only fix is removing a grant. The audit therefore asks two questions: what can this identity do today, and does the task need it?
Two patterns need a closer look. The first is the shared default account. The service account page says that a Pod with no serviceAccountName uses the default account in its namespace. The RBAC page adds that permissions given to default are available to any pod in the namespace that does not specify a serviceAccountName. The second is a grant that lets an agent create bindings, which can widen its own access. For the wider threat picture, read AI agent threat model for Kubernetes.
Step 1: Find the identity each agent runs as
- List the pods that run each agent and read
spec.serviceAccountName. If the field is missing, the pod runs asdefaultin its namespace. If a Pod sets no serviceAccountName, the ServiceAccount admission controller sets it to default. - Note any agent that shares an account with another workload. Two agents on one account show the same identity in grants and in logs, so you cannot tell which one acted.
- Plan the fix before you test. You cannot change
.spec.serviceAccountNameon an existing pod. Give each agent its own account and recreate its pod. The service identities for custom AI agents post covers that setup.
Step 2: Test what each identity can do
kubectl auth can-i uses the SelfSubjectAccessReview API to check whether the current user can perform an action, and it works regardless of the authorization mode. Administrators can add --as to check what another identity can do. The documentation gives a ServiceAccount example in this form:
kubectl auth can-i list pods \
--namespace agents \
--as system:serviceaccount:agents:release-agent
A yes means the account holds that permission in that namespace. It does not show whether the agent ever used it. Run the same check for each verb and resource the agent’s task could touch, and record the results per agent.
for check in "list secrets" "list pods" "create pods" "create roles" "create rolebindings"; do
kubectl auth can-i $check --namespace agents --as system:serviceaccount:agents:release-agent
done
Step 3: Look for an escalation path
RBAC limits who can write bindings, and that limit is the part to test. The RBAC page says you can only create or update a role binding “if you already have all the permissions contained in the referenced role (at the same scope as the role binding) or if you have been authorized to perform the bind verb on the referenced role.” An agent that can create RoleBindings and already holds a role’s permissions can bind that role to another account.
The same page says an identity can create or update a Role with permissions it does not hold only if it has the escalate verb on roles or clusterroles. Check all three.
kubectl auth can-i create rolebindings --namespace agents --as system:serviceaccount:agents:release-agent
kubectl auth can-i bind clusterroles --namespace agents --as system:serviceaccount:agents:release-agent
kubectl auth can-i escalate roles --namespace agents --as system:serviceaccount:agents:release-agent
Any yes on these three lines deserves a review, even if the agent’s other checks look narrow.
These unnamed checks do not see grants restricted by resourceNames. An agent allowed to bind only one named ClusterRole can return no on the bind line above. Read the rules of each Role and ClusterRole bound to the agent and look for resourceNames.
Compare the grants with the task
Write down the permissions the task needs before you read the results. Then compare each row of the table with what the checks returned.
| Question | How to check | Red flag |
|---|---|---|
| Does the agent call the Kubernetes API at all? | Task design, plus automountServiceAccountToken in the pod spec |
A token is mounted for an agent that never calls the API |
| Can it read Secrets? | kubectl auth can-i list secrets and kubectl auth can-i get secrets |
yes, when the task never reads one. A get grant restricted to named Secrets is not caught by these unnamed checks, so read the rules as described in Step 3 |
| Can it write where it only needs to read? | kubectl auth can-i create pods and similar |
yes |
| Can it write bindings or roles? | Step 3 checks | Any yes |
| Is the grant namespace-wide or cluster-wide? | The binding kind | A ClusterRoleBinding for a single-namespace task |
Does it run as default? |
Step 1 | yes |
The token row needs care. If the ServiceAccount and the Pod both set automountServiceAccountToken, the Pod spec takes precedence, so check the pod as well as the account. For the scope row, the RBAC page says that “a RoleBinding grants permissions within a specific namespace whereas a ClusterRoleBinding grants that access cluster-wide.”
Check what the agent actually used
Permission checks show what an identity could do. The audit log shows what it did. The audit documentation says that if the audit policy flag is omitted, “no events are logged.” A minimal policy records every request at the Metadata level:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
Pass the file to the API server with --audit-policy-file. The Metadata level records the requesting user, the timestamp, the resource and the verb, but not the request or response body. The log backend writes events to a file in JSON lines format, at the path set with --audit-log-path. For how to read those events, see How to audit AI agent actions in Kubernetes.
- Enable the policy and let the agent run its normal task.
- Filter the log for the agent’s account as the requesting user.
- List the verbs and resources the agent used. Compare them with the
can-iresults. Any grant it never used during the run is a candidate for removal.
Narrow the grant and test again
Since RBAC cannot deny, removal is the only fix. Work through these steps in order:
- Move any agent off
defaultfirst. Grants bound todefaultreach every pod that names no account, so the agent keeps that access until it has its own account. - Remove the permissions that neither the task nor the audit log needs, from the Role or the binding. Keep the change in version control so it can be reviewed.
- Run the Step 2 loop and the Step 3 checks again. Expect
nofor each removed permission andyesonly for what the task uses. - Repeat the check whenever the agent’s task or its image changes, since the grants it needs can change with them.
Enforcing this with AIControls
The checks above run on any Kubernetes cluster and need no product. Nirmata has confirmed per-user tokens and IdP integration for XAA as identity features of AIControls. For agents that act for a signed-in person, read How to map AI agents and humans to one identity model with OIDC. For what the product enforces on each call, check the AIControls platform page before you rely on it as a control.
Frequently asked questions
Does Kubernetes RBAC support deny rules for AI agents?
No. The RBAC documentation says permissions are purely additive, with no deny rules, so an agent keeps every permission that any binding grants its account. To limit an agent, remove the grant or move the agent to an account with fewer bindings. Then test the result with kubectl auth can-i and the --as flag, naming the agent's account as the identity.
Can I check an agent's permissions without running it?
Yes. kubectl auth can-i uses the SelfSubjectAccessReview API, and the --as flag lets you check another identity, such as a ServiceAccount written as system:serviceaccount, then the namespace, then the account name. Each check returns yes or no for one action in one namespace. It does not show what the agent did in the past, which is what the audit log is for.
Does an agent on the default service account count as over-privileged?
Not by itself, but it is a risk. Any pod that names no service account uses the default account in its namespace, so a grant bound to default reaches every such pod, including an agent. Give the agent its own account, set spec.serviceAccountName when you create its pod, and then review the grants bound to default.
What audit level is enough to find over-privileged agents?
Metadata is enough for most of this work. It records the requesting user, the timestamp, the resource and the verb, which shows what an agent used. It leaves out request and response bodies. Set the policy with the --audit-policy-file flag on the API server, because if that flag is omitted, no events are logged.