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'