Cloud-Native ✓ fields verified against sample logs

Kubernetes Audit Log Fields: Reference With Examples

Published

One JSON event per request handled by the Kubernetes API server: who called it, from which address, which verb against which object, what the authorizer decided and how the server responded.

Sample record

  {"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"RequestResponse","auditID":"a1b2c3d4-0001","stage":"ResponseComplete","requestURI":"/api/v1/namespaces/production/secrets","verb":"list","user":{"username":"attacker-sa","uid":"abc123","groups":["system:serviceaccounts","system:authenticated"]},"sourceIPs":["10.0.4.55"],"userAgent":"kubectl/v1.28.0 (linux/amd64) kubernetes/abc1234","objectRef":{"resource":"secrets","namespace":"production","apiVersion":"v1"},"responseStatus":{"code":200},"requestReceivedTimestamp":"2024-03-15T03:10:00.000000Z","stageTimestamp":"2024-03-15T03:10:00.050000Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding"}},
  {"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"RequestResponse","auditID":"a1b2c3d4-0002","stage":"ResponseComplete","requestURI":"/api/v1/namespaces/production/secrets/db-credentials","verb":"get","user":{"username":"attacker-sa","uid":"abc123","groups":["system:serviceaccounts","system:authenticated"]},"sourceIPs":["10.0.4.55"],"userAgent":"kubectl/v1.28.0 (linux/amd64) kubernetes/abc1234","objectRef":{"resource":"secrets","namespace":"production","name":"db-credentials","apiVersion":"v1"},"responseStatus":{"code":200},"requestReceivedTimestamp":"2024-03-15T03:10:05.000000Z","stageTimestamp":"2024-03-15T03:10:05.030000Z","annotations":{"authorization.k8s.io/decision":"allow"}},

From the LogTriage parser test corpus — synthetic data, no real hosts or users.

Field reference

Field Type Meaning Why it matters for security
apiVersione.g. audit.k8s.io/v1 string Schema version of the audit event itself, not of the object being acted on. Confirms which field set a parser should expect; older clusters may emit earlier audit API versions.
levele.g. RequestResponse None | Metadata | Request | RequestResponse How much of the request the audit policy recorded for this rule. Metadata-level events contain no request or response body, so you can see that a secret was read but not what it contained.
auditIDe.g. a1b2c3d4-0001 string (UID) Identifier shared by every event emitted for one API request. Join or deduplicate stages of the same request, and quote it when handing an event to the platform team.
stagee.g. ResponseComplete RequestReceived | ResponseStarted | ResponseComplete | Panic Point in request handling at which the event was written. ResponseComplete carries the final status; counting all stages double-counts activity.
requestURIe.g. /api/v1/namespaces/production/secrets/db-credentials string Full request path including namespace, resource, name and any query string. Shows subresource paths such as /exec, and is the quickest way to read what was touched.
verbe.g. list string API verb: get, list, watch, create, update, patch, delete, deletecollection, and connect-style verbs. A pod exec appears as verb create against the exec subresource, not as a verb named "exec".
user.usernamee.g. system:serviceaccount:kube-system:deployment-controller string Authenticated identity as presented by the authenticator; service accounts normally appear as system:serviceaccount:namespace:name. The principal to pivot on. system:anonymous means the request was never authenticated.
user.groupse.g. system:serviceaccounts array of strings Groups the authenticator attached to the identity, used by RBAC. Membership of system:masters or a cluster-admin-bound group explains why a dangerous call was allowed.
sourceIPse.g. 10.0.4.55 array of strings Client address chain seen by the API server. Pod CIDR addresses mean the caller was inside the cluster; an unexpected external address on an admin call is worth escalating.
userAgente.g. kubectl/v1.28.0 (linux/amd64) string User-Agent sent by the client, typically kubectl, a controller or a client library. Controller traffic has stable agents; a kubectl agent on a service-account token suggests a human or a tool using stolen credentials.
objectRef.resourcee.g. secrets string Resource type the request targeted. secrets, clusterrolebindings and pods with a subresource are the high-value types to watch.
objectRef.namespacee.g. production string Namespace of the target object; absent for cluster-scoped resources. One identity touching many namespaces in minutes is enumeration.
objectRef.namee.g. db-credentials string Name of the specific object, present when the request targeted one object rather than a collection. Tells you exactly which secret or binding to rotate or delete.
objectRef.subresourcee.g. exec string Subresource such as exec, attach, portforward, log or status. exec, attach and portforward give shell-level or network access into a running pod.
responseStatus.codee.g. 200 integer HTTP status returned to the client. 403 bursts are RBAC probing; 101 is the protocol upgrade that a successful exec or attach produces; 201 marks a created object.
annotations."authorization.k8s.io/decision"e.g. allow string Authorizer decision recorded for the request, with the matching rule often in authorization.k8s.io/reason. Separates what was attempted from what was permitted — the key filter when triaging a noisy identity.

What it records

Every request the kube-apiserver handles can produce an audit event: the authenticated identity in user.username and user.groups, the client address in sourceIPs, the verb and requestURI, a structured objectRef describing the target, the authorizer’s verdict in the annotations map, and the outcome in responseStatus.code. Because almost everything in Kubernetes goes through the API server — kubectl, controllers, CI pipelines, operators and the kubelet — this is the single best record of who did what in a cluster.

How much is recorded is decided per request by the audit policy. A rule can be None (drop), Metadata (who, what, when, result), Request (plus the submitted object) or RequestResponse (plus what the server returned). The sample files on this page mix Metadata and RequestResponse events, which is what a real policy usually looks like: full bodies for a few interesting resources, metadata for the rest.

Where to get it

  • Self-managed control planes: start the API server with --audit-policy-file and either the log backend (--audit-log-path, newline-delimited JSON) or the webhook backend, which posts events to a collector. Without a policy file, no audit events are generated.
  • Amazon EKS: enable the audit control plane log type; events land in CloudWatch Logs.
  • Azure Kubernetes Service: add the kube-audit (or the lower-volume kube-audit-admin) diagnostic log category and route it to a Log Analytics workspace, storage account or event hub.
  • Google Kubernetes Engine: API server activity arrives as Cloud Audit Logs entries in Cloud Logging, wrapped in Google’s log structure rather than the raw audit.k8s.io/v1 shape.

Parsing pitfalls

  • Array vs. JSON lines. The log backend writes one JSON object per line; exports and API responses (including the samples here) are a JSON array. Handle both or you will read zero events.
  • Multiple events per request. Filter to stage ResponseComplete before counting actions, and deduplicate on auditID.
  • Missing fields are normal. objectRef.name is absent on collection requests, objectRef.namespace on cluster-scoped resources such as clusterrolebindings, and request/response bodies on Metadata-level events. Treat absence as “not recorded”, not “did not happen”.
  • sourceIPs is a list. Behind a load balancer or proxy it can hold several hops; the first entry is not always the real client.
  • The verb is not the action name. Exec, attach and port-forward all appear as create against a subresource.
  • Status code 101. A successful exec or attach returns a protocol upgrade, so a query that looks only for 2xx misses it.
  • Volume. Controller list and watch loops dominate; most clusters filter system: identities in the policy or at ingest, which also means attacker activity that borrows a controller identity may be filtered away.

Detection ideas

  • Secret harvesting: one user.username performing list or get on objectRef.resource secrets across several objectRef.namespace values in a short window — the basis of the Kubernetes secret exfiltration rule.
  • Shell into a pod: objectRef.subresource of exec, attach or portforward with responseStatus.code 101, especially from a service account that normally only patches deployments.
  • Privilege escalation: create or update on clusterrolebindings and rolebindings, then check which subject was bound.
  • RBAC probing: repeated events with annotations."authorization.k8s.io/decision" of forbid and responseStatus.code 403 from one identity.
  • Unauthenticated access: user.username of system:anonymous or a user.groups entry of system:unauthenticated on anything beyond health endpoints.
  • Out-of-place clients: a kubectl userAgent on a service-account identity, or a sourceIPs value outside your pod and bastion ranges.

You can run these checks on an exported audit file in the free browser log analyzer without sending it anywhere.

Detections and guides for Kubernetes Audit Log

Frequently Asked Questions

Why does my cluster produce no audit events at all?
The API server only audits when it is started with an audit policy and a backend. On managed control planes you enable it in the provider console instead — EKS control plane logging, AKS diagnostic settings, GKE Cloud Audit Logs — and those offerings may not let you supply your own policy file, so the level of detail is fixed.
Can I see the contents of a secret that was read?
Only if the audit policy recorded that request at RequestResponse level, which writes the response body into the event. Many policies deliberately drop bodies for secrets and configmaps, because audit logs stored at that level become as sensitive as the secrets themselves.
How do I tell a pod exec from an ordinary pod read?
Look for objectRef.subresource of exec with verb create, or a requestURI ending in /exec. A successful exec upgrades the connection, so responseStatus.code is 101 rather than 200, and the command itself is not in the event body.
Why do I see several events with the same auditID?
One request can be audited at multiple stages — RequestReceived, ResponseStarted, ResponseComplete and Panic. Deduplicate on auditID and keep the ResponseComplete event, which is the one that carries the final responseStatus, before counting operations per user.

Analyze Kubernetes Audit Log logs

LogTriage auto-detects this format, enriches every IP, user agent and hash with threat intelligence, and writes an incident report mapped to MITRE ATT&CK.