Docker - is it safe to switch to non-root user in ENTRYPOINT?
Asked Answered
S

2

9

Is it considered a secure practice to run root privileged ENTRYPOINT ["/bin/sh", entrypoint.sh"], that later switches to non-root user before running the application?


More context:

There are a number of articles (1, 2, 3) suggesting that running the container as non-root user is a best practice in terms of security. This can be achieved using the USER appuser command, however there are cases (4, 5) when running the container as root and only switching to non-root in the an entrypoint.sh script is the only way to go around, eg:

#!/bin/sh

chown -R appuser:appgroup /path/to/volume
exec runuser -u appuser "$@"

and in Dockerfile:

COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/bin/sh", "entrypoint.sh"]
CMD ["/usr/bin/myapp"]

When calling docker top container I can see two processes, one root and one non-root

PID                 USER                TIME                COMMAND
5004                root                0:00                runuser -u appuser /usr/bin/myapp
5043                1000                0:02                /usr/bin/myapp

Does it mean my container is running with a vulnerability given that root process, or is it considered secure?

I found little discussion on the subject (6, 7) and none seem definitive. I've looked for similar questions on StackOverflow but couldn't find anything related (8, 9, 10) that would address the security.

Sharpnosed answered 5/1, 2021 at 6:49 Comment(2)
"Start as root then drop privileges" is a pretty routine Unix pattern; I know the Consul image works the way you describe, for one example. Interestingly this copy of runuser(1) suggests using setpriv(1) for this use case, and that man page in turn documents itself as using execve(2) (so it should not leave a process behind).Therapeutic
Do I understand well that using the commands you outlined it is possible to exit that root process upon switching to unprivileged process?Sharpnosed
G
9

I just looked through what relevant literature (Adrian Mouat's Docker, Liz Rice's Container Security) has to say on the topic and added my own thoughts to it:

The main intention behind the much cited best practice to run containers as non-root is to avoid container breakouts via vulnerabilities in the application code. Naturally, if your application runs as root and then your container has access to the host, e.g. via a bind mount volume, a container breakout is possible. Likewise, if your application has rights to execute system libraries with vulnerabilities on your container file system, a denial of service attack looms.

Against these risks you are protected with your approach of using runuser, since your application would not have rights on the host's root file system. Similarly, your application could not be abused to call system libraries on the container file system or even execute system calls on the host kernel.

However, if somebody attaches to your container with exec, he would be root, since the container main process belongs to root. This might become an issue on systems with elaborate access right concepts like Kubernetes. Here, certain user groups might be granted a read-only view of the cluster including the right to exec into containers. Then, as root, they will have more rights than necessary, including possible rights on the host.

In conclusion, I don't have strong security concerns regarding your approach, since it mitigates the risk of attacks via application vulnerabilities by running the application as non-root. The fact that you run to container main process as root, I see as a minor disadvantage that only creates problems in niche access control setups, where not fully trusted subjects get read-only access to your system.

Grison answered 5/1, 2021 at 10:12 Comment(4)
Great answer! You gave a pretty good overview of the two sides, although I wonder - what's the verdict? Is somebody attaching to my container with exec a real risk on a cloud platform such as AWS or GCP?Sharpnosed
Being level headed about it, I would say the read-only K8s users scenario is fairly constructed. Therefore, you approach is fine, since it addresses the real issue: container break outs and denial of service attaches via application vulnerabilities.Grison
If I may: In your answer you say if your application runs as root [...] breakout is possible, then in the comment you state that this issue is addressed, which I read as contradictory. Just so I understand you well, could you please clarify on that? Also, if you'd have some good reading on container breakout please attach it too as I'd be very interested to read up more on it. Lastly - If you've got a minute could I ask you add these observations to your answer so it is easier for others to find in the future and draw a conclusion? Once again, cheers!Sharpnosed
With your approach the container main process is run as root but the application is NOT, so breakouts are not possible. The best reading on the topic I know is "Container Security" by Liz Rice. Good idea to amend my answer - why not take a position even though that is always a bit risky when it comes to security ;)Grison
C
4

In your case runuser process (PID 1) stays alive. If you want to substitute PID 1, use

Dockerfile

ENTRYPOINT ["/bin/bash", "/var/usr/entrypoint.sh" ]

entrypoint.sh

#add user&group if not existing
exec su -l USERNAME -c "/bin/bash /var/usr/starting.sh"

In starting.sh you do everything you have to do as non-root, or call it directly after -c if it's just a one liner.

Result

docker top

UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
1000                11577               11556               0                   14:58               ?                   00:00:00            /bin/bash /var/usr/starting.sh
1000                11649               11577               0                   14:58               ?                   00:00:00            sleep 24h

There is no process using root anymore (all use UID 1000 in this case), and starting.sh containing bash runs as PID 1, which solves the docker logs problem also (just logs PID 1). Sub-Processes are also started with non-root User. Only if you docker exec from host, it is still using root. But this is mostly wanted for debugging.

top inside container

  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
    1     0 usr1000  S     2316   0%   7   0% /bin/bash /var/usr/starting.sh
   48     0 root     S     1660   0%   7   0% /bin/sh
   54    48 root     R     1592   0%   1   0% top
   47     1 usr1000  S     1580   0%   5   0% sleep 24h

So you see: PID 1 runs the stuff (run everything with exec in there if you need it in the same process). Only thing running as root is the docker exec shell and therefore top.

Collencollenchyma answered 7/2, 2022 at 15:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.