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
The socat
The solution to our problem is a small application –
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!