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 missing an enforce label or using an incorrect value (ignore kube-system, kube-public, kube-node-lease if desired):

    kubectl get namespaces --show-labels
  2. For a single noncompliant namespace, set the Pod Security Admission enforce label to baseline (or restricted if your policy requires that) using kubectl:

    kubectl label namespace my-namespace pod-security.kubernetes.io/enforce=baseline --overwrite

    Replace my-namespace with the actual namespace name.

  3. If you want to update all noncompliant namespaces at once to baseline, run:

    for ns in $(kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' \
    | grep -Ev '^(kube-system|kube-public|kube-node-lease)$'); do
    lvl=$(kubectl get ns "$ns" -o jsonpath='{.metadata.labels.pod-security\.kubernetes\.io/enforce}' 2>/dev/null || true)
    if [ "$lvl" != "baseline" ] && [ "$lvl" != "restricted" ]; then
    kubectl label namespace "$ns" pod-security.kubernetes.io/enforce=baseline --overwrite
    fi
    done
  4. For namespaces where you want restricted instead of baseline, label them explicitly:

    kubectl label namespace prod-namespace pod-security.kubernetes.io/enforce=restricted --overwrite
  5. (Optional) If you manage namespaces via manifests or GitOps, ensure each Namespace manifest includes the enforce label so changes persist:

    apiVersion: v1
    kind: Namespace
    metadata:
    name: my-namespace
    labels:
    pod-security.kubernetes.io/enforce: baseline
  6. Verification (on any machine with kubectl access): rerun the audit command and confirm all relevant namespaces report 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 (same as audit):
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'
  1. Patch a specific namespace to enforce baseline (example: dev-namespace):
kubectl label namespace dev-namespace pod-security.kubernetes.io/enforce=baseline --overwrite

To enforce restricted instead:

kubectl label namespace dev-namespace pod-security.kubernetes.io/enforce=restricted --overwrite
  1. Declarative manifest example (preferred for GitOps/IaC):

Create namespace-dev-namespace.yaml:

apiVersion: v1
kind: Namespace
metadata:
name: dev-namespace
labels:
pod-security.kubernetes.io/enforce: baseline

Apply it:

kubectl apply -f namespace-dev-namespace.yaml
  1. Bulk label all existing non-system namespaces to baseline:
for ns in $(kubectl get ns --no-headers | awk '{print $1}' | grep -vE '^(kube-system|kube-public|kube-node-lease)$'); do
kubectl label namespace "$ns" pod-security.kubernetes.io/enforce=baseline --overwrite
done
  1. Verification (re-run compliance-style check):
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
#
# Enforce Pod Security Admission baseline (or keep restricted) on all namespaces
# except kube-system, kube-public, kube-node-lease.
#
# Requirements:
# - Run on any machine with kubectl access to the cluster.
# - kubectl must be configured to talk to the target EKS cluster.
#
# Behavior:
# - Namespaces with no enforce label get "baseline".
# - Namespaces already set to "baseline" or "restricted" are left as-is.
# - System namespaces kube-system, kube-public, kube-node-lease are skipped.
# - Safe to re-run (idempotent).

set -euo pipefail

echo "Discovering namespaces that need Pod Security Admission enforcement..."

# Get all non-system namespaces and their current enforce label
mapfile -t NS_INFO < <(
kubectl get ns -o jsonpath='{range .items[?(@.metadata.name!="kube-system" && @.metadata.name!="kube-public" && @.metadata.name!="kube-node-lease")]}{.metadata.name}{" "}{.metadata.labels.pod-security\.kubernetes\.io/enforce}{"\n"}{end}'
)

if [ "${#NS_INFO[@]}" -eq 0 ]; then
echo "No non-system namespaces found."
fi

for line in "${NS_INFO[@]}"; do
ns_name="$(echo "$line" | awk '{print $1}')"
enforce_val="$(echo "$line" | awk '{print $2}')"

# Normalize empty to "none"
if [ -z "${enforce_val:-}" ]; then
enforce_val="none"
fi

case "$enforce_val" in
restricted)
echo "Namespace ${ns_name}: already 'restricted', leaving unchanged."
;;
baseline)
echo "Namespace ${ns_name}: already 'baseline', leaving unchanged."
;;
*)
echo "Namespace ${ns_name}: enforce='${enforce_val}', setting to 'baseline'."
kubectl label namespace "${ns_name}" \
pod-security.kubernetes.io/enforce=baseline \
--overwrite
;;
esac
done

echo
echo "Verification (should show enforce=baseline or enforce=restricted for all non-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)
| .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'