Files
rootnode-academy/content/root/root-09-kubernetes-security/m03-network-and-supplychain-security/03-demonstration-default-deny.md
T
ced4568andClaude Sonnet 5 7f6e1857c0 feat(curriculum): Phase 6C module upgrades, security assessment bank, ROOT 11 orientation
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]>
2026-08-26 11:20:17 -05:00

3.1 KiB

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
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

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).