Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

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

Sponsored

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

  1. Choose a profile that matches your workload's security needs. Start with baseline for 80% of your deployments. Use privileged only for cluster-critical system pods.
  2. Activate the Pod Security Admission controller in your kube-apiserver (it's enabled by default in most modern distributions like Kubeadm, EKS, AKS, GKE).
  3. Label your namespace with the chosen profile and mode. The label format is: pod-security.kubernetes.io/<mode>: <profile>
  4. Test a pod against the namespace labels—PSA validates every pod creation or update.
  5. Check violations via kubectl describe ns <namespace> or audit logs if using audit mode.

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 warn mode first in development namespaces. Developers will see warnings without breaking their pipelines. When you're confident the workloads comply, switch to enforce.

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-plugins It should include PodSecurity.
  • If using a managed cluster (EKS, AKS, GKE), verify the API server version is ≥ 1.23. Older versions require the --feature-gates=PodSecurity=true flag.

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 audit mode, 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 your securityContext or use an image that doesn't require root.

Blockquote pro tip: If you're migrating from PodSecurityPolicy to PSA, the kubectl plugin kubectl-convert can 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/enforce is correct, not podSecurity.kubernetes.io/enforce or misspelling the profile name (e.g., baselin instead of baseline).
  • Setting enforce=restricted on a namespace with workloads that need host networking or privileged access—PSA will reject them immediately. Always start with warn mode.
  • 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

  1. Instead of the built-in PSA, use the PodSecurity plugin via Kyverno (which wraps PSA) for policy-as-code with more flexible rules.
  2. Use OPA/Gatekeeper with a K8sRequiredLabels constraint to enforce PSA labels as code via Kustomize overlays or GitOps.
  3. 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 restricted profile on all production namespaces handling PII, preventing any privileged container escape.
  • A SaaS startup uses warn=baseline on development namespaces to give developers early feedback while audit=restricted logs potential violations for review.
  • A cluster admin migrating from PSP to PSA labels all existing namespaces with audit=baseline and runs a cronjob to identify workloads that need adjusting before switching to enforce.

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> (or audit, warn).
  • Use warn mode first to let developers see violations without breaking their workflows; then switch to enforce.
  • 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.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.