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]>
4.1 KiB
Externalizing Configuration: ConfigMaps & Secrets
The problem: hardcoded configuration doesn't scale
If your application's database URL, log level, or feature flags are baked directly into its container image, changing any of them means rebuilding and redeploying the image — for a one-line config change. Worse, the SAME image can't be reused across dev/staging/production if each environment needs different settings compiled in.
Kubernetes solves this by letting you inject configuration into a container at RUNTIME, separately from the image itself — the same image, different config per environment.
ConfigMap: configuration that isn't secret
A ConfigMap holds arbitrary key/value configuration data — log levels, feature flags, URLs, whole config files. It's a plain Kubernetes object, readable by anyone with API access to that Namespace, so it's for configuration that ISN'T sensitive.
Two ways to consume a ConfigMap in a Pod:
- As environment variables — each key becomes an env var the application reads normally.
- As a mounted volume — each key becomes a FILE inside the container, useful for applications that expect to read configuration from disk rather than environment variables.
envFrom:
- configMapRef:
name: app-env
Secret: the same idea, for sensitive data
A Secret has the identical shape and consumption pattern as a ConfigMap (env vars or a mounted volume) but is meant for sensitive values — passwords, API keys, tokens. Kubernetes stores Secret data base64-ENCODED, not encrypted — this is an important distinction: base64 is trivially reversible, so a Secret's real protection comes from Kubernetes' RBAC (who can even read Secret objects at all, ROOT 09) and, in a properly configured cluster, encryption at rest — not from the encoding itself.
apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
data:
api-key: c3VwZXJzZWNyZXQ= # base64 of "supersecret" — NOT encrypted
Why the same mechanism, two different objects
You might ask why Kubernetes doesn't just have one "Config" object with a "sensitive" flag.
Keeping them separate makes the DISTINCTION visible and enforceable — RBAC rules can grant
"read ConfigMaps" without granting "read Secrets," and a quick kubectl get configmaps never
accidentally exposes something that should have been a Secret. The separation is a deliberate
safety boundary, not redundant API surface.
Instructor Notes
Approximate timing: 15 minutes read; 10 minutes live.
Demo plan: Before the demonstration activity, it's worth showing kubectl get secret app-secret -o yaml live and pointing out the base64 value, then decoding it on the spot
(echo <value> | base64 -d) — the "this isn't actually encrypted" realization lands much better
seen than described.
Likely misconceptions to watch for:
- Students very commonly assume Secrets are encrypted by Kubernetes automatically. Correct this explicitly and early — it's a genuine, consequential security misunderstanding.
- The env-var vs. volume-mount choice is sometimes seen as arbitrary. Note one real
consideration: env vars are visible via
kubectl describe podand process inspection tools inside the container more easily than a mounted file's contents — not a hard rule, but a reason some teams prefer volume mounts specifically for Secrets.
Discussion question: "If someone with kubectl get secrets access on your namespace can
trivially read every Secret's real value, what is a Secret actually protecting you from?"
(Answer: it protects against accidental exposure through normal channels like
kubectl get configmaps, source control, or logs — not against a determined actor with direct
API read access, which is exactly why RBAC restricting who has that access is the real control.)
Optional extension: ask what changes about this picture on a cluster with encryption-at-rest enabled for Secrets (answer: the ETCD-stored data itself becomes encrypted, protecting against someone who gains access to the underlying etcd storage directly — still separate from and in addition to RBAC controlling API-level access).