mirror of
https://github.com/ghndrx/k8s-manifests.git
synced 2026-02-10 06:45:09 +00:00
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:
@@ -15,12 +15,14 @@ Production-ready Kubernetes manifests with security best practices, resource lim
|
|||||||
├── secrets/ # Secret management patterns
|
├── secrets/ # Secret management patterns
|
||||||
├── networkpolicies/ # Network isolation
|
├── networkpolicies/ # Network isolation
|
||||||
├── rbac/ # Role-based access control
|
├── rbac/ # Role-based access control
|
||||||
└── monitoring/ # Prometheus, alerts, ServiceMonitors
|
├── monitoring/ # Prometheus, alerts, ServiceMonitors
|
||||||
|
└── pod-security/ # Pod Security Standards (PSA) configuration
|
||||||
```
|
```
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- ✅ Security contexts and pod security standards
|
- ✅ Security contexts and pod security standards
|
||||||
|
- ✅ **Pod Security Admission (PSA)** namespace configurations
|
||||||
- ✅ Resource requests/limits
|
- ✅ Resource requests/limits
|
||||||
- ✅ Liveness/readiness probes
|
- ✅ Liveness/readiness probes
|
||||||
- ✅ Network policies for isolation
|
- ✅ Network policies for isolation
|
||||||
|
|||||||
57
pod-security/README.md
Normal file
57
pod-security/README.md
Normal 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/)
|
||||||
74
pod-security/examples/baseline-deployment.yaml
Normal file
74
pod-security/examples/baseline-deployment.yaml
Normal 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
|
||||||
105
pod-security/examples/restricted-deployment.yaml
Normal file
105
pod-security/examples/restricted-deployment.yaml
Normal 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
|
||||||
52
pod-security/migration/audit-namespaces.sh
Executable file
52
pod-security/migration/audit-namespaces.sh
Executable 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"
|
||||||
30
pod-security/namespaces/baseline.yaml
Normal file
30
pod-security/namespaces/baseline.yaml
Normal 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
|
||||||
25
pod-security/namespaces/privileged.yaml
Normal file
25
pod-security/namespaces/privileged.yaml
Normal 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
|
||||||
34
pod-security/namespaces/restricted.yaml
Normal file
34
pod-security/namespaces/restricted.yaml
Normal 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)
|
||||||
Reference in New Issue
Block a user