rspec/rules/S6471/docker/rule.adoc

180 lines
5.4 KiB
Plaintext
Raw Normal View History

Running containers as a privileged user weakens their runtime security,
allowing any user whose code runs on the container to perform administrative
actions. +
In Linux containers, the privileged user is usually named `root`. In Windows containers, the equivalent is `ContainerAdministrator`.
A malicious user can run code on a system either thanks to actions that could
be deemed legitimate - depending on internal business logic or operational
management shells - or thanks to malicious actions. For example, with arbitrary
code execution after exploiting a service that the container hosts.
Suppose the container is not hardened to prevent using a shell, interpreter, or
https://man7.org/linux/man-pages/man7/capabilities.7.html[Linux capabilities].
In this case, the malicious user can read and exfiltrate any file (including
from Docker volumes), open new network connections, install malicious software,
or, worse, break out of the container's isolation context by exploiting other
components.
Depending on the infrastructure's resilience, attackers may then extend their
attack to other services, such as Kubernetes clusters or cloud providers, in
order to maximize their reach.
== Ask Yourself Whether
This container:
* Serves internet-facing services.
* Does not require a privileged user to run.
There is a risk if you answered yes to any of those questions.
== 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:
[source,docker]
----
# 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:
[source,docker]
----
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:
[source,docker]
----
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:
[source,docker]
----
FROM mcr.microsoft.com/windows/servercore:ltsc2019
RUN net user /add nonroot
USER nonroot
----
If the `scratch` Dockerfile untars a Linux distribution, the "Linux image"
solution should be applied. Else, you have a choice between using a pre-written
`/etc/passwd` file (regardless of the host operating system) or using a
multi-stage build.
[source,docker]
----
FROM scratch
COPY etc_passwd /etc/passwd
# contains "nonroot:x:1337:1337:nonroot:/nonroot:/usr/sbin/nologin"
USER nonroot
COPY production_binary /app
ENTRYPOINT ["/app/production_binary"]
----
or you can use a multi-stage build:
[source,docker]
----
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"]
----
For multi-stage builds:
[source,docker]
----
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"]
----
== See
* https://cwe.mitre.org/data/definitions/284.html[MITRE, CWE-284] - Improper Access Control
* https://hub.docker.com/r/nginxinc/nginx-unprivileged[nginxinc/nginx-unprivileged: Example of a non-root container by default]
* https://learn.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/container-security#when-to-use-containeradmin-and-containeruser-user-accounts[Microsoft docs, When to use ContainerAdmin and ContainerUser user accounts]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
* Noncompliant scratch images: "Scratch images run as root by default. Make sure it is safe here."
* Official noncompliant image: "The `image` image runs with root as the default user. Make sure it is safe here."
* Microsoft non-compliant images: "This image runs with root or containerAdministrator as the default user. Make sure it is safe here."
* Unofficial noncompliant image: "This image might run with root as the default user. Make sure it is safe here."
* The last USER is root or containerAdministrator: "Setting the default user as root might unnecessarily make the application unsafe. Make sure it is safe here."
=== Highlighting
* Unsafe FROM: "FROM name" instruction
* Last USER is root or containerAdministrator: "USER name" instruction
endif::env-github,rspecator-view[]