Is Your Container's Root Acting a Bit More Rooty than It Should?


Today, we take you on a free of charge tour in the world of user namespaces in Docker containers. If you want to know how containers manage users and permissions, you are in the right place. So, buckle up, and most importantly, put on your shades, cause it is about to be LIT!
What Are User Namespaces?
Think of user namespaces as a magic spell that transforms how we see user IDs (UIDs) and group IDs (GIDs) inside a container.
User namespaces are a linux kernel feature. It can be viewed as a form of isolation, where processes within a user namespace do not have any privileges outside of it.
Processes can run as a root UID:0
inside a usernamespace. They will serve the purpose that some processes may need root capabilities to do certain tasks.
Alright, but then who is that "root" inside my usernamespace? Well, it is just a random superhero who comes to the realization that he can't do much outside of his little bubble. So, outside of usernamespace it is a non root user, for example,UID: 10001
! cue sad music.
Why Should We Care?
User namespaces add an extra layer of security. By mapping container users to less-privileged host users, we reduce the risk of someone wreaking havoc on the host system if they break out of the container.
you can read more about the vulnerabilities that usernamespaces mitigated here
The Magic of Mapping Users
Now, let’s get our hands dirty with a bit of Docker wizardry. Imagine you have a Docker container running an application, and you want to map the root user inside the container to a non-root user on the host. Here’s how you can do it:
Enable User Namespace Remapping: First, you need to enable user namespace remapping in Docker. This is usually done by editing the Docker daemon configuration file /etc/docker/daemon.json
and adding the following lines:
{
"userns-remap": "default"
}
Save and then restart docker
sudo systemctl restart docker
Docker will automatically configure subordinate UID and GID mappings for us under /etc/subuid
and /etc/subgid
files.
Let's go together through the content of those two files:
cat /etc/subuid
dockremap:165536:65536
cat /etc/subgid
dockremap:165536:65536
What we see is that docker will map users from our containers, first user is 165536
which will correspond to UID 0
in the docker container, second user will be 165537
which coresspond to UID 1
.. etc and we can have up to a total of 65536
users.
Maybe a Little Fun experiment?
Let's create a container using docker
docker run --name nginx -d nginx
Now that our container is running in the background we access it and see what we have!
➜ docker exec -it nginx bash
root@8c9e45d0f244: id
uid=0(root) gid=0(root) groups=0(root)
We see that nginx is running as root! Without our previous configuration, this would be the actual root on the host system!!So, what about now?
➜ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
165536 2055858 0.0 0.0 11400 7296 ? Ss 08:41 0:00 nginx: master process nginx -g daemon off;
As we see in the host machine, nginx is running as 165536
kubernetes 1.30
On the latest kubernetes version 1.30, kubernetes' team addresses this issue by making the configuration available on k8s itself,Therefore, you do not need to go all the way and configure your container runtime.
Finally, you can simply opt in user namespaces mapping by setting pod.spec.hostUsers
to false