Four new modules, moving ROOT 09 off PLANNED and building the 22-scenario CKS-style bank into a coherent course instead of disconnected exam tasks — see docs/ROOT_CURRICULUM_COVERAGE_MATRIX.md for the CORE/GUIDED/INDEPENDENT/ADVANCED/ASSESSMENT-BANK classification behind every placement. root-09-m01-security-model-and-securitycontext — READY, full gold-standard pattern: defense in depth, least privilege, hardening a Pod with SecurityContext. Lesson, demonstration (a Pod before/after hardening), knowledge check, guided exercise (a hardening checklist), real Labs (CORE: non-root/drop-capabilities/read-only-fs/ pod-security-context; GUIDED: seccomp; ADVANCED: AppArmor; INDEPENDENT: the generic pod-security-context duplicate), and a reflection. root-09-m02-serviceaccounts-and-rbac — PARTIAL: workload identity and least-privilege permissions. Lesson + knowledge check + real Labs. root-09-m03-network-and-supplychain-security — PARTIAL: NetworkPolicy as a security control (default-deny), image/registry trust, TLS ingress. Lesson + knowledge check + real Labs. root-09-m04-cluster-hardening-and-runtime — PARTIAL, deliberately survey-depth: Pod Security Admission and a lighter treatment of audit-policy/runtime-sandboxing topics that don't belong in the core daily-driver path. Co-Authored-By: Claude Sonnet 5 <[email protected]>
2.9 KiB
Demonstration: A Pod Before and After Hardening
Before: the default, unhardened Pod
apiVersion: v1
kind: Pod
metadata:
name: app-unhardened
spec:
containers:
- name: app
image: myapp:latest
Checking what this actually runs as:
$ kubectl exec app-unhardened -- id
uid=0(root) gid=0(root) groups=0(root)
$ kubectl exec app-unhardened -- touch /etc/anything-i-want
$ kubectl exec app-unhardened -- ls -la /etc/anything-i-want
-rw-r--r-- 1 root root 0 Jan 1 00:00 /etc/anything-i-want
Running as root, with a fully writable filesystem, including system directories. This is not a misconfiguration — it's simply what happens when nothing restricts it.
After: the same application, hardened
apiVersion: v1
kind: Pod
metadata:
name: app-hardened
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
containers:
- name: app
image: myapp:latest
securityContext:
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
$ kubectl exec app-hardened -- id
uid=1000 gid=0 groups=0
$ kubectl exec app-hardened -- touch /etc/anything-i-want
touch: /etc/anything-i-want: Read-only file system
Same application image, same functionality (assuming the app doesn't itself require root or filesystem writes it wasn't told about) — but a compromise of this container now can't write to the filesystem, can't use the elevated capabilities that were dropped, and never had root to begin with.
What this actually buys you
Nothing here fixed a bug in myapp:latest — the image is identical in both cases. What changed
is what an attacker (or a bug behaving unexpectedly) could DO if something inside that container
went wrong. The unhardened version had almost no limits; the hardened version has several
independent ones, any of which alone would have blocked the touch command above.
Instructor Notes
Demo script: Run both Pods live if time allows — watching touch succeed on one and fail
on the other in real time is far more convincing than reading it.
Approximate timing: 8-10 minutes live; 5-7 minutes self-paced.
Common misconception to preempt: students sometimes read the hardened Pod's failed touch
as "hardening broke something." Reinforce: the FUNCTIONALITY didn't change (the application
itself never needed to write to /etc) — only an unnecessary, unused capability was removed.
If hardening breaks something the application genuinely needs, that's real information about
the application, not a reason to skip hardening.
Optional extension: ask what would happen if myapp:latest were built assuming it could
write a cache file to /tmp inside the container — a good bridge to the fact that
readOnlyRootFilesystem often needs an explicit emptyDir volume mounted at the specific path
an application legitimately needs to write to (ROOT 08), rather than being an all-or-nothing
choice.