After receiving an email where someone was moving from Docker to Podman and hit some SELinux issues, I decided to write an article about it.
Leveraging SELinux for container security
First, sadly, while Docker has SELinux support, it is disabled by default. I believe this is a big mistake, since SELinux is the best tool to protect the file system from container breakout. SELinux has blocked most of the container breakout scenarios over the past decade-plus.
SELinux has had a reputation for being difficult to manage, mainly because it attempts to control communication paths between applications running on a host. These applications can be configured multiple different ways, causing SELinux to block communication paths. These varied configurations require thorough SELinux knowledge to set booleans or fix file system labels to allow the applications to communicate in a safe and secure manner.
Containers fix this by simplifying the way applications work. When an application is run inside of a container, SELinux can just control the exterior of the container. The application can do whatever it wants within the container, but if it needs to communicate outside of the container, it does it through the network.
SELinux controls the outside of the container through process labeling and file system labels. A database, web service, AI model server, mail server... All look the same to SELinux when these applications are running in a container.
I often say SELinux and containers are a marriage made in heaven. For a better understanding of how SELinux works, check out my SELinux coloring book.

Most users of container technology do not even know that SELinux is controlling the processes. But the main exception to this is when users poke holes in the container and leak information from the host into the container.
The primary way the people leak information into containers is through volume mounts.
The rest of this article will discuss how container engines Podman and Docker (although most of these can also be handled in the Kubernetes world) can allow access to these volume mounts.
Let’s look at 7 different ways you can adjust the container engines to allow them to run and the ramification of these settings.
When analyzing security features of containers, there are multiple different security tools involved. The following analysis assumes that all other security mechanisms have failed and only SELinux stands between you and an exploited system.
1. Run container normally
On Podman, this is the default and requires no modifications. With Docker, the Docker daemon needs to be configured with the --selinux-enabled
option.
Security ramifications
This is the most secure way a container is run. An escaped container process has no access to any host files at all other than container content within the container from an SELinux point of view.
Recommendation
I strongly recommend that all containers run with SELinux separation enabled and enforcing.
2. Run container with a private volume
Use the volume or mount :Z
option. This option tells the container engine to relabel the content under the volume for the exclusive use of the container. Depending on the size of the volume, this can take some time, since the relabel needs to modify every file in the volume. This will only happen the initial time the container uses the volume.
Example: the file label unconfined_u:object_r:container_file_t:s0:c124,c967
for containers running with the s0:c124,c967
MCS label.
Note
If you have a volume which contains millions of files, relabeling might be too cumbersome. If the source Volume for this content is a file system, one option you could investigate for the volume would be to use a context mount for the entire file system. Search for a description of context mounts under the mount man page.
-v /SOURCE:/DEST:Z
--mount type bind,source=/SOURCE,dest=/DEST,Z
$ podman run -ti -v ./db:/var/lib/mariadb:Z quay.io/rhatdan/mariadb
Security ramification
The container has full read/write access over the volume, but other containers are blocked from using it, even if they escaped confinement. If the container process escapes, it only has access to this volume and its other content.
Recommendation
If it is not necessary to share the volume with other containers, use the :Z
option.
Note
Do not relabel default system files and directories, like /home
, /etc
, /var/log
, etc. Relabeling system content might cause other non-containerized confined services on the machine to fail. For these types of containers, look at recommendations 6 or 7.
3. Run two or more containers sharing the same volume
Use the volume or mount :Z
option. This option tells the container engine to relabel the content under the volume with the container shared SELinux label. Depending on the size of the volume, this can take some time because the relabel needs to modify every file in the volume. This will only happen the initial time the container uses the volume. The second container to use this volume will not relabel.
Example: the file label unconfined_u:object_r:container_file_t:s0
for all containers. Note that the MCS label for this container volume is s0
. This means the file has no categories, so any container can read/write the content from an SELinux point of view.
-v /SOURCE:/DEST:z
--mount type bind,source=/SOURCE,dest=/DEST,z
$ podman run -d -v ./shared:/shared:z quay.io/rhatdan/app1
$ podman run -ti -v ./shared:/shared:z quay.io/rhatdan/app2
Security ramification
Both containers have full read/write access over the volume, but other containers are NOT blocked from using it, if they escaped confinement. If a third container’s processes escape they have access to this volume.
Recommendation
When it is necessary to share the volume with other containers, use the :z
option or look at option 5. Use the volume or mount :z
option.
4. Run two or more containers sharing the same volume (option 2)
Another option is to run each container with the same SELinux label. The container engines allow users to select which MCS label to run with. In this case, use the :Z
option.
--security-opt label=level:s0:c100,c259
$ podman run -d --security-opt label=level:s0:c1,c2 -v ./shared:/shared:Z quay.io/rhatdan/app1
$ podman run -ti --security-opt label=level:s0:c1,c2 -v ./shared:/shared:Z quay.io/rhatdan/app2
$ podman run --security-opt label=level:s0:c1,c2 fedora cat /proc/self/attr/current; echo
system_u:system_r:container_t:s0:c1,c2
Security ramification
These containers can read/write the volume, but if a process in the second container escapes, it is able to attack the processes in the first container and potentially able to ptrace them and see their memory.
Processes that escape from other other containers running on the system are not able to read/write the content on the system.
Recommendation
I prefer to use the :z
option because of its simplicity, but if the content of the volume is really sensitive, this mechanism might be preferable. Containers running within the same pod share the same SELinux label, so sharing volumes between containers in a pod is simpler.
5. Run a container with a host volume that can not be relabeled
Sometimes it is necessary to run a container with a host directory that is not only used by the container but also by host processes—for example, if a container needs to examine all of the logs in /var/log
or scan users' content in their home directory for files.
In this case, most users can use option 2 and turn off SELinux separation. But those who want more security can write a new SELinux type to allow access to the content.
--security-opt label=type:container_logreader_t
$ podman run -d --security-opt label=type:container_logreader_t -v /var/log:/logs docker.io/rhatdan/loganalyze
Security ramification
In the preceding example, a new SELinux type was created that runs with standard container protections, but has additional ability to read all of the log file types. This loosens the security only slightly and allows the defined process to run as a container but have read/only access to a particular directory.
This is much better than disabling SELinux separation all together, but a lot more complex.
Recommendation
Investigate the Udica SELinux project, which can create SELinux policy for just this situation.
The Udica can simply inspect a container’s configuration and generate a tailored SELinux policy for it. In the following example, the same container will be started to analyze logs. First, create the container, then pipe the output of podman inspect
to Udica, creating a policy based on the container.
$ podman create –name logreader -d -v /var/log:/logs docker.io/rhatdan/loganalyze
$ podman inspect -l | udica mylog_container
$ sudo semodule -i mylog_container.cil \ /usr/share/udica/templates/{base_container.cil,log_container.cil}
$ podman run –replace –name logreader --security-opt label=type:mylog_container.process -d -v /var/log:/logs docker.io/rhatdan/loganalyze
6. Run container with SELinux separation disabled
--security-opt label=disabled
$ podman run --security-opt label=disable fedora cat /proc/self/attr/current; echo
unconfined_u:unconfined_r:spc_t:s0
The spc_t
SELinux type is an unconfined type ("super privileged container").
Containers run with the --privileged
flag or with --pid=host
or --ipc=host
also set this option.
Security ramification
If processes within the container escape to the host gaining access to the host file system, they have full access to every file that UID=$USER
has access to. If running rootful containers, then it has full access to every file on the host system (ignoring Linux capabilities, DAC_READ_SEARCH
and DAC_OVERRIDE
). The escaped container process also has full access to every process that its UID is able to interact with.
For example, an escaped container process running a rootless container has the ability to read and write the $HOME/.ssh
content.
Recommendation
This option should be rarely used, only in situations when it is necessary to leak major parts of the system into the container; for example, a container needs to examine the entire home directory volume mounted into it.
7. Run system with SELinux disabled
stopdisablingselinux.com. It makes me cry.
Because the container engines have lots of ways to get the job done, including disabling SELinux separation, this must never happen.
Conclusion
Containers make SELinux a lot easier to deal with. In most cases, SELinux just works and the user does not need to worry about it. It quietly protects the containerized process and the host system in the background.
When holes are poked into the container, SELinux has several options to deal with it.
Learn more about SELinux:
Last updated: April 16, 2025