Recommended Secure Coding Practices
In the Dockerfile:
Create a new default user and use it with the USER statement.
Some container maintainers create a specific user to be used without explicitly setting it as default, such as postgresql or zookeeper. It is recommended to use these users instead of root.
On Windows containers, the ContainerUser is available for this purpose.
Or, at launch time:
Use the user argument when calling Docker or in the docker-compose file.
Add fine-grained Linux capabilities to perform specific actions that require root privileges.
If this image is already explicitly set to launch with a non-privileged user, you can add it to the safe images list rule property of your SonarQube instance, without the tag.
Sensitive Code Example
For any image that does not provide a user by default, regardless of their underlying operating system:
# Sensitive
FROM alpine
ENTRYPOINT ["id"]
For multi-stage builds, the last stage is non-compliant if it does not contain the USER instruction with a non-root user:
FROM alpine AS builder
COPY Makefile ./src /
RUN make build
USER nonroot
# Sensitive, previous user settings are dropped
FROM alpine AS runtime
COPY --from=builder bin/production /app
ENTRYPOINT ["/app/production"]
Compliant Solution
For Linux-based images and scratch-based images that untar a Linux distribution:
FROM alpine
RUN addgroup -S nonroot \
&& adduser -S nonroot -G nonroot
USER nonroot
ENTRYPOINT ["id"]
For Windows-based images, you can use ContainerUser or create a new user:
FROM mcr.microsoft.com/windows/servercore:ltsc2019
RUN net user /add nonroot
USER nonroot
For multi-stage builds, the non-root user should be on the last stage:
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"]
For images that use scratch as their base, it is not possible to add non-privileged users by default. To do this, add an additional build stage to add the group and user, and later copy /etc/passwd.
Here is an example that uses adduser in the first stage to generate a user and add it to the /etc/passwd file. In the next stage, this user is added by copying that file over from the previous 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"]
Recommended Secure Coding Practices
In the Dockerfile:
Create a new default user and use it with the USER statement.
Some container maintainers create a specific user to be used without explicitly setting it as default, such as postgresql or zookeeper. It is recommended to use these users instead of root.
On Windows containers, the ContainerUser is available for this purpose.
Or, at launch time:
Use the user argument when calling Docker or in the docker-compose file.
Add fine-grained Linux capabilities to perform specific actions that require root privileges.
If this image is already explicitly set to launch with a non-privileged user, you can add it to the safe images list rule property of your SonarQube instance, without the tag.
Sensitive Code Example
For any image that does not provide a user by default, regardless of their underlying operating system:
For multi-stage builds, the last stage is non-compliant if it does not contain the USER instruction with a non-root user:
Compliant Solution
For Linux-based images and scratch-based images that untar a Linux distribution:
For Windows-based images, you can use ContainerUser or create a new user:
For multi-stage builds, the non-root user should be on the last stage:
For images that use scratch as their base, it is not possible to add non-privileged users by default. To do this, add an additional build stage to add the group and user, and later copy /etc/passwd.
Here is an example that uses adduser in the first stage to generate a user and add it to the /etc/passwd file. In the next stage, this user is added by copying that file over from the previous stage: