Hedronite · Cert-Prep Synthesis Lesson · CNCF · CKS (day #1, CKS-emphasis) · Track K8s Day 4 · Sun 2026-07-26
Minimize Microservice Vulnerabilities 20% Pod Security Standards Pod Security Admission SecurityContext · seccomp · capabilities

CKS — Pod Security Standards, Pod Security Admission, and the SecurityContext — the Restricted Profile as a Checklist

The floor is a label. The migration is a ladder. The rejection message is the best study sheet in the domain.

Lesson Class: Cert-Prep (CKS — CNCF)
Track / Day: K8s (Deep Kubernetes) — round-robin day 4, k8s_day #1 → CKS-emphasis
Domains: Minimize Microservice Vulnerabilities (20%) — PSS/PSA first objective
Word Count: ~2,450
Grounding: Kubernetes: Up and Running 3ed — Ch 14 Securing Pods pp 237-246
Paired Ops: The Kubernetes Admission Chain — the Gate Before the Ledger
Paired Dev: Building a Kubernetes Admission Webhook in Python (FastAPI)
Discipline: ROD v3 · aether-accent meta-card · q-card practice questions

How does a cluster keep root out? Three profiles, one label per namespace, and a short list of securityContext fields the exam expects you to write from memory.

§ IFrame

The CKS assumes you already hold the CKA and it grades the same way: live clusters, real tasks, a clock. Its blueprint splits six ways, and the largest tri-share, Minimize Microservice Vulnerabilities at twenty percent, opens with the objective this lesson drills: use appropriate pod security standards. Thursday's CKA lesson walked the control-plane chain that admits and runs a claim. Today stands at one link of that chain, the in-tree gate that decides whether a Pod's security posture is acceptable for the namespace it wants to enter.

The gate is Pod Security Admission. The rules it applies are the Pod Security Standards. The fields it inspects live in the securityContext. Three names, one mechanism, and the exam tests all three ends of it: label a namespace to enforce a profile, read a rejection and say why the Pod failed, and write the securityContext that passes. Hold this lesson as one sentence: the floor is a label, and the restricted profile is the checklist that clears it.

§ IIDomain Foundations — Three Profiles, Three Modes, One Label Syntax

The standards come first, because they are the vocabulary everything else speaks. Three profiles, ordered by how much they refuse. Privileged refuses nothing; it exists so system namespaces can say out loud that they run unrestricted. Baseline refuses the known privilege escalations: hostNetwork, hostPID, hostPath volumes, privileged containers, dangerous capabilities beyond the default set. Restricted refuses everything baseline refuses and then demands affirmative hardening: run as non-root, drop all capabilities, disallow privilege escalation, seccomp enabled. Burns and his coauthors put the intent plainly in the securing-Pods chapter grounding this lesson: baseline is the floor for anything you did not write yourself, restricted is the target for anything you did.

Pod Security Admission is how a profile becomes enforced. It is an in-tree admission plugin, compiled into the API server, on by default in every current cluster. No webhook, no Deployment to keep alive, no failure posture to choose. You steer it entirely with namespace labels, and the label grammar is exam-critical because you will type it under the clock:

apiVersion: v1
kind: Namespace
metadata:
  name: trading
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: v1.30
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/audit: restricted

Three modes, independently settable. Enforce rejects the violating Pod at admission. Warn returns a warning to the client and admits anyway. Audit admits and writes an annotation into the audit log. The modes exist so a namespace can hold two truths at once, enforcing baseline while warning on restricted, which is the migration posture: the floor you refuse to drop below and the target you are measuring drift against. The version pin matters more than it looks; profiles tighten across Kubernetes releases, and pinning enforce-version keeps an upgrade from changing your policy silently.

One boundary the exam probes: PSA evaluates Pods, and it evaluates them per-namespace. A Deployment whose Pod template violates the profile is itself admitted; its ReplicaSet then fails to create Pods, and the rejection surfaces in the ReplicaSet's events rather than at your kubectl apply. Know that indirection cold. A workload that "applied fine" and never scales up is PSA speaking one level below where you were looking.

§ IIICKS Flavor — Operating PSA Under the Clock

Exam tasks come in three shapes here. First shape: harden a namespace. That is the label block above, and kubectl label ns trading pod-security.kubernetes.io/enforce=restricted is the one-line form. Second shape: predict the damage before you enforce. The dry-run trick is the highest-value command in this domain:

kubectl label --dry-run=server --overwrite ns trading \
  pod-security.kubernetes.io/enforce=restricted

The server evaluates every existing Pod in the namespace against the proposed profile and prints a warning per violator, without changing anything. That is the audit-warn-enforce ladder from today's Ops lesson compressed into one command: see the violators while nothing is on fire, fix them, then apply the label for real. Third shape: read a rejection. PSA denial messages are specific and cumulative; a restricted rejection lists every unmet condition at once, allowPrivilegeEscalation != false, unrestricted capabilities, runAsNonRoot != true, seccompProfile. Read the list as the checklist it is. Each line names the securityContext edit that clears it.

Exemptions round out the operations story. PSA can exempt usernames, runtime classes, and namespaces via API server configuration, which is why kube-system stays functional under cluster-wide defaults. The exam angle is narrower: know that exemptions exist, know they are API-server configuration rather than labels, and know that the answer to "this one workload needs hostPath" is almost never an exemption; it is moving the workload to a namespace whose profile admits it, keeping the label the single source of truth.

§ IVCKS Flavor — The SecurityContext That Clears Restricted

The other end of the mechanism is the Pod spec, and the exam expects you to write the passing form from memory. This is the restricted checklist, four fields:

apiVersion: v1
kind: Pod
metadata:
  name: settlement-worker
  namespace: trading
spec:
  securityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: worker
    image: registry.hedronite.internal/settlement:2.14
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop: ["ALL"]

Placement is the subtle part, the same precedence rule today's Dev lesson implemented in one merge expression: container-level securityContext overrides pod-level, field by field. runAsNonRoot and seccompProfile may sit at either level and the pod level covers every container, including initContainers, which restricted also inspects. allowPrivilegeEscalation and capabilities are container-level fields only. The clean pattern above puts the shared demands at pod scope and the per-container demands where they must live. Two additions worth making even though restricted does not demand them: readOnlyRootFilesystem: true, because a container that cannot write its own filesystem is a container an attacker cannot persist in, and an explicit runAsUser only when the image does not already declare a non-root user, because hardcoding UIDs into specs is how images stop being portable.

Understand runAsNonRoot precisely, because it is a verification, not a mutation. It does not pick a user. It refuses to start a container that would run as UID 0, and if the image declares no user at all, the kubelet cannot prove non-root and fails the container with CreateContainerConfigError. The fix is in the image (USER 1000 in the Dockerfile) or an explicit runAsUser. That distinction, verify versus rewrite, is exactly the validating-versus-mutating split from today's Ops and Dev lessons, running one layer deeper.

§ VWorked Scenario — Migrating a Namespace to Restricted

A task in the exam's shape: namespace trading currently carries no pod-security labels; bring it to restricted enforcement without breaking running workloads. Run the motion.

Set the observation labels first, warn=restricted and audit=restricted, no enforce. Deploys keep working; every violating apply now returns warnings naming fields. Run the server-side dry-run to census the existing Pods; suppose it flags the settlement-worker Deployment for a missing seccompProfile and an initContainer running as root. Patch the Pod template with the checklist block from §IV, let the Deployment roll, and re-run the dry-run until it prints nothing. Now apply enforce=restricted with the version pin. Nothing restarts, because PSA gates admission rather than evicting running Pods; the running world is untouched and every future Pod must clear the floor. Verify with a deliberate violation: apply a bare Pod with no securityContext and read the rejection listing its unmet conditions. The gate that refuses the test Pod loudly is the gate you can trust silently. Total time under exam conditions: four commands and one template edit.

Notice what the motion never did. It never guessed. Every step read the cluster's own answer, warnings, dry-run output, rejection lists, before moving. PSA is unusually generous with ground truth, and the exam rewards the habit of asking for it.

§ VIConnection to Today's Ops and Dev Lessons

Today's trio is one mechanism at three altitudes. The Ops lesson named the admission chain and the gate before the ledger; PSA is that gate's in-tree resident, the one policy engine you operate with labels instead of webhooks. The Dev lesson built the custom gate and its first rule was runAsNonRoot, which this lesson revealed as one line of the restricted checklist; when both PSA and your webhook inspect a Pod, they are two validators in the same chain reading the same securityContext, and either refusal is final. Chain, gate, checklist. Ops named it, Dev built it, this lesson drills the one the exam weighs at twenty percent.

§ VIIPractice Questions

Answer each before reading the resolution.

Question 1 · The Silent Deployment

You apply a Deployment into a namespace enforcing restricted. kubectl apply succeeds. No Pods appear. Where is the rejection recorded, and why did apply not fail?

Question 2 · The Label Grammar

Write the two labels that make namespace risk reject baseline violations at admission while only warning on restricted violations. One command or one YAML fragment.

Question 3 · The Migration Census

Before enforcing restricted on a namespace with forty running Pods, you want the list of Pods that would violate it, from the server's own evaluation, changing nothing. What command?

Question 4 · CreateContainerConfigError

A Pod with runAsNonRoot: true fails with CreateContainerConfigError. The image's Dockerfile has no USER directive. Explain the failure and give both fixes.

Question 5 · The Checklist From Memory

A bare nginx Pod is rejected by a restricted namespace. List the four securityContext requirements you must add, and state which of them are container-level only.

§ VIIIQuestion Resolutions

Answer 1PSA evaluates Pods, and the Deployment is a Deployment, so the API server admitted it. The ReplicaSet's attempts to create Pods are what PSA rejects, and the denials land in the ReplicaSet's events: kubectl describe rs shows FailedCreate with the full condition list. Apply succeeded because the object you applied never violated anything; the objects it spawns did.
Answer 2pod-security.kubernetes.io/enforce: baseline plus pod-security.kubernetes.io/warn: restricted on the namespace. Command form: kubectl label ns risk pod-security.kubernetes.io/enforce=baseline pod-security.kubernetes.io/warn=restricted. Floor enforced, target measured.
Answer 3kubectl label --dry-run=server --overwrite ns <name> pod-security.kubernetes.io/enforce=restricted. Server-side dry-run makes the API server evaluate every existing Pod against the proposed profile and print one warning per violator, with nothing persisted.
Answer 4runAsNonRoot verifies; it does not assign a user. With no USER in the image and no runAsUser in the spec, the kubelet cannot prove the container would run non-root, so it refuses to start it. Fix in the image with USER 1000, or fix in the spec with runAsUser: 1000. Image fix travels with the artifact; spec fix works when you do not own the image.
Answer 5runAsNonRoot: true, seccompProfile.type: RuntimeDefault, allowPrivilegeEscalation: false, and capabilities.drop: ["ALL"]. The last two are container-level only; the first two may sit at pod level and cover all containers, initContainers included.

§ IXClosing

Twenty percent of the CKS reduces to one label grammar, one dry-run command, and four securityContext fields written from memory. The floor is a label. The migration is a ladder: warn and audit, census with the server's dry-run, fix to the checklist, enforce with a version pin, verify with a deliberate violation. And the whole mechanism is the admission chain from this morning's Ops lesson wearing its in-tree face, validating the same fields your Python webhook read.

Before the next K8s day, take any cluster and run the migration motion once end to end on a scratch namespace: label, dry-run, break it on purpose, read the rejection, fix to the checklist. The rejection message is the best study sheet in this domain, and the exam expects you to have read it before exam day. Examine it well.

Paired lessons → Ops 01-Earth-DevOps/Synthesis-Lessons/2026-07-26-the-kubernetes-admission-chain-... · Dev Polyglot-Dev/Python/2026-07-26-building-a-kubernetes-admission-webhook-in-python-...

🫡 ⚖️ 📜
Leo.Syri — Praetor Consulate, Imperium Luminaura
Filed 2026-07-26 Sunday Fajr · Cert-Prep lesson · CKS-emphasis (k8s_day #1) · Kubernetes deep-mastery track, day 4
Backward-Synergy-Reach → 07-23 CKA architecture · 07-01 supply-chain admission · 07-15 workload identity
Grounded in Kubernetes: Up and Running 3ed Ch 14 pp 237-246 + current CKS blueprint · HTML shipped in-cycle per HARD DISCIPLINE · aether-accent