podman: rootless container: permissions for container user

In nginx podman container nginx user is used to run nginx server.

On the host machine ls -alh:

drwxrwx--- 2 myuser myuser 4.0K Aug 10 22:23 .
drwxrwx--- 3 myuser myuser 4.0K Aug 10 22:59 ..
-rw-rw---- 1 myuser myuser   46 Aug 10 22:24 .htpasswd

The same folder inside container ls -alh:

drwxrwx--- 2 root root 4.0K Aug 10 22:23 .
drwxr-xr-x 1 root root 4.0K Aug 10 11:05 ..
-rw-rw---- 1 root root   46 Aug 10 22:24 .htpasswd

nginx user inside container can't access .htpasswd because of o-rwx.

Question: what is the commonly used pattern to handle this kind of cases in rootless container universally? Maybe it is possible to create group (used later as file-group owner) which gathers all ranges from subuid/subgid for particular host user - but how to achieve this?


Solution 1:

By using the command-line option --uidmap you can specify how the myuser UID and the myuser sub UIDs are mapped into the container. (See the man page for podman run).

The command-line option --gidmap works in the same way but for GIDs instead of UIDs.

Let's look up the UID and GID for the user nginx in the container image docker.io/library/nginx

$ podman run --rm docker.io/library/nginx grep nginx /etc/passwd
nginx:x:101:101:nginx user,,,:/nonexistent:/bin/false
$

Result:

  • UID: 101
  • GID: 101

The numbers are later used when setting two shell variables

$ container_uid=101
$ container_gid=101

(The shell variables container_uid and container_gid don't have any meaning outside this post. They are just introduced to make the answer easier to read)

Take a look in the files /etc/subuid and /etc/subgid on the host

The user myuser has these sub UIDs and sub GIDs.

$ grep myuser /etc/subuid
myuser:231072:65536
$ grep myuser /etc/subgid
myuser:231072:65536
$ 

Result:

myuser has 65536 sub UIDs and 65536 sub GIDs.

The numbers are later used when setting two shell variables

$ subuid_size=65536
$ subgid_size=65536

(The shell variables subuid_size and subgid_size don't have any meaning outside this post. They are just introduced to make the answer easier to read)

Instead of looking up subuid_size and subgid_size in /etc/subuid and /etc/subgid, a more general method is to run the commands

 subuid_size=$(( $(podman info --format "{{ range .Host.IDMappings.UIDMap }}+{{.Size }}{{end }}" ) - 1 ))
 subgid_size=$(( $(podman info --format "{{ range .Host.IDMappings.GIDMap }}+{{.Size }}{{end }}" ) - 1 ))

The advantage is that this also works when the file /etc/nsswitch.conf is used instead of /etc/subuid and /etc/subgid. (See man subuid)

Demo 1: map the user myuser on the host to the user root inside the container

There is no need to specify --uidmap nor --gidmap because this is the standard mapping.

Create the world writable directory demo1

$ mkdir demo1
$ chmod 777 demo1
$

Create a new file, running as root inside the container:

$ podman run --rm \
    -v ./demo1:/dir:Z \
      docker.io/library/nginx touch /dir/created_by_root
$

Create a new file, running as nginx inside the container:

$ podman run --rm \
    --user 101:101 \
    -v ./demo1:/dir:Z \
      docker.io/library/nginx touch /dir/created_by_nginx
$

List the files on the host

$ ls -l demo1
total 0
-rw-r--r--. 1  231172  231172 0 Aug 27 20:24 created_by_nginx
-rw-r--r--. 1 myuser myuser 0 Aug 27 20:22 created_by_root
$ 

Result: The file created_by_root is owned by myuser:myuser

Demo 2: map the user myuser on the host to the user nginx inside the container

Create the world writable directory demo2

$ mkdir demo2
$ chmod 777 demo2
$

Create a new file, running as root inside the container:

$ subuid_size=65536
$ subgid_size=65536
$ container_uid=101
$ container_gid=101
$ podman run --rm \
    --uidmap=0:1:$container_uid \
    --uidmap=$((container_uid + 1)):$((container_uid + 1)):$((subuid_size - $container_uid)) \
    --uidmap=$container_uid:0:1 \
    --gidmap=0:1:$container_gid \
    --gidmap=$((container_gid + 1)):$((container_gid + 1)):$((subgid_size - $container_gid)) \
    --gidmap=$container_gid:0:1 \
    -v ./demo2:/dir:Z \
      docker.io/library/nginx touch /dir/created_by_root
$

Create a new file, running as nginx inside the container.

$ subuid_size=65536
$ subgid_size=65536
$ container_uid=101
$ container_gid=101
$ podman run --rm \
    --user $container_uid:$container_gid \
    --uidmap=0:1:$container_uid \
    --uidmap=$((container_uid + 1)):$((container_uid + 1)):$((subuid_size - $container_uid)) \
    --uidmap=$container_uid:0:1 \
    --gidmap=0:1:$container_gid \
    --gidmap=$((container_gid + 1)):$((container_gid + 1)):$((subgid_size - $container_gid)) \
    --gidmap=$container_gid:0:1 \
    -v ./demo2:/dir:Z \
      docker.io/library/nginx touch /dir/created_by_nginx
$

List the files on the host

$ ls -l demo2
total 0
-rw-r--r--. 1 myuser myuser 0 Aug 27 20:26 created_by_nginx
-rw-r--r--. 1  231072  231072 0 Aug 27 20:25 created_by_root
$ 

Result: The file created_by_nginx is owned by myuser:myuser

Conclusion

Use --uidmap and --gidmap in the same way as in Demo 2.