CKS is the terminal-speed security finish line: a 2-hour, performance-based exam on Kubernetes v1.35. You must have passed CKA before attempting it. Current domains: Cluster Setup 15% · Cluster Hardening 15% · System Hardening 10% · Minimize Microservice Vulnerabilities 20% · Supply Chain Security 20% · Monitoring, Logging & Runtime Security 20%. Make the smallest safe change, preserve availability, and prove the control works.
0. First-minute setup and evidence loop
alias k=kubectl export do="--dry-run=client -o yaml" source <(kubectl completion bash) complete -o default -F __start_kubectl k # every task k config use-context <named-context> k config set-context --current --namespace=<namespace> k get nodes # preserve before editing host/control-plane files sudo cp /etc/kubernetes/manifests/kube-apiserver.yaml \ /etc/kubernetes/manifests/kube-apiserver.yaml.bak-outside-manifest-dir
/etc/kubernetes/manifests can be read by the kubelet as another static Pod. Put backups elsewhere, change one flag at a time, and watch the component return before continuing.For each task: identify boundary → inspect current state → preserve rollback → change → wait → verify positive behavior → verify denied behavior. A policy that exists but selects nothing is not a security control.
1. Cluster Setup (15%)
Network security policies
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: api-ingress, namespace: team }
spec:
podSelector: { matchLabels: { app: api } }
policyTypes: [Ingress]
ingress:
- from:
- namespaceSelector: { matchLabels: { kubernetes.io/metadata.name: ingress } }
podSelector: { matchLabels: { app: gateway } }
ports: [{ protocol: TCP, port: 8443 }]
# verify labels and actual reachability from allowed and denied sources k get pods -n team --show-labels k get ns --show-labels k describe netpol api-ingress -n team k exec -n ingress deploy/gateway -- wget -qO- --timeout=2 https://api.team:8443 k run denied -n default --image=busybox:1.36 --rm -it --restart=Never -- \ wget -qO- --timeout=2 http://api.team:8443
namespaceSelector and podSelector in the same from item are ANDed. Separate list items are ORed. Indentation changes the security meaning.CIS benchmark review
# use the binary/config supplied by the exam environment kube-bench run --targets master kube-bench run --targets node # confirm the effective configuration, not only flags ps -ef | grep kube-apiserver sudo grep -nE 'anonymous-auth|authorization-mode|read-only-port' \ /var/lib/kubelet/config.yaml /etc/kubernetes/manifests/*.yaml
CIS output is an assessment, not an automatic mandate. Fix the named control in the named component and validate availability. Some managed or lab configurations have justified exceptions.
Ingress TLS
k create secret tls web-tls --cert=tls.crt --key=tls.key -n app
spec:
tls:
- hosts: [app.example.com]
secretName: web-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service: { name: web, port: { number: 80 } }
Certificate hostnames must match the request host, the Secret must be in the Ingress namespace, and a controller must implement the Ingress.
Node metadata, endpoints and binaries
- Prevent Pods from reaching cloud metadata unless explicitly needed; prefer workload identity to node-wide credentials.
- Restrict kubelet, etcd, runtime sockets and control-plane ports by network and authentication.
- Download binaries from the official channel and verify checksum/signature before execution.
sha256sum kubectl echo '<expected-sha256> kubectl' | sha256sum --check openssl x509 -in tls.crt -noout -subject -issuer -dates -ext subjectAltName
2. Cluster Hardening (15%)
Least-privilege RBAC
k auth can-i --list --as=jane -n team k auth can-i get secrets --as=system:serviceaccount:team:builder -n team k get rolebindings,clusterrolebindings -A -o wide k create role deploy-reader --verb=get,list,watch --resource=deployments.apps -n team k create rolebinding jane-deploy-read --role=deploy-reader --user=jane -n team k auth can-i list deployments --as=jane -n team k auth can-i delete deployments --as=jane -n team
- Avoid wildcards in verbs/resources and avoid
cluster-adminfor routine workloads. - Permissions to create Pods can indirectly expose mounted Secrets, service-account tokens, host paths or privileged execution.
- Permissions to create/update Roles or bindings can be privilege escalation.
- Test allowed and forbidden operations with the exact identity and namespace.
Service-account hygiene
apiVersion: v1
kind: Pod
metadata: { name: worker }
spec:
serviceAccountName: worker
automountServiceAccountToken: false
containers:
- { name: worker, image: example/worker:1.2 }
Use dedicated service accounts, minimal RoleBindings and projected short-lived tokens. Disable automount when the process does not call the Kubernetes API.
Restrict API access and upgrade
- Disable anonymous authentication where required and use secure authorization modes.
- Expose the API on trusted networks; use TLS and controlled bastion/VPN paths.
- Protect admin kubeconfigs and rotate compromised credentials.
- Upgrade supported Kubernetes releases to receive security fixes; drain and verify nodes as in CKA.
sudo grep -n -- '--anonymous-auth\|--authorization-mode' \ /etc/kubernetes/manifests/kube-apiserver.yaml k get --raw='/readyz?verbose' k version k get nodes -o custom-columns=NAME:.metadata.name,KUBELET:.status.nodeInfo.kubeletVersion
3. System Hardening (10%)
Reduce the host attack surface: minimal OS/packages, patched kernel/runtime, no unused network services, protected runtime sockets, least-privilege users, strong file permissions and host-level mandatory access control.
# inspect listening services and running units
sudo ss -lntup
sudo systemctl --type=service --state=running
sudo find /etc/kubernetes -type f -maxdepth 3 -ls
# AppArmor availability and profiles
sudo aa-status
# seccomp is expressed in the Pod security context
k get pod app -o jsonpath='{.spec.securityContext.seccompProfile.type}'
| Control | What it restricts |
|---|---|
| seccomp | Linux system calls available to a process |
| AppArmor | Profile-based file, capability, signal and network access |
| capabilities | Breaks root privilege into discrete powers |
| runAsNonRoot | Prevents UID 0 execution when enforceable |
| readOnlyRootFilesystem | Reduces runtime filesystem mutation |
4. Minimize Microservice Vulnerabilities (20%)
Restricted workload pattern
apiVersion: v1
kind: Pod
metadata: { name: api, namespace: team }
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
seccompProfile: { type: RuntimeDefault }
containers:
- name: api
image: registry.example/api@sha256:<digest>
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 500m, memory: 256Mi }
volumeMounts:
- { name: tmp, mountPath: /tmp }
volumes:
- { name: tmp, emptyDir: {} }
If a read-only image needs writable paths, mount narrow emptyDir volumes instead of making the whole root filesystem writable. Never add privilege merely to silence an application error without understanding the required access.
Pod Security Admission
# stage with warn/audit, then enforce after remediation k label ns team pod-security.kubernetes.io/warn=restricted \ pod-security.kubernetes.io/warn-version=latest --overwrite k label ns team pod-security.kubernetes.io/audit=restricted \ pod-security.kubernetes.io/audit-version=latest --overwrite k label ns team pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/enforce-version=latest --overwrite
Secrets and isolation
- Encrypt Secret data at rest; restrict RBAC and backup access; rotate leaked values.
- Use separate namespaces, identities, quotas and NetworkPolicies for tenants, but recognize namespaces alone are not hard multi-tenancy.
- Use sandboxed runtimes/VM isolation for stronger untrusted-workload boundaries.
- Use Cilium, Istio or another appropriate dataplane for workload-to-workload encryption when required; NetworkPolicy alone does not encrypt.
5. Supply Chain Security (20%)
Minimize and inspect images
FROM golang:1.24 AS build WORKDIR /src COPY . . RUN CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o /out/app ./cmd/app FROM gcr.io/distroless/static-debian12:nonroot COPY --from=build /out/app /app USER nonroot:nonroot ENTRYPOINT ["/app"]
- Smaller base, fewer packages and no shell/package manager reduce attack surface.
- Run as non-root, pin digest and rebuild from maintained bases.
- Do not place build credentials or secrets in layers; multi-stage builds do not erase a secret copied into an earlier published stage.
Scan, inventory, sign and admit
# exact commands depend on tools installed in the exam environment trivy image --severity HIGH,CRITICAL registry.example/app:1.4 trivy config deployment.yaml kube-linter lint deployment.yaml kubesec scan deployment.yaml # example Sigstore workflow when cosign is supplied cosign verify --key cosign.pub registry.example/app@sha256:<digest> cosign verify-attestation --key cosign.pub \ --type slsaprovenance registry.example/app@sha256:<digest>
| Evidence/control | Answers |
|---|---|
| Vulnerability scan | Does known affected software exist? |
| Static manifest analysis | Does configuration violate a policy or best practice? |
| SBOM | Which software components are present? |
| Signature | Who signed this exact artifact; is integrity intact? |
| Provenance attestation | How and where was the artifact built? |
| Admission policy | May this artifact/configuration enter the cluster? |
Enforce trusted registries, digest pinning, signatures/attestations and policy through an admission mechanism available in the environment. Test an allowed image and a deliberately disallowed image.
6. Monitoring, Logging and Runtime Security (20%)
Kubernetes audit logging
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
- level: RequestResponse
verbs: ["create", "update", "patch", "delete"]
- level: None
nonResourceURLs: ["/healthz*", "/readyz*"]
- level: Metadata
# API server flags need both policy and log destination plus hostPath mounts sudo grep -n -- '--audit-' /etc/kubernetes/manifests/kube-apiserver.yaml sudo tail -f /var/log/kubernetes/audit/audit.log k get --raw=/readyz
Audit levels are None, Metadata, Request, RequestResponse. Rules are evaluated in order; first match wins. Avoid logging sensitive request bodies unless the task explicitly requires it.
Runtime detection and investigation
Behavioral detection observes what running workloads actually do: unexpected shells, package installation, writes to sensitive paths, privilege changes, suspicious network connections or access to credentials. Falco-style rules commonly match kernel/runtime events against conditions.
# investigate a suspicious workload k get pod suspicious -o yaml k describe pod suspicious k logs suspicious --all-containers --since=30m k get events --field-selector involvedObject.name=suspicious k auth can-i --list --as=system:serviceaccount:team:app -n team # node/runtime evidence when authorized sudo crictl ps -a sudo crictl inspect <container-id> sudo journalctl -u kubelet --since '30 min ago'
- Preserve relevant evidence and timestamps.
- Scope identity, namespace, node, image digest, network and affected objects.
- Contain with minimal blast radius—policy, scale/isolate, credential rotation as appropriate.
- Remove persistence and repair the exploited control.
- Recover from trusted artifacts and verify monitoring catches recurrence.
Runtime immutability
- Use read-only root filesystems with explicit writable volumes.
- Disallow exec/package-manager workflows for production change; rebuild and redeploy a new image.
- Pin images by digest and detect drift from declared state.
- Drop privilege and prevent hostPath/host namespaces unless explicitly required.
7. Admission policy and encryption at rest
Validate before persistence
Admission runs after authentication and authorization but before an object is stored. Mutating admission changes a request; validating admission accepts or rejects it. Policy can enforce trusted registries, digest pinning, required labels, restricted security context and prohibited host access. Failure policy matters: Fail preserves enforcement when a webhook is unavailable; Ignore favors availability but can fail open.
k get validatingwebhookconfigurations,mutatingwebhookconfigurations k get validatingadmissionpolicies,validatingadmissionpolicybindings 2>/dev/null || true k get events -A --sort-by=.lastTimestamp | tail -30 # test policy with server-side dry-run before persistence k apply --dry-run=server -f compliant.yaml k apply --dry-run=server -f deliberately-denied.yaml
Encrypt Kubernetes API data at rest
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources: [secrets]
providers:
- aesgcm:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}
The first provider writes new data; later providers can read existing data. The identity fallback supports migration from plaintext but means plaintext remains readable. Mount the configuration into the API server, add --encryption-provider-config, verify readiness, then rewrite existing Secrets so they are re-encrypted:
# after the API server is healthy with the provider k get secrets -A -o json | k replace -f - # verify storage carefully from etcd: ciphertext should not expose the value ETCDCTL_API=3 etcdctl get /registry/secrets/team/db \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key
Protect and rotate encryption keys; losing them can make data unrecoverable. Encryption at rest does not replace RBAC, TLS in transit or secret rotation.
Runtime rule reasoning
# conceptual Falco-style rule; use the installed tool's schema - rule: Shell in production container condition: container and proc.name in (bash, sh, zsh) and k8s.ns.name = "prod" output: "shell in prod (user=%user.name pod=%k8s.pod.name cmd=%proc.cmdline)" priority: WARNING
A useful runtime rule has a narrow behavior, enough context for triage and tolerable noise. Baseline legitimate automation, exclude by stable identity rather than broad process names, and test with a controlled event. Runtime detection complements prevention because signed, scanned and admitted software can still be exploited after startup.
8. Security verification matrix
| Control | Positive test | Negative test |
|---|---|---|
| RBAC | Required verb succeeds as target identity | Unneeded verb returns forbidden |
| NetworkPolicy | Allowed source reaches exact port | Denied source times out/fails |
| Pod security | Compliant Pod admits and starts | Privileged/non-compliant Pod is rejected |
| Image policy | Trusted signed digest admits | Unknown registry/signature is denied |
| Audit | Relevant request produces expected event | Noise/sensitive bodies are excluded as designed |
| Control plane | /readyz and kubectl return | Anonymous/unauthorized access fails |
9. Common mistakes and final study order
- Editing the wrong cluster or namespace.
- Leaving a static-Pod manifest backup in the manifests directory.
- Applying a default-deny policy before proving required DNS/egress paths.
- Writing a NetworkPolicy that selects no Pods because labels are wrong.
- Granting broad RBAC because the narrow Role was slightly harder to write.
- Assuming non-root alone prevents privilege escalation.
- Breaking an app with read-only rootfs without mounting its required writable paths.
- Calling a scan, SBOM or signature interchangeable.
- Enabling audit flags without mounting the policy/log path into the API server static Pod.
- Making several control-plane changes before verifying the first one.
Practice order: NetworkPolicy → RBAC/service accounts → restricted Pods/PSA → API/CIS hardening → seccomp/AppArmor → image scanning/signing/admission → audit policy → runtime investigation. Rehearse every task with a positive and negative test; security without evidence is only configuration.
Official sources & freshness
This guide was checked against the official Linux Foundation exam page and CNCF curriculum on 19 July 2026. Exam versions and policies can change; re-check the source page before booking.