- Authors
- Name
- 1. What Are Pod Security Standards?
- 2. Three Security Profiles
- 3. PSA Operating Modes
- 4. Writing Pods Compatible with the Restricted Profile
- 5. Migration Strategy
- 6. Per-Namespace Exception Handling
- 7. Setting Cluster Defaults with AdmissionConfiguration
- 8. Practical Troubleshooting
- 9. Quiz

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:
| Mode | Behavior | Purpose |
|---|---|---|
| enforce | Rejects Pods that violate policy | Actual enforcement |
| audit | Records in audit logs | Monitoring |
| warn | Displays warnings to users | Migration 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?
- enforce: Rejects the creation of Pods that violate the policy.
- audit: Records violations in audit logs but allows Pod creation.
- 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?
- runAsNonRoot: true — Prohibits running as the root user
- allowPrivilegeEscalation: false — Prohibits privilege escalation
- capabilities.drop: ["ALL"] — Removes all Linux capabilities
- 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?
- Audit the current state: Identify violations using dry-run
- Apply warn/audit: Monitor without actual blocking
- Fix violating workloads: Add securityContext, minimize privileges
- Enable enforce: Apply actual policy enforcement after sufficient testing
- Set cluster defaults: Use AdmissionConfiguration to auto-apply to new namespaces
A gradual approach is key — never apply enforce all at once.