Chapter 9. Creating SELinux policies for containers
Red Hat Enterprise Linux 9 provides a tool for generating SELinux policies for containers using the udica
package. With udica
, you can create a tailored security policy for better control of how a container accesses host system resources, such as storage, devices, and network. This enables you to harden your container deployments against security violations and it also simplifies achieving and maintaining regulatory compliance.
9.1. Introduction to the udica SELinux policy generator
To simplify creating new SELinux policies for custom containers, RHEL 9 provides the udica
utility. You can use this tool to create a policy based on an inspection of the container JavaScript Object Notation (JSON) file, which contains Linux-capabilities, mount-points, and ports definitions. The tool consequently combines rules generated using the results of the inspection with rules inherited from a specified SELinux Common Intermediate Language (CIL) block.
The process of generating SELinux policy for a container using udica
has three main parts:
- Parsing the container spec file in the JSON format
- Finding suitable allow rules based on the results of the first part
- Generating final SELinux policy
During the parsing phase, udica
looks for Linux capabilities, network ports, and mount points.
Based on the results, udica
detects which Linux capabilities are required by the container and creates an SELinux rule allowing all these capabilities. If the container binds to a specific port, udica
uses SELinux user-space libraries to get the correct SELinux label of a port that is used by the inspected container.
Afterward, udica
detects which directories are mounted to the container file-system name space from the host.
The CIL’s block inheritance feature allows udica
to create templates of SELinux allow rules focusing on a specific action, for example:
- allow accessing home directories
- allow accessing log files
- allow accessing communication with Xserver.
These templates are called blocks and the final SELinux policy is created by merging the blocks.
Additional resources
- Generate SELinux policies for containers with udica Red Hat Blog article
9.2. Creating and using an SELinux policy for a custom container
To generate an SELinux security policy for a custom container, follow the steps in this procedure.
Prerequisites
-
The
podman
tool for managing containers is installed. If it is not, use thednf install podman
command. - A custom Linux container - ubi8 in this example.
Procedure
Install the
udica
package:# dnf install -y udica
Alternatively, install the
container-tools
module, which provides a set of container software packages, includingudica
:# dnf module install -y container-tools
Start the ubi8 container that mounts the
/home
directory with read-only permissions and the/var/spool
directory with permissions to read and write. The container exposes the port 21.# podman run --env container=podman -v /home:/home:ro -v /var/spool:/var/spool:rw -p 21:21 -it ubi8 bash
Note that now the container runs with the
container_t
SELinux type. This type is a generic domain for all containers in the SELinux policy and it might be either too strict or too loose for your scenario.Open a new terminal, and enter the
podman ps
command to obtain the ID of the container:# podman ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 37a3635afb8f registry.access.redhat.com/ubi8:latest bash 15 minutes ago Up 15 minutes ago heuristic_lewin
Create a container JSON file, and use
udica
for creating a policy module based on the information in the JSON file:# podman inspect 37a3635afb8f > container.json # udica -j container.json my_container Policy my_container with container id 37a3635afb8f created! […]
Alternatively:
# podman inspect 37a3635afb8f | udica my_container Policy my_container with container id 37a3635afb8f created! Please load these modules using: # semodule -i my_container.cil /usr/share/udica/templates/{base_container.cil,net_container.cil,home_container.cil} Restart the container with: "--security-opt label=type:my_container.process" parameter
As suggested by the output of
udica
in the previous step, load the policy module:# semodule -i my_container.cil /usr/share/udica/templates/{base_container.cil,net_container.cil,home_container.cil}
Stop the container and start it again with the
--security-opt label=type:my_container.process
option:# podman stop 37a3635afb8f # podman run --security-opt label=type:my_container.process -v /home:/home:ro -v /var/spool:/var/spool:rw -p 21:21 -it ubi8 bash
Verification
Check that the container runs with the
my_container.process
type:# ps -efZ | grep my_container.process unconfined_u:system_r:container_runtime_t:s0-s0:c0.c1023 root 2275 434 1 13:49 pts/1 00:00:00 podman run --security-opt label=type:my_container.process -v /home:/home:ro -v /var/spool:/var/spool:rw -p 21:21 -it ubi8 bash system_u:system_r:my_container.process:s0:c270,c963 root 2317 2305 0 13:49 pts/0 00:00:00 bash
Verify that SELinux now allows access the
/home
and/var/spool
mount points:[root@37a3635afb8f /]# cd /home [root@37a3635afb8f home]# ls username [root@37a3635afb8f ~]# cd /var/spool/ [root@37a3635afb8f spool]# touch test [root@37a3635afb8f spool]#
Check that SELinux allows binding only to the port 21:
[root@37a3635afb8f /]# dnf install nmap-ncat [root@37a3635afb8f /]# nc -lvp 21 … Ncat: Listening on :::21 Ncat: Listening on 0.0.0.0:21 ^C [root@37a3635afb8f /]# nc -lvp 80 … Ncat: bind to :::80: Permission denied. QUITTING.
Additional resources
-
udica(8)
andpodman(1)
man pages on your system - udica - Generate SELinux policies for containers
- Building, running, and managing containers