最近,我通过电子邮件回答了一个关于 SELinux 和容器运行时的问题。之后,我意识到其他人可能也在思考同样的问题,所以我决定将我的答案变成一篇 Opensource.com 的文章,希望能帮助其他有同样问题的人。这封邮件开头是:
“Dan,几年前您曾好心回答过我的一个 SELinux 问题,我希望您还在这个行业。”
虽然我现在领导 Red Hat 的容器团队,并且不再在 SELinux 团队工作,但在很多方面,我仍然深度参与 SELinux,因为我一直在努力使 SELinux 和容器能够良好地协同工作。SELinux 和容器技术是完美的结合。
邮件继续写道:
“我刚开始接触一些通过 Docker 容器化的软件,这些软件通常只在 Ubuntu 上运行。这自然意味着没有人考虑过它将如何与 SELinux 交互。”
“我知道默认情况下容器会获得一对随机选择的 MCS [多类别安全] 标签,并且它们创建的文件显然最终会带有相同的类别。但是,当需要重建或升级容器时,这些文件现在变得不可访问,因为新容器具有不同的类别对。”
“我们是否应该使用新类别重新标记这些文件?或者我们是否必须自己选择类别,然后在运行容器时使用 Docker 的
--security-opt
选项?我们如何在不冒着其他容器最终使用相同类别的风险的情况下做到这一点?”
关于第一个问题,当像 Docker 这样的容器运行时,以及我们一直在研究的一些新的运行时—podman、CRI-O 和 Buildah—创建容器时,它们会选择一个随机的 MCS 标签来运行容器。MCS 标签由 0 到 1,023 之间的两个随机数组成,并且必须是唯一的。它们以 c
或类别作为前缀。SELinux 还需要一个敏感级别 s0
。
因此,MCS 标签看起来像 s0:c1,c2
。请注意,s0:c2,c1
是相同的。此外,这两个数字可能不相同;SELinux 会将 s0:c1,c1
转换为 s0:c1
。这为我们提供了大约 (1024 * 1024) /2 -1024
个类别—大约 500,000 个主机上的唯一容器。
我们最初在 2008 年创建了 MCS 标签,用于虚拟机,它通常被称为 sVirt。我们认为在单台机器上运行 50 万台虚拟机在几年内不会发生。对于容器而言,这个数字最终可能会受到威胁。但是我们总是可以为每个标签使用三个或更多类别,尽管算法会变得更加复杂。
SELinux 不仅仅做 MCS 标签。进程和内容也分配了 SELinux“类型”。进程通常以 container_t
类型运行,内容以 container_file_t
类型创建。
进程 | system_u:system_r:container_t:s0:c1,c2 |
内容 | system_u:object_r:container_file_t:s0:c1,c2 |
注意: 我编写了 SELinux/MCS golang 绑定,以原生实现 SELinux 接口来设置标签。这些绑定已贡献给开放容器倡议 (OCI)。
问题的第二部分询问容器在磁盘上创建的内容。作者是正确的,卷上的内容将被标记为内容标签 system_u:object_r:container_file_t:s0:c1,c2
。
但他做了一个错误的假设,即当容器重启时,它将选择不同的 MCS 标签,因此将不再能够读取内容。当容器中的进程停止时,容器运行时不会销毁“容器”。它们记录有关如何运行容器的信息,包括用于运行容器的 SELinux 标签。因此,当您停止并启动容器时,它将始终使用相同的 MCS 标签运行。不仅如此,容器运行时在启动时还会读取其数据库或现有容器,保留已使用的 MCS 标签,以便它们可以保证所有新容器都不会与已保留的 MCS 标签冲突。
您可以覆盖此行为。如果您想创建第二个容器,该容器能够访问第一个容器创建的数据,您可以告诉容器运行时使用相同的 MCS 标签。
# podman run -ti -v /var/lib/previouscontainer:/var/lib/db --security-
opt label=level:s0:c1,c2 fedora sh
# docker run -ti -v /var/lib/previouscontainer:/var/lib/db --security-
opt label=level:s0:c1,c2 fedora sh
现在,如果您从容器运行时中删除容器并将内容留在磁盘上,则标签可能会被重用。处理此内容的最佳方法是在删除容器时更改内容的类型。命令 restorecon -rF /var/lib/previouscontainer
将强制将内容的标签恢复为容器无法读取/写入的标签。
在阅读我的回复后,邮件作者回复了另一个问题:
“哦,如果我同时拥有 Docker 和 libvirt 创建的容器怎么办?”
我可以从两种不同的方式来理解这个问题。一种是他担心通过 Docker 创建的容器和通过 libvirt 创建的虚拟机。这个问题的简单答案是:即使两者都使用相同的 MCS 范围进行标记,它们也使用不同的类型。Libvirt 使用 svirt_t
(进程)和 svirt_image_t
(文件),SELinux 将根据类型强制执行来维护隔离。
但另一种看待这个问题的方式是查看 libvirt-lxc,它也创建容器。使用 container-selinux 的其他工具链包括我们的新工具链 CRI-O、podman 和 Buildah,以及 RKT 和 Systemd-nspawn,甚至 lxc 工具也利用了这一点。
Docker 不与任何其他工具共享 MCS 数据存储,因此最好不要在同一台机器上同时运行它们,或者使用更高级别的工具(如 OpenShift 或 Kubernetes)来选择在容器中运行的 SELinux/MCS 标签,以便保证唯一性。(有关更多详细信息,请参阅此 OpenShift 示例。)
我们的新容器运行时工具 CRI-O、Buildah 和 podman 都共享同一个数据库。您可以在同一主机上同时运行所有这三个工具,而不必担心冲突。
底线
SELinux 为您的容器运行时提供了出色的文件系统隔离,但当您在同一台机器上同时运行多个容器运行时时,以及在删除容器时注意清理主机上遗留的任何内容时,您需要小心。
评论已关闭。