Module 4.2: Pod Security Admission (PSA)
Complexity:
[MEDIUM]- Core CKS skillTime to Complete: 40-45 minutes
Prerequisites: Module 4.1 (Security Contexts), namespace basics
What You’ll Be Able to Do
Section titled “What You’ll Be Able to Do”After completing this module, you will be able to:
- Configure Pod Security Admission labels on namespaces for enforce, audit, and warn modes
- Implement baseline and restricted security profiles across cluster namespaces
- Diagnose PSA rejection messages and adjust pod specs to comply with security standards
- Design a namespace-level security strategy using PSA profiles appropriate to workload sensitivity
Why This Module Matters
Section titled “Why This Module Matters”Pod Security Admission (PSA) enforces security standards at the namespace level. Instead of manually checking every pod’s security context, PSA automatically validates pods against defined security profiles and blocks non-compliant ones.
PSA replaced PodSecurityPolicy (removed in Kubernetes 1.25). This is a critical CKS topic.
Pod Security Standards
Section titled “Pod Security Standards”┌─────────────────────────────────────────────────────────────┐│ POD SECURITY STANDARDS │├─────────────────────────────────────────────────────────────┤│ ││ Privileged (most permissive) ││ ───────────────────────────────────────────────────────── ││ • No restrictions ││ • For system-level workloads ││ • CNI, CSI, logging agents ││ ││ Baseline (reasonable default) ││ ───────────────────────────────────────────────────────── ││ • Prevents known privilege escalations ││ • Blocks hostNetwork, hostPID, hostIPC ││ • Blocks privileged containers ││ • Allows running as root ││ ││ Restricted (most secure) ││ ───────────────────────────────────────────────────────── ││ • Maximum security ││ • Requires running as non-root ││ • Requires dropping ALL capabilities ││ • Requires read-only root filesystem ││ • Requires seccomp profile ││ │└─────────────────────────────────────────────────────────────┘Stop and think: You enable Pod Security Admission in
enforcemode with therestrictedprofile on thekube-systemnamespace. What happens to your control plane components (kube-proxy, CoreDNS, CNI pods) that need privileged access?
PSA Modes
Section titled “PSA Modes”┌─────────────────────────────────────────────────────────────┐│ PSA ENFORCEMENT MODES │├─────────────────────────────────────────────────────────────┤│ ││ enforce ││ └── Reject pods that violate the policy ││ (Pod creation fails) ││ ││ warn ││ └── Allow but show warning to user ││ (Good for migration) ││ ││ audit ││ └── Record violation in audit log ││ (Pod still created) ││ ││ You can use different profiles for each mode: ││ • enforce: baseline (block obvious issues) ││ • warn: restricted (show what would fail) ││ • audit: restricted (log for review) ││ │└─────────────────────────────────────────────────────────────┘Configuring PSA via Labels
Section titled “Configuring PSA via Labels”# Apply PSA to a namespace using labelsapiVersion: v1kind: Namespacemetadata: name: secure-ns labels: # Enforce baseline standard pod-security.kubernetes.io/enforce: baseline pod-security.kubernetes.io/enforce-version: latest
# Warn on restricted violations pod-security.kubernetes.io/warn: restricted pod-security.kubernetes.io/warn-version: latest
# Audit restricted violations pod-security.kubernetes.io/audit: restricted pod-security.kubernetes.io/audit-version: latestLabel Format
Section titled “Label Format”pod-security.kubernetes.io/<MODE>: <PROFILE>pod-security.kubernetes.io/<MODE>-version: <VERSION>
MODE: enforce | warn | auditPROFILE: privileged | baseline | restrictedVERSION: latest | v1.28 | v1.27 | etc.Profile Requirements
Section titled “Profile Requirements”Baseline Profile Restrictions
Section titled “Baseline Profile Restrictions”# Baseline blocks these:hostNetwork: true # BlockedhostPID: true # BlockedhostIPC: true # Blockedprivileged: true # BlockedhostPorts: [80, 443] # Blocked (hostPort)
# These capabilities blocked:capabilities: add: - NET_ADMIN # Blocked - SYS_ADMIN # Blocked - ALL # Blocked
# Baseline ALLOWS:runAsUser: 0 # Running as root OKrunAsNonRoot: false # Not requiredreadOnlyRootFilesystem: false # Not requiredRestricted Profile Requirements
Section titled “Restricted Profile Requirements”# Restricted REQUIRES:securityContext: runAsNonRoot: true # Required allowPrivilegeEscalation: false # Required seccompProfile: type: RuntimeDefault # Required (or Localhost) capabilities: drop: ["ALL"] # Required
# For containers specifically:containers:- name: app securityContext: runAsNonRoot: true allowPrivilegeEscalation: false readOnlyRootFilesystem: true # Recommended but not required capabilities: drop: ["ALL"]What would happen if: You label a namespace with
pod-security.kubernetes.io/enforce: baselineandpod-security.kubernetes.io/warn: restricted. A developer deploys a pod that passesbaselinebut failsrestricted. Does the pod run? What does the developer see?
Practical Examples
Section titled “Practical Examples”Create Restricted Namespace
Section titled “Create Restricted Namespace”# Create namespace with restricted enforcementcat <<EOF | kubectl apply -f -apiVersion: v1kind: Namespacemetadata: name: production labels: pod-security.kubernetes.io/enforce: restricted pod-security.kubernetes.io/enforce-version: latest pod-security.kubernetes.io/warn: restricted pod-security.kubernetes.io/audit: restrictedEOFTest Policy Enforcement
Section titled “Test Policy Enforcement”# This pod will be REJECTED in restricted namespacecat <<EOF | kubectl apply -f - -n productionapiVersion: v1kind: Podmetadata: name: insecure-podspec: containers: - name: app image: nginxEOF
# Error: pods "insecure-pod" is forbidden:# violates PodSecurity "restricted:latest":# allowPrivilegeEscalation != false,# unrestricted capabilities,# runAsNonRoot != true,# seccompProfile
# This pod SUCCEEDS in restricted namespacecat <<EOF | kubectl apply -f - -n productionapiVersion: v1kind: Podmetadata: name: secure-podspec: securityContext: runAsNonRoot: true runAsUser: 1000 seccompProfile: type: RuntimeDefault containers: - name: app image: nginx securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"]EOFExemptions
Section titled “Exemptions”Namespace-Level Exemptions
Section titled “Namespace-Level Exemptions”# Exempt specific namespaces in AdmissionConfigurationapiVersion: apiserver.config.k8s.io/v1kind: AdmissionConfigurationplugins:- name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1 kind: PodSecurityConfiguration defaults: enforce: "baseline" enforce-version: "latest" exemptions: # Exempt kube-system namespace namespaces: - kube-system # Exempt specific users usernames: - system:serviceaccount:kube-system:* # Exempt specific runtime classes runtimeClasses: - gvisorExempt System Namespaces
Section titled “Exempt System Namespaces”# kube-system typically needs privileged accesskubectl label namespace kube-system pod-security.kubernetes.io/enforce=privileged --overwritePause and predict: Your cluster has 50 namespaces with no PSA labels. You want to enforce
restrictedeverywhere. If you apply the label to all namespaces at once, what percentage of existing pods would likely be rejected on recreation? What’s the safer migration path?
Migration Strategy
Section titled “Migration Strategy”┌─────────────────────────────────────────────────────────────┐│ PSA MIGRATION STRATEGY │├─────────────────────────────────────────────────────────────┤│ ││ Phase 1: Audit Only ││ ───────────────────────────────────────────────────────── ││ pod-security.kubernetes.io/audit: restricted ││ • All pods still created ││ • Violations logged ││ • Review audit logs for issues ││ ││ Phase 2: Warn and Audit ││ ───────────────────────────────────────────────────────── ││ pod-security.kubernetes.io/warn: restricted ││ pod-security.kubernetes.io/audit: restricted ││ • Developers see warnings ││ • Still non-blocking ││ ││ Phase 3: Enforce Baseline, Warn Restricted ││ ───────────────────────────────────────────────────────── ││ pod-security.kubernetes.io/enforce: baseline ││ pod-security.kubernetes.io/warn: restricted ││ • Block obvious violations ││ • Warn on restricted issues ││ ││ Phase 4: Full Enforcement ││ ───────────────────────────────────────────────────────── ││ pod-security.kubernetes.io/enforce: restricted ││ • Maximum security ││ │└─────────────────────────────────────────────────────────────┘Real Exam Scenarios
Section titled “Real Exam Scenarios”Scenario 1: Enable Restricted Mode
Section titled “Scenario 1: Enable Restricted Mode”# Apply restricted enforcement to namespacekubectl label namespace production \ pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/enforce-version=latest
# Verify labelkubectl get namespace production --show-labelsScenario 2: Fix Pod for Restricted Namespace
Section titled “Scenario 2: Fix Pod for Restricted Namespace”# Original pod (fails in restricted)apiVersion: v1kind: Podmetadata: name: webspec: containers: - name: nginx image: nginx
# Fixed pod (works in restricted)apiVersion: v1kind: Podmetadata: name: webspec: securityContext: runAsNonRoot: true runAsUser: 101 # nginx user seccompProfile: type: RuntimeDefault containers: - name: nginx image: nginx securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] readOnlyRootFilesystem: true volumeMounts: - name: cache mountPath: /var/cache/nginx - name: run mountPath: /var/run volumes: - name: cache emptyDir: {} - name: run emptyDir: {}Scenario 3: Debug PSA Rejection
Section titled “Scenario 3: Debug PSA Rejection”# Check namespace labelskubectl get namespace production -o yaml | grep -A10 labels
# Create pod and see detailed errorkubectl apply -f pod.yaml -n production 2>&1 | head -20
# Dry run to check without creatingkubectl apply -f pod.yaml -n production --dry-run=serverDid You Know?
Section titled “Did You Know?”-
PSA replaced PodSecurityPolicy (PSP) which was deprecated in Kubernetes 1.21 and removed in 1.25. PSA is simpler and based on namespace labels.
-
The “latest” version follows the Kubernetes version. Using “latest” means the policy updates when you upgrade Kubernetes.
-
System namespaces need privileged. kube-system pods (CNI, CSI, kube-proxy) often require host access. Always exempt or use privileged level.
-
PSA is built into the API server and enabled by default since Kubernetes 1.25. No additional installation needed.
Common Mistakes
Section titled “Common Mistakes”| Mistake | Why It Hurts | Solution |
|---|---|---|
| Enforcing restricted on kube-system | System pods fail | Use privileged for system namespaces |
| No runAsNonRoot in restricted | Pods rejected | Add runAsNonRoot: true |
| Missing seccompProfile | Restricted requires it | Add RuntimeDefault |
| Not dropping capabilities | Restricted requires it | Drop ALL |
| Skipping warn phase | Sudden failures | Migrate gradually |
-
A developer deploys a pod to the
productionnamespace and gets: “violates PodSecurity ‘restricted:latest’: allowPrivilegeEscalation != false.” They fix that but hit another error aboutrunAsNonRoot. How many fields total does therestrictedprofile require, and what’s the best way to avoid this one-at-a-time debugging?Answer
The `restricted` profile requires: `runAsNonRoot: true`, `allowPrivilegeEscalation: false`, `capabilities.drop: ["ALL"]`, `seccompProfile.type: RuntimeDefault` or `Localhost`, plus no `hostNetwork`, `hostPID`, `hostIPC`, or `privileged`. PSA validates each independently, so developers hit errors one at a time. The best approach is providing a secure pod template that satisfies all restricted requirements at once. Use `kubectl --dry-run=server` to validate before deploying, or set `warn: restricted` alongside `enforce: baseline` so developers see all warnings upfront before restricted enforcement is enabled. -
Your team rolls out
pod-security.kubernetes.io/enforce: restrictedto theproductionnamespace. Eight of twelve deployments break immediately. Management says PSA is too disruptive. What migration strategy would have prevented this?Answer
Use a three-phase migration: Phase 1 -- Apply `audit: restricted` and `warn: restricted` (no enforcement). This logs violations and warns developers without blocking pods. Review audit logs to see what fails. Phase 2 -- Fix pod specs across teams (add security contexts, non-root images, drop capabilities). Phase 3 -- Apply `enforce: baseline` first (catches the worst issues), then gradually upgrade to `enforce: restricted` per namespace as pods are remediated. The key mistake was jumping directly to enforcement. The warn/audit modes exist precisely for gradual migration. -
During an incident response, your security team needs to deploy a privileged debug container in a namespace with
enforce: restricted. PSA blocks it. How do you get the debug capabilities without weakening the namespace’s security policy?Answer
Never remove the PSA label from the compromised namespace -- that weakens security during active investigation. Instead: (1) Use `kubectl debug node/--image=busybox` which deploys in a system namespace with elevated privileges. (2) Create a dedicated `incident-response` namespace with `privileged` PSA that is normally empty and heavily audited. (3) Configure PSA exemptions in the admission configuration for specific incident response ServiceAccounts. (4) Deploy debug pods in `kube-system` which should have `privileged` PSA. These approaches maintain production security while enabling investigation. -
You accidentally label
kube-systemwithenforce: restricted. CoreDNS pods are terminated and DNS fails cluster-wide. New pods can’t pull images because they can’t resolve registry hostnames. How do you recover?Answer
Remove the label immediately: `kubectl label namespace kube-system pod-security.kubernetes.io/enforce-` (trailing dash removes it). CoreDNS should self-heal once the restriction is lifted. If `kubectl` itself fails due to DNS issues, access the API server directly via IP: `kubectl --server=https://:6443 label namespace kube-system pod-security.kubernetes.io/enforce-`. The lesson: `kube-system` must use `privileged` PSA because system components (kube-proxy, CNI, CoreDNS) legitimately need elevated permissions. Apply `restricted` only to workload namespaces, never system namespaces.
Hands-On Exercise
Section titled “Hands-On Exercise”Task: Configure and test Pod Security Admission.
# Step 1: Create namespace with baseline enforcementkubectl create namespace psa-testkubectl label namespace psa-test \ pod-security.kubernetes.io/enforce=baseline \ pod-security.kubernetes.io/warn=restricted
# Step 2: Verify labelskubectl get namespace psa-test --show-labels
# Step 3: Test baseline allows regular podskubectl run baseline-test --image=nginx -n psa-testkubectl get pod baseline-test -n psa-test
# Step 4: Test baseline blocks privilegedcat <<EOF | kubectl apply -f - -n psa-testapiVersion: v1kind: Podmetadata: name: privileged-podspec: containers: - name: app image: nginx securityContext: privileged: trueEOF# Should be rejected
# Step 5: Upgrade to restrictedkubectl label namespace psa-test \ pod-security.kubernetes.io/enforce=restricted --overwrite
# Step 6: Previous pod now shows warning at restricted level# Delete and recreatekubectl delete pod baseline-test -n psa-testkubectl run restricted-test --image=nginx -n psa-test 2>&1 || echo "Rejected (expected)"
# Step 7: Create compliant podcat <<EOF | kubectl apply -f - -n psa-testapiVersion: v1kind: Podmetadata: name: compliant-podspec: securityContext: runAsNonRoot: true runAsUser: 1000 seccompProfile: type: RuntimeDefault containers: - name: app image: busybox command: ["sleep", "3600"] securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"]EOF
kubectl get pod compliant-pod -n psa-test
# Cleanupkubectl delete namespace psa-testSuccess criteria: Understand how PSA blocks non-compliant pods.
Summary
Section titled “Summary”Pod Security Standards:
- Privileged: No restrictions
- Baseline: Block known escalations
- Restricted: Maximum security
Modes:
- enforce: Block violations
- warn: Show warnings
- audit: Log violations
Required for Restricted:
- runAsNonRoot: true
- allowPrivilegeEscalation: false
- capabilities.drop: [“ALL”]
- seccompProfile: RuntimeDefault
Exam Tips:
- Know namespace label format
- Memorize restricted requirements
- Migrate gradually (audit → warn → enforce)
Next Module
Section titled “Next Module”Module 4.3: Secrets Management - Securing Kubernetes secrets.