EKS, GKE and AKS Audit Logs: Enable and Export Them
How to enable and export Kubernetes audit logs on Amazon EKS, Google GKE and Azure AKS: where they live, what the managed policy drops, and the export commands.
TL;DR. On managed Kubernetes you cannot edit the audit policy, but you decide whether the audit log is kept at all. EKS: enable the audit control-plane log type; events land in CloudWatch Logs, group /aws/eks/<cluster>/cluster, streams kube-apiserver-audit-*. GKE: Admin Activity (writes) is always on; reads, including secret reads, go to Data Access logs, which you must enable for the Kubernetes Engine API. AKS: create a diagnostic setting with the kube-audit category (or kube-audit-admin, which drops get and list). Export as JSON, then drop the files into the audit log analyzer.
None of these three is on by default in a way that covers an investigation. The worst time to discover that is the morning after an alert. This guide covers what to switch on now and how to get the data out when you need it.
Amazon EKS
Enable
By default, EKS sends no control-plane logs to CloudWatch; each log type is enabled individually, as the EKS control plane logging documentation states. In the console: cluster, Observability, Control plane logging, Manage logging, turn on Audit (and Authenticator, which maps IAM principals to Kubernetes users). With the CLI:
aws eks update-cluster-config --region eu-west-1 --name my-cluster \
--logging '{"clusterLogging":[{"types":["audit","authenticator"],"enabled":true}]}'
Standard CloudWatch Logs ingestion and storage charges apply. Delivery is best effort and usually takes a few minutes. Set a retention on the log group that matches your investigation needs.
What the EKS policy logs
AWS publishes the EKS audit policy in the EKS best practices guide. Points that matter for forensics:
- Secrets, configmaps and token reviews:
Metadataonly. serviceaccounts/token:Request.- Reads (
get,list,watch) on core and well-known API groups:Request; writes:RequestResponse. Workload specs and RBAC bodies are therefore available. eventsare not logged at all. A deletion of Events will not appear.- CloudWatch Logs entries are capped at 1 MB while an API request can reach 1.5 MiB, so very large objects may be truncated or reduced to metadata.
Export
For a short range, filter-log-events is enough:
aws logs filter-log-events --log-group-name /aws/eks/my-cluster/cluster \
--log-stream-name-prefix kube-apiserver-audit \
--start-time 1789344000000 --end-time 1789430400000 > eks-audit.json
Times are in epoch milliseconds. For days or weeks of logs, use an export task to S3 (aws logs create-export-task) and download the .gz objects: each line is a timestamp followed by the JSON event. CloudWatch Logs Insights results exported as JSON work too. The analyzer reads all three shapes, including gzip files and whole folders.
IAM identities show up in the audit log under the username mapped by access entries or the aws-auth ConfigMap. To follow the same principal on the AWS side (CloudTrail, the EKS API, AssumeRole calls), see AWS Forensics.
Google GKE
Two logs, one of them off by default
GKE writes Kubernetes audit entries to Cloud Audit Logs under the k8s_cluster resource type. The GKE audit policy routes them:
create,updateanddeleterequests go to the Admin Activity log, which is always on and cannot be disabled.get,listandupdateStatusgo to the Data Access log, which, as the GKE audit logging page notes, is disabled by default and billed when enabled.
The consequence is blunt: on a default GKE project, an attacker who reads every secret with get leaves no audit entry for those reads. Enable Data Access logs for the Kubernetes Engine API in IAM & Admin, Audit Logs before you need them, and weigh the volume against the cost.
What the GKE policy logs
The same page states that requests on secrets, configmaps and token reviews are logged at Metadata, reads are generally Metadata, and writes are generally RequestResponse. Workload specs and RBAC bindings are therefore visible for writes.
In Cloud Logging the event is rewritten: protoPayload.methodName (for example io.k8s.core.v1.pods.exec.create), protoPayload.resourceName, protoPayload.authenticationInfo.principalEmail, protoPayload.requestMetadata.callerIp and callerSuppliedUserAgent, a gRPC status code instead of HTTP. The analyzer maps these back to verb, resource, subresource, user, IP and HTTP code.
Export
gcloud logging read \
'resource.type="k8s_cluster" AND logName:"cloudaudit.googleapis.com"' \
--project=my-project --freshness=30d --format=json > gke-audit.json
You can also download results from Logs Explorer as JSON, or, for long periods, route the logs with a sink to Cloud Storage or BigQuery. BigQuery stores the payload in protopayload_auditlog with request and response bodies as JSON text; the analyzer reads those rows when you export the table or query results as JSON or newline-delimited JSON (not CSV, Avro or Parquet). Retention depends on the log bucket: Admin Activity logs go to the _Required bucket, Data Access logs to _Default, unless you changed the routing. For the Google Cloud side of an incident (IAM, service account keys, Compute), GCP Forensics covers those logs.
One current limitation of the analyzer: GKE impersonation details carried in serviceAccountDelegationInfo are not yet mapped.
Azure AKS
Enable
AKS sends control-plane logs through a diagnostic setting on the cluster resource. The AKS monitoring documentation describes the audit categories:
| Category | Contains |
|---|---|
kube-audit | Every audit event, including get and list |
kube-audit-admin | The same without get and list events |
kube-audit is the one you want for credential theft, since secret reads are get and list. Microsoft warns that it can be expensive; kube-audit-admin is the cheaper compromise, at the price of losing reads. Destinations:
- Log Analytics, in resource-specific mode (tables
AKSAuditandAKSAuditAdmin) or legacy Azure diagnostics mode (tableAzureDiagnostics, event JSON inlog_s). - Storage account: hourly
PT1H.jsonblobs under theinsights-logs-kube-auditcontainer. - Event Hubs:
{"records": [...]}batches, typically consumed by a SIEM.
Example with the Azure CLI, sending to Log Analytics in resource-specific mode:
az monitor diagnostic-settings create --name aks-audit \
--resource <aks-resource-id> --workspace <workspace-resource-id> \
--export-to-resource-specific true \
--logs '[{"category":"kube-audit","enabled":true}]'
Export
From a storage account, download the PT1H.json blobs for the period (the folder structure encodes the date and hour) and drop the whole folder. From Log Analytics, query the table and save the result as JSON rather than CSV:
az monitor log-analytics query --workspace <workspace-guid> \
--analytics-query "AKSAudit | where TimeGenerated between (datetime(2026-09-13) .. datetime(2026-09-15))" \
-o json > aks-audit.json
Query results are capped in size, so split long ranges into several queries. The analyzer reads AKSAudit rows (PascalCase columns), AzureDiagnostics rows, storage-account blobs and Event Hubs batches. For Entra ID sign-ins and Azure activity around the cluster, see Azure Forensics.
Self-managed clusters
On kubeadm, k3s, RKE2 or on-premises distributions, you own the policy and the files. Copy audit.log and its rotated siblings from every control-plane node (each API server logs only the requests it served), or export from wherever your webhook or log shipper sends them. The audit policy article has a forensics-oriented policy to start from.
Before you analyze
- Cover the whole period, starting days before the first suspicious event.
- Keep the originals untouched and work on copies; record hashes if the case may go to court.
- Note the time zone. Audit timestamps are UTC; console exports may not be.
- Check for gaps. A day with no events at all usually means a logging change, not a quiet cluster.
Then open the Kubernetes audit log analyzer, drop the files or folders, and follow the step-by-step analysis guide.