Docker internal user concerns #18

Open
opened 2023-08-26 13:55:10 +00:00 by scott · 0 comments
Owner

By default, docker containers run as root.

$ docker run ubuntu id -u 
0

This applies to our work on the Working Class History app1.

$ cd ~/src/WCH-OTD-Backend-v2/
$ docker compose run --entrypoint bash web
[+] Building 0.0s (0/0)                                                                                                                                                                                                                     
[+] Building 0.0s (0/0)                                                                                                                                                                                                                     
# id -u
0

This has several implications.

Containers Which Mount the Docker Socket

Some containers need access to the host's docker socket in order to gather information about other services it interacts with. An example of this is Traefik, which uses access to the docker socket to watch for which services are running, and to gather rules for proxying them from their labels.

If a container has access to the docker socket, to do anything, they have access to do...anything1.

$ docker run -v /var/run/docker.sock:/docker.sock -it python bash
# pip install docker
(pip install output)
# python
>>> from docker import DockerClient
>>> client = DockerClient(base_url="unix://docker.sock")
>>> # the next line runs "touch /host/uh-oh" in an ubuntu container with the host's root directory mounted at /host
>>> client.containers.run('ubuntu', ['touch', '/host/uh-oh'], volumes=['/:/host'])
b''
>>> quit() # (python shell)
# exit     # (containerized bash shell)
exit
$ ls -l /uh-oh
-rw-r--r-- 1 root root 0 Aug 26 08:24 /uh-oh

This means that if you have a container which has bind-mounted the docker socket and the application running contains a vulnerability which allows for arbitrary file writes within the container as the user the application is running as, that an attacker would have a way to run arbitrary code as root in the host system.

Since the application has a need to work with the docker socket directly, mitigating this isn't really possible.

Container Escape Vulnerabilities

This is something that has as of yet never happened, but if a container-escape vulnerability should exist within docker itself, it may imply that running as root within a container may have access to the host system as root, where if they were running as a non-root user, access may be limited. Though, if running as a user who is a member of the docker group, that would be moot anyway.

This is, admittedly, a crazy nitpick to worry about. It's not a thing that should hold up development or deployment.

Concerns With Bind Mounts

Postgres databases are written out with UID and GID 70. MySQL uses 999. Both make some of their files readable only by the owner. In either case, with a standard linux install with the first user created by default being 1000, an rsync job from a remote will fail. Which, fair, you shouldn't really try to back up from a running database with rsync anyway. My solution to this is to schedule a database dump with cron. Ideally that should be a systemd job, but the solution works, at least for infrequent backups.

This also causes some annoyances with code built inside of a container and then accessed from outside the container (i.e. with a bind mount).

In Conclusion

I suppose this isn't a thing worth worrying after all. We could standardize on running container internal software as some arbitrary UID which doesn't share the UID with the main admin user, but does share a separate group membership, which may add a tiny layer of security and minimal inconvenience, but maybe it's just fine to run as root and trust containerization to do its thing. On the other hand, I have run into situations where the file being owned by root causes some annoyances, so maybe a non-root user would be nice to have.


  1. this output is slightly modified for formatting ↩︎

By default, docker containers run as root. ```console $ docker run ubuntu id -u 0 ``` This applies to our work on the Working Class History app[^mod]. ```console $ cd ~/src/WCH-OTD-Backend-v2/ $ docker compose run --entrypoint bash web [+] Building 0.0s (0/0) [+] Building 0.0s (0/0) # id -u 0 ``` This has several implications. ## Containers Which Mount the Docker Socket Some containers need access to the host's docker socket in order to gather information about other services it interacts with. An example of this is Traefik, which uses access to the docker socket to watch for which services are running, and to gather rules for proxying them from their labels. If a container has access to the docker socket, to do anything, they have access to do...anything[^mod]. ```console $ docker run -v /var/run/docker.sock:/docker.sock -it python bash # pip install docker (pip install output) # python >>> from docker import DockerClient >>> client = DockerClient(base_url="unix://docker.sock") >>> # the next line runs "touch /host/uh-oh" in an ubuntu container with the host's root directory mounted at /host >>> client.containers.run('ubuntu', ['touch', '/host/uh-oh'], volumes=['/:/host']) b'' >>> quit() # (python shell) # exit # (containerized bash shell) exit $ ls -l /uh-oh -rw-r--r-- 1 root root 0 Aug 26 08:24 /uh-oh ``` This means that if you have a container which has bind-mounted the docker socket and the application running contains a vulnerability which allows for arbitrary file writes within the container as the user the application is running as, that an attacker would have a way to run arbitrary code as root in the host system. Since the application has a need to work with the docker socket directly, mitigating this isn't *really* possible. ## Container Escape Vulnerabilities This is something that has as of yet never happened, but if a container-escape vulnerability should exist within docker itself, it may imply that running as root within a container may have access to the host system as root, where if they were running as a non-root user, access may be limited. Though, if running as a user who is a member of the `docker` group, that would be moot anyway. This is, admittedly, a crazy nitpick to worry about. It's not a thing that should hold up development or deployment. ## Concerns With Bind Mounts Postgres databases are written out with UID and GID 70. MySQL uses 999. Both make some of their files readable only by the owner. In either case, with a standard linux install with the first user created by default being 1000, an rsync job from a remote will fail. Which, fair, you shouldn't really try to back up from a running database with rsync anyway. My solution to this is to schedule a database dump with cron. Ideally that should be a systemd job, but the solution works, at least for infrequent backups. This also causes some annoyances with code built inside of a container and then accessed from outside the container (i.e. with a bind mount). ## In Conclusion I suppose this isn't a thing worth worrying after all. We could standardize on running container internal software as some arbitrary UID which doesn't share the UID with the main admin user, but does share a separate group membership, which may add a tiny layer of security and minimal inconvenience, but maybe it's just fine to run as root and trust containerization to do its thing. On the other hand, I have run into situations where the file being owned by root causes some annoyances, so maybe a non-root user would be nice to have. [^mod]: this output is slightly modified for formatting
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: TWS/meta#18
No description provided.