How to access the host's Docker Socket without root
I needed to run a Docker container from inside another container. While it's possible to run Docker inside Docker, the recommended way is running siblings containers. The challenge now is how to create a Docker container in the host machine from inside another Docker container.
It's easy in theory. You just have to map the host's /var/run/docker.sock
to
the container's, like:
docker run -v /var/run/docker.sock:/var/run/docker.sock IMAGE
If the Docker binary is installed in that container, any command like docker ps
will actually return the containers running in the host. The problem is that
only the container's root
user is able to run those commands. This is because
/var/run/docker.sock
is only readable by root
and the docker
group (if you
created this group, if not check this tutorial). To run
as a regular user, we need to add our user inside the container to the docker
group in the host. This is where things start getting messy. Let's see the
permissions for /var/run/docker.sock
via the host machine and inside the
container:
$ ls -lah /var/run/docker.sock
srw-rw---- 1 root docker 0 Jan 13 16:44 /var/run/docker.sock
$ docker run -v /var/run/docker.sock:/var/run/docker.sock ubuntu ls -lah /var/run/docker.sock
srw-rw---- 1 root 999 0 Jan 13 19:44 /var/run/docker.sock
Notice that the group is named docker
in the host, and 999
in the container?
This happens because the group exists only on the host, the container only sees
that the file is owned by group with GID (group ID) number 999, but doesn't know
its name. To be able to access this file, we need to:
- Create a group inside the container with the same GID as in the host;
- Add our non-root user to this group;
- Run the rest of the commands as the non-root user.
This can't be done at image build time because the GID depends on where the
container is running. It's 999
on my machine, but can be another number in
yours. We need to do this when the machine is ran with a script like:
#!/usr/bin/env bash
# Based on https://github.com/jenkinsci/docker/issues/196#issuecomment-179486312
# This only works if the docker group does not already exist
DOCKER_SOCKET=/var/run/docker.sock
DOCKER_GROUP=docker
REGULAR_USER=ubuntu
if [ -S ${DOCKER_SOCKET} ]; then
DOCKER_GID=$(stat -c '%g' ${DOCKER_SOCKET})
groupadd -for -g ${DOCKER_GID} ${DOCKER_GROUP}
usermod -aG ${DOCKER_GROUP} ${REGULAR_USER}
fi
# Change to regular user and run the rest of the entry point
su ${REGULAR_USER} -c "/usr/bin/env bash runner.sh ${@}"
This creates a docker
group with the same GID as the one in
/var/run/docker.sock
and assign the user REGULAR_USER
to it. The last step
is to switch to the non-root user and continue with whatever process you were
running. In this example, I'm running runner.sh
and giving it whatever
arguments we received. It'll be able to run any Docker command as if it was the
host.
Be aware that there're security implications in allowing
containers to access /var/run/docker.sock
. Make sure you trust the containers'
code.