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'