In Red Hat Enterprise Linux 8.1 beta, we included a new utility for generating tailored SELinux policies for containers. To learn what Udica offers, why you might want to start testing Udica, and how to get started, read on!
Confined container environment
Containers in the default Red Hat OpenShift environment are separated from each other, and from the system, in part by SELinux technology. SELinux is a process isolation technology which can help mitigate or block various attacks on the system. A recent flaw is the "malicious container escape" (CVE-2019-5736), which could enable an attacker to escape from the container namespace and run arbitrary code on the host system with root privileges. Fortunately, SELinux in Enforcing mode helps make this exploit ineffective.
While it is our observation that no customer environment is the same, we see that most if not all containerised environments have the same need for isolation that can be provided by SELinux.
In order to satisfy diverse customer environments, container SELinux policies need to be present, but also easily tailored. Our way to achieve the easy tailoring of our policies, is to use Udica—a tool for generating tailored SELinux policies for production containers. In the following paragraphs we introduce Udica and its features.
SELinux policy for containers
By default, containers on OpenShift are confined by one general SELinux policy for all containers on the system. The SELinux type for container processes is container_t
. This policy allows containers to read or execute files in /usr
only and to read, write and execute all files on the system labeled container_file_t
. As you can see this is really strict confinement, and mainly protects the host system from container processes.
However, there is also the Super Privileged Container SELinux policy spc_t
, with this policy the container is basically unconfined from SELinux point of view. This policy has to be specified by the system administrator during container startup.
All containers use the same SELinux type (container_t
) which protects the host system, but how do we separate and protect containers from attacking each other?
For this purpose, Multi Category Security (MCS) is enabled for the container_t
SELinux type. Container runtimes (docker, Podman, CRI-O) will dynamically assign two categories when starting a container and concatenate these categories to the SELinux label of the running container. Categories can be unique and randomly generated, or can be specified by the system administrator who is starting containers. These categories protect containers from attacking each other even though they have same SELinux type.
Example of containers labels:
-
container1 - container_t:s0:c349,c851
-
container2 - container_t:s0:c270,c963
Problems with SELinux Container Confinement
The current solution brings several problems with confining containers. For all containers there is just one general SELinux policy. This cannot fulfill the ideal balance between security and usability for containers.
On one hand, for certain use cases container type (container_t
) is too strict, such as when some directory is bind mounted to container filesystem namespace. This is required to allow container processes to access this directory.
A real example is a container with the Fluentd service inside—this container needs to read log files in /var/log
on the host filesystem.
On the other hand, for certain use cases the container type is too loose. There are two main situations when SELinux policy should be tighter. The first one is network controlling, when all container processes can bind to any network port. The second one is capability controlling, when all container processes can use all Linux capabilities.
We can approach these problems with two solutions.
Solutions
In the past there were two solutions how to solve all problems mentioned in the previous section.
The first would be to write completely new SELinux policy for custom containers. This has been the best solution so far and it can help tailor security policy to the needs of your application. However, it’s difficult for system administrators because deep SELinux expertise is required.
The second is to create a new SELinux custom module with additional rules for the generic SELinux type container_t
and load this custom module to the kernel. This is not really a good approach, as additional allow rules will be granted to all containers on the system, because container_t is a generic label assigned to containers by default and deep SELinux expertise is required.
To bring a solution which can address the disadvantages mentioned above, the Udica tool was created. Udica is a tool for generating SELinux security policies for containers. The whole concept is based on the "block inheritance" feature inside SELinux's Common Intermediate Language (CIL). This is an intermediate language supported by SELinux utilities.
Udica creates a policy which combines templates using rules inherited from a specified CIL block and rules discovered by the inspection of the container JSON (lightweight data-interchange format) file, which contains mount points and ports definitions.
How it works
The process of generating SELinux policy for a container using Udica could be split into three main parts:
-
Parsing the container spec file
-
Finding suitable allow rules based on the results of the first part
-
Generating final SELinux policy
During parsing, Udica parses the spec file of a container which can be provided on its standard input or using a command-line parameter. The tool requires the inspection of a file in JSON format. The container the policy is being generated for doesn’t need to be running on the system where the tool is executed. Udica looks for three types of information:
-
Linux capabilities
-
Network ports
-
Mount points
Based on the results, Udica detects which Linux capabilities are required by the container and creates a SELinux rule allowing all these capabilities. The network ports are a similar situation, Udica uses SELinux userspace libraries to get the correct SELinux label of a port that is used by the inspected container.
This step applies only if the container binds to a specific port. As the next step, Udica detects which directories are mounted to the container filesystem namespace from the host.
CIL's block inheritance feature allows Udica to create templates of SELinux allow rules focusing on some concrete action, like: “Allows access home directories, Allows access log files, Allows access communication with Xserver, etc.” These templates are called blocks and by building (“merging”) blocks together the final SELinux policy is created.
Let’s say Udica generates SELinux policy with the name my_container
for example container which is mounting the /home
directory from the host filesystem to container filesystem namespace. The directory /home
is used for storing users data on the filesystem. Our example container is also exposing port tcp/21, used for the File Transfer Protocol (ftp).
Udica detects that the /home
directory is mounted to container space and the port tcp/21 is exposed off the container inspection file. The tool searches for existing blocks (templates), and if the suitable block doesn’t exist, Udica generates a new one, just for this SELinux policy. In this case, all blocks exist, and are used for final SELinux policy.
The first block which is used is the “base” block, it’s used in every SELinux policy generated by the Udica tool. The base block is is quite similar to SELinux policy included in the container-selinux rpm package. This is used by Podman and CRI-O when starting containers. It allows reading and executing files in “/usr” and reading generic configuration files stored in “/etc.”
The second block which is used is the “net” block, because the example container is exposing tcp port 21. The tool will enhance the block by adding the bind permission only for the tcp port 21 labeled by SELinux as ftp_port_t
. The third block used is the “home” block, because the example container mounts the /home
directory and based on the information in the container inspection file, the tool can use a block just with read permissions or read/write permissions. All these blocks are shown in figure 1.
![Figure 1: permissions for containers set by udica Figure 1: permissions for containers set by udica](/rhdc/managed-files/image2_12.png)
Figure 1
In the third phase of the Udica tool workflow, all blocks are merged and enhanced based on the needs from the inspection container file, and the final SELinux policy for example container is generated. This process is described in Image 2. The final SELinux security policy for the example container can be loaded immediately or moved to another system where it can be loaded via the SELinux userspace semodule tool.
![third phase of the Udica tool workflow third phase of the Udica tool workflow](/rhdc/managed-files/image1_18_0.png)
Figure 2
Supported container engines and systems
The Udica tool now supports generating SELinux security policies for containers started from podman and docker container engines. The CRI-O container engine is expected to be supported in the near future. Already generated SELinux policies could then be used for containers using podman, CRI-O, docker engines. SELinux security policies generated from the Users have been able to Udica tool with Fedora since Fedora 28 and on Red Hat Enterprise Linux 8.1 beta.
Conclusion
Udica is a new tool which complements the containers tools (Podman, Skopeo, Buildah, etc.) family supported by Red Hat to help improve the security of customers container environments. It provides a new way to help deal with 0-day vulnerabilities in containers tooling and malicious programs inside containers.
This tool provides a solution finer with grained control, essentially placing it between spc_t
(Super Privileged Container) and container_t
(Standard Containers). When a container is labeled as spc_t
by SELinux, basically the container is unconfined from the SELinux point of view. This is undesirable for security reasons. Our goal is to make SELinux security technology more user friendly and help system administrators in their job to provide highly secure production environments.
The name “Udica” means “Fishing rod” in the author's native Slovak language, and it was selected based on this popular though contested quote:
Give a man a fish, and you feed him for a day. Teach a man to fish, and you feed him for a lifetime.
With this tool we provide you a fishing rod not to fish, but to custom-tailor your policies without the need for SELinux policy expertise. We welcome you to use the tool and please let us know about your experience!
저자 소개
Lukas Vrabec is a Senior Software engineer & SELinux technology evangelist at Red Hat. He is part of Security Controls team working on SELinux projects focusing especially on security policies. Lukas is author of udica, the tool for generating custom SELinux profiles for containers and currently maintains the selinux-policy packages for Fedora and Red Hat Enterprise Linux distributions.
채널별 검색
오토메이션
기술, 팀, 인프라를 위한 IT 자동화 최신 동향
인공지능
고객이 어디서나 AI 워크로드를 실행할 수 있도록 지원하는 플랫폼 업데이트
오픈 하이브리드 클라우드
하이브리드 클라우드로 더욱 유연한 미래를 구축하는 방법을 알아보세요
보안
환경과 기술 전반에 걸쳐 리스크를 감소하는 방법에 대한 최신 정보
엣지 컴퓨팅
엣지에서의 운영을 단순화하는 플랫폼 업데이트
인프라
세계적으로 인정받은 기업용 Linux 플랫폼에 대한 최신 정보
애플리케이션
복잡한 애플리케이션에 대한 솔루션 더 보기
오리지널 쇼
엔터프라이즈 기술 분야의 제작자와 리더가 전하는 흥미로운 스토리
제품
- Red Hat Enterprise Linux
- Red Hat OpenShift Enterprise
- Red Hat Ansible Automation Platform
- 클라우드 서비스
- 모든 제품 보기
툴
체험, 구매 & 영업
커뮤니케이션
Red Hat 소개
Red Hat은 Linux, 클라우드, 컨테이너, 쿠버네티스 등을 포함한 글로벌 엔터프라이즈 오픈소스 솔루션 공급업체입니다. Red Hat은 코어 데이터센터에서 네트워크 엣지에 이르기까지 다양한 플랫폼과 환경에서 기업의 업무 편의성을 높여 주는 강화된 기능의 솔루션을 제공합니다.