Pod Security Admission (PSA) in Kubernetes
Lab Objective
By the end of this lab, you'll:
- Understand what PSA is and how it differs from PSP.
- Learn how to configure PSA in your cluster.
- Apply and test different security levels (
privileged
,baseline
,restricted
) across namespaces. - Validate pod behavior based on enforced policies.
Prerequisites
- Kubernetes v1.23+ (PSA became stable in v1.25).
kubectl
configured with admin access.- A cluster (KIND or Minikube is fine for labs).
- YAML editing tool (or any editor).
Step 1: Understand Pod Security Levels
Kubernetes provides 3 built-in policy levels under PSA:
Level | Description |
---|---|
privileged |
Unrestricted pods. Allows all capabilities, similar to running as root. |
baseline |
Minimally restrictive for common container use cases. Disallows privileged containers. |
restricted |
Highly secure. Enforces strict security, suitable for production workloads. |
Step 2: Label Namespaces with PSA Modes
Namespaces can be configured with:
enforce
: blocks pods that violate the policy.audit
: logs violations.warn
: sends warnings but allows the pod.
Create Namespaces
kubectl create ns secure-ns
kubectl create ns test-ns
Apply PSA Labels
# Enforce restricted policy
kubectl label ns secure-ns \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest
# Warn for baseline policy
kubectl label ns test-ns \
pod-security.kubernetes.io/warn=baseline \
pod-security.kubernetes.io/warn-version=latest
Step 3: Test Pod Deployment in Labeled Namespaces
Create a Non-Compliant Pod YAML (violates restricted policy)
# insecure-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: insecure-pod
spec:
containers:
- name: app
image: nginx
securityContext:
allowPrivilegeEscalation: true
capabilities:
add: ["NET_ADMIN"]
Try Applying in Different Namespaces
kubectl apply -f insecure-pod.yaml -n test-ns # Should get warnings
kubectl apply -f insecure-pod.yaml -n secure-ns # Should be blocked
✅
secure-ns
will reject the pod due torestricted
enforcement. ⚠️test-ns
will accept it but emit warnings (check viakubectl events
).
Step 4: Create a Compliant Pod
# secure-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
containers:
- name: app
image: nginx
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
kubectl apply -f secure-pod.yaml -n secure-ns
Step 5: Fixing the Compliant Pod for restricted:latest
Here’s what we must fix:
Capabilities must explicitly drop ALL.
Seccomp profile must be explicitly set to RuntimeDefault.
Run as non-root should be specified.
# secure-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
kubectl apply -f secure-pod.yaml -n secure-ns # ✅ Should succeed
Why These Fields Matter in restricted
Field | Why It's Required |
---|---|
runAsNonRoot |
Ensures containers don’t run as UID 0 |
allowPrivilegeEscalation: false |
Blocks escalated privileges even if permitted by image |
capabilities.drop: ["ALL"] |
Drops all Linux capabilities by default |
seccompProfile: RuntimeDefault |
Applies the runtime’s default syscall filter to limit kernel attack surface |
Step 6: View PSA Labels and Behavior
Check labels:
kubectl get ns --show-labels
Check pod events:
kubectl get events -n test-ns
kubectl get events -n secure-ns
Cleanup
kubectl delete ns secure-ns test-ns
Summary
Feature | PSA | PSP (Deprecated) |
---|---|---|
Type | Built-in Admission | API Resource |
Version | Stable in v1.25 | Removed in v1.25 |
Configuration | Namespace labels | Cluster-wide policies |
Use Case | Gradual enforcement | Static blocking |