Docker vs Host user
By default when we run a process inside a Docker container, it is run as the root user, and that’s all right; after all, we are talking about inside the container, but as far as the host machine is concerned, what user is running the process? Spoiler alert: It is the same root user as on the host.
Let’s take a step back and try to understand what’s happening. So I created a fresh VM server and installed Docker. Now let’s create a user called host1 on the host machine and let’s add this user to the sudo group and docker group, so that we can use Docker commands without sudo every time.
root@aki:~# adduser host1
root@aki:~# usermod -aG sudo host1
root@aki:~# usermod -aG docker host1
root@aki:~# mkhomedir_helper host1
root@aki:~# su host1
host1@aki:/root$
Now we have two users on the host that we care about, we can get those two with cat /etc/passwd | grep -E "root|host1"
root@aki:~# cat /etc/passwd | grep -E "root|host1"
root:x:0:0:root:/root:/bin/bash
host1:x:1000:1000:,,,:/home/host1:/bin/bash
Now let’s try to run a simple Ubuntu Docker container and log into it. To keep it simple, we are not going to create a Docker Compose file, but this applies to that as well.
host1@aki:~# docker run -it ubuntu:latest bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
49b384cc7b4a: Pull complete
Digest: sha256:3f85b7caad41a95462cf5b787d8a04604c8262cdcdf9a472b8c52ef83375fe15
Status: Downloaded newer image for ubuntu:latest
root@ca368628bddf:/#
Now that we are inside docker let’s try to find our id with id command
root@ca368628bddf:/# id
uid=0(root) gid=0(root) groups=0(root)
root@ca368628bddf:/# sleep 500
We are root user, that’s cool, we are inside the container, now let’s try to run a process, maybe sleep 500. Now let’s open another terminal and try to grep the sleep process in the host.
host1@aki:/root$ ps aux | grep sleep
root 7319 0.0 0.1 2688 1044 pts/0 S+ 13:19 0:00 sleep 500
host1 6845 0.0 0.0 6432 724 pts/2 S+ 13:26 0:00 grep --color=auto sleep
We can see the actual sleep process is under root user and I (as host1) will not be able to kill this process either, even though I started the container. Well, one way to kill the process is to try it with sudo. This goes to show that the process running inside the container is perceived by the host as an actual root process.
host1@aki:/root$ kill -9 7319
bash: kill: (7319) - Operation not permitted
host1@aki:/root$ sudo !!
sudo kill -9 7319
host1@aki:/root$ ps aux | grep sleep
host1 7325 0.0 0.0 6432 656 pts/2 S+ 13:30 0:00 grep --color=auto sleep
host1@aki:/root$
Okay, but what about files? Let’s switch to the other terminal where we had executed the docker sleep command and exit this container and create a folder in the host and map this folder to the container and then start it.
host1@aki:~/experiment$ ls -larth containerMapped/
total 8.0K
drwxrwxr-x 3 host1 host1 4.0K May 26 13:34 ..
drwxrwxr-x 2 host1 host1 4.0K May 26 13:34 .
host1@aki:~/experiment$ docker run -v /home/host1/experiment/containerMapped:/container/data -it ubuntu bash
Now that we have mapped a volume, let’s try to create some folders in this location, and for testing, let’s create two new users inside Docker and try to create directories with them as well.
root@c45aebb50fd4:/# cd /container/data/
root@c45aebb50fd4:/container/data# useradd container1
root@c45aebb50fd4:/container/data# useradd container2
root@c45aebb50fd4:/container/data# mkdir ascontainer1
root@c45aebb50fd4:/container/data# mkdir ascontainer2
root@c45aebb50fd4:/container/data# chown container1 ascontainer1/
root@c45aebb50fd4:/container/data# chown container2 ascontainer2
root@c45aebb50fd4:/container/data# ls -larth
total 20K
drwxr-xr-x 3 root root 4.0K May 26 14:00 ..
drwxr-xr-x 2 root root 4.0K May 26 14:00 asroot
drwxr-xr-x 2 container1 root 4.0K May 26 14:03 ascontainer1
drwxr-xr-x 2 container2 root 4.0K May 26 14:04 ascontainer2
drwxrwxr-x 5 ubuntu ubuntu 4.0K May 26 14:04 .
root@c45aebb50fd4:/container/data#
So far it seems good, but now let’s exit out of the container and check it from the host machine’s perspective.
host1@aki:~/experiment/containerMapped$ ls -larth
total 20K
drwxrwxr-x 3 host1 host1 4.0K May 26 14:00 ..
drwxr-xr-x 2 root root 4.0K May 26 14:00 asroot
drwxr-xr-x 2 1001 root 4.0K May 26 14:03 ascontainer1
drwxr-xr-x 2 1002 root 4.0K May 26 14:04 ascontainer2
drwxrwxr-x 5 host1 host1 4.0K May 26 14:04 .
host1@aki:~/experiment/containerMapped$
Okay, fine; it seems like there are two directories on the host machine which don’t seem to have a proper user ID associated with them, because 1001 and 1002 were not resolved properly. Now let’s try to create a couple more users on the host machine, host2 and host3.
root@aki:/home/host1/experiment/containerMapped# adduser host2
root@aki:/home/host1/experiment/containerMapped# adduser host3
root@aki:/home/host1/experiment/containerMapped# ls -larth
total 20K
drwxrwxr-x 3 host1 host1 4.0K May 26 14:00 ..
drwxr-xr-x 2 root root 4.0K May 26 14:00 asroot
drwxr-xr-x 2 host2 root 4.0K May 26 14:03 ascontainer1
drwxr-xr-x 2 host3 root 4.0K May 26 14:04 ascontainer2
drwxrwxr-x 5 host1 host1 4.0K May 26 14:04 .
root@aki:/home/host1/experiment/containerMapped#
Now those invalid IDs seem to have become owned by host2 and host3, but originally they were owned by container1 and container2.
This goes to show that users are managed by IDs in both the container and the host, and as long as the ID of the user is the same in both, they will be identified as the same user. This is because when we take a look at the /etc/passwd file, it becomes a little clearer.
root@aki:~# tail -n3 /etc/passwd
host1:x:1000:1000:,,,:/home/host1:/bin/bash
host2:x:1001:1001:,,,:/home/host2:/bin/bash
host3:x:1002:1002:,,,:/home/host3:/bin/bash
We can see that the two new users that we created got the user IDs 1001 and 1002, so when ls was trying to look for user 1001 it was resolved as host2. And the root user inside the container is the same root user as on the host machine because both of them have the ID 0.
It is due to these reasons that when we create Docker containers, it’s a good idea to explicitly mention the user ID and group ID both in docker run and docker compose files. In the following command, 1000:1000 represents the user ID and the group ID with which we want to run the process.
docker run --user 1000:1000 ubuntu:latest tail -f /dev/null
version: '3.8'
services:
app:
build: .
container_name: ubuntu_container
volumes:
- ./host_data:/container_data
user: "1000:1000"
We have mostly discussed user IDs here, but this is the same case with group IDs as well.
Tags · Tech, Linux