Enable Kubernetes Audit Logging with a Policy File
First, reconfigure the cluster's API server, so that: the basic audit policy located at /etc/kubernetes/logpolicy/audit-policy.yaml is used, logs are stored at /var/log/kubernetes/audit-logs.txt, and…
Live lab access
This task description is public. Live practice requires full access unless this is the selected free weekly task. AI Lab Coach is available in single-task labs, including the free weekly task. See this week's free task.
Create an account to continue →
On a phone? Read the task and create your account here, then sign in on a desktop or laptop for the terminal lab. Nothing starts until you choose Start.
The task
Context
You must implement auditing for the kubeadm provisioned cluster.
Task
First, reconfigure the cluster's API server, so that:
- the basic audit policy located at
/etc/kubernetes/logpolicy/audit-policy.yamlis used, - logs are stored at
/var/log/kubernetes/audit-logs.txt, - and a maximum of
2logs are retained for10days.
The cluster uses the Docker Engine as its container runtime. If needed, use the docker command to troubleshoot running containers.
> The basic policy only specifies what not to log.
Next, edit and extend the basic policy to log:
- namespaces interactions at
RequestResponselevel - the request body of deployments interactions in the namespace
webapps - ConfigMap and Secret interactions in all namespaces at the
Metadatalevel - all other requests at the
Metadatalevel
> Make sure the API server uses the extended policy. Failure to do so may result in a reduced score.
*Lab note: this practice node runs containerd rather than the Docker Engine, so use crictl (or kubectl) where you would use docker.*
Original Prepium guide · Written for Kubernetes v1.35 and checked against the official documentation on 6 October 2026. Use a disposable practice cluster. You will edit the API server's static Pod, so work on a throwaway kubeadm VM.
Audit logging: write a policy, wire it into the API server, read the result
Audit logging records who did what to which object, and it lives entirely inside kube-apiserver. Two pieces must agree: a Policy file that decides how much detail each request gets, and API server flags that load the policy and say where the log goes. On kubeadm the server runs in a container, so host paths must also be mounted into its static Pod.
request ──→ policy rules (top to bottom, first match wins) ──→ level
level None │ Metadata │ Request │ RequestResponse ──→ JSON lines in the log file
1. Understand levels and rule order
None: no event at all.Metadata: user, verb, resource, timestamps, no bodies.Request: adds the request body.RequestResponse: adds the response body too. The two body levels do not apply to non-resource URLs such as/healthz.- Rules are evaluated in order and the first matching rule sets the level. A broad rule placed early silently shadows every specific rule below it.
- With no matching rule the level is
None, so policies usually end with a catch-all rule. omitStagescan be set for the whole policy or per rule; the union of both is skipped.
2. Write your own policy
Save this example as /etc/kubernetes/audit/policy.yaml on the practice node. The apiVersion is audit.k8s.io/v1 and rules must contain at least one rule.
apiVersion: audit.k8s.io/v1
kind: Policy
omitStages:
- "RequestReceived" # applies to every rule
rules:
- level: None # drop health probe noise
nonResourceURLs: ["/healthz*", "/livez*", "/readyz*"]
- level: RequestResponse
resources:
- group: "rbac.authorization.k8s.io"
resources: ["roles", "rolebindings"]
namespaces: ["prepium-tutorial"]
- level: Request
verbs: ["create", "update", "patch", "delete"]
resources:
- group: "networking.k8s.io"
resources: ["networkpolicies"]
- level: Metadata # catch-all, keep it last
The narrow rules sit above the catch-all, otherwise they would never match. An omitted verbs or namespaces field means "all"; group: "" is the core API group.
3. Add flags and hostPath mounts
Back up the manifest outside /etc/kubernetes/manifests, then add flags to the kube-apiserver command and matching mounts. Retention flags are independent: --audit-log-maxage is days, --audit-log-maxbackup is a count of old files, --audit-log-maxsize is megabytes before rotation.
- --audit-policy-file=/etc/kubernetes/audit/policy.yaml
- --audit-log-path=/var/log/kube-audit/audit.log
- --audit-log-maxage=7
- --audit-log-maxbackup=5
- --audit-log-maxsize=50
...
volumeMounts:
- {name: audit-policy, mountPath: /etc/kubernetes/audit/policy.yaml, readOnly: true}
- {name: audit-logs, mountPath: /var/log/kube-audit/}
...
volumes:
- {name: audit-policy, hostPath: {path: /etc/kubernetes/audit/policy.yaml, type: File}}
- {name: audit-logs, hostPath: {path: /var/log/kube-audit/, type: DirectoryOrCreate}}
The container can only read paths you mount. If a flag points at a path the container cannot see, the policy cannot be loaded and the API server does not come back; crictl ps -a and crictl logs on the exited container show why.
4. Prove the policy is active
kubectl create namespace prepium-tutorial
kubectl -n prepium-tutorial create role peek --verb=get --resource=pods
sudo tail -n 20 /var/log/kube-audit/audit.log | grep '"resource":"roles"'
Each line is one JSON event. For the role creation expect "level":"RequestResponse" plus both requestObject and responseObject. A kubectl get pods should produce only Metadata events. If the policy file is edited later, restart the API server (move its manifest out of the directory and back) and test again; do not assume the running process picked up the change.
Common mistakes
- Catch-all rule first: every event comes out at one level and the specific rules never fire.
- Flags added but no
volumes/volumeMounts: the container cannot read the policy andkubectlstops answering. - Logging Secrets at
Requestor above: their data is written in clear to the audit file. - Writing a group with a version, such as
apps/v1: rules take the group name only.
| Example check | Evidence |
|---|---|
| Policy loaded | --audit-policy-file in the running container's command, file mounted readOnly |
| Log written | New JSON lines appear at the --audit-log-path location after any kubectl call |
| Rule order correct | A matching request shows the specific level, not the catch-all level |
| Rotation configured | maxage, maxbackup and maxsize flags present with intended values |
Cleanup: kubectl delete namespace prepium-tutorial, then restore your backed-up manifest or discard the VM. References: Auditing, kube-apiserver Audit Configuration (v1), kube-apiserver flags. Next, try API server authentication flags or runtime detection with Falco.
What this tests
Detect threats at runtime with Falco, behavioral analytics, and audit logging. On the CKS exam, Monitoring, Logging and Runtime Security tasks are graded purely on what you build in the cluster - not multiple choice - so the only way to get faster is to do them on a real cluster against a clock.
Practice it for real
The public description lets you study the task before signing up. Full access includes this live lab; the selected Task of the Week is free with an account. You solve the task in a real terminal, hit validate, and a programmatic checker scores exactly what you got right and wrong (with partial credit). You can open the solution while practising, then retry and use the failed checks to improve your score.