我最近在 container-selinux 问题 中讨论了为什么我们默认情况下允许容器使用某些功能。讨论围绕 DAC_OVERRIDE 展开,这是一种 Linux 功能,它允许特权进程(通常是 root)忽略所有权和读/写权限 自主访问控制 (DAC)。
“正如 @wrabcak 在 为什么你会看到 DAC_OVERRIDE SELinux 拒绝? 中指出的那样,在大多数情况下 [使用 dac_override] 是应用程序包中的错误,正如 @rhatdan 在 SELinux 团队致力于移除 DAC_OVERRIDE 权限 中指出的那样,在大多数情况下,对 DAC_OVERRIDE 的需求是程序员在设置应用程序时犯下的简单错误,可以通过调整文件系统对象上的权限/所有权来解决。放宽 SELinux 限制应该是最后的手段,当我看容器时,我们默认情况下允许 DAC_OVERRIDE,因为很多容器写得不好,但我认为我们可以默认删除此权限会很棒。”
说容器写得不好需要 DAC_OVERRIDE 是不正确的。实际上,几乎所有容器都不需要 DAC_OVERRIDE 以及其他一大堆功能。
几年前,我做了一个关于 金发姑娘和三只熊 的演讲。在这个演讲中,我解释说,为了保证容器安全,我们走在一条平衡线上,试图让容器尽可能安全,但仍然允许大多数应用程序运行,而不会关闭安全性。如果用户遇到权限被拒绝的情况,他们的本能反应是使用 --privileged 模式,这会关闭几乎所有容器安全性。
在我的博客 容器权限被拒绝:如何诊断此错误 中,我解释了诊断权限被拒绝错误有多难。关于 SELinux 对容器的限制,我的底线是,我在对功能和网络访问的控制上采取了宽松的策略。
使用 container-selinux 策略,主要目标是使用强制访问控制 (MAC) 限制容器对文件系统的访问。
诸如功能之类的自主访问权限是在 Linux 内核中直接处理的,以及用户命名空间。如果 SELinux 默认情况下阻止所有功能访问,那么我们需要为每种功能组合创建不同的类型。
podman run --cap-add CAP_DAC_OVERRIDE –security-opt label=type:container_dac_override_t …
由于有超过 40 种不同的 Linux 功能,这将导致 40 阶乘 (40!) 种不同的类型,仅用于所有可能的 Linux 功能组合。也可以使用布尔值,但很难区分哪些功能比其他功能更强大。例如,CAP_SYS_ADMIN 比 CAP_DAC_OVERRIDE 更加危险。添加 40 多个布尔值会使事情变得过于复杂,对于那些几乎不了解 Linux 安全性的普通用户来说更是如此。同样,我们需要对网络堆栈进行控制。
内核的其他部分支持控制功能和网络访问。这些控制比 SELinux 类型强制执行更灵活。在容器中,容器功能的删除和网络命名空间的使用都是通过与 SELinux 相同的内核强制执行的。我们决定专注于最常见的容器逃逸,即文件系统。
当我编写通用的 container-selinux 策略来控制容器时,我被迫做出类似金发姑娘的妥协。在理想世界中,每个人都会尽可能安全地运行他们的容器,用户可以使用像 Udica 这样的工具来生成自定义策略,以进一步锁定特定容器。您甚至可以获取 containers-selinux 策略并编写自己的类型来运行您的容器。例如
podman run –security-opt label=type:confined_container_t …
依赖容器引擎来控制其他安全子系统(如功能,如 DAC_OVERRIDE)已被证明是有效的。container-selinux 包已用于控制数百万个由 Podman、Docker、CRI-O、Containerd 和 Buildah 等容器引擎启动的容器,已有十年以上。容器 SELinux 策略在 Kubernetes 等容器编排器下运行。OpenShift,Red Hat 的 Kubernetes,为它们提供分离。此外,SELinux 非常有效,并且阻止了许多 容器逃逸。
发表评论