17 Jan

Accessing host socket when using namespaces for Docker isolation

Nothing is secure by default and Docker is no exception. One of the recommended change to improve Docker security is isolation of the containers in user namespace which was introduced in Docker Engine 1.10. The namespaces makes the process run on the host thinks that it has its own access to some global resources like the PIDs. User namespaces provides the mechanism of remapping container resources to host resources limiting container access to the host system.

In some cases you may need to access the host resource from the container like the Docker own socket. It is required if you run Docker inside of the Docker container or you deploy a tool that will manage your Docker hosts or Docker Swarm cluster. The problem is the socket on the hosts is owned by the root, while the root PID from inside of your container is remapped to non-root PID on the host. It make the host socket inaccessible for the processes inside the container. But there is an easy workaround for this problem

Namespaces and containers

Unix socket is a special handler that allows applications to communicate. To make it simpler, in this case, think of it as of TCP connections, but instead of connecting to IP and port you connect to a socket on the host. To make it, even more, simpler think of the socket as of the file on the filesystem which you read from and write to sequences of bytes. The owner of the socket is the process creating it.

Using the user namespaces we face the situation where the dockerd process (the Docker Engine) is run as root while the containers itself uses non-root PID. The Docker socket permissions in such case should look like that

srw-rw----  1 root   docker     0 Jan 16 22:52 docker.sock

The process running in the container from the host perspective is a non-root process if we use the user namespaces

231072     512  0.0  1.4  20888 13856 ?        Ss   Jan16   0:16 /usr/bin/python2 /usr/bin/supervisord

The first column is the user ID – on my system it is 231072. If we bind the socket to the container it won’t be accessible because of the ownersip and permission mismatch

The socat

The solution to our problem is a small application – socat. It is available in most Linux distributions by default. It created two bidirectional pipes (byte streams) and transfer data between them. The endpoints can be by example the socket type of SOCK_STREAM. The other end might be…. another socket! And another process may be the owner of the socket.

srw-rw----  1 root   docker     0 Jan 16 22:52 docker.sock
srw-rw----  1 231072  231072    0 Jan 16 22:52 docker-userns.sock

The second socket can easily be assigned and accessible for processes inside the container.

Starting the socat we need to specify the both endpoints – first the new one following by existing root-owned socket.

# /usr/bin/socat UNIX-LISTEN:/var/run/docker-userns.sock,user=231072,group=231072,mode=0660,fork UNIX-CLIENT:/var/run/docker.sock

And thats it!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: