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:65536The 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:65536Note 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 user2will return the content of the subuid file for the userpodman system migrateensures that the relevant container mappings are updated if the subuid file ever changes for the userpodman 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
- subuid(5) / subgid(5) (man pages)
- user_namespaces(7) (man page)
- Podman: Rootless containers (official docs)
- getsubids(1) (man page)
- newuidmap(1) / newgidmap(1) (man pages)
- login.defs(5) (man page)
- Understanding rootless Podman’s user namespace modes (Red Hat blog)