Skip to content

Docker user namespaces


TL;DR

Docker user namespaces map User ID's (UIDs) running within the container to a non-privileged User ID on the host machine.

What is it and how does it work?

Linux identifies users by usernames (ex. user@localhost), and by using User ID's (UIDs). Users may be assigned to groups with group names and associated Group ID's (GIDs).

root, by default, has UID 0. This indicates that it is the most privileged user on the system.

Typically, without user namespaces, root within a Docker container is directly mapped to root on the host system. Docker still achieves some separation by denying certain sensitive capabilities. Being root within the container and outside the container is very useful for bind mounts, since root on the host will have access to any folder that you want to mount to the container.

Obviously, running as root on the host isn't too great. Enter user namespaces.

User namespaces map root within the container (UID 0) to a very high UID on the host (ex. 231072). Processes that run within the container as a different user than root use a UID on the host higher than 231072.

After enabling user namespaces, in the /var/lib/docker directory on the host system, Docker will create a folder named 231072.231072 (where the second 231072 is the base GID). Within this folder, Docker will store images, volumes, etc. instead of in the base /var/lib/docker directory.

But how can an unprivileged user on the host bind to privileged ports, create networks, etc.?

Although the container user is mapped to an unprivileged user on the host system, the Docker daemon on the host still runs as root. The Docker daemon handles network creation, volume creation, port bindings, etc. This is unlike Podman, which runs its container daemon as an unprivileged user on the host system, and so its containers cannot bind to privileged ports.

What's the catch?

One big catch is that if you want to bind mount a directory on the host system to the container, you must grant access (via chown) to the directory to the unprivileged user on the host system. For example, if a directory named data is to be mounted to a container, first issue chown 231072:231072 data in order to allow the container to access the directory once mounted.

Ok, so how do I set it up?

Consult the official documentation for the full spiel.

(but essentially): - Stop the Docker daemon. - Create (if does not already exist) the file /etc/docker/daemon.json. - Fill daemon.json with the following content:

{
  "userns-remap": "default"
}
  • Start the Docker daemon.
  • Verify the dockremap user creation using id dockremap, grep /etc/subuid, and grep /etc/subgid.

Warning

Do note that on Alpine Linux and perhaps other distros, you may have to manually create the user, assign UIDs and GIDs, etc. The Alpine Linux wiki has more detailed info. Do note that on Alpine, the manual setup process may assign a lower GID (iirc) that you may want. You can manually specify the starting UID and GID in the initial user and group creation commands.