User privileges in Docker containers
Over the past few years, Docker has become a quintessential technology used in software development. Its concept of containerization has made it easy to set up, share and deploy software projects. Therefore it’s used by a lot of tech companies (to name a few: PayPal, Uber, Spotify, VISA…). Tech companies care about the security of their data and applications. So it’s logical that Docker security is an important subject.
However, a lot of Dockerfiles contain vulnerabilities. A lot of them are written without keeping the best practices in mind. And that presents a serious threat to the security of the app that’s using Docker.
I’ll illustrate this with an example of user used in a Dockerfile.
By default, Docker containers run as root. That root user is the same root user of the host machine, with UID 0. This fact can enable hackers to perform various types of attacks on your app if they get hold of your vulnerable container:
- they can copy sensitive files from the host to the container and access them
- remote command execution
- if some sensitive root-owned file is mounted to the container, they can access it from the container as they’re root
A simple example of an attack
Imagine that you have access a machine that has Docker installed and your current user is a non-root user named “vlatka” that belongs to the docker group. The root user of that host machine is making the best avocado toast ever and you’d like to know her secret which she stored in /home/vlatka/recipes/secret_ingredient.txt file.
Also, you’d like to have root privileges on the host, by not having to type the password when running sudo.
Let’s see how you can do this with the help of Docker.
On the machine there are some Dockerfiles, namely one that is used for a Python project:
RUN apt-get update
RUN apt-get install -y python python-pip wget
RUN pip install Flask
You build it as an image called avocado_secret_theft:
docker build -t avocado_secret_theft .
Then you mount the whole root filesystem of your host machine to the avocado_secret_theft container and run it in interactive mode.
docker run -v /:/host -it avocado_secret_theft
Once in the container, by doing
ls you can see that you have the whole host file system in the host directory. As you are root now, you can access root-owned files and finally get the avocado toast secret:
And to be able to run sudo without a password, you do:
echo "vlatka ALL=(ALL) NOPASSWD: ALL" > /host/etc/sudoers.d/toto
When you exit the container, you can do all the damage you want to the host machine because you have the root privilege even though you’re not root.
Most base images have the current user set to root because they’re used as a “base” to build on and install needed packages, for which root privileges are often needed. However, it is up to the creator of the Dockerfile to override that root user when it’s no longer needed so that the container is run with a less-privileged user.
That can be done with two simple instructions which create a user in the container and set it as the current user.
RUN useradd -ms /bin/bash toto_user
After having appended those lines to the existing Dockerfile, we build the image, run its container in the same way as before and try to access the avocado toast secret:
As we can see, the new container is running with toto_user and that user can’t read the root-owned files from the host machine.
To sum up, always change the user from root to a non-privileged user in your Dockerfile when you no longer need root privileges. That way your container is run in a (more) secure way.
Another lesson we can draw from this is that we should avoid adding users to docker user group unless we really trust them.
I hope this example illustrates how important it is to get acquainted with the Docker best practices. To do that, here are some articles I found useful:
In this installment of our cheat sheets, we'd like to focus on Docker and discuss tips and guidelines that ensures a…snyk.io
This document covers recommended best practices and methods for building efficient images. Docker builds images…docs.docker.com
Also, I suggest using a linter like Hadolint that can warn you before merging new unsafe Docker code.
Of course, a lot more can be done to secure your Docker containers, so we’d be happy to learn some of your go-to techniques. Please feel free to share and let’s discuss it!