Skip to main content
security · kubernetes

Containers running as UID 0

resource types
3
rule IDs covered
9
severity
high

What does ZopNight detect here?

A container without runAsNonRoot runs as UID 0 inside the container by default. That is not root on the node, but it removes a meaningful barrier: a container escape starts from root rather than an unprivileged user. ZopNight checks 3 workload kinds.

Signal and threshold

How ZopNight evaluates Containers running as UID 0.
Field Value
Rule IDsRC-1723 · RC-1823 · RC-1923 · RC-1724 · RC-1824 · RC-1924 · RC-1725 · RC-1825 · RC-1925
Categorysecurity
Severityhigh
Metricnone — pure configuration read
Sourceinternal/rules/k8s/container_root.go

Root in a container is not root on the node

Worth stating precisely, because this finding is often over- and under-stated.

UID 0 inside a container is namespaced. Without privileged mode or added capabilities, that process still cannot touch the host directly. The container boundary holds.

What it changes is what happens after a boundary failure. A container-escape vulnerability exploited from UID 0 lands you as root. The same escape from UID 1000 lands you as an unprivileged user who then needs a second privilege-escalation step.

It is defence in depth rather than a boundary in itself, which is why this is worth fixing broadly and rarely worth an emergency.

Why so many images default to root

Because building them that way is easier. Installing packages needs root, binding to ports below 1024 needs root or NET_BIND_SERVICE, and writing to paths created during build needs matching ownership.

Many official images still run as root unless you override it, so a workload can be running as UID 0 without anyone having chosen that.

The three settings, and the one that actually enforces

Terminal window
securityContext:
runAsNonRoot: true # admission REJECTS the pod if the image would run as UID 0
runAsUser: 1000 # sets the UID, but an image can be built to ignore intent
allowPrivilegeEscalation: false

runAsNonRoot: true is the enforcing one, failing closed at admission rather than trusting the manifest. Setting runAsUser alone is a request, not a guarantee.

What breaks when you switch

Two things, predictably: a process binding to port 80 or 443, and a container writing to a path owned by root. Fix the first by binding above 1024 and letting the Service map the port; fix the second with fsGroup or by correcting ownership in the image.

Testing runAsNonRoot on the pod securityContext

Terminal window
kubectl get deploy,statefulset,daemonset -A -o json | jq -r '
.items[] | . as $w | .spec.template.spec
| select((.securityContext.runAsNonRoot // false) == false)
| "\($w.kind) \($w.metadata.namespace)/\($w.metadata.name)"'

Enforcing it

Pod Security Admission at restricted requires runAsNonRoot. Apply it to new namespaces first. Retrofitting it to a namespace full of root-running workloads blocks deploys until every image is fixed.

See it fire on your bill.

Connect an account read-only. The first findings land in minutes.

417 rule families across 353 resource types on 22 platforms. Every threshold, metric, and IAM action is documented on these pages before you grant anything.

417 rule families documented
353 resource types covered
read-only default access level
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·