Namespaces Should Enforce Pod Security Admission Baseline
More Info:
Verifies each namespace is labeled with pod-security.kubernetes.io/enforce set to baseline or restricted so the built-in Pod Security Admission controller rejects unsafe pods.
Risk Level
High
Address
Security
Compliance Standards
- Cloudanix Best Practice
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
On any machine with kubectl access, list all namespaces and identify those without an enforce label set to baseline or restricted (excluding default system namespaces):
kubectl get ns --show-labels -
For each non-compliant namespace you want at baseline, add or update the enforce label:
kubectl label namespace <NAMESPACE> pod-security.kubernetes.io/enforce=baseline --overwrite -
For each namespace you want at restricted (stricter than baseline), add or update the enforce label:
kubectl label namespace <NAMESPACE> pod-security.kubernetes.io/enforce=restricted --overwrite -
Optionally protect against accidental downgrade by setting the audit and warn levels to match the enforce level (example for restricted):
kubectl label namespace <NAMESPACE> \pod-security.kubernetes.io/audit=restricted \pod-security.kubernetes.io/warn=restricted \--overwrite -
If you manage namespaces via manifests or GitOps, mirror the change by adding a labels block like this to each Namespace manifest you updated, then apply with kubectl apply -f:
metadata:name: <NAMESPACE>labels:pod-security.kubernetes.io/enforce: baseline -
Verification (on any machine with kubectl access): run the original audit and confirm that all non-system namespaces show enforce=baseline or enforce=restricted and is_compliant=true:
kubectl get namespaces -o json | jq -r '[ .items[]| select(.metadata.name as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)| .metadata as $m| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels| (($m.labels // {})["pod-security.kubernetes.io/enforce"] // "") as $lvl| "kind=Namespace name=\($m.name) uid=\($m.uid) apiVersion=v1"+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)+ (if $labels == "" then "" else " labels=\($labels)" end)+ " enforce=\(if $lvl == "" then "none" else $lvl end)"+ " is_compliant=\(if ($lvl == "baseline" or $lvl == "restricted") then "true" else "false" end)"] as $rows| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
Using kubectl
On any machine with kubectl access:
- Identify noncompliant namespaces (excluding system namespaces):
kubectl get namespaces -o json | jq -r '
.items[]
| select(.metadata.name as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| select((.metadata.labels["pod-security.kubernetes.io/enforce"] // "") as $lvl | ($lvl != "baseline" and $lvl != "restricted"))
| .metadata.name'
- Label each noncompliant namespace to enforce
baseline(example fordev,test,prod—adjust names as needed):
kubectl label namespace dev pod-security.kubernetes.io/enforce=baseline --overwrite
kubectl label namespace test pod-security.kubernetes.io/enforce=baseline --overwrite
kubectl label namespace prod pod-security.kubernetes.io/enforce=baseline --overwrite
If you prefer restricted for a namespace, use:
kubectl label namespace secure pod-security.kubernetes.io/enforce=restricted --overwrite
- (Optional, declarative) Create or update namespace manifests to include the label, then apply:
dev-namespace.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: dev
labels:
pod-security.kubernetes.io/enforce: baseline
Apply:
kubectl apply -f dev-namespace.yaml
- Verification (same as audit, run on any machine with kubectl):
kubectl get namespaces -o json | jq -r '
[ .items[]
| select(.metadata.name as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| .metadata as $m
| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
| (($m.labels // {})["pod-security.kubernetes.io/enforce"] // "") as $lvl
| "kind=Namespace name=\($m.name) uid=\($m.uid) apiVersion=v1"
+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
+ (if $labels == "" then "" else " labels=\($labels)" end)
+ " enforce=\(if $lvl == "" then "none" else $lvl end)"
+ " is_compliant=\(if ($lvl == "baseline" or $lvl == "restricted") then "true" else "false" end)"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
Automation
#!/usr/bin/env bash
set -euo pipefail
# This script:
# - Finds all namespaces except kube-system, kube-public, kube-node-lease
# - Labels them with pod-security.kubernetes.io/enforce=baseline if they are not already
# set to "baseline" or "restricted"
# - Prints the final enforcement level per namespace
#
# Run on: any machine with kubectl access to the cluster
# Requirements: kubectl, jq in PATH, current context pointing at target cluster
# Fail fast if required tools are missing
command -v kubectl >/dev/null 2>&1 || {
echo "kubectl not found in PATH" >&2
exit 1
}
command -v jq >/dev/null 2>&1 || {
echo "jq not found in PATH" >&2
exit 1
}
echo "Discovering non-excluded namespaces..."
mapfile -t NAMESPACES < <(
kubectl get namespaces -o json \
| jq -r '.items[]
| select(.metadata.name as $n
| ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| .metadata.name'
)
if [ "${#NAMESPACES[@]}" -eq 0 ]; then
echo "No namespaces found to process."
else
echo "Namespaces to process: ${NAMESPACES[*]}"
fi
# Apply label where needed
for ns in "${NAMESPACES[@]}"; do
current_lvl="$(
kubectl get namespace "$ns" -o json \
| jq -r '.metadata.labels["pod-security.kubernetes.io/enforce"] // "none"'
)"
case "$current_lvl" in
baseline|restricted)
echo "Namespace '$ns' already compliant (enforce=$current_lvl), skipping."
;;
*)
echo "Labeling namespace '$ns' with pod-security.kubernetes.io/enforce=baseline"
kubectl label namespace "$ns" \
"pod-security.kubernetes.io/enforce=baseline" \
--overwrite
;;
esac
done
echo
echo "Verification (should show enforce=baseline or enforce=restricted and is_compliant=true):"
kubectl get namespaces -o json | jq -r '
[ .items[]
| select(.metadata.name as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| .metadata as $m
| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
| (($m.labels // {})["pod-security.kubernetes.io/enforce"] // "") as $lvl
| "kind=Namespace name=\($m.name) uid=\($m.uid) apiVersion=v1"
+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
+ (if $labels == "" then "" else " labels=\($labels)" end)
+ " enforce=\(if $lvl == "" then "none" else $lvl end)"
+ " is_compliant=\(if ($lvl == "baseline" or $lvl == "restricted") then "true" else "false" end)"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'