Kubernetes clusters with audit logging enabled
Check the kube-apiserver manifest to confirm Kubernetes audit logging is enabled
grep audit /etc/kubernetes/manifests/kube-apiserver.yaml
- --audit-log-maxage=30
- --audit-log-maxbackup=10
- --audit-log-maxsize=100
- --audit-log-path=/var/log/kubernetes/audit.log
- --audit-policy-file=/etc/kubernetes/audit-policy.yaml
name: audit-logs
- mountPath: /etc/kubernetes/audit-policy.yaml
name: audit-policy
name: audit-logs
path: /etc/kubernetes/audit-policy.yaml
name: audit-policy
The kube-apiserver uses a structured log format. It logs Events in json format. Below is an example of a single event
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"level": "Request",
"timestamp": "2021-09-01T10:53:08Z",
"auditID": "77f9b6d1-7d3d-4408-875e-0f7ab1b1e7a4",
"stage": "ResponseComplete",
"requestURI": "/api/v1/namespaces/default/pods/example-pod",
"verb": "get",
"user": {
"username": "user1",
"groups": ["system:masters", "system:authenticated"]
},
"sourceIPs": ["192.168.0.1"],
"responseStatus": {
"metadata": {},
"code": 200
},
"requestObject": {
// request spec
},
"responseObject": {
// response spec
}
}
Few important fields to understand information contained in the Events are
Here are few frequently used commands that can help extract useful information from Kubernetes audit logs. Depending on the scenario this can be used to find out useful information about performance issues. Note that these commands use jq for json processing which should be installed before analyzing audit logs
cat /var/log/kubernetes/audit.log | jq -r .user.username | sort | uniq -c | sort -nr | head -n 10
cat /var/log/kubernetes/audit.log | jq -r .sourceIPs[] | sort | uniq -c | sort -nr | head -n 10
cat /var/log/kubernetes/audit.log | jq -r .requestURI | sort | uniq -c | sort -nr | head -n 10
cat /var/log/kubernetes/audit.log | jq -r .verb | sort | uniq -c | sort -nr | head -n 10
cat /var/log/kubernetes/audit.log | jq -r .responseStatus.code | sort | uniq -c | sort -nr | head -n 10
cat /var/log/kubernetes/audit.log | jq -r '.requestURI | match("/namespaces/(.*?)/").captures[0].string' | sort | uniq -c | sort -nr | head -n 10
cat /var/log/kubernetes/audit.log | jq -r 'select(.verb == "create") | .requestURI' | sort | uniq -c | sort -nr | head -n 10
cat /var/log/kubernetes/audit.log | jq -r 'select(.verb == "delete") | .requestURI' | sort | uniq -c | sort -nr | head -n 10
cat /var/log/kubernetes/audit.log | jq -r 'select(.verb == "update") | .requestURI' | sort | uniq -c | sort -nr | head -n 10
cat /var/log/kubernetes/audit.log | jq -r 'select(.requestURI | contains("/pods")) | .requestURI' | sort | uniq -c | sort -nr | head -n 10
cat /var/log/kubernetes/audit.log | jq .
cat /var/log/kubernetes/audit.log | jq .user.username | sort | uniq
cat /var/log/kubernetes/audit.log | jq 'select(.user.username == "user1")'
cat /var/log/kubernetes/audit.log | jq 'select(.requestURI == "/api/v1/namespaces/default/pods/example-pod")'
cat /var/log/kubernetes/audit.log | jq 'select(.verb == "create" and .requestObject.kind == "Eviction")'
cat /var/log/kubernetes/audit.log | jq .verb | sort | uniq
cat /var/log/kubernetes/audit.log | jq 'select(.verb == "get")'
cat /var/log/kubernetes/audit.log | jq 'select(.verb == "delete")'
cat /var/log/kubernetes/audit.log | jq .sourceIPs[] | sort | uniq
cat /var/log/kubernetes/audit.log | jq 'select(.sourceIPs[] == "192.168.0.1")'
cat /var/log/kubernetes/audit.log | jq .responseStatus.code | sort | uniq
cat /var/log/kubernetes/audit.log | jq 'select(.responseStatus.code != 200)'
cat /var/log/kubernetes/audit.log | jq 'select(.responseStatus.code == 200)'
cat /var/log/kubernetes/audit.log | jq 'select(.timestamp >= "2021-09-01T00:00:00Z" and .timestamp <= "2021-09-01T23:59:59Z")'
cat /var/log/kubernetes/audit.log | jq 'select(.requestURI | startswith("/api/v1/namespaces/default"))'
cat /var/log/kubernetes/audit.log | jq 'select(.requestURI | contains("/pods") and .verb == "create")'
cat /var/log/kubernetes/audit.log | jq 'select(.auditID == "77f9b6d1-7d3d-4408-875e-0f7ab1b1e7a4")'
cat /var/log/kubernetes/audit.log | jq 'select(.requestObject.kind == "Pod" and .verb == "update")'
cat /var/log/kubernetes/audit.log | jq 'select(.stage == "ResponseComplete")'
cat /var/log/kubernetes/audit.log | jq 'select(.user.groups[] == "system:masters")'
cat /var/log/kubernetes/audit.log | jq 'select(.responseStatus.code != 200 and .responseStatus.code != 201)'
cat /var/log/kubernetes/audit.log | jq 'select(.requestObject.kind == "Pod")'
cat /var/log/kubernetes/audit.log | jq 'select(.responseObject.status == "Failure")'
cat /var/log/kubernetes/audit.log | jq 'select(.requestURI == "/healthz")'
cat /var/log/kubernetes/audit.log | jq 'select(.responseObject.kind == "Status")'
cat /var/log/kubernetes/audit.log | jq 'select(.verb == "watch")'
Learn more about Kubernetes Audit Logging at Auditing in K8s