Fatskills
Practice. Master. Repeat.
Study Guide: TECH **PodSecurityPolicies (PSP) & Pod Security Admission (PSA) – Zero-Fluff Study Guide**
Source: https://www.fatskills.com/kubernetes/chapter/tech-podsecuritypolicies-psp-pod-security-admission-psa-zero-fluff-study-guide

TECH **PodSecurityPolicies (PSP) & Pod Security Admission (PSA) – Zero-Fluff Study Guide**

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~7 min read

PodSecurityPolicies (PSP) & Pod Security Admission (PSA) – Zero-Fluff Study Guide

For engineers who need to lock down Kubernetes clusters without wasting time on theory.


1. What This Is & Why It Matters

You’re a DevOps engineer at a fintech startup. Your team just migrated a legacy monolith to Kubernetes, and now any pod can run as root, mount host paths, or escalate privileges. A single misconfigured pod could let an attacker pivot to the host node, steal secrets, or cripple the cluster.

PodSecurityPolicies (PSP) (deprecated in Kubernetes 1.21, removed in 1.25) and Pod Security Admission (PSA) (the modern replacement) are your last line of defense against insecure workloads. They enforce security constraints like: - No root containers.
- No privilege escalation.
- No host filesystem access.
- Read-only root filesystems.

Why this matters in production:
- Compliance: PCI-DSS, SOC2, and HIPAA require least-privilege workloads.
- Security: A single privileged: true pod can compromise the entire cluster.
- Stability: Host path mounts can fill up node disks, crashing the node.
- Cost: Unrestricted pods can consume all CPU/memory, starving other workloads.

Real-world scenario:
You inherit a cluster where a developer deployed a pod with hostPID: true to debug a process. An attacker exploits this to read /etc/shadow from the host, then pivots to other nodes. PSA/PSP would’ve blocked this at admission time.


2. Core Concepts & Components


? PodSecurityPolicy (PSP) (Deprecated, but still seen in legacy clusters)

  • Definition: A cluster-wide resource that defines security constraints for pods.
  • Production insight: If you’re on Kubernetes <1.25, you must migrate to PSA before upgrading. PSPs are not forward-compatible.
  • Key fields:
  • privileged: Allows full host access (⚠️ never enable in production).
  • allowPrivilegeEscalation: Lets containers gain more privileges (e.g., via sudo).
  • runAsUser: Forces containers to run as a non-root user.
  • readOnlyRootFilesystem: Prevents writes to the container filesystem.
  • volumes: Restricts volume types (e.g., block hostPath).

? Pod Security Admission (PSA) (Modern replacement)

  • Definition: A built-in admission controller that enforces security standards at pod creation time.
  • Production insight: PSA is enabled by default in Kubernetes 1.23+. If you’re not using it, you’re running an insecure cluster.
  • Three enforcement modes:
  • enforce: Blocks non-compliant pods.
  • audit: Logs violations but allows pods.
  • warn: Warns users but allows pods.
  • Three security levels:
  • privileged: Unrestricted (⚠️ avoid in production).
  • baseline: Minimal restrictions (blocks known privilege escalations).
  • restricted: Strictest (enforces least privilege, read-only root, etc.).

? Admission Controller

  • Definition: A Kubernetes plugin that intercepts API requests (e.g., pod creation) and validates/modifies them.
  • Production insight: PSA is an admission controller, not a standalone resource. If admission controllers are disabled, PSA won’t work.

? Namespace Labels (PSA)

  • Definition: PSA applies policies based on namespace labels.
  • Production insight: You must label namespaces to enable PSA. Unlabeled namespaces default to privileged (insecure!).
  • Key labels:
    yaml pod-security.kubernetes.io/enforce: baseline pod-security.kubernetes.io/warn: restricted

? SecurityContext

  • Definition: A pod/container-level field that defines security settings (e.g., runAsNonRoot: true).
  • Production insight: PSA overrides SecurityContext if they conflict. Always test your pods with PSA enabled.

? Capabilities (Linux)

  • Definition: Fine-grained permissions for containers (e.g., CAP_NET_BIND_SERVICE for binding to ports <1024).
  • Production insight: PSA’s restricted level drops all capabilities. If your app needs CAP_SYS_ADMIN, you’ll need to adjust.


3. Step-by-Step: Enabling Pod Security Admission (PSA)

Prerequisites:
- A Kubernetes cluster (v1.23+).
- kubectl configured.
- Cluster admin permissions.

Step 1: Verify PSA is enabled

kubectl api-versions | grep admissionregistration.k8s.io/v1

Expected output: admissionregistration.k8s.io/v1 (if missing, PSA isn’t available).

Step 2: Label a namespace for PSA

kubectl label ns default pod-security.kubernetes.io/enforce=baseline
kubectl label ns default pod-security.kubernetes.io/warn=restricted

Verify:


kubectl get ns default --show-labels

Expected output:


NAME      STATUS   AGE   LABELS
default   Active   10d   pod-security.kubernetes.io/enforce=baseline,pod-security.kubernetes.io/warn=restricted

Step 3: Test PSA with a compliant pod

# compliant-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: compliant-pod
spec:
  securityContext:
runAsNonRoot: true
runAsUser: 1000 containers: - name: nginx
image: nginx
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
kubectl apply -f compliant-pod.yaml

Expected: Pod creates successfully.

Step 4: Test PSA with a non-compliant pod

# non-compliant-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: non-compliant-pod
spec:
  containers:
  - name: nginx
image: nginx
securityContext:
privileged: true # Violates baseline/restricted
kubectl apply -f non-compliant-pod.yaml

Expected output:


Error from server (Forbidden): pods "non-compliant-pod" is forbidden: violates PodSecurity "baseline:latest": privileged (container "nginx" must not set securityContext.privileged=true)

Step 5: Check audit logs (if using audit mode)

kubectl get events --sort-by='.metadata.creationTimestamp' | grep "PodSecurity"

Expected output:


Warning  PodSecurityViolation  2m   admission-controller  Pod non-compliant-pod violates PodSecurity "restricted:latest": ...

Step 6: (Optional) Enable PSA cluster-wide

Edit the API server manifest (/etc/kubernetes/manifests/kube-apiserver.yaml) and add:


- --enable-admission-plugins=NodeRestriction,PodSecurity
- --admission-control-config-file=/etc/kubernetes/pod-security-admission-config.yaml

Create /etc/kubernetes/pod-security-admission-config.yaml:


apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
apiVersion: pod-security.admission.config.k8s.io/v1
kind: PodSecurityConfiguration
defaults:
enforce: "baseline"
enforce-version: "latest"
audit: "restricted"
audit-version: "latest"
warn: "restricted"
warn-version: "latest"
exemptions:
usernames: []
runtimeClasses: []
namespaces: [kube-system] # Exempt system namespaces

⚠️ Restart the API server:


sudo systemctl restart kubelet


4. ? Production-Ready Best Practices


Security

  • Always enforce restricted in production namespaces (e.g., default, prod).
  • Use baseline for dev/staging (allows some flexibility but blocks obvious risks).
  • Exempt kube-system (some system pods need elevated privileges).
  • Audit existing workloads before enforcing PSA: bash kubectl get pods --all-namespaces -o json | jq '.items[] | select(.spec.securityContext == null or .spec.securityContext.runAsNonRoot != true)'
  • Rotate certificates and secrets if you suspect a breach (PSA won’t protect against stolen credentials).

Cost Optimization

  • PSA has zero cost (it’s built into Kubernetes).
  • Avoid privileged pods – they can consume all node resources, leading to scaling costs.

Reliability & Maintainability

  • Label all namespaces (unlabeled namespaces default to privileged).
  • Use kubectl explain to debug violations:
    bash kubectl explain pod.spec.securityContext
  • Document exceptions (e.g., "Namespace monitoring is exempt because Prometheus needs hostPath").

Observability

  • Monitor PSA violations with Prometheus: promql kube_pod_security_violations_total{level="restricted"}
  • Set up alerts for repeated violations (indicates misconfigured apps).


5. ⚠️ Common Mistakes & Traps

Mistake Symptom Fix/Prevention
Not labeling namespaces Pods run with privileged defaults. Label all namespaces at creation: kubectl label ns <name> pod-security.kubernetes.io/enforce=restricted.
Assuming SecurityContext overrides PSA Pods get rejected even with runAsNonRoot: true. PSA always takes precedence. Test with kubectl apply --dry-run=server.
Exempting too many namespaces Attackers pivot to exempt namespaces. Only exempt kube-system and kube-public. Audit exemptions quarterly.
Using baseline in production Pods can still mount hostPath or run as root. Use restricted in production.
Ignoring audit logs Violations go unnoticed until a breach. Set up alerts for PodSecurityViolation events.


6. ? Exam/Certification Focus

CKA/CKAD/CKS question patterns:
1. Scenario: "A pod fails to start with Error: Forbidden: violates PodSecurity. What’s the issue?"
- Answer: The pod violates the namespace’s PSA level (e.g., restricted blocks privileged: true).
2. Trick question: "Which PSA level allows hostPath volumes?"
- Answer: Only privileged. baseline and restricted block them.
3. Command question: "How do you check a namespace’s PSA labels?"
- Answer: kubectl get ns <name> --show-labels.
4. Debugging question: "A pod works in dev but fails in prod. Why?"
- Answer: prod likely has restricted PSA, while dev has baseline.

Key distinctions:
- PSP vs. PSA:
- PSP: Deprecated, cluster-wide, uses ClusterRole.
- PSA: Modern, namespace-scoped, uses labels.
- baseline vs. restricted:
- baseline: Blocks known privilege escalations (e.g., hostPath, privileged).
- restricted: Enforces least privilege (e.g., runAsNonRoot, readOnlyRootFilesystem).


7. ? Hands-On Challenge

Challenge:
Deploy a pod that: 1. Runs as a non-root user.
2. Has a read-only root filesystem.
3. Fails to start under restricted PSA.

Solution:


# challenge-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: challenge-pod
spec:
  containers:
  - name: alpine
image: alpine
command: ["sh", "-c", "echo 'I should fail!' && sleep 3600"]
securityContext:
readOnlyRootFilesystem: true
runAsUser: 1000
allowPrivilegeEscalation: true # Violates restricted
kubectl apply -f challenge-pod.yaml

Why it fails:
allowPrivilegeEscalation: true is blocked by restricted PSA.


8. ? Rapid-Reference Crib Sheet

Command/Concept Usage
kubectl label ns <name> pod-security.kubernetes.io/enforce=restricted Enforce PSA on a namespace.
kubectl get ns --show-labels Check PSA labels on namespaces.
kubectl explain pod.spec.securityContext Debug SecurityContext fields.
kubectl get events --sort-by='.metadata.creationTimestamp' \| grep PodSecurity Check PSA violations.
PSA Levels
privileged No restrictions (⚠️ avoid).
baseline Blocks hostPath, privileged, etc.
restricted Enforces runAsNonRoot, readOnlyRootFilesystem.
Default Unlabeled namespaces default to privileged.
Exemptions kube-system is often exempted.


9. ? Where to Go Next

  1. Kubernetes PSA Documentation
  2. PSA Migration Guide (from PSP)
  3. CKS Exam Curriculum – Pod Security
  4. Kubernetes Security Best Practices (Book)


ADVERTISEMENT