Pod Security Standards
Apply Kubernetes Pod Security Standards (PSA) to enforce security policies on pods. This lesson covers the core concept, step-by-step implementation, and hands-on exercises.
Focus: apply pod security standards psa
You've built your pods, tuned resource limits, even set up readiness probes. But have you secured those pods against common container compromises? Without enforcement, a developer could accidentally (or maliciously) run containers with root privileges, mount the host filesystem, or escape the container runtime. That's where Pod Security Standards (PSA) come in—they replace Kubernetes' deprecated Pod Security Policies with a simpler, built-in admission controller that lets you define and enforce security postures across your namespaces. In this lesson, you'll learn the three standard profiles (Privileged, Baseline, Restricted), how to apply them via labels and the built-in controller, and how to troubleshoot enforcement issues.
The problem this lesson solves
Container security in Kubernetes is not optional—it's a foundational layer of defense. Without pod-level security controls, a compromised image or misconfigured workload can:
- Escape the container and access the host kernel.
- Mount sensitive host paths (like
/var/run/docker.sock). - Run as root and install malicious software.
- Use host networking to bypass network policies.
Older clusters used PodSecurityPolicy (PSP), but PSP was deprecated in Kubernetes v1.21 and removed in v1.25. The replacement—Pod Security Admission (PSA)—is built directly into the kube-apiserver and requires no external webhook. It enforces the three Pod Security Standards (PSS) at the namespace level using simple labels. If you're still running PSP, you must migrate to PSA before upgrading to v1.25+.
Core concept / mental model
Think of Pod Security Standards as canned security profiles that define a pod's acceptable behavior. There are exactly three profiles, each more restrictive:
| Profile | Use case | Key restrictions |
|---|---|---|
| Privileged | System pods (CNI, storage, monitoring). Unrestricted. | No restrictions—all capabilities allowed. |
| Baseline | General-purpose workloads. Minimally restrictive. | Prevents known privilege escalations (e.g., hostPID, hostNetwork, privileged containers). |
| Restricted | Security-critical workloads (e.g., payment processing). Heavily locked down. | Enforces layers 1–5 of the Pod Security Standard: no root filesystem, no privileged escalation, constrained seccomp/apparmor. |
How enforcement modes work: For each namespace, you set a mode (one of enforce, audit, warn) plus a profile. The mode defines what happens when a pod violates the profile:
- enforce – Rejects the pod creation/update immediately.
- audit – Logs the violation in the audit log (does not block).
- warn – Returns a warning to the API caller (does not block).
You can apply different modes to the same namespace, e.g., enforce the Baseline profile but warn on the Restricted profile. This lets you phase in restrictions: first warn developers, then enforce.
How it works step by step
- Choose a profile that matches your workload's security needs. Start with
baselinefor 80% of your deployments. Useprivilegedonly for cluster-critical system pods. - Activate the Pod Security Admission controller in your kube-apiserver (it's enabled by default in most modern distributions like Kubeadm, EKS, AKS, GKE).
- Label your namespace with the chosen profile and mode. The label format is:
pod-security.kubernetes.io/<mode>: <profile> - Test a pod against the namespace labels—PSA validates every pod creation or update.
- Check violations via
kubectl describe ns <namespace>or audit logs if usingauditmode.
Hands-on walkthrough
Let's apply the Baseline profile in enforce mode to a new namespace, then try to create both compliant and non‑compliant pods.
Step 1: Create a namespace with PSA labels
kubectl create namespace secure-app
kubectl label namespace secure-app pod-security.kubernetes.io/enforce=baseline
kubectl label namespace secure-app pod-security.kubernetes.io/warn=restricted
Check the labels:
kubectl get ns secure-app -o yaml | grep pod-security
Expected output:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/warn: restricted
Step 2: Try a disallowed pod (Baseline violation)
This pod requests hostPID which is forbidden under Baseline:
# bad-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: bad-pod
namespace: secure-app
spec:
hostPID: true
containers:
- name: nginx
image: nginx:latest
Create it:
kubectl apply -f bad-pod.yaml
Expected output:
Error from server (Forbidden): error when creating "bad-pod.yaml": pods "bad-pod" is forbidden: violates PodSecurity "baseline:latest": host namespaces (hostPID=true)
Step 3: Create a compliant pod (Baseline passes)
# good-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: good-pod
namespace: secure-app
spec:
containers:
- name: nginx
image: nginx:latest
kubectl apply -f good-pod.yaml
Expected output:
pod/good-pod created
Pro tip: Use
warnmode first in development namespaces. Developers will see warnings without breaking their pipelines. When you're confident the workloads comply, switch toenforce.
Step 4: Verify enforcement status
kubectl describe ns secure-app
Look for the Pod Security section near the bottom. It will show the current profile in effect.
Compare options / when to choose what
| Profile | When to use | Risk if over‑restricted |
|---|---|---|
| Privileged | System DaemonSets (CNI, kube-proxy, storage). | None—it's deliberately permissive. |
| Baseline | Most application pods (web servers, APIs, background workers). Prevents 90% of known container escapes. | Some monitoring tools that need host path access will fail. |
| Restricted | High‑security workloads (PCI‑DSS, financial transactions, multi‑tenant). Prevents all known fixes except those explicitly allowed. | Many containers require privileged: true or allowPrivilegeEscalation: false – you may need to rebuild images. |
Alternative: Third‑party webhooks (Kyverno, OPA/Gatekeeper) – PSA is Kubernetes-native and simpler but less flexible. For complex policies (e.g., "only allow images from a specific registry"), use Kyverno or OPA. For simple enforcement of the three profiles, stick with PSA.
Troubleshooting & edge cases
1. PSA not enforcing
- Check that the Pod Security Admission controller is enabled:
bash kubectl -n kube-system get pod kube-apiserver-<node> -o yaml | grep enable-admission-pluginsIt should includePodSecurity. - If using a managed cluster (EKS, AKS, GKE), verify the API server version is ≥ 1.23. Older versions require the
--feature-gates=PodSecurity=trueflag.
2. Pod creation succeeds despite labels
- Check the namespace labels exactly. Mistyped label key will silently skip enforcement.
bash kubectl get ns <ns> -o yaml | grep 'pod-security' - If you see no
pod-security.kubernetes.io/enforce, PSA won't block anything.
3. Warnings appear but no audit logs
- For
auditmode, you must have audit logging enabled on your API server. See Kubernetes auditing.
4. Restricted profile breaks your application
- Common violations:
runAsNonRoot: true,seccompProfile.type: RuntimeDefault,allowPrivilegeEscalation: false. You must adapt yoursecurityContextor use an image that doesn't require root.
Blockquote pro tip: If you're migrating from PodSecurityPolicy to PSA, the
kubectlpluginkubectl-convertcan help translate PSP rules. But you'll still need to manually label namespaces and test workloads.
What you learned & what's next
You now understand:
- The three Pod Security Standards profiles (Privileged, Baseline, Restricted).
- How to apply PSA to a namespace using labels (
enforce,audit,warn). - How to test and troubleshoot pod rejections.
- When to choose PSA vs. third-party admission controllers (Kyverno/OPA).
- The migration path from deprecated PodSecurityPolicy to PSA.
Next step: In the following lesson, you'll learn how to enforce image policies using Container Image Signing with cosign and admission controllers. This builds on PSA by verifying that every pod runs an image signed by a trusted source.
Practice recap
Create a new namespace practice-secure and label it with enforce=baseline. Then try to create a pod with privileged: true and observe the rejection. Next, create a compliant pod. Finally, switch the namespace label to warn=restricted and observe the warning when attempting the same privileged pod. This teaches you the difference between enforcement and warning modes.
Common mistakes
- Applying PSA labels to the namespace but forgetting to enable the PodSecurity admission plugin on the API server (kube-apiserver flag
--enable-admission-plugins=PodSecurity). - Using the wrong label key—
pod-security.kubernetes.io/enforceis correct, notpodSecurity.kubernetes.io/enforceor misspelling the profile name (e.g.,baselininstead ofbaseline). - Setting
enforce=restrictedon a namespace with workloads that need host networking or privileged access—PSA will reject them immediately. Always start withwarnmode. - Assuming PSA alone covers all security needs—PSA only checks pod specs. For admission policies on images, environment variables, or volumes, combine with OPA/Gatekeeper or Kyverno.
Variations
- Instead of the built-in PSA, use the PodSecurity plugin via Kyverno (which wraps PSA) for policy-as-code with more flexible rules.
- Use OPA/Gatekeeper with a
K8sRequiredLabelsconstraint to enforce PSA labels as code viaKustomizeoverlays or GitOps. - In OpenShift, PSA is replaced by the built-in Security Context Constraints (SCC) system, which offers a similar but more granular profile model.
Real-world use cases
- A fintech company enforces the
restrictedprofile on all production namespaces handling PII, preventing any privileged container escape. - A SaaS startup uses
warn=baselineon development namespaces to give developers early feedback whileaudit=restrictedlogs potential violations for review. - A cluster admin migrating from PSP to PSA labels all existing namespaces with
audit=baselineand runs a cronjob to identify workloads that need adjusting before switching toenforce.
Key takeaways
- Pod Security Standards define three profiles: Privileged (unrestricted), Baseline (minimal), and Restricted (full lockdown).
- PSA is the built-in replacement for deprecated PodSecurityPolicy; it is enabled by default in Kubernetes ≥ 1.23.
- Apply PSA to a namespace by labeling it with
pod-security.kubernetes.io/enforce=<profile>(oraudit,warn). - Use
warnmode first to let developers see violations without breaking their workflows; then switch toenforce. - PSA check is performed at pod creation/update time—it does not audit existing pods; only new pods respect the labels.
- Start with the Baseline profile for 80% of your workloads; it blocks known privilege escalations while remaining compatible with most containers.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.