feat(security): add Pod Security Standards (PSA) namespace configurations

- Add namespace templates for privileged, baseline, and restricted levels
- Include compliant deployment examples for baseline and restricted
- Add migration audit script for checking namespace compliance
- Document PSA levels, enforcement modes, and migration strategy

Follows Kubernetes Pod Security Admission best practices for 2025+.
Reference: https://kubernetes.io/docs/concepts/security/pod-security-standards/
This commit is contained in:
Greg Hendrickson
2026-02-06 18:02:00 +00:00
parent ef86c1a6c7
commit 58e8140f36
8 changed files with 380 additions and 1 deletions

57
pod-security/README.md Normal file
View File

@@ -0,0 +1,57 @@
# Pod Security Standards (PSS) Configuration
Kubernetes Pod Security Admission (PSA) enforces the [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/) at the namespace level.
## Security Levels
| Level | Description | Use Case |
|-------|-------------|----------|
| **Privileged** | Unrestricted, allows all capabilities | System workloads, CNI, monitoring agents |
| **Baseline** | Prevents known privilege escalations | Most application workloads |
| **Restricted** | Hardened, follows best practices | Sensitive/untrusted workloads |
## Enforcement Modes
- `enforce` - Rejects pods that violate the policy
- `audit` - Logs violations but allows pods
- `warn` - Sends warnings to users but allows pods
## Quick Start
```bash
# Apply all namespace configurations
kubectl apply -f namespaces/
# Test a deployment against restricted namespace
kubectl apply -f examples/restricted-deployment.yaml -n restricted-apps
```
## Namespace Configuration
Each namespace is configured with PSA labels:
```yaml
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
```
## Migration Strategy
1. Start with `audit` and `warn` modes to identify violations
2. Fix non-compliant workloads
3. Enable `enforce` mode
## Files
- `namespaces/` - Pre-configured namespaces for each security level
- `examples/` - Compliant deployment examples for each level
- `migration/` - Tools for auditing existing namespaces
## References
- [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/)
- [Pod Security Admission](https://kubernetes.io/docs/concepts/security/pod-security-admission/)
- [Migrate from PSP](https://kubernetes.io/docs/tasks/configure-pod-container/migrate-from-psp/)

View File

@@ -0,0 +1,74 @@
# Example deployment compliant with BASELINE Pod Security Standard
# Suitable for most application workloads
apiVersion: apps/v1
kind: Deployment
metadata:
name: standard-app
namespace: baseline-apps
labels:
app: standard-app
security.kubernetes.io/compliant: "baseline"
spec:
replicas: 2
selector:
matchLabels:
app: standard-app
template:
metadata:
labels:
app: standard-app
spec:
# Baseline allows running as root, but we still recommend non-root
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:1.27
ports:
- containerPort: 80
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
# Baseline allows writable root filesystem
# readOnlyRootFilesystem: false # default
capabilities:
drop:
- ALL
add:
# Baseline allows these capabilities
- NET_BIND_SERVICE
- CHOWN
- SETGID
- SETUID
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
# Service account with minimal permissions
serviceAccountName: default
automountServiceAccountToken: false

View File

@@ -0,0 +1,105 @@
# Example deployment compliant with RESTRICTED Pod Security Standard
# This is the gold standard for secure workloads
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-app
namespace: restricted-apps
labels:
app: secure-app
security.kubernetes.io/compliant: "restricted"
spec:
replicas: 2
selector:
matchLabels:
app: secure-app
template:
metadata:
labels:
app: secure-app
spec:
# Pod-level security context
securityContext:
runAsNonRoot: true
runAsUser: 65534 # nobody user
runAsGroup: 65534
fsGroup: 65534
seccompProfile:
type: RuntimeDefault
# Prevent service account token auto-mount unless needed
automountServiceAccountToken: false
containers:
- name: app
image: nginx:1.27-alpine
ports:
- containerPort: 8080
protocol: TCP
# Container-level security context
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65534
capabilities:
drop:
- ALL
# Only add NET_BIND_SERVICE if binding to ports < 1024
# add:
# - NET_BIND_SERVICE
# Resource limits (required for production)
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
# Probes for reliability
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
# Volume mounts for writable paths (since root is read-only)
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache/nginx
- name: run
mountPath: /var/run
volumes:
# EmptyDir for ephemeral writable storage
- name: tmp
emptyDir:
sizeLimit: 100Mi
- name: cache
emptyDir:
sizeLimit: 100Mi
- name: run
emptyDir:
sizeLimit: 10Mi
# Topology spread for high availability
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: secure-app

View File

@@ -0,0 +1,52 @@
#!/bin/bash
# audit-namespaces.sh - Check PSA compliance across all namespaces
# Usage: ./audit-namespaces.sh [restricted|baseline]
set -euo pipefail
LEVEL="${1:-restricted}"
echo "=== Pod Security Standards Compliance Audit ==="
echo "Target level: $LEVEL"
echo "Date: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo ""
# Get all namespaces
NAMESPACES=$(kubectl get namespaces -o jsonpath='{.items[*].metadata.name}')
for NS in $NAMESPACES; do
# Skip system namespaces
if [[ "$NS" == "kube-system" || "$NS" == "kube-public" || "$NS" == "kube-node-lease" ]]; then
echo "[$NS] SKIPPED (system namespace)"
continue
fi
# Check current PSA labels
CURRENT_LEVEL=$(kubectl get namespace "$NS" -o jsonpath='{.metadata.labels.pod-security\.kubernetes\.io/enforce}' 2>/dev/null || echo "none")
# Dry-run label to check violations
echo ""
echo "=== Namespace: $NS (current: $CURRENT_LEVEL) ==="
# Use --dry-run to preview what would be blocked
kubectl label namespace "$NS" \
"pod-security.kubernetes.io/enforce=$LEVEL" \
"pod-security.kubernetes.io/warn=$LEVEL" \
--dry-run=server \
--overwrite 2>&1 | grep -E "(Warning|Error)" || echo "✓ No violations detected"
# List pods that might violate
echo ""
echo "Pods in namespace:"
kubectl get pods -n "$NS" -o wide --no-headers 2>/dev/null | head -5 || echo " (no pods)"
done
echo ""
echo "=== Audit Complete ==="
echo ""
echo "To apply restricted level to a namespace:"
echo " kubectl label namespace <name> \\"
echo " pod-security.kubernetes.io/enforce=restricted \\"
echo " pod-security.kubernetes.io/audit=restricted \\"
echo " pod-security.kubernetes.io/warn=restricted \\"
echo " --overwrite"

View File

@@ -0,0 +1,30 @@
# Baseline namespace - for most application workloads
# Prevents known privilege escalations while allowing common configurations
apiVersion: v1
kind: Namespace
metadata:
name: baseline-apps
labels:
# PSA labels - baseline enforcement with restricted auditing
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted # Audit at higher level
pod-security.kubernetes.io/audit-version: latest
pod-security.kubernetes.io/warn: restricted # Warn about restricted violations
pod-security.kubernetes.io/warn-version: latest
# Metadata
environment: production
security-level: baseline
annotations:
description: "Baseline security for standard application workloads"
---
# Baseline allows:
# - Default container configurations
# - Non-privileged containers
# - Standard capabilities (NET_BIND_SERVICE, etc.)
#
# Baseline blocks:
# - Privileged containers
# - Host namespaces (hostPID, hostIPC, hostNetwork)
# - Host path volumes
# - Privileged capabilities

View File

@@ -0,0 +1,25 @@
# Privileged namespace - for system-level workloads only
# Use sparingly: CNI plugins, monitoring agents, storage drivers
apiVersion: v1
kind: Namespace
metadata:
name: privileged-system
labels:
# PSA labels - privileged level
pod-security.kubernetes.io/enforce: privileged
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: privileged
pod-security.kubernetes.io/warn: privileged
# Metadata
environment: system
security-level: privileged
annotations:
description: "Privileged namespace for system workloads requiring host access"
---
# Example: kube-system should typically be privileged
# To label an existing namespace:
# kubectl label namespace kube-system \
# pod-security.kubernetes.io/enforce=privileged \
# pod-security.kubernetes.io/audit=privileged \
# pod-security.kubernetes.io/warn=privileged \
# --overwrite

View File

@@ -0,0 +1,34 @@
# Restricted namespace - maximum security hardening
# For sensitive workloads and untrusted code
apiVersion: v1
kind: Namespace
metadata:
name: restricted-apps
labels:
# PSA labels - restricted at all levels
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: latest
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: latest
# Metadata
environment: production
security-level: restricted
annotations:
description: "Restricted security for sensitive and untrusted workloads"
---
# Restricted REQUIRES:
# - runAsNonRoot: true
# - allowPrivilegeEscalation: false
# - Drop ALL capabilities (except NET_BIND_SERVICE)
# - seccompProfile: RuntimeDefault or Localhost
# - Read-only root filesystem (recommended)
#
# Restricted BLOCKS:
# - Everything baseline blocks, plus:
# - Running as root
# - Privilege escalation
# - Most capabilities
# - HostPath volumes
# - Writable root filesystems (warning only)