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.
7 thoughts on “Podman volumes and SELinux”
Thanks so much this helps me close like 3 tickets xD
Great! It makes me happy to hear that it helped you 🙂
Great examples! I think I finally understand the differences now!
Thank you for such a great article. It helped me to fix a problem with containers on a SELinux host.
This is so very neatly done. Providing that insight about how things work behind the scenes is real teaching / sharing of knowledge and that is very well done here. Saved me tons of research and resolved my issues today. I know I can re-apply what I learnt here to solve similar issues in the future. Thank you Chris. I owe it to you!
Now i understand why i will use Z or z. Great examples.
Thank you! Very helpful, very clear, excelent examples!