Podman volumes and SELinux

In my previous post on volumes with podman, I touched on SELinux but I think it’s worthy of its own post to work through some details.

If your host has SELinux enabled, then container processes are confined to the system_u:system_r:container_t:s0 domain. Volumes you pass to podman will need to have appropriate labels, otherwise the container won’t be able access the volume, no-matter what the filesystem permissions are.

When running rootless containers (as your non-root user), files in your homedir will probably have the context of unconfined_u:object_r:data_home_t:s0, however the context that is required for container volumes is system_u:object_r:container_file_t:s0.

Fortunately, container volumes which podman creates at runtime will have the appropriate context set automatically. However, for host-dir volumes podman will not change the context by default, you will need to tell it to.

Let’s spin up a busybox container without setting the SELinux context and note that the container is not able to access the host-dir volume.

$ mkdir src

$ ls -dZ src/
unconfined_u:object_r:user_home_t:s0 src/

$ podman run -dit --name busybox -v ./src:/dest busybox
395924189c95d05dae65d5616fbdd7054095b1c3318603642e03047af9c893c7

$ podman exec -it busybox touch /dest/file
touch: /dest/file: Permission denied

Shared labels

OK, let’s delete that container and spin it up again but with the :z SELinux volume option.

$ podman rm -f busybox
395924189c95d05dae65d5616fbdd7054095b1c3318603642e03047af9c893c7

$ podman run -dit --volume ~/src:/dest:z --name busybox busybox
e5176a7acee86e17b42d417e7d2c570b2ecc51cccf6fd938688998d176e60df1

$ podman exec -it busybox touch /dest/file

OK, it didn’t error, that’s good! Let’s have a look on the host.

$ ls -Z ./src/file
system_u:object_r:container_file_t:s0 ./src/file

Great! We were able to write the file and we can see on the host that it exists with the correct SELinux context.

Thus, the :z option is critical as it tells podman to at least set the context to system_u:object_r:container_file_t:s0.

Note however, that with this context, SELinux will not stop any other container from being able to access that same directory. Yes, that can introduce a security risk if applied incorrectly (or perhaps through a vulnerability), but it’s also how you would share the same volume between multiple containers.

Private labels

So what if you wanted to restrict a volume to a specific container only? Well, that’s what the the UPPERCASE :Z option is all about. It not only tells podman to set the context on the volume, like lowercase :z, but it also ensures that other containers are not able to access it.

How does it do this? Each container process also has unique MCS (Multi-Category Security) categories. This is what podman uses for the private label, setting the SELinux context on the volume to match those of the process. For example, if the process runs in the confined domain with unique MCS categories c123,c456 then the volume context will be set to match, e.g. system_u:object_r:container_file_t:s0:c123,c456.

Let’s try a real world example with busybox running the top command (note the UPPERCASE :Z option).

$ mkdir -p src

$ podman run -d --name busybox-top -v ./src:/dest:Z busybox top
eceb13fee0e79cd328fde948e4dd9f1b39cddfc04f147e5eebcbfd2ee2d6abae

Looking for the container’s top process running on the host we can see that its MCS label is c260,c602.

$ ps -eZ | grep container_t |grep top
system_u:system_r:container_t:s0:c260,c602 26474 pts/0 00:00:00 top

Now let’s look at the host directory for the volume and note that it has a matching MCS label of c260,c602.

$ ls -Zd ./src
system_u:object_r:container_file_t:s0:c260,c602 src

Note that if you attach that same host-dir volume to multiple containers, only the last container with that volume attached will be able to access it as the context is updated each time.

Proving protection with private labels

Let’s spin up a second busybox container running iostat command this time, using the same host dir volume.

$ podman run -d --name busybox-iostat -v ./src:/dest:Z busybox iostat 1
1ad1ee6413f0b222468a2f540c4316a6a504c25245435fb51ee125f465514576

Let’s grab the MCS label for the iostat process, which we can see is c327,c995.

$ ps -eZ | grep container_t |grep iostat
system_u:system_r:container_t:s0:c327,c995 26876 pts/0 00:00:00 iostat

Now we can see that the label for the host dir has changed to match this new container (it used to be c260,c602).

$ ls -Zd ./src
system_u:object_r:container_file_t:s0:c327,c995 src

Finally, if we try to touch a file inside each of the containers, we’ll see the original busybox container now fails.

$ podman exec busybox-top touch /dest/file
touch: /dest/file: Permission denied

However, the second container running iostat works, and the file has a matching label.

$ podman exec busybox-iostat touch /dest/file

$ ls -Z src/file
system_u:object_r:container_file_t:s0:c327,c995 src/file

This shows how uppercase :Z is the more secure of the two SELinux volume options.

Leave a Reply

Your email address will not be published. Required fields are marked *