Skip to main content

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

Manual Steps
  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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:

  1. 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'
  1. Label each noncompliant namespace to enforce baseline (example for dev, 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
  1. (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
  1. 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'