What you need to know about containers
2023-10-31This document is currently work in progress.
User namespaces
Note: The following examples assume that your Linux machine is configured to allow unprivileged user namespaces, i.e. non-root users are able to create new user namespaces. Some might consider it a security issue to leave this enabled (more on this later) so it’s possible that it is disabled for you. If that’s that case you should use the root user if you want to test the commands out.
1 | user@localhost:~$ |
Currently we don’t have any users mapped in our new namespace so we are the user nobody with the uid 65534.
We can validate that there are no uid and gid mappings for this process by checking corresponding uid_map and gid_map:
1 | nobody@localhost:~$ cat /proc/self/uid_map |
Creating the uid and gid mappings in the new namespace
When we create a new user namespace we can specify the uid and gid mappings only once. After it is set, it cannot be overwritten. To do this we can use the newuidmap
and newgidmap
commands.
The syntax is:
1 | newuidmap [PID] [uid in new namespace] [uid in parent namespace] [count] |
For example:
1 | newuidmap 123 0 1000 1 |
This command will create a single uid mapping for the process with pid 123
and will map the root user (with id 0
) inside the user namespace of the process to uid 1000
in the parent’s user namespace. If we were to change the count
parameter to 2 (newuidmap 123 0 1000 2
), we would map two consecutive uids: 0 -> 1000
and 1 -> 1001
. You could also specify multiple ranges if you want to map ids that are not contiguous, e.g. newuidmap 123 0 1000 2 2 2000 2
will do the following mappings: 0 -> 1000
, 1 -> 1001
, 2 -> 2000
and 3 -> 2001
.
If we were to try to create a new uid_map from this newly created namespace it would fail obviously because we are an unmapped user, we don’t have the permissions to do so:
1 | nobody@localhost:~$ newuidmap $$ 1000 1000 1 |
So to do this get the process id, and inside an other terminal issue the newuidmap
command:
1 | nobody@localhost:~$ echo $$ |
If the command succeeded and you head back to the original terminal you will… see nothing new in particular. Your shell prompt will still say you are nobody
. But if you check the uid_map of the process you will see that it was applied correctly:
1 | nobody@localhost:~$ echo /proc/self/uid_map |
In fact if you check your username or id, you will also find that the mapping took effect. When you applied the new uid_map for the user namespace you automatically became the first available id in the mapping (which is your id outside the user namespace, in my case 1000
).
1 | nobody@localhost:~$ whoami |
So it’s simply the case of your shell prompt not updating, but you can force it by reentering your shell via exec $SHELL
:
1 | nobody@localhost:~$ exec $SHELL |
Just to emphasize again, you can create this new uid map only once. If you try to write it more than once, you will get an operation not permitted error. So if you want to map multiple ranges, you must specify every range in the first call.
1 | BAD |
The newgidmap
command and generally group id mappings work exactly the same way.
But you might be wondering:
- What is this exactly useful for?
- Can you do arbitrary mappings or are there limitations?
We will keep digging deeper to discover the answer for these questions.