Skip to main content

Minimize The Admission Root Containers

More Info:

Do not generally permit containers to be run with the allowPrivilegeEscalation flag set to true. Allowing this right can lead to a process running a container getting more rights than it started with.

Risk Level

High

Address

Security

Compliance Standards

  • APRA CPS 234 (Australia)
  • BSI C5 (Germany)
  • Brazil LGPD
  • CCPA / CPRA (California)
  • CIS AKS
  • CIS Critical Security Controls v8
  • CMMC 2.0
  • CSA Cloud Controls Matrix v4
  • DPDPA
  • Digital Operational Resilience Act (EU)
  • Essential 8
  • ISO/IEC 27017
  • ISO/IEC 27018
  • ISO/IEC 27701
  • KSA PDPL
  • MAS Technology Risk Management (Singapore)
  • MITRE ATT&CK (Cloud)
  • NIS2 Directive
  • NIST SP 800-171
  • NYDFS 23 NYCRR 500
  • SWIFT Customer Security Controls Framework
  • Sarbanes-Oxley IT General Controls
  • UK NCSC Cyber Assessment Framework

Triage and Remediation

Remediation

Manual Steps
  1. List namespaces and identify security controls in use
    Run on any machine with kubectl access:

    kubectl get ns --show-labels
    kubectl get psp
    kubectl get psp -o yaml
    kubectl get clusterrole,clusterrolebinding,role,rolebinding --all-namespaces -o yaml

    Review which PodSecurityPolicy (PSP) objects exist and how they are bound (which users/service accounts/namespaces are allowed to use them).

  2. Identify PSPs that allow root containers
    Run on any machine with kubectl access:

    kubectl get psp -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{.spec.runAsUser}{"\n\n"}{end}'

    For each PSP, examine .spec.runAsUser.rule. Flag any PSP where:

    • .spec.runAsUser.rule is RunAsAny, or
    • .spec.runAsUser.rule is MustRunAs and any configured UID range includes 0, or
    • .spec.runAsUser.rule is missing.
  3. Map “unsafe” PSPs to affected namespaces
    From the Role/ClusterRole bindings gathered in step 1, determine which service accounts and namespaces can use each flagged PSP.

    • Look for use verbs on podsecuritypolicies in ClusterRole/Role objects.
    • Trace the associated RoleBinding/ClusterRoleBinding subjects (service accounts, groups) back to namespaces where workloads run.
      Document the namespaces where workloads can be admitted with root UIDs.
  4. Decide on policy for each namespace / workload
    For each affected namespace:

    • Determine whether any workloads must run as root (e.g., low-level system agents).
    • For namespaces that do not require root: plan to enforce non‑root by using a PSP with .spec.runAsUser.rule: MustRunAsNonRoot or .spec.runAsUser.rule: MustRunAs with UID ranges excluding 0.
    • For exceptional workloads that require root: isolate them into dedicated namespaces and limit access to the more‑permissive PSP to only those specific service accounts.
  5. Adjust PSPs and bindings to minimize root containers
    Using manifests and kubectl on any machine with kubectl access:

    • Create or update PSPs so that general‑purpose PSPs used by normal workloads have:
      spec:
      runAsUser:
      rule: MustRunAsNonRoot
      or
      spec:
      runAsUser:
      rule: MustRunAs
      ranges:
      - min: 1000
      max: 65535
    • Update Role/ClusterRole and corresponding bindings so that:
      • Non‑root‑enforcing PSPs are bound only to namespaces/service accounts that truly require them.
      • All other namespaces use PSPs that forbid UID 0 according to the rule above.
        Apply your edited manifests with:
    kubectl apply -f <file>.yaml
  6. Re‑verify and spot‑check running workloads
    Run on any machine with kubectl access:

    kubectl get psp -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{.spec.runAsUser}{"\n\n"}{end}'

    Confirm that commonly used PSPs now have .spec.runAsUser.rule set to MustRunAsNonRoot or MustRunAs with ranges excluding 0.
    Optionally, spot‑check running pods:

    kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{" "}{.metadata.name}{" "}{.spec.securityContext.runAsUser}{"\n"}{end}'

    and verify that, where enforced, new pods are not admitted with UID 0 unless explicitly allowed by your exception policy.

Using kubectl
# 1) List all namespaces
# Run on: any machine with kubectl access
kubectl get namespaces -o name

Review each relevant namespace for defaulting/forcing root or allowing escalation via PodSecurity admission labels and SecurityContextDefaults (if used).

# 2) Check PodSecurity labels on each namespace
# Replace <namespace> with a namespace from the previous output
kubectl get ns <namespace> -o jsonpath='{.metadata.name}{"\t"}{.metadata.labels}{"\n"}'

Problem indication:

  • Missing pod-security.kubernetes.io/enforce or pod-security.kubernetes.io/warn labels.
  • Labels set to privileged or baseline in namespaces that should block root/escalation, instead of restricted.
    • restricted is the profile that enforces non-root and disallows privilege escalation by default.

If you use SecurityContextDefaults (alpha feature) via the PodSecurity admission plugin, review them:

# 3) Check for SecurityContextDefaults configuration via validating admission configuration (if accessible)
# This requires access to the API server config in the cluster config namespace, when present
kubectl get configmap -n kube-system admission-configuration -o yaml

Problem indication:

  • Any default securityContext (pod or container) that:
    • Sets runAsUser: 0 or runAsNonRoot: false.
    • Sets allowPrivilegeEscalation: true or leaves it unset while allowing privileged: true.

Next, inspect real workloads for evidence that namespaces are effectively allowing root or privilege escalation.

# 4) List all pods in a namespace with their service accounts
kubectl get pods -n <namespace> -o wide

Then, for a sample of pods in each namespace:

# 5) Inspect pod-level and container-level securityContext
kubectl get pod <pod-name> -n <namespace> -o yaml

Problem indication in the spec.securityContext or containers[].securityContext / initContainers[].securityContext:

  • runAsUser: 0
  • runAsNonRoot: false or omitted with images known to run as root.
  • allowPrivilegeEscalation: true (explicit).
  • privileged: true, or capabilities adding powerful capabilities (e.g., ALL, NET_ADMIN, SYS_ADMIN) combined with missing allowPrivilegeEscalation: false.

If your cluster still uses PodSecurityPolicy (legacy), review PSPs bound to the namespace:

# 6) List PSPs
kubectl get podsecuritypolicies.policy

# 7) Describe a PSP used in the namespace (replace <psp-name>)
kubectl describe psp <psp-name>

Problem indication in PSP .spec:

  • .spec.runAsUser.rule is RunAsAny or allows UID 0 (either directly or in ranges).
  • .spec.allowPrivilegeEscalation is true or unset while other fields permit privilege escalation (e.g., privileged: true, broad capabilities).

These kubectl commands only surface the current state. A human must decide whether any namespace should permit root or privilege escalation (for example, dedicated “admin/ops” namespaces may intentionally allow it) and then adjust policies/manifests accordingly.

Automation
#!/usr/bin/env bash
# Report namespaces that allow root or privileged containers, and pods using them.
# Run on any machine with kubectl access and current context set.

set -euo pipefail

echo "=== 1) Namespaces and their PodSecurity / PodSecurityPolicy configuration ==="

echo
echo "--- PodSecurity labels (admission plugin) per namespace ---"
kubectl get ns --show-labels | sed 's/,/\n /g'

echo
echo "Explanation:"
echo " - Look for namespaces WITHOUT any of these labels:"
echo " pod-security.kubernetes.io/enforce"
echo " pod-security.kubernetes.io/warn"
echo " pod-security.kubernetes.io/audit"
echo " - Also review namespaces where the enforce level is 'privileged' or 'baseline'."
echo " For this specific finding, namespaces NOT at 'restricted' are more likely"
echo " to allow root containers and privilege escalation."

echo
echo "--- PodSecurityPolicies (if PSP is enabled) and their runAsUser rules ---"
kubectl get psp -o json | jq -r '
.items[]
| [
.metadata.name,
( .spec.runAsUser.rule // "NONE" ),
( ( .spec.runAsUser.ranges // [] )
| map("\(.min)-\(.max)") | join(",") )
]
| @tsv
' 2>/dev/null | \
awk 'BEGIN {
OFS="\t"
print "PSP_NAME", "runAsUser.rule", "UID_RANGES"
} { print }'

echo
echo "Explanation:"
echo " - Problematic PSPs for this control:"
echo " * runAsUser.rule is RunAsAny, or"
echo " * runAsUser.rule is MustRunAs and the ranges include 0, or"
echo " * runAsUser.rule is RunAsAny and there are no ranges defined."
echo " - Desired for this control:"
echo " * runAsUser.rule = MustRunAsNonRoot, OR"
echo " * runAsUser.rule = MustRunAs with UID ranges that DO NOT include 0."

echo
echo "=== 2) Pods and containers actually running as root or allowing privilege escalation ==="

echo
echo "--- Pods with containers that explicitly run as root (runAsUser: 0) ---"
kubectl get pods -A -o json | jq -r '
.items[]
| . as $pod
| [
$pod.metadata.namespace,
$pod.metadata.name,
"container",
(.spec.securityContext.runAsUser // empty),
( [ $pod.spec.containers[]?
| select( .securityContext.runAsUser == 0 )
| .name ] | join(",") )
]
| select(.[4] != "")
| @tsv
' 2>/dev/null | \
awk 'BEGIN {
OFS="\t"
print "NAMESPACE","POD","TYPE","pod.runAsUser","containers_with_runAsUser_0"
} { print }'

echo
echo "--- Pods with containers that may run as root (no runAsNonRoot + no nonroot runAsUser) ---"
kubectl get pods -A -o json | jq -r '
.items[]
| . as $pod
| $pod.spec.containers[]
| . as $c
| {
ns: $pod.metadata.namespace,
pod: $pod.metadata.name,
ctn: $c.name,
podRunAsNonRoot: ($pod.spec.securityContext.runAsNonRoot // "unset"),
ctnRunAsNonRoot: ($c.securityContext.runAsNonRoot // "unset"),
podRunAsUser: ($pod.spec.securityContext.runAsUser // "unset"),
ctnRunAsUser: ($c.securityContext.runAsUser // "unset")
}
| select(
# No explicit runAsNonRoot true at pod or container level
( .podRunAsNonRoot != true and .ctnRunAsNonRoot != true )
# And neither pod nor container specifies a non-zero runAsUser
and ( .podRunAsUser == "unset" or .podRunAsUser == 0 )
and ( .ctnRunAsUser == "unset" or .ctnRunAsUser == 0 )
)
| [ .ns, .pod, .ctn, .podRunAsNonRoot, .ctnRunAsNonRoot, .podRunAsUser, .ctnRunAsUser ]
| @tsv
' 2>/dev/null | \
awk 'BEGIN {
OFS="\t"
print "NAMESPACE","POD","CONTAINER","pod.runAsNonRoot","ctn.runAsNonRoot","pod.runAsUser","ctn.runAsUser"
} { print }'

echo
echo "Explanation:"
echo " - Any row here indicates a container that COULD run as root (nothing forces non-root)."
echo " - You must review whether that is intentional for that workload."

echo
echo "--- Pods where containers allow privilege escalation (allowPrivilegeEscalation != false) ---"
kubectl get pods -A -o json | jq -r '
.items[]
| . as $pod
| $pod.spec.containers[]
| {
ns: $pod.metadata.namespace,
pod: $pod.metadata.name,
ctn: .name,
allow: (.securityContext.allowPrivilegeEscalation // "unset")
}
| select(.allow != false)
| [ .ns, .pod, .ctn, .allow ]
| @tsv
' 2>/dev/null | \
awk 'BEGIN {
OFS="\t"
print "NAMESPACE","POD","CONTAINER","allowPrivilegeEscalation"
} { print }'

echo
echo "Explanation:"
echo " - Rows where allowPrivilegeEscalation is 'true' or 'unset' are potential problems."
echo " - For this finding, you should aim for allowPrivilegeEscalation=false on all"
echo " non-exempt containers, enforced via PSP / PodSecurity admission / policies."

echo
echo "=== 3) How to interpret problems for this CIS control ==="
echo "Problem indicators (to be manually reviewed):"
echo " - Namespaces:"
echo " * Missing PodSecurity labels, or"
echo " * enforce level not set to 'restricted' when you expect strict isolation."
echo " - PSPs (if used):"
echo " * runAsUser.rule != MustRunAsNonRoot, and"
echo " * UID ranges (for MustRunAs) include 0."
echo " - Workloads:"
echo " * Containers listed as explicitly runAsUser: 0."
echo " * Containers that can run as root (no runAsNonRoot true and no non-zero runAsUser)."
echo " * Containers with allowPrivilegeEscalation != false."
echo
echo "Use this report to decide where to tighten PodSecurity labels/PSPs and update"
echo "pod specs. There is no one-shot automated fix; you must confirm which workloads"
echo "are allowed to run as root or with privilege escalation before changing policies."

Additional Reading: