Unlike many technologies, Docker is designed with security in mind. There are several functionalities within the Docker's ecosystem that support effective security for the containerized environments, although the lengthy official documentation can perhaps hide these functionalities! Amongst these is an obscure advanced security feature, that provides an interesting security layer to protect Docker nodes in case an attacker compromises a container.
Let's start by taking a look at how Docker manages users and groups in containers execution:
By default, the user within a container is root (id = 0), which maps the root user on the host machine as well. Running a container and then executing a command, can confirm that the user running that command is root, on both the container and the host.
To follow the principle of least privilege, containerized applications should not be run as root. To achieve this, then the --user parameter can be used to specify a user other than root for the container. It accepts as value a username (if it was previously created in the Dockerfile) or a UID, optionally a GID as well.
That was a straight forward solution. Unfortunately, there are applications that need to be executed inside the container with the UID 0 (root). For these cases, using the --user parameter to specify a user with less privileges would not be possible. What can be done in these situations?
An excellent security feature in Docker, is the userns-remap or user namespace remapping. It makes use of the Linux USER namespace to re-map the root user within the container to a less-privileged user in the host machine. In this way, the container will be running as root, but that root is mapped to an user that has no privileges on the host.
Enable userns-remap on Docker daemon
First of all the docker installation must be cleaned by removing any previous image. The output of “docker image ls” has to be empty. Then edit the daemon.json file located under “/etc/docker/” (If the file does not exist, create it).
Add to the daemon.json the following entry:
Save it and restart the docker daemon.
By using the “default” value, docker will use the “dockremap” user and group to make the remapping. It is possible to use a custom user; in that case it must be added to the /etc/subuid and /etc/subgid files. Each of those files contains three fields: the username or ID of the user, followed by a beginning UID or GID (which is treated as UID or GID 0 within the namespace) and a maximum number of UIDs or GIDs available to the user.
It is possible to verify if the user “dockremap” was actually created by docker, running the “id dockremap” command and inspecting the subuid and subgid files.
A namespaced directory now exists within /var/lib/docker/ named with the UID & GID of the namespaced user (dockremap in this case) and owned by it, without group-or-world-readable permissions.
The directories which are owned by the remapped user are now used instead of the same directories directly beneath /var/lib/docker/.
The userns-remap feature can be checked by running a container and inspecting the user inside it (it should be root) and the user mapped in the host (it should be 165536 – dockremap).
Effectively, the root inside the container is mapped to a non-privileged user in the host machine.
The following standard Docker features are incompatible with running a Docker daemon with user namespaces enabled:
- Sharing PID or NET namespaces with the host (--pid=host or --network=host).
- External (volume or storage) drivers which are unaware or incapable of using daemon user mappings.
- Using the --privileged mode flag on docker run without also specifying --userns=host.
User namespaces are an advanced feature and require coordination with other capabilities. For example, if volumes are mounted from the host, file ownership must be pre-arranged to provide read or write access to the volume contents.
While the root user inside a user-namespaced container process has many of the expected privileges of the superuser within the container, the Linux kernel imposes restrictions based on internal knowledge that this is a user-namespaced process.
Do you want to learn more?