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.
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.
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?
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.
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?
A Pod with runAsNonRoot: true fails with CreateContainerConfigError. The image's Dockerfile has no USER directive. Explain the failure and give both fixes.
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
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.pod-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.kubectl 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.runAsNonRoot 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.runAsNonRoot: 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-...
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