Files
rootnode-academy/content/root/root-09-kubernetes-security/m01-security-model-and-securitycontext/02-demonstration-securitycontext.md
T
ced4568andClaude Sonnet 5 960a638ec9 feat(root): add Kubernetes security curriculum
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]>
2026-08-26 09:07:07 -05:00

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.