Skip to content

Pod Security

Harden containers with SecurityContext and Pod Security Standards.

Time: ~10 minutes Difficulty: Intermediate

  • Pod Security Standards: Privileged, Baseline, Restricted
  • Pod Security Admission: enforce, warn, audit modes
  • SecurityContext: runAsNonRoot, readOnlyRootFilesystem, drop ALL capabilities
  • Why running as root is dangerous
  • What gets blocked and why
LevelWhat It Allows
PrivilegedEverything. No restrictions.
BaselineBlocks known privilege escalations (no privileged containers, no hostNetwork)
RestrictedStrictest. Non-root, read-only filesystem, all capabilities dropped.

Navigate to the demo directory:

Terminal window
cd demos/pod-security

The namespace enforces baseline and warns on restricted violations:

Terminal window
kubectl apply -f manifests/namespace.yaml
Terminal window
kubectl apply -f manifests/privileged-pod.yaml

This is blocked by the baseline enforcement. Privileged containers can access the host kernel.

Test 2: Insecure Pod (Allowed with Warnings)

Section titled “Test 2: Insecure Pod (Allowed with Warnings)”
Terminal window
kubectl apply -f manifests/insecure-pod.yaml

This passes baseline (not privileged) but triggers restricted warnings because it runs as root. Check the warnings in the output.

Verify it runs as root:

Terminal window
kubectl exec insecure-pod -n security-demo -- id
# uid=0(root)
kubectl exec insecure-pod -n security-demo -- whoami
# root
Terminal window
kubectl apply -f manifests/secure-pod.yaml

No warnings. This pod follows all restricted requirements:

Terminal window
kubectl exec secure-pod -n security-demo -- id
# uid=1000 gid=1000
# Read-only root filesystem
kubectl exec secure-pod -n security-demo -- touch /test-file
# touch: /test-file: Read-only file system
# /tmp is writable (emptyDir mount)
kubectl exec secure-pod -n security-demo -- touch /tmp/test-file
# Works
manifests/
namespace.yaml # Namespace with PSA labels (enforce=baseline, warn=restricted)
insecure-pod.yaml # Runs as root, no security hardening
secure-pod.yaml # Non-root, read-only FS, all caps dropped
privileged-pod.yaml # Privileged container (blocked by baseline)

Security settings in the secure pod:

SettingWhat It Does
runAsNonRoot: truePrevents running as UID 0
runAsUser: 1000Sets the user ID
readOnlyRootFilesystem: trueContainer filesystem is read-only
allowPrivilegeEscalation: falseBlocks setuid/setgid binaries
capabilities.drop: [ALL]Removes all Linux capabilities
seccompProfile: RuntimeDefaultRestricts syscalls to a safe set
  1. Change the namespace to enforce restricted:

    Terminal window
    kubectl label namespace security-demo \
    pod-security.kubernetes.io/enforce=restricted --overwrite

    Now the insecure-pod will be blocked too.

  2. Check what capabilities a container has:

    Terminal window
    kubectl exec insecure-pod -n security-demo -- cat /proc/1/status | grep Cap
  3. Reset to baseline:

    Terminal window
    kubectl label namespace security-demo \
    pod-security.kubernetes.io/enforce=baseline --overwrite
Terminal window
kubectl delete namespace security-demo

See docs/deep-dive.md for a detailed explanation of Linux capabilities, seccomp profiles, AppArmor, SELinux contexts, and migrating from PodSecurityPolicy to Pod Security Admission.

Move on to kubectl Debug to learn how to troubleshoot running and crashed pods.