CKSKubernetes 1.35

Lessons

Pod Security Admission: labels, levels, and the manifest that gets rejected

updated 2026-10-22 by seed

PSP is dead

PodSecurityPolicy was removed in 1.25. Anything you read that tells you to create
a PSP is wrong. The replacement is built in: Pod Security Admission, driven
entirely by namespace labels, enforcing the Pod Security Standards.

Three levels

  • privileged — no restrictions (system namespaces)
  • baseline — blocks known privilege escalations: hostNetwork/hostPID/hostIPC, privileged, hostPath volumes, added capabilities beyond a small allow-list, sysctls outside the safe set, procMount, seccomp Unconfined
  • restricted — everything in baseline plus: non-root required, allowPrivilegeEscalation: false, all capabilities dropped, seccomp RuntimeDefault or Localhost, no host ports

Four labels

apiVersion: v1
kind: Namespace
metadata:
  name: prod
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: v1.35
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted
  • enforce = reject non-compliant Pods (403)
  • audit = allow, record in the audit log
  • warn = allow, return a warning to the client (kubectl prints it)
  • -version = which standard version to apply (latest by default)

enforce: baseline + warn: restricted is the sane migration setting: nothing
breaks, and you see what would break.

The minimal Pod that passes restricted

apiVersion: v1
kind: Pod
metadata: {name: ok, namespace: prod}
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 2000
    runAsGroup: 3000
    seccompProfile: {type: RuntimeDefault}
  containers:
  - name: ok
    image: registry.k8s.io/pause:3.10
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop: ["ALL"]

Memorise this. It is the answer to half the PSA questions, and you can paste it
faster than you can reason about it.

Reading a rejection

Error from server (Forbidden): error when creating "pod.yaml": pods "bad" is
forbidden: violates PodSecurity "restricted:latest":
  allowPrivilegeEscalation != false (container "bad" must set
  securityContext.allowPrivilegeEscalation=false),
  unrestricted capabilities (container "bad" must set securityContext.capabilities.drop=["ALL"]),
  runAsNonRoot != true (pod or container "bad" must set securityContext.runAsNonRoot=true),
  seccompProfile (pod or container "bad" must set securityContext.seccompProfile.type
  to "RuntimeDefault" or "Localhost")

Four lines, four fixes. The message is a checklist — read it instead of guessing.

"Which of these three manifests will be rejected, and why"

kubectl label ns prod pod-security.kubernetes.io/enforce=restricted --overwrite
kubectl apply -f one.yaml --dry-run=server    # the fastest way to find out

--dry-run=server runs the real admission chain without writing. Use it before
apply whenever PSA is involved; it costs one second and tells you exactly which
field is wrong.

Exempting a namespace (and when not to)

The API server flags:

--admission-control-config-file=/etc/kubernetes/psa/config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    defaults:
      enforce: baseline
    exemptions:
      namespaces: ["kube-system", "monitoring"]

The exam task is nearly always "label the namespace", not "configure exemptions".
Know that exemptions exist so you do not try to label kube-system.