UID mapping and user namespaces will come into picture when we are running a rootless container. In this article, we will see how podman uid mapping really works, and on what basis the uid mapping happens
This article can be considered as a continuation of the podman rootless container introduction article, so consider go through the rootless container article, for better understanding.
We will do one exercise for understanding the uid mapping, and it starts by running a rootless container.
- Lets run a rootless container, for doing this, start a container with a non-root user, in my case user abhi
[root@localhost ~]# su abhi [abhi@localhost root]$ whoami abhi [abhi@localhost root]$ id uid=1001(abhi) gid=1001(abhi) groups=1001(abhi),0(root),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 [abhi@localhost root]$ podman run -it centos sh sh-4.4# id uid=0(root) gid=0(root) groups=0(root)
- When we inspect the user inside the container we can see that, the user is root even though the container is started by a non-root user
- From the container's point of view, it will be running as a root user
- Run some process and verify its behavior from containers point of view and host point of view
#Execute on container sh-4.4# sleep 2130 & [1] 41 sh-4.4# hostname 7c622daca98c sh-4.4# ps -ef|grep 2130 root 41 1 0 06:06 pts/0 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 2130 root 43 1 0 06:06 pts/0 00:00:00 grep 2130
- From the containers point of view , the sleep processes is running with root user
#Execute on host [abhi@localhost root]$ ps -ef|grep 2130 abhi 61765 48256 0 11:36 pts/0 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 2130 abhi 61824 48176 0 11:42 pts/1 00:00:00 grep --color=auto 2130
- From the above code snippet, it can be seen that, from host point of view the process which is started inside the container is running as user abhi. It should be noted that with this same user only we have started our container also
- As continuation of this exercise lets create one user inside the container and lets run the a sample process and see its behavior
#Execute on container sh-4.4# useradd -u 5000 justin sh-4.4# su - justin [justin@7c622daca98c ~]$ sleep 5600 & [1] 35 [justin@7c622daca98c ~]$ ps -ef|grep 5600 justin 35 18 0 04:06 pts/0 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 5600
- From the above code snippet, it can be seen that, a user justin is created with uid 5000 and with user justin a sleep process is created
- While analyzing the sleep process, it can be seen that from container's point of view, the process is running by user justin
- Lets verify from host point of view, how the sleep 5600 is running
#Execute on host [abhi@localhost root]$ ps -ef|grep sleep 170535 48459 48425 0 09:36 pts/0 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 5600 abhi 48462 48176 0 09:36 pts/1 00:00:00 grep --color=auto sleep
- From host point of view, process is running with a uid of 170535. But How the uid of user justin (5000) become 170535 ?
How Container to Host UID mapping works ?
User namespaces are feature in linux, that allow user accounts to be isolated in namespaces and allow for processes to have different uid and gid numbers inside and outside of the user namespace. This is what actually happening for the user justin in our above example
- For viewing the namespaces which are accessible to the container, we can execute lsns -t user command
#Execute on host sh-4.4# lsns -t user NS TYPE NPROCS PID USER COMMAND 4026532381 user 2 1 root sh
- The user namespace 4026532381 was automatically created when we the container was created by the podman
- When we analyze the same command from the host machine, we can see that the host user abhi have access to two namespaces, one of which is the container namespace itself. The second namespace is of the user abhi itself
[abhi@localhost root]$ lsns -t user NS TYPE NPROCS PID USER COMMAND 4026531837 user 2 48176 abhi bash 4026532381 user 10 20219 abhi catatonit -P
- The subuid or subordinate uid which a user is permitted to use in a user namespace is available in the /etc/subuid file and gid is present in /etc/subgid file
- The content of the subuid and subgid file for my machine is as follows
[abhi@localhost root]$ cat /etc/subuid linuxdatahub:100000:65536 abhi:165536:65536 [abhi@localhost root]$ cat /etc/subgid linuxdatahub:100000:65536 abhi:165536:65536
- By default when a user is created, in RHEL 8 a range of 65536 sub ordinate uid is assigned to it
- Below the syntax of the entry in subuid file. First entry is username, second entry is the starting of the uid series and third entry will be number of sub uids that the usernamespace can allow. In other words we can say that the user abhi is having sub oridinate uid range of 165536 to 165536+65536-1=231071 (-1 is there since, the number itself is having one count)
- For a new user, a new entry will get automatically added in /etc/subuid and /etc/subgid. The starting uid will be the end of the previous users sub uid range
- A new user test is added, when we inspect the subuid file. It can be seen that a range of 65536 subuid is getting added. So now the starting uid will be 231072
[abhi@localhost root]$ cat /etc/subuid linuxdatahub:100000:65536 abhi:165536:65536 test:231072:65536 [abhi@localhost root]$ cat /etc/subgid linuxdatahub:100000:65536 abhi:165536:65536 test:231072:65536
- Login back to the rootless container which we have created in the beginning of this exercise. We will see the content of the /proc/1/uid_map file
- proc/1/uid_map will have the uid map for the process with pid 1
- When podman creates a container, it sets the uid_map based on the content of /etc/subuid. Below is the content of the uid_map for my container
sh-4.4# cat /proc/1/uid_map
0 1001 1
1 165536 65536
- Format of the uid_map is shown below
- The uid 0 in the container is mapped to the uid 1000 in the host, and length of the uid is 1, which indicates that this is for single user (abhi). In short root user inside the container is mapped as user abhi (uid 1000) in the host which started the rootless container
- second line of the uid_map file shows the there is 65536 uids that are mapped starting from uid 1 in the container to 165536 uid in the host. Below screen snippet shows the complete mapping
- Lets add a new user justin with a uid of 5000 and lets start a process with user justin. Below code snippet shows the same
sh-4.4# useradd -u 5000 justin sh-4.4# su - justin [justin@7c622daca98c ~]$ sleep 5600 & [1] 35 [justin@7c622daca98c ~]$ ps -ef UID PID PPID C STIME TTY TIME CMD justin 18 17 0 04:04 pts/0 00:00:00 -bash justin 35 18 0 04:06 pts/0 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 5600
- It can be seen that from containers point of view, the process is started with user justin who is having uid of 5000
- Now lets check the same user from the host point of view. But before that lets calculate the host uid number based on the uid_map file
- Below can be considered as the equation for calculating the hostuid. We are substracting 1, as the container starts and runs with id of 1
Host UID =starting uid within the container + container uid -1
170535= 165536+5000 -1
- Upon inspecting the uid in the host machine, we could see that our calculation of the host uid was correct
[abhi@localhost root]$ ps -ef|grep sleep
170535 48459 48425 0 09:36 pts/0 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 5600
- Inorder to get a complete mapping of the user between the host and the container, we can user podman top command
[abhi@localhost root]$ podman top 7c622daca98c user huser args USER HUSER COMMAND root 1001 sh tom 172535 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 780 root 1001 su justin justin 170535 bash justin 170535 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 3400
- From the above code snippet, it can be seen that first column USER shows the user inside the container, and the second column "HUSER" will show the user for the same process from host