Skip to content

All posts

KubernetesAI

How to audit AI agent actions in Kubernetes

Ritesh Patel

Kubernetes: How to audit AI agent actions in Kubernetes

To audit what an AI agent does in Kubernetes, give the agent its own service account, then turn on API server auditing with an audit policy that names that account. Each event records the authenticated user, the verb, the target object where the request has one, and the response status. Without a policy file, the API server logs nothing.

This post covers the Kubernetes API server audit log. It does not cover application logs inside the agent’s container. For a coding agent’s own session on a developer’s machine, read how to audit what your AI coding agent did. For the response after an agent has misbehaved, read how to investigate an AI agent incident.

Checked against the Kubernetes auditing, API reference and service account pages as fetched on 2026-11-08. The pages print no version number, so this post does not name one.

What an audit event records

An audit event is a JSON object defined in the audit.k8s.io API group. Only user, verb, requestURI, stage and auditID are required. The other fields appear when the request has them. The table lists the ones that matter most for an agent audit.

Field What it holds What to know
user The authenticated identity Required. For an agent, this is the service account username.
impersonatedUser The identity being impersonated Present for impersonated requests.
verb The Kubernetes verb For non-resource requests, the lower-cased HTTP method.
objectRef The target object Not set for List requests or non-resource requests.
responseStatus The response status Filled in even when the response is not a Status object.
sourceIPs Where the request came from Partly set by the client, so it is not proof of origin.
userAgent The client’s user agent string The docs say it must not be trusted.

Give each agent its own identity

Each service account has a username of the form system:serviceaccount:<namespace>:<name>. The account also belongs to the groups system:serviceaccounts and system:serviceaccounts:<namespace>. A policy rule can match on either, so one rule can cover every agent in a namespace.

That is only useful if each agent has its own account. If two agents share one service account, their events look the same, and the log cannot say which agent acted. Give each agent a service account of its own and bind it to a narrow Role, as the RBAC for AI agents post describes. Agents that act for a person need more than a service account. The agent identity post covers how to tell the agent’s identity apart from the person it works for.

Write the audit policy for the agent’s namespace

The policy is a file the kube-apiserver reads at start-up. These steps build one for agents in the agents namespace.

  1. Create a file with apiVersion: audit.k8s.io/v1 and kind: Policy. The rules list is required, and the docs treat a policy with no rules as illegal.
  2. Put the secret rule first, so that agent access to Secrets is recorded without request bodies.
  3. Add a rule for agent writes that records the request body.
  4. Add a rule for all other agent requests at the Metadata level.
  5. End with a catch-all Metadata rule for every other user.

Source: https://kubernetes.io/docs/tasks/debug/debug-cluster/audit/ (fetched 2026-11-08); field names from https://kubernetes.io/docs/reference/config-api/apiserver-audit.v1/ (fetched 2026-11-08).

apiVersion: audit.k8s.io/v1
kind: Policy
omitStages:
  - "RequestReceived"
rules:
  # Agent access to Secrets: metadata only, so secret values stay out of the log.
  - level: Metadata
    userGroups: ["system:serviceaccounts:agents"]
    resources:
    - group: ""
      resources: ["secrets"]
  # Agent writes: record the request body.
  - level: Request
    userGroups: ["system:serviceaccounts:agents"]
    verbs: ["create", "update", "patch", "delete"]
  # Everything else the agent does: metadata only.
  - level: Metadata
    userGroups: ["system:serviceaccounts:agents"]
  # Catch-all: every other user, metadata only.
  - level: Metadata

Rules are checked from top to bottom, and the first rule that matches sets the level. Within one rule, every field must match. The Secret rule sits first, so the Request rule never sees agent Secret writes. The omitStages list drops the RequestReceived stage, as the docs’ own example does.

Turn on the log file

Pass the policy and the log path as kube-apiserver flags. The log backend is off unless you set --audit-log-path, and kube-apiserver logs no events unless you set --audit-policy-file. The rotation values below are examples for sizing your own retention, not defaults.

Source: https://kubernetes.io/docs/tasks/debug/debug-cluster/audit/ (fetched 2026-11-08).

kube-apiserver \
  --audit-policy-file=/etc/kubernetes/audit-policy.yaml \
  --audit-log-path=/var/log/kubernetes/audit/audit.log \
  --audit-log-maxage=30 \
  --audit-log-maxbackup=10 \
  --audit-log-maxsize=100

If the control plane runs kube-apiserver as a static Pod, mount the policy file and the log directory into it with hostPath volumes, so that the audit records are persisted, as the docs advise. On a managed control plane, check the provider’s own audit settings. This post does not cover them.

Read the agent’s records

The log backend writes JSON Lines, one event per line. Read it with the filter below.

  1. Open the audit log file. Each line is one event.
  2. Keep the events whose user.username is system:serviceaccount:agents:<name>, using the agent’s account name.
  3. Keep the ResponseComplete stage for the outcome. The docs define that stage as the point when the response body has been completed and no more bytes will be sent.
  4. Read verb and objectRef to see what the agent tried to do and to which object.
  5. Read responseStatus.code to see whether the request succeeded.
  6. Compare the verbs with the agent’s Role. Any verb the agent used that its Role does not grant needs a look.

The docs do not say whether a request that RBAC denies gets an event. Test a denied request on your own cluster before you rely on the log to show refusals.

Know where the record is weak

The level you choose sets how much each event carries. The docs define the four levels below.

Level What the event records Request and response bodies
None Nothing for matching requests Not recorded
Metadata User, timestamp, resource and verb Not recorded
Request Metadata plus the request body Request body only, and not for non-resource requests
RequestResponse Metadata plus request and response bodies Both, and not for non-resource requests

Several gaps are worth checking before you trust the log:

  • The API reference says the default audit level is None, so a request that no rule matches is not recorded unless a catch-all rule at the end of the list covers it.
  • Request bodies can hold sensitive values. The docs’ own example logs Secrets at Metadata, and this policy follows that.
  • A JSON Patch request body is an array of operations. Merge patch and strategic merge patch bodies are objects. Write your rules for the content type the agent actually sends. Source: API concepts, patch and apply (fetched 2026-11-08).
  • The log backend has batching disabled by default. The webhook backend has batching enabled by default.
  • The API docs say audit logging increases the memory use of the API server.

Sources

Enforcing this with AIControls

AIControls has confirmed per-user tokens, and confirmed per-user and per-session cost limits for Claude Code through the gateway. This post makes no AIControls claim about recording Kubernetes API calls. The audit policy above is the record for those calls. To see the platform, start at the AIControls platform page, or the governing AI agents hub for the wider context.

Frequently asked questions

Does the Kubernetes API server log an AI agent's requests by default?

No. The API server writes no audit events unless you pass --audit-policy-file. The API reference says the default audit level is None, and a catch-all rule at the end of the policy's rule list can override it. Add a catch-all Metadata rule if you want every request recorded with its user, verb and object.

What username does an AI agent's service account show in audit logs?

The username has the form system:serviceaccount:<namespace>:<name>, for example system:serviceaccount:agents:claude-ops. The account also belongs to the groups system:serviceaccounts and system:serviceaccounts:<namespace>. A userGroups rule can match every agent in one namespace, but give each agent its own account so the log can tell them apart.

Should the audit policy record request bodies for every agent action?

No. Request bodies add detail, but they make the log much larger and can hold sensitive data. Use the Request level only for the writes you need to reconstruct. Keep Secret bodies out of the log by putting a Metadata rule for secrets ahead of any broader rule, and check the log size on a staging cluster before you roll the policy out.

Why does my audit event include managedFields?

Managed fields are omitted only when omitManagedFields is set to true, either in the policy or on a single rule. Otherwise they stay in the request and response bodies that the log records. Most agent audits do not need them, so setting omitManagedFields to true keeps the log shorter.

Ritesh Patel, Founder & VP Product, Nirmata

Ritesh Patel is a founder of Nirmata and leads its products. He writes about governing AI coding agents, MCP security and Kubernetes policy as code.

LinkedIn

Govern AI agents with AIControlsControl what AI agents do, and prove it.
ShareXLinkedIn

Stay up-to-date with the latest in AI, governance, and infrastructure

Subscribe to our free monthly newsletter to get industry news, DevOps tips, and articles right to your inbox on Kyverno, containerization, Kubernetes deployment & management and more.