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]>
63 lines
2.5 KiB
Markdown
63 lines
2.5 KiB
Markdown
# Demonstration: Watching RBAC Actually Deny Something
|
|
|
|
## The setup
|
|
|
|
A ServiceAccount `readonly-bot` has a Role granting only `get`/`list` on `pods` — nothing else.
|
|
|
|
## What it CAN do
|
|
|
|
```
|
|
$ kubectl auth can-i list pods --as=system:serviceaccount:default:readonly-bot
|
|
yes
|
|
|
|
$ kubectl get pods --as=system:serviceaccount:default:readonly-bot
|
|
NAME READY STATUS RESTARTS AGE
|
|
web-abc123 1/1 Running 0 2h
|
|
```
|
|
|
|
## What it CANNOT do
|
|
|
|
```
|
|
$ kubectl auth can-i delete pods --as=system:serviceaccount:default:readonly-bot
|
|
no
|
|
|
|
$ kubectl delete pod web-abc123 --as=system:serviceaccount:default:readonly-bot
|
|
Error from server (Forbidden): pods "web-abc123" is forbidden:
|
|
User "system:serviceaccount:default:readonly-bot" cannot delete resource "pods"
|
|
in API group "" in the namespace "default"
|
|
```
|
|
|
|
This is what least privilege actually looks like in practice — not a policy document, a real,
|
|
enforced denial with a specific, readable error naming exactly which permission was missing.
|
|
`kubectl auth can-i` is your fastest way to CHECK a permission before attempting an action that
|
|
might fail — useful both for debugging "why can't my workload do X" and for verifying a Role is
|
|
actually as narrow as intended.
|
|
|
|
## What happens when the ServiceAccount tries something it was never even considered for
|
|
|
|
```
|
|
$ kubectl auth can-i get secrets --as=system:serviceaccount:default:readonly-bot
|
|
no
|
|
```
|
|
|
|
Notice this returns `no` the same way `delete pods` did — RBAC has no concept of "not yet
|
|
decided," only "explicitly allowed" or "everything else, denied." This is the practical meaning
|
|
of default-deny applied to permissions: nothing is grey area.
|
|
|
|
## Instructor Notes
|
|
|
|
**Demo script:** Run all three `kubectl auth can-i`/action pairs live — the FORBIDDEN error
|
|
message's exact wording (naming the verb, resource, and namespace) is worth reading aloud, since
|
|
that's exactly the evidence a student would use to debug a real permissions issue later.
|
|
|
|
**Approximate timing:** 8 minutes live; 5 minutes self-paced.
|
|
|
|
**Common misconception to preempt:** students sometimes think `kubectl auth can-i` REQUIRES the
|
|
action to actually be attempted to know the answer. It doesn't — it's a dry-run permission
|
|
check against the same RBAC rules, safe to run repeatedly without side effects.
|
|
|
|
**Optional extension:** ask what the error message would look like if `readonly-bot` tried to
|
|
act on a DIFFERENT namespace than the one its Role/RoleBinding applies to — a good check that
|
|
students understand Role scope is namespace-specific unless a ClusterRole+ClusterRoleBinding is
|
|
used instead.
|