Skip to main content
  1. Notes/

TIL: The UID Mapping Behind Rootless Podman

·3 mins
Ixonae
Author
Ixonae

I’ve been running rootless Podman for quite a while now. At first, one of the things that surprised me was seeing some of the volumes mounted to my file system owned by odd UIDs that did not exist within my Linux system.

Turns out it was due to the Linux User Namespace features. More specifically the subuid and subgid.

But what is that? Basically, when running a rootless container, you still want things running in the container to think they are root, but you do not want to use the UID 0 - the system root UID - in order to offer some additional security in case of container escape.

You also want the container to be able to represent more than one user. A container may have root, service accounts, application users, and groups of its own. Subordinate UID and GID ranges let those internal identities map to separate unprivileged IDs on the host, instead of collapsing everything onto the user running the container.

To enable this, the system will create some mapping in /etc/subuid and /etc/subgid. The files will look as follows:

$ cat /etc/subuid
ixonae:100000:65536
user2:165536:65536
$ cat /etc/subgid
ixonae:100000:65536
user2:165536:65536

The first number is the start of the UID mapping, and the second the number of sub ids that can be created. For a simple contiguous subordinate range, the mapping is host_uid = subuid_start + container_uid. For example, root would be 100000 on the host system, and if a user is created inside of podman with ID 1000, it will map to 101000 on the host system.

That way, on the host system, things running from podman are still unprivileged, but within the container, they can run as if they were.

Where does 65536 come from, you may ask. On many systems, this default is defined inside of /etc/login.defs. Specifically: SUB_UID_MIN, SUB_UID_MAX, SUB_UID_COUNT. You will note that the file also defines UID_MIN and UID_MAX that are used when using useradd. The default ranges are usually set to avoid overlap with regular user IDs.

When executing rootless Podman commands and containers, the newuidmap and newgidmap utils will be called to initialize the user namespace, and help writing the /proc/<pid>/uid_map file, which will then be used by the kernel for permissions management and anything involving user IDs.

You can see these permissions as follows:

# The three columns displayed from the below command are:
#  - the ID inside the user namespace
#  - the ID outside the namespace
#  - the length of the mapping.
[user2@myhost ~]$ podman unshare cat /proc/self/uid_map
         0       1001          1
         1     165536      65536
$ id user2
uid=1001(user2) gid=1001(user2) groups=1001(user2)
$ cat /etc/subuid | grep user2
user2:165536:65536

Note that in this case, root in the container maps to the user running podman in the host OS. All the other users will be linked to a subuid. This is the default behaviour in podman, but it can be edited.

For example, as follows if you use podman compose:

services:
  app:
    userns_mode: auto # default is "" (unset)

Note that changing the behaviour will require permissions remapping. Using :U on mounted paths in a podman compose file can take care of it automatically.

Some useful related commands:

  • getsubids user2 will return the content of the subuid file for the user
  • podman system migrate ensures that the relevant container mappings are updated if the subuid file ever changes for the user
  • podman unshare [your command] allows you to run a command inside the same user namespace as podman, therefore granting you access to the files that are not owned by the host user

Extra Resources