The admission control you get without installing anything: three named profiles enforced by the built-in Pod Security admission controller, driven entirely by namespace labels. Less expressive than Kyverno or Gatekeeper, and that is its virtue.

needsmake up sec

Orientation

competency 5.4 · Pod Security Standards

Know exactly what each profile means and the label grammar, and these become the fastest points on the paper. PSS replaced PodSecurityPolicy, which was removed; if a scenario mentions PSP, the expected answer is "removed; use PSS, or a policy engine for anything PSS cannot express".

Profiles and modes

three profiles × three modes
ProfileBlocks / requiresUse for
privilegednothing: unrestrictedsystem namespaces, CNI, storage drivers
baselineblocks the known-bad: privileged containers, hostNetwork/hostPID/hostIPC, hostPath volumes, added capabilities beyond a safe list, unconfined seccomp/AppArmor, host portsthe sane tenant default
restricteddemands actively-good: runAsNonRoot, allowPrivilegeEscalation: false, all capabilities dropped (ALL, NET_BIND_SERVICE permitted back), seccompProfile: RuntimeDefault, no hostPath, restricted volume typesanything you can make comply

The one-liner: baseline stops you being dangerous, restricted forces you to be safe.

The modes are set independently per namespace: enforce rejects at admission, audit annotates the audit log, warn prints warnings to the client. Label grammar:

pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: v1.31     # pin, or "latest"
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/audit: restricted

Version pinning matters more than it looks: profiles gain checks across releases, so latest means an upgrade can start rejecting pods that were fine yesterday. Pinning trades that surprise for a deliberate migration.

Build a pod spec and watch the three profiles decide:

The lab's stance is the recommended one

team-a runs enforce baseline, warn and audit restricted: nothing overtly dangerous gets in, and every restricted violation is visible to both the user (a warning in their terminal) and the platform (the audit log) before anyone tightens the screw. That staged posture mirrors the Audit→Enforce choreography from 5.2; the pattern generalises to every control in this domain.

Two operational facts that decide troubleshooting tasks

and one preview command
  1. PSS evaluates pods. A Deployment whose template violates the profile is created successfully and then fails to make pods, with the evidence on the ReplicaSet, two levels below where you were looking. Same indirection as quota (1.4). Recognising the pattern is worth more than any single profile detail.
  2. Enforcement is admission-time. Tightening a namespace label does not evict existing violators; they run until their next write. Which is why the preview command exists:
kubectl label --dry-run=server --overwrite ns <ns> pod-security.kubernetes.io/enforce=restricted

The API server evaluates every existing pod against the proposed profile and warns about each one that would break, without changing anything. It is the single most useful PSS command to know exists, and it turns a risky tightening into a planned migration.

What a compliant pod looks like

securityContext:                 # pod level
  runAsNonRoot: true
  runAsUser: 1000
  seccompProfile: { type: RuntimeDefault }
containers:
  - name: app
    securityContext:             # container level
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true          # not required by restricted, but good practice
      capabilities: { drop: ["ALL"] }

The demo app's manifest (examples/demo-app/base/deployment.yaml) is a worked answer to "make this pass restricted": read its securityContext blocks line by line, because writing exactly that block from memory is a plausible task. Note the level split: runAsNonRoot and seccompProfile sit naturally at pod level, capabilities and allowPrivilegeEscalation are per container.

PSS versus a policy engine

PSS is fixed, built in, free, and covers exactly the pod-hardening dimension. A policy engine is arbitrary, installable, and covers everything else (image registries, labels, ownership, cross-object rules) at the cost of running a webhook. The professional answer to "which should we use" is both: PSS as the always-on floor, an engine for organisation-specific rules, and the engine can also enforce PSS-like rules in namespaces where you need exceptions PSS cannot express.

Exercises

tick the dot when its check passes

In team-a (enforce: baseline):

kubectl -n team-a run priv --image=busybox:1.37 --restart=Never \
  --overrides='{"spec":{"containers":[{"name":"priv","image":"busybox:1.37","command":["sleep","300"],"securityContext":{"privileged":true}}]}}'

Then run a merely-lazy pod (kubectl -n team-a run lazy --image=busybox:1.37 --restart=Never -- sleep 300) and read the warnings it prints: admitted under baseline, flagged against restricted by the warn label.

verify: the first is rejected at admission with an error naming the violated control; the second is admitted with warnings. Two different outcomes, one namespace, and you can name which label produced each line of output.

Create namespace hardened with enforce restricted. Take the lazy pod spec and make it pass: runAsNonRoot: true (busybox needs runAsUser too, e.g. 1000), allowPrivilegeEscalation: false, capabilities drop ALL, seccompProfile RuntimeDefault. Iterate against the live error messages; they name the missing control each time, which makes PSS self-documenting under exam conditions.

verify: the pod runs in hardened, and your final securityContext agrees with the demo app's.

In hardened, kubectl create deploy sneaky --image=busybox:1.37 -- sleep 300. The deploy is created; no pod appears. Find the rejection where it actually lives:

kubectl -n hardened get deploy sneaky   # READY 0/1, no error here
kubectl -n hardened describe rs -l app=sneaky | tail -5
verify: the ReplicaSet events carry the PSS denial. This indirection is a stock exam scenario for every admission mechanism (5.2's engines included); PSS is just where it is cheapest to practise.

kubectl label --dry-run=server --overwrite ns team-b pod-security.kubernetes.io/enforce=restricted and read which existing pods would violate.

verify: a warning per non-compliant pod, zero mutations made. Pair with the audit log (section 5.4) to see what the audit label has been quietly recording all along.

Self-check

answer before opening
Name the three profiles in one clause each.

Privileged: no restrictions. Baseline: blocks known privilege escalations (privileged containers, host namespaces, hostPath, extra capabilities). Restricted: additionally requires non-root, no privilege escalation, all capabilities dropped, and RuntimeDefault seccomp.

A Deployment is created and no pods appear. Where is the PSS error?

On the ReplicaSet: PSS is a pod-level admission decision, so the Deployment controller records the rejection one level down. kubectl describe rs -l … or namespace events.

You want to know who breaks if you tighten a namespace. Command?

kubectl label --dry-run=server --overwrite ns <ns> pod-security.kubernetes.io/enforce=restricted: the server evaluates existing pods and warns for each violator, changing nothing.

Why pin enforce-version?

Because profiles gain checks in new Kubernetes releases: with latest, a cluster upgrade can start rejecting pods that were compliant before. Pinning turns that into a deliberate migration you schedule rather than a surprise you discover.

PSS or Kyverno for "images must come from our registry"?

Kyverno (or Gatekeeper): PSS covers only the fixed pod-hardening dimension and has no notion of registries. PSS is the floor; the engine is for organisation-specific rules, and it is fine, indeed normal, to run both.

Docs to know your way around

study time, not exam time
  • kubernetes.io: Pod Security Standards (the profile tables; do not memorise the capability lists, know where they are) and Enforce Pod Security Standards with Namespace Labels.
  • Offline: the rejection messages themselves; PSS names every violated control, which makes it the most self-documenting admission mechanism in the cluster; plus kubectl explain pod.spec.securityContext.