Skip to content
Published on

Pod Security Standards (PSA/PSS) Practical Guide

Authors
  • Name
    Twitter
Pod Security Standards Guide

1. What Are Pod Security Standards?

Starting with Kubernetes 1.25, PodSecurityPolicy (PSP) was completely removed and Pod Security Admission (PSA) became the default security mechanism. PSA operates based on Pod Security Standards (PSS), a three-tier security profile system.

Background: Transition from PSP to PSA

PSP required complex RBAC configuration and was difficult to debug. PSA dramatically reduces operational overhead by allowing security policies to be applied simply through namespace labels.

2. Three Security Profiles

Privileged

A profile that permits everything. Used for system-level workloads such as CNI plugins and storage drivers.

# Apply Privileged to kube-system namespace
apiVersion: v1
kind: Namespace
metadata:
  name: kube-system
  labels:
    pod-security.kubernetes.io/enforce: privileged
    pod-security.kubernetes.io/audit: privileged
    pod-security.kubernetes.io/warn: privileged

Baseline

A profile that prevents known privilege escalations while allowing most workloads to function.

Restricted items:

  • hostNetwork, hostPID, hostIPC usage prohibited
  • Privileged containers prohibited
  • Adding dangerous capabilities prohibited (NET_RAW, etc.)
  • hostPath volumes prohibited
apiVersion: v1
kind: Namespace
metadata:
  name: app-staging
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/enforce-version: v1.30
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

Restricted

The most stringent profile that enforces security best practices.

Additional restricted items:

  • runAsNonRoot required
  • Seccomp profile configuration required
  • Dropping all capabilities required
  • allowPrivilegeEscalation: false required
apiVersion: v1
kind: Namespace
metadata:
  name: app-production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: v1.30
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

3. PSA Operating Modes

PSA operates in three modes:

ModeBehaviorPurpose
enforceRejects Pods that violate policyActual enforcement
auditRecords in audit logsMonitoring
warnDisplays warnings to usersMigration readiness
# Apply labels to a namespace
kubectl label namespace my-app \
  pod-security.kubernetes.io/enforce=baseline \
  pod-security.kubernetes.io/audit=restricted \
  pod-security.kubernetes.io/warn=restricted

4. Writing Pods Compatible with the Restricted Profile

Here is an example Pod spec that satisfies the Restricted profile:

apiVersion: v1
kind: Pod
metadata:
  name: secure-app
  namespace: app-production
spec:
  securityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      image: nginx:1.27-alpine
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        runAsNonRoot: true
        runAsUser: 1000
        capabilities:
          drop:
            - ALL
      resources:
        limits:
          memory: '128Mi'
          cpu: '500m'
        requests:
          memory: '64Mi'
          cpu: '250m'
      volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: cache
          mountPath: /var/cache/nginx
        - name: run
          mountPath: /var/run
  volumes:
    - name: tmp
      emptyDir: {}
    - name: cache
      emptyDir: {}
    - name: run
      emptyDir: {}

5. Migration Strategy

A step-by-step approach for migrating existing clusters to PSA.

Step 1: Audit the Current State

# Dry-run check against all namespaces
kubectl label --dry-run=server --overwrite ns --all \
  pod-security.kubernetes.io/enforce=baseline

# Check violations in a specific namespace
kubectl get pods -n my-app -o json | \
  kubectl apply --dry-run=server -f - 2>&1 | grep -i "warning"

Step 2: Start with warn/audit Mode

# Apply warn and audit first
kubectl label namespace my-app \
  pod-security.kubernetes.io/warn=baseline \
  pod-security.kubernetes.io/audit=baseline

Step 3: Fix Violating Workloads

# Check violations in audit logs
kubectl logs -n kube-system -l component=kube-apiserver | \
  grep "pod-security.kubernetes.io"

Step 4: Enable enforce Mode

# Apply enforce after sufficient testing
kubectl label namespace my-app \
  pod-security.kubernetes.io/enforce=baseline \
  --overwrite

6. Per-Namespace Exception Handling

When specific workloads require elevated privileges, separating namespaces is recommended:

# Separate namespace for monitoring agents
apiVersion: v1
kind: Namespace
metadata:
  name: monitoring-agents
  labels:
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/audit: baseline
    pod-security.kubernetes.io/warn: restricted
---
# Namespace for general applications
apiVersion: v1
kind: Namespace
metadata:
  name: production-apps
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

7. Setting Cluster Defaults with AdmissionConfiguration

You can specify cluster-wide default policies through API server configuration:

# /etc/kubernetes/psa-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
          - kube-public
          - istio-system

8. Practical Troubleshooting

Common Errors and Solutions

# Error: Pod creation denied
# Error: pods "my-pod" is forbidden: violates PodSecurity "restricted:v1.30"

# 1. Identify which field is in violation
kubectl describe pod my-pod -n production-apps 2>&1

# 2. Temporarily check details in warn mode
kubectl label namespace production-apps \
  pod-security.kubernetes.io/enforce=baseline \
  pod-security.kubernetes.io/warn=restricted \
  --overwrite

Frequently Occurring Violations Checklist

#!/bin/bash
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
  echo "=== Namespace: $ns ==="
  kubectl get pods -n "$ns" -o json | \
    jq -r '.items[] | select(
      .spec.containers[].securityContext == null or
      .spec.securityContext == null
    ) | .metadata.name'
done

9. Quiz

Q1: What are the three operating modes of PSA, and what are their roles?
  1. enforce: Rejects the creation of Pods that violate the policy.
  2. audit: Records violations in audit logs but allows Pod creation.
  3. warn: Displays warning messages to kubectl users but allows Pod creation.

During migration, it is safest to apply warn/audit first, then enable enforce after thorough validation.

Q2: What are the 4 securityContext fields that must be set under the Restricted profile?
  1. runAsNonRoot: true — Prohibits running as the root user
  2. allowPrivilegeEscalation: false — Prohibits privilege escalation
  3. capabilities.drop: ["ALL"] — Removes all Linux capabilities
  4. seccompProfile.type: RuntimeDefault — Sets the Seccomp profile

If all four are not set, Pod creation will be rejected in a Restricted namespace.

Q3: What are the recommended steps for migrating an existing cluster to PSA?
  1. Audit the current state: Identify violations using dry-run
  2. Apply warn/audit: Monitor without actual blocking
  3. Fix violating workloads: Add securityContext, minimize privileges
  4. Enable enforce: Apply actual policy enforcement after sufficient testing
  5. Set cluster defaults: Use AdmissionConfiguration to auto-apply to new namespaces

A gradual approach is key — never apply enforce all at once.