我最近在一个 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 已经非常有效,并阻止了许多容器逃逸。
发表回复