3

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?

dmnsta
  • 31
  • 2

2 Answers2

2

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.

Troubleshooting tip

I wrote a troubleshooting tip: Passed-in devices or files can't be accessed in rootless container (UID/GID mapping problem) that contains the method described in Demo 2

Erik Sjölund
  • 1,965
  • 5
  • 21
  • 26
  • This should be the best answer. It resolves so many of my problems trying to have a single non-root host user to run multiple containers that run with different internal uid/gids and all have to access and write to host mounted volumes. Thank you so much! – Yanick Girouard Feb 05 '22 at 04:23
0

You should figure out what is the initial host UID for the container's nginx UID (just touch a file from the container using the user under which nginx is running, or check its log files, or check the running nginx process. Then use stat or ls -ln from the initial host user): some big UID value, let's suppose it's 100030.

Then from the initial host's unprivileged user you can use setfactl to grant additional access to user 100030. This should be something like:

setfacl -R -m u:100030:rX,d:u:100030:rX folder

If you want the nginx user to be able to write (which might not be a good idea), you can replace rX with rwX or do it only in select directories. The duplication starting with d: stands for default ACLs, so newly created directories and files also inherit the same additional ACLs. When ACLs are set, output of ls displays a + and the ACL mask instead of the group, so it might look surprising.

Here are various references about ACLs:

A.B
  • 9,037
  • 2
  • 19
  • 37