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]>
85 lines
4.1 KiB
Markdown
85 lines
4.1 KiB
Markdown
# 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.
|
|
|
|
```yaml
|
|
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.
|
|
|
|
```yaml
|
|
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 pod` and 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).
|