在现代 IT 环境向 Linux 容器发展的同时,保护这些环境的需求与以往一样重要。 容器是一种进程隔离技术。 虽然容器可以成为一种防御机制,但它们只有在与 SELinux 结合使用时才能发挥出色。
Fedora SELinux 工程构建了一个新的独立工具, 尤迪卡,通过自动检查容器为容器生成 SELinux 策略配置文件。 本文重点介绍为什么容器世界需要 udica,以及它如何使 SELinux 和容器更好地协同工作。 您会发现 SELinux 分离容器的示例,这些示例可以让您避免关闭保护,因为通用 SELinux 类型 container_t 太紧了。 使用 udica,您可以使用有限的 SELinux 策略编写技能轻松自定义策略。
SELinux 技术
SELinux 是一种为 Linux 系统带来主动安全的安全技术。 它是一个标签系统,为所有主题(进程和用户)和对象(文件、目录、套接字等)分配一个标签。 然后,这些标签将用于控制整个系统的访问的安全策略。 值得一提的是,SELinux 安全策略中不允许的内容在默认情况下会被拒绝。 策略规则由内核强制执行。 该安全技术已在 Fedora 几年来。 一个真实的 example 这样的规则是:
allow httpd_t httpd_log_t: file { append create getattr ioctl lock open read setattr };
该规则允许任何标记为 httpd_t 的进程 创建、附加、读取和锁定标记为 httpd_log_t 的文件。 使用 ps 命令,您可以列出所有进程及其标签:
$ ps -efZ | grep httpd
system_u:system_r:httpd_t:s0 root 13911 1 0 Apr14 ? 00:05:14 /usr/sbin/httpd -DFOREGROUND
...
要查看哪些对象被标记为 httpd_log_t,请使用 semanage:
# semanage fcontext -l | grep httpd_log_t
/var/log/httpd(/.)? all files system_u:object_r:httpd_log_t:s0
/var/log/nginx(/.)? all files system_u:object_r:httpd_log_t:s0
...
SELinux 安全策略 Fedora 在 selinux-policyRPM 包中提供。
SELinux 与容器
在 Fedora,container-selinux RPM 包为所有由 podman 或 docker 等引擎启动的容器提供了通用的 SELinux 策略。 它的主要目的是保护主机系统免受容器进程的影响,并将容器彼此分离。 例如,SELinux 限制的进程类型为 container_t 的容器只能读取/执行 /usr 中的文件并写入 container_file_t 主机文件系统上的文件类型。 为了防止容器相互攻击,使用了多类别安全性(MCS)。
由于容器使用的种类繁多,只对容器使用一种通用策略是有问题的。 一方面,默认的容器类型 (container_t) 通常过于严格。 为了 example:
另一方面,对于某些用例,默认容器类型可能过于宽松:
- 它没有 SELinux 网络控制——所有容器进程都可以绑定到任何网络端口
- 它没有 SELinux 控制 Linux 功能 — 所有容器进程都可以使用所有功能
有一种解决方案可以处理这两种用例:为容器编写自定义 SELinux 安全策略。 这可能很棘手,因为需要 SELinux 专业知识。 为此,创建了 udica 工具。
介绍 udica
Udica 为容器生成 SELinux 安全配置文件。 它的概念是基于内部的“块继承”特性 通用中间语言 (CIL) SELinux 用户空间支持。 该工具创建的策略结合了:
- 从指定的 CIL 块(模板)继承的规则,以及
- 通过检查包含挂载点和端口定义的容器 JSON 文件发现的规则
您可以立即加载最终策略,或将其移动到另一个系统以加载到内核中。 这是一个 example,使用一个容器:
- 以只读方式挂载 /home
- 将 /var/spool 挂载为读/写
- 暴露端口 tcp/21
容器以以下命令启动:
# podman run -v /home:/home:ro -v /var/spool:/var/spool:rw -p 21:21 -it fedora bash
默认容器类型 (container_t) 不允许这三个操作中的任何一个。 为了证明这一点,您可以使用搜索工具查询系统上是否存在允许规则:
# sesearch -A -s container_t -t home_root_t -c dir -p read
不存在允许标记为 container_t 的进程访问标记为 home_root_t 的目录(如 /home 目录)的允许规则。 标记为 var_spool_t 的 /var/spool 也会出现同样的情况:
# sesearch -A -s container_t -t var_spool_t -c dir -p read
另一方面,默认策略完全允许网络访问。
# sesearch -A -s container_t -t port_type -c tcp_socket
allow container_net_domain port_type:tcp_socket { name_bind name_connect recv_msg send_msg };
allow sandbox_net_domain port_type:tcp_socket { name_bind name_connect recv_msg send_msg };
固定容器
限制这种访问并允许容器仅绑定到 TCP 端口 21 或使用相同的标签会很棒。 想象一下你找到一个 example 使用 ID 为 37a3635afb8f 的 podman ps 的容器:
# podman ps -q
37a3635afb8f
您现在可以检查容器并将检查文件传递给 udica 工具。 新策略的名称是 my_container。
# podman inspect 37a3635afb8f > container.json
# udica -j container.json 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
就是这样! 您刚刚为 example 容器。 现在您可以将此策略加载到内核中并使其处于活动状态。 上面的 udica 输出甚至告诉您要使用的命令:
# semodule -i my_container.cil /usr/share/udica/templates/{base_container.cil,net_container.cil,home_container.cil}
现在您必须重新启动容器以允许容器引擎使用新的自定义策略:
# podman run --security-opt label=type:my_container.process -v /home:/home:ro -v /var/spool:/var/spool:rw -p 21:21 -it fedora bash
这 example 容器现在正在新创建的 my_container.process SELinux 进程类型中运行:
# 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 fedora bash
system_u:system_r:my_container.process:s0:c270,c963 root 2317 2305 0 13:49 pts/0 00:00:00 bash
看到结果
命令 sesearch 现在显示访问 /home 和 /var/spool 的允许规则:
# sesearch -A -s my_container.process -t home_root_t -c dir -p read
allow my_container.process home_root_t:dir { getattr ioctl lock open read search };
# sesearch -A -s my_container.process -t var_spool_t -c dir -p read
allow my_container.process var_spool_t:dir { add_name getattr ioctl lock open read remove_name search write }
新的自定义 SELinux 策略还允许 my_container.process 仅绑定到标记为与 TCP 端口 21 相同的 TCP/UDP 端口:
# semanage port -l | grep 21 | grep ftp
ftp_port_t tcp 21, 989, 990
# sesearch -A -s my_container.process -c tcp_socket -p name_bind
allow my_container.process ftp_port_t:tcp_socket name_bind;
结论
udica 工具可帮助您基于检查文件为容器创建 SELinux 策略,而无需任何 SELinux 专业知识。 现在您可以提高容器化环境的安全性。 来源可在 GitHub,并且 RPM 包可在 Fedora 存储库 Fedora 28 岁及以后。