Docker images run as root by default unless a non-privileged user is explicitly set using the USER instruction.

Why is this an issue?

Running containers as a privileged user weakens their runtime security, allowing any code running inside the container to perform administrative actions. In Linux containers, the privileged user is root. In Windows containers, the equivalent is ContainerAdministrator.

If an attacker achieves code execution inside a container — for example, by exploiting a vulnerability in a hosted service — they inherit the container’s user privileges. With elevated privileges and access to a shell, interpreter, or Linux capabilities, the attacker can read and exfiltrate files including Docker volumes, open new network connections, and install malicious software.

What is the potential impact?

Container escape and lateral movement

An attacker running as root inside a container can exploit other vulnerable components to break out of the container’s isolation context. This may give them access to the host system and, from there, to other services in the infrastructure such as Kubernetes clusters or cloud environments.

Data exfiltration

With root access inside the container, an attacker can read any file accessible to it, including mounted Docker volumes. This puts sensitive infrastructure configuration, intellectual property, and personal data at risk.

How to fix it

In the Dockerfile, create a dedicated non-privileged user and switch to it with the USER instruction. Some official images already provide a non-root user — for example, postgresql or zookeeper — and it is recommended to use these instead of root. On Windows containers, the built-in ContainerUser account is available for this purpose.

As an alternative, a non-root user can be specified at launch time using the --user argument in docker run or the user field in a Docker Compose file. For cases where specific privileged operations are required, consider adding fine-grained Linux capabilities instead of running as root.

If an image is already configured to run as a non-privileged user by default, it can be added to the safe images list in the SonarQube rule configuration to suppress the issue.

Code examples

Noncompliant code example

# Noncompliant
FROM alpine

ENTRYPOINT ["id"]

Compliant solution

FROM alpine

RUN addgroup -S nonroot \
    && adduser -S nonroot -G nonroot

USER nonroot

ENTRYPOINT ["id"]

For Windows-based images, use the built-in ContainerUser account or create a dedicated user:

FROM mcr.microsoft.com/windows/servercore:ltsc2019

RUN net user /add nonroot

USER nonroot

For multi-stage builds, the USER instruction must be present in the final stage. User settings from earlier build stages are not carried over:

FROM alpine as builder
COPY Makefile ./src /
RUN make build

FROM alpine as runtime
RUN addgroup -S nonroot \
    && adduser -S nonroot -G nonroot
COPY --from=builder bin/production /app
USER nonroot
ENTRYPOINT ["/app/production"]

scratch-based images cannot create users directly. Instead, create the user in a prior stage and copy /etc/passwd into the final stage:

FROM alpine:latest as security_provider
RUN addgroup -S nonroot \
    && adduser -S nonroot -G nonroot

FROM scratch as production
COPY --from=security_provider /etc/passwd /etc/passwd
USER nonroot
COPY production_binary /app
ENTRYPOINT ["/app/production_binary"]

Resources

Documentation

Articles & blog posts

Standards