A container escape usually starts with a privileged or root container; defaults are too permissive.
The securityContext (pod-level and per container) controls what a container may do:
runAsNonRoot: true,runAsUser: 10001: never run as UID 0.allowPrivilegeEscalation: false: blocks setuid tricks.readOnlyRootFilesystem: true: write only to mounted volumes (emptyDirfor/tmp).capabilities: {drop: ["ALL"]}: remove Linux capabilities, adding back only what's needed (NET_BIND_SERVICE).seccompProfile: {type: RuntimeDefault}: filter dangerous syscalls.- Never
privileged: true,hostPID,hostNetworkorhostPathmounts for ordinary apps.
Pod Security Admission (built in) enforces the Pod Security Standards per namespace via labels:
privileged: no restrictions (system namespaces),baseline: blocks known privilege escalations (privileged, host namespaces, hostPath),restricted: requires the hardened settings above.
Each level can be applied in enforce (reject), audit (log) or warn (kubectl warning) mode, so you can roll out restricted gradually.
For custom rules (allowed registries, required labels, no latest tags) use ValidatingAdmissionPolicy with CEL expressions (built in) or a policy engine like Kyverno or OPA Gatekeeper.
Example
# kubectl label ns shop pod-security.kubernetes.io/enforce=restricted \
# pod-security.kubernetes.io/warn=restricted
apiVersion: v1
kind: Pod
metadata: {name: api, namespace: shop}
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
seccompProfile: {type: RuntimeDefault}
containers:
- name: api
image: ghcr.io/acme/api:2.1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: {drop: ["ALL"]}
volumeMounts: [{name: tmp, mountPath: /tmp}]
volumes: [{name: tmp, emptyDir: {}}]Remember
- Non-root, no escalation, drop all capabilities, read-only root FS, RuntimeDefault seccomp
- Pod Security Admission: privileged/baseline/restricted per namespace
- warn/audit modes let you roll out safely
Watch out for
- Images that need root to start: build them to run as a non-root user instead of relaxing the namespace.