Files
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

88 lines
4.6 KiB
Markdown

# Defense in Depth & the Kubernetes Security Model
## Security is a posture, not a single setting
There is no single "make it secure" switch in Kubernetes. Real security comes from **defense in
depth** — multiple independent layers, each reducing what an attacker (or a bug) can do if an
earlier layer fails. If a container has a vulnerability that lets an attacker execute arbitrary
code, the question that actually matters is: what can that code DO once it's running? A
container running as root, with every Linux capability, on a writable filesystem, with no
restrictions at all, can do far more damage than one deliberately hardened to need almost
nothing.
## Least privilege: grant only what's actually needed
The organizing principle behind everything in this course is **least privilege**: a workload
should have exactly the permissions and capabilities it needs to do its job, and nothing more.
This sounds obvious stated plainly, but the DEFAULT behavior of a plain container is closer to
"maximum privilege unless told otherwise" — root user, all standard Linux capabilities, a
writable filesystem. Hardening a Pod means deliberately narrowing that down.
## SecurityContext: where you actually configure this
`securityContext` (settable at the Pod level and per-container) is where you declare these
restrictions:
```yaml
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
containers:
- name: app
securityContext:
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
```
- **`runAsNonRoot` / `runAsUser`** — the container process runs as a specific non-root user
instead of root. If a container image was built to require root and you set this, it will
fail to start — which is a good, loud failure, not a silent security gap. Hardening
sometimes surfaces an image's OWN bad assumptions.
- **`capabilities: drop: ["ALL"]`** — Linux capabilities are fine-grained permissions
(`NET_BIND_SERVICE`, `SYS_ADMIN`, dozens more) a process can hold even without full root.
Dropping all of them, then adding back only the specific ones actually needed, is the
least-privilege pattern applied to capabilities specifically.
- **`readOnlyRootFilesystem: true`** — the container's own filesystem cannot be written to at
all (use an explicit `emptyDir` volume, ROOT 08, for anything that genuinely needs to write
temporary files). This closes off a whole category of attack that relies on writing a
malicious file to disk.
## Why this matters even when "nothing is currently wrong"
None of these settings fix a bug or a vulnerability — they limit the BLAST RADIUS if one exists
that you don't yet know about. A container hardened with non-root, dropped capabilities, and a
read-only filesystem gives a successful exploit dramatically less to work with than an
unhardened one, even against the exact same vulnerability. This is the entire point of defense
in depth: you don't rely on any single layer being perfect.
## Instructor Notes
**Approximate timing:** 15 minutes read; 10-12 minutes live with the demonstration.
**Demo plan:** Have the demonstration activity ready to run right after this lesson — seeing a
concrete before/after Pod definition lands the abstract "least privilege" principle far better
than the lesson text alone.
**Likely misconceptions to watch for:**
- Students sometimes think hardening settings are only relevant "if you expect to be attacked."
Reframe: defense in depth assumes SOMETHING will eventually go wrong somewhere (a
vulnerability, a bug, a misconfiguration) and prepares for that, rather than betting
everything on nothing ever going wrong.
- `runAsNonRoot: true` failing a Pod's startup is sometimes read as "the security setting is
broken." It's the opposite — it's correctly refusing to let a root-requiring image start,
which is valuable, actionable information about the image itself.
**Discussion question:** "If dropping all capabilities breaks a container that turns out to
genuinely need one specific capability, what's the right fix — removing the drop, or something
more targeted?" (Answer: add back only that ONE specific capability rather than removing the
blanket drop — least privilege means "exactly what's needed," not "give up and grant
everything again.")
**Optional extension:** ask students to identify one setting from this lesson that would have
made zero difference against a vulnerability that lets an attacker read environment variables
(a good check that they understand these are layered, not universal, protections — the answer
is that none of SecurityContext's settings here directly protect secrets stored in env vars;
that's a different concern, covered later in this course).