From 58e8140f363f4cf06bb92cdcd0871abeac2eca1a Mon Sep 17 00:00:00 2001 From: Greg Hendrickson Date: Fri, 6 Feb 2026 18:02:00 +0000 Subject: [PATCH] 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/ --- README.md | 4 +- pod-security/README.md | 57 ++++++++++ .../examples/baseline-deployment.yaml | 74 ++++++++++++ .../examples/restricted-deployment.yaml | 105 ++++++++++++++++++ pod-security/migration/audit-namespaces.sh | 52 +++++++++ pod-security/namespaces/baseline.yaml | 30 +++++ pod-security/namespaces/privileged.yaml | 25 +++++ pod-security/namespaces/restricted.yaml | 34 ++++++ 8 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 pod-security/README.md create mode 100644 pod-security/examples/baseline-deployment.yaml create mode 100644 pod-security/examples/restricted-deployment.yaml create mode 100755 pod-security/migration/audit-namespaces.sh create mode 100644 pod-security/namespaces/baseline.yaml create mode 100644 pod-security/namespaces/privileged.yaml create mode 100644 pod-security/namespaces/restricted.yaml diff --git a/README.md b/README.md index 91958af..a9282c2 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,14 @@ Production-ready Kubernetes manifests with security best practices, resource lim ├── secrets/ # Secret management patterns ├── networkpolicies/ # Network isolation ├── rbac/ # Role-based access control -└── monitoring/ # Prometheus, alerts, ServiceMonitors +├── monitoring/ # Prometheus, alerts, ServiceMonitors +└── pod-security/ # Pod Security Standards (PSA) configuration ``` ## Features - ✅ Security contexts and pod security standards +- ✅ **Pod Security Admission (PSA)** namespace configurations - ✅ Resource requests/limits - ✅ Liveness/readiness probes - ✅ Network policies for isolation diff --git a/pod-security/README.md b/pod-security/README.md new file mode 100644 index 0000000..c2a2741 --- /dev/null +++ b/pod-security/README.md @@ -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/) diff --git a/pod-security/examples/baseline-deployment.yaml b/pod-security/examples/baseline-deployment.yaml new file mode 100644 index 0000000..e66b37d --- /dev/null +++ b/pod-security/examples/baseline-deployment.yaml @@ -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 diff --git a/pod-security/examples/restricted-deployment.yaml b/pod-security/examples/restricted-deployment.yaml new file mode 100644 index 0000000..61d9e5d --- /dev/null +++ b/pod-security/examples/restricted-deployment.yaml @@ -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 diff --git a/pod-security/migration/audit-namespaces.sh b/pod-security/migration/audit-namespaces.sh new file mode 100755 index 0000000..dc96e6b --- /dev/null +++ b/pod-security/migration/audit-namespaces.sh @@ -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 \\" +echo " pod-security.kubernetes.io/enforce=restricted \\" +echo " pod-security.kubernetes.io/audit=restricted \\" +echo " pod-security.kubernetes.io/warn=restricted \\" +echo " --overwrite" diff --git a/pod-security/namespaces/baseline.yaml b/pod-security/namespaces/baseline.yaml new file mode 100644 index 0000000..aae6945 --- /dev/null +++ b/pod-security/namespaces/baseline.yaml @@ -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 diff --git a/pod-security/namespaces/privileged.yaml b/pod-security/namespaces/privileged.yaml new file mode 100644 index 0000000..0402fb9 --- /dev/null +++ b/pod-security/namespaces/privileged.yaml @@ -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 diff --git a/pod-security/namespaces/restricted.yaml b/pod-security/namespaces/restricted.yaml new file mode 100644 index 0000000..cd6f724 --- /dev/null +++ b/pod-security/namespaces/restricted.yaml @@ -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)