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.

Avocado toast. Image source: https://www.instagram.com/bluestonelane/

On the machine there are some Dockerfiles, namely one that is used for a Python project:

FROM ubuntu:latest 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.

Mitigation

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

USER 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.