Upgrades six PARTIAL modules to the gold-module standard (lesson/demonstration/ knowledge-check/guided-exercise/lab(s)/reflection), preserving every existing lab activity_id and scenario_id: root-06-m03 (jobs/cronjobs/daemonsets), root-07-m04 (ingress/networkpolicy), root-07-m02 (configuration/secrets, converted in place from a bare 5-lab bank), root-09-m02 (serviceaccounts/rbac), root-09-m03 (network/supply-chain security), root-10-m03 (troubleshooting framework). root-05-m01 (20-task kubectl-fluency bank) gets a minimal bookending lesson + reflection rather than a full redesign, since each task already carries its own explanation. root-12-m01, root-capstone-m01, and root-09-m04 are promoted to READY via documented exceptions (no new activities) where adding one would misrepresent their intentional design. Wires a new rootnode-security-assessment (exam, 60min, hint_policy disabled) using the existing Phase 3B assessment engine: 3 required hands-on CORE scenarios + 4 random-draw pools covering SecurityContext, RBAC, network policy, and the 7 previously-unwired CKS MCQs. Wires the previously-unwired readiness-probe-task scenario as an OPTIONAL practice lab in root-10-m01, using the existing is_required model. Adds root-11-m01-devops-platform-thinking — an honest, lesson-only ROOT 11 orientation module naming DevOps/platform engineering practices already exercised in ROOT 05-10/12, explicit that GitOps/CI-CD/IaC/observability need lab infrastructure this platform doesn't yet provide. No fake labs. Adds infra/scripts/validate-course-content.js: cross-checks the DB against scenario/content files for duplicate ids, orphans, invalid references, cycles, sequence collisions, instructor-notes leakage, and (new) self-paced content deferring explanation to a live instructor. 0 errors against the resulting 22 courses/modules, 163 activities, 98 scenario files. No application code changed — every gap was satisfiable with existing tables (course_modules, module_activities, assessment_definitions) and the existing content-delivery pipeline. Co-Authored-By: Claude Sonnet 5 <[email protected]>
90 lines
3.1 KiB
Markdown
90 lines
3.1 KiB
Markdown
# Demonstration: Default-Deny, Before and After
|
|
|
|
## Before: everything can reach everything
|
|
|
|
```
|
|
$ kubectl run test-pod --image=busybox --rm -it -- wget -qO- --timeout=2 http://backend-svc
|
|
<real response from backend>
|
|
```
|
|
|
|
Any Pod in the namespace can reach `backend-svc`, regardless of what it is or who deployed it —
|
|
this is the un-configured default the ROOT 07 lesson described, now made concrete from a
|
|
security angle.
|
|
|
|
## Applying a default-deny policy
|
|
|
|
```
|
|
$ kubectl apply -f default-deny.yaml
|
|
networkpolicy.networking.k8s.io/default-deny created
|
|
```
|
|
|
|
```yaml
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: default-deny
|
|
spec:
|
|
podSelector: {}
|
|
policyTypes: ["Ingress"]
|
|
```
|
|
|
|
## After: the same request, now blocked
|
|
|
|
```
|
|
$ kubectl run test-pod --image=busybox --rm -it -- wget -qO- --timeout=2 http://backend-svc
|
|
wget: download timed out
|
|
```
|
|
|
|
No error naming a permission (that's RBAC's job, a different layer) — just a timeout, because
|
|
the packets are being dropped at the network layer before they ever reach the Pod. This is a
|
|
DIFFERENT failure signature than an RBAC denial, worth being able to tell apart: RBAC denials
|
|
are explicit and immediate; NetworkPolicy denials look like the destination is simply
|
|
unreachable.
|
|
|
|
## Explicitly allowing only what's needed
|
|
|
|
```yaml
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: allow-frontend-to-backend
|
|
spec:
|
|
podSelector:
|
|
matchLabels: { app: backend }
|
|
ingress:
|
|
- from:
|
|
- podSelector:
|
|
matchLabels: { app: frontend }
|
|
```
|
|
|
|
```
|
|
$ kubectl run frontend-test --labels="app=frontend" --image=busybox --rm -it -- wget -qO- --timeout=2 http://backend-svc
|
|
<real response from backend>
|
|
|
|
$ kubectl run other-test --labels="app=other" --image=busybox --rm -it -- wget -qO- --timeout=2 http://backend-svc
|
|
wget: download timed out
|
|
```
|
|
|
|
Same destination, same request — the ONLY difference is the caller's label. This is
|
|
least-privilege network access made concrete: reachability now reflects an explicit decision,
|
|
not an accident of both Pods existing in the same cluster.
|
|
|
|
## Instructor Notes
|
|
|
|
**Demo script:** Run all three states live if a real cluster is available — the timeout (not an
|
|
error) for the denied case is worth calling out explicitly, since it looks identical to a
|
|
genuinely broken/unreachable service from the caller's perspective.
|
|
|
|
**Approximate timing:** 8-10 minutes live; 5-7 minutes self-paced.
|
|
|
|
**Common misconception to preempt:** students sometimes expect a NetworkPolicy denial to
|
|
produce a clear "forbidden" error like RBAC does. It doesn't — reinforce that a NetworkPolicy-
|
|
caused timeout is genuinely hard to distinguish from an actual network/DNS problem without
|
|
already suspecting NetworkPolicy, which is exactly why checking existing policies is a real
|
|
diagnostic step worth knowing.
|
|
|
|
**Optional extension:** ask how a student would distinguish "blocked by NetworkPolicy" from
|
|
"the backend Pod is actually down" purely from the caller's side (answer: check the backend
|
|
Pod's own status/logs directly — if it's healthy and Ready, but still unreachable from one
|
|
caller and reachable from another with a different label, NetworkPolicy is the likely cause).
|