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
Remediation
Manual Steps
-
List namespaces and identify security controls in use
Run on any machine with kubectl access:kubectl get ns --show-labelskubectl get pspkubectl get psp -o yamlkubectl get clusterrole,clusterrolebinding,role,rolebinding --all-namespaces -o yamlReview which PodSecurityPolicy (PSP) objects exist and how they are bound (which users/service accounts/namespaces are allowed to use them).
-
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.ruleisRunAsAny, or.spec.runAsUser.ruleisMustRunAsand any configured UID range includes0, or.spec.runAsUser.ruleis missing.
-
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
useverbs onpodsecuritypoliciesinClusterRole/Roleobjects. - Trace the associated
RoleBinding/ClusterRoleBindingsubjects (service accounts, groups) back to namespaces where workloads run.
Document the namespaces where workloads can be admitted with root UIDs.
- Look for
-
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: MustRunAsNonRootor.spec.runAsUser.rule: MustRunAswith UID ranges excluding0. - 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.
-
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:
orspec:runAsUser:rule: MustRunAsNonRootspec:runAsUser:rule: MustRunAsranges:- min: 1000max: 65535
- Update
Role/ClusterRoleand 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 - Create or update PSPs so that general‑purpose PSPs used by normal workloads have:
-
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.ruleset toMustRunAsNonRootorMustRunAswith ranges excluding0.
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/enforceorpod-security.kubernetes.io/warnlabels. - Labels set to
privilegedorbaselinein namespaces that should block root/escalation, instead ofrestricted.restrictedis 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: 0orrunAsNonRoot: false. - Sets
allowPrivilegeEscalation: trueor leaves it unset while allowingprivileged: true.
- Sets
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: 0runAsNonRoot: falseor omitted with images known to run as root.allowPrivilegeEscalation: true(explicit).privileged: true, orcapabilitiesadding powerful capabilities (e.g.,ALL,NET_ADMIN,SYS_ADMIN) combined with missingallowPrivilegeEscalation: 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.ruleisRunAsAnyor allows UID0(either directly or in ranges)..spec.allowPrivilegeEscalationistrueor unset while other fields permit privilege escalation (e.g.,privileged: true, broadcapabilities).
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."