CKS — Kubernetes Security Specialist
Designing an Audit Policy
Goal
You build an audit.k8s.io/v1 audit policy file one rule at a time, checking for yourself
what rule order and level choice keep and what they throw away.
Why it matters
The first question of incident response is always "when, who, and what was done," and in Kubernetes the only data that can answer it is the API server audit log. If you record everything, costs explode, and if you record sloppily, there is nothing when you actually need it. So an audit policy is a document that decides not "what to record" but "what to throw away."
You experience the two most common mistakes yourself in this lab. First, rules are evaluated from the top and only the first
match is applied, so an exclusion rule (level: None) has no effect if it is lower down.
Second, if you apply RequestResponse to Secrets, the secret values go as they are into the audit log file.
Audit log collection systems usually have broader access permissions than the application, so
it ends up spreading what you meant to protect.
In this environment, you can't actually turn on the audit log (you can't reattach a policy file to the kwok control plane).
Grading looks only at the contents and order of the policy file. What the CKS exam grades is, in the end, this
file too. Runtime investigation with Falco rules and crictl is covered in this module's reading and quiz.
Steps
- Create
/root/cks-audit-runtime/audit-policy.yaml. It hasapiVersion: audit.k8s.io/v1andkind: Policy, and the very last rule inrulesis a catch-all rule with no conditions andlevel: Metadata. - Put
omitStagesat the top level of the same file and includeRequestReceived. - Add a rule to
rulesthat records Secrets and ConfigMaps atlevel: Metadata. It targetssecretsandconfigmapsin the core group. No rule in the file may specifyRequestorRequestResponseforsecrets. - At the very top of
rules, put a rule that excludesgetrequests forsecretsin the kube-system namespace withlevel: None. This rule must be positioned before the rule made in step 3. - Add a rule to
rulesthat records the verbsescalate,bind, andimpersonateon RBAC resources (roles,rolebindings,clusterroles, andclusterrolebindingsin therbac.authorization.k8s.iogroup) atlevel: RequestResponse. - Add a rule to
rulesthat recordscreate,update,patch, anddeleteonpodsin the core group atlevel: Request. - In
/root/cks-audit-runtime/apiserver-audit-flags.txt, write the flags to attach to the API server, one per line. The five are--audit-policy-file=/etc/kubernetes/audit-policy.yaml,--audit-log-path=/var/log/kubernetes/audit.log,--audit-log-maxage=30,--audit-log-maxbackup=10, and--audit-log-maxsize=100.
Notes
- The shape of one rule: under
- level: None, attachresources,namespaces, andverbsas needed. - A
resourcesentry is a list of pairs of- group: ""andresources: [...]. The core group is an empty string. - Checking the number of rules with
yq '.rules | length' /root/cks-audit-runtime/audit-policy.yamlmakes it easy to check the order. - Common mistake 1: putting the
level: Noneexclusion rule lower in the file. The upper rule matches first and it has no effect. - Common mistake 2: putting the catch-all rule at the top. Then the rules below are never evaluated.
The audit policy skeleton and the catch-all rule
Create /root/cks-audit-runtime/audit-policy.yaml. It has apiVersion: audit.k8s.io/v1 and
kind: Policy, and the very last rule in rules is a catch-all rule with no conditions and level: Metadata.
The policy is a Policy resource of audit.k8s.io/v1. A catch-all rule to take requests that match nothing must be at the very bottom.
Remove the noisy stage
Put omitStages at the top level of the same file and include RequestReceived.
A single request produces events at several stages. The event at the moment the request is received is mostly useless. If you specify it as a top-level field, it applies to everything.
Secrets only up to metadata
Add a rule to rules that records Secrets and ConfigMaps at level: Metadata.
It targets secrets and configmaps in the core group. No rule in the file may specify
Request or RequestResponse for secrets.
If you apply a level of Request or above to Secrets, the secret values go as they are into the audit log file. No rule may specify Request or above for secrets.
Exclude kube-system noise
At the very top of rules, put a rule that excludes get requests for secrets in the kube-system namespace
with level: None. This rule must be positioned
before the rule made in step 3.
Rules are evaluated from the top and only the first match is applied. If the exclusion rule is lower down, the upper rule catches it first.
Record every privilege escalation attempt
Add a rule to rules that records the verbs escalate, bind, and impersonate on RBAC resources (roles, rolebindings,
clusterroles, and clusterrolebindings in the rbac.authorization.k8s.io group) at
level: RequestResponse.
RBAC resources are in the rbac.authorization.k8s.io group, not the core group. List all three dangerous verbs in verbs.
Pod changes up to the request body
Add a rule to rules that records create, update, patch, and delete on pods in the core group
at level: Request.
Leave out reads and pick only the mutating verbs. If you keep the request body, you can reconstruct which manifest was deployed after a compromise.
Putting it together: document the audit log backend flags
In /root/cks-audit-runtime/apiserver-audit-flags.txt, write the flags to attach to the API server, one per line.
The five are --audit-policy-file=/etc/kubernetes/audit-policy.yaml,
--audit-log-path=/var/log/kubernetes/audit.log, --audit-log-maxage=30,
--audit-log-maxbackup=10, and --audit-log-maxsize=100.
You must write all three: the policy file location, the log output location, and the rotation policy. Without rotation settings the disk fills up, and when the disk fills up the API server stops.