容器中请拒绝 root 权限

即使是聪明的管理员也可能做出错误的决定。
418 位读者喜欢这篇文章。
tree roots breaking through brick wall

Rikki Endsley。CC BY-SA 4.0

我经常被问及用于控制系统上容器进程行为的不同安全措施。我在 Opensource.com 上之前的文章中已经介绍了其中的大部分内容。

以上几乎所有文章都关于控制系统上的特权进程在容器中可以做什么。例如:如何在容器中以 root 身份运行,又不允许它逃逸出去?

用户命名空间完全是为了在容器中运行特权进程,这样即使它们逃逸出去,在容器外部也不再具有特权。例如,在容器中我的 UID 是 0(零),但在容器外部我的 UID 是 5000。由于文件系统支持方面的问题,用户命名空间并没有像宣传的那样成为万能灵药。直到现在。

OpenShift 是红帽的容器平台,构建于 Kubernetes、红帽企业 Linux 和 OCI 容器之上,它有一个很棒的安全特性:默认情况下,不允许任何容器以 root 身份运行。管理员可以覆盖此设置,否则所有用户容器都将在不以 root 身份运行的情况下运行。这在多租户 OpenShift Kubernetes 集群中尤为重要,在这些集群中,单个集群可能为多个应用程序和多个开发团队提供服务。为每个集群运行单独的集群并不总是实际或甚至是明智的。可悲的是,关于 OpenShift 最大的抱怨之一是用户无法轻松运行 docker.io 上提供的所有社区容器镜像。这是因为当今世界上绝大多数容器镜像都需要 root 权限。

为什么所有这些镜像都需要 root 权限?

如果您实际检查成为 root 权限的原因,在系统上,这些原因非常有限。

修改主机系统

  • 在系统上成为 root 权限的一个主要原因是更改系统的默认设置,例如修改内核的配置。
  • 在 Fedora、CentOS 和红帽企业 Linux 中,我们有系统容器的概念,它们是可以使用 atomic 命令安装在系统上的特权容器。它们可以完全以特权身份运行,并被允许修改系统以及内核。在系统容器的情况下,我们使用容器镜像作为内容交付系统,而不是真正寻求容器隔离。系统容器更适用于核心操作系统主机服务,而不是大多数容器运行的用户应用服务。
  • 在应用程序容器中,我们几乎从不希望容器内部的进程修改内核。这绝对不是默认情况下需要的。

Unix/Linux 传统

  • 操作系统软件供应商和开发人员早就知道以 root 身份运行进程是危险的,因此内核添加了许多 Linux capabilities,允许进程以 root 身份启动,然后尽快放弃特权。大多数 UID/GID capabilities 允许像 Web 服务这样的进程以 root 身份启动,然后变为非 root 身份。这样做是为了绑定到 1024 以下的端口(稍后会详细介绍)。
  • 容器运行时可以一开始就以非 root 身份启动应用程序。事实上,systemd 也可以做到这一点,但过去 20 年中构建的大多数软件都假定它以 root 身份启动并放弃特权。

绑定到 < 1024 端口

  • 早在 1960 年代和 1970 年代,当计算机很少的时候,非特权用户无法绑定到 < 1024 的网络端口被认为是安全特性。因为只有管理员才能做到这一点,所以您可以信任在这些端口上侦听的应用程序。> 1024 的端口可以被系统上的任何用户绑定,因此它们不可信任。这种安全优势在很大程度上已经消失,但我们仍然受此限制的约束。
  • 此限制的最大问题是 Web 服务,人们喜欢让他们的 Web 服务器在端口 80 上侦听。这意味着 apache 或 nginx 最初以 root 身份运行的主要原因是为了它们可以绑定到端口 80,然后变为非 root 身份。
  • 容器运行时可以使用端口转发来解决此问题。您可以设置容器以侦听任何网络端口,然后让容器运行时将该端口映射到主机上的端口 80。

在此命令中,podman 运行时将在您的机器上运行一个 apache_unpriv 容器,该容器在主机上的端口 80 上侦听,而容器内部的 Apache 进程永远不是 root 身份,而是以 apache 用户身份启动,并被告知在端口 8080 上侦听。

podman run -d -p 80:8080 -u apache apache_unpriv

或者

docker run -d -p 80:8080 -u apache apache_unpriv

将软件安装到容器镜像中

  • 当 Docker 引入使用 docker build 构建容器时,容器中的内容只是用于发行版的标准打包软件。该软件通常通过 rpm 包或 Debian 包提供。嗯,发行版打包软件是为了由 root 用户安装。软件包希望能够执行诸如通过添加用户来操作 /etc/passwd 文件之类的操作,并在文件系统上放置具有不同 UID/GID 的内容。许多软件还期望通过 init 系统 (systemd) 启动,并以 root 身份启动,然后在启动后放弃特权。
  • 可悲的是,在容器革命五年后,这仍然是现状。几年前,我曾尝试让 httpd 包知道它是由非 root 用户安装的,并具有不同的配置。但我放弃了。我们需要让打包者和包管理系统开始理解这种差异,然后我们就可以制作出无需 root 权限即可运行的优秀容器。
  • 我们现在可以做的一件解决此问题的事情是将构建系统与安装系统分开。我对 #nobigfatdaemons 的一个问题是,Docker 守护程序导致运行容器的权限与构建容器镜像的权限相同。
  • 如果我们更改系统或使用不同的工具,例如 Buildah 用于构建具有较宽松约束的容器,以及 CRI-O/Kubernetes/OpenShift 用于在生产环境中运行容器,那么我们可以使用提升的权限进行构建,但随后以更严格的约束运行容器,或者希望以非 root 用户身份运行。

底线

您在容器中运行的几乎所有软件都不需要 root 权限。您的 Web 应用程序、数据库、负载均衡器、数值计算程序等永远不需要以 root 身份运行。当我们让人们开始构建完全不需要 root 权限的容器镜像,并让其他人基于非特权容器镜像构建他们的镜像时,我们将看到容器安全性方面的一次巨大飞跃。

围绕在容器内部运行 root 权限仍然需要进行大量的教育。如果没有教育,聪明的管理员也可能做出错误的决定。

User profile image.
Daniel Walsh 在计算机安全领域工作了近 30 年。Dan 于 2001 年 8 月加入红帽。

5 条评论

软件包希望能够执行诸如通过添加用户来操作 /etc/passwd 文件之类的操作,并在文件系统上放置具有不同 UID/GID 的内容。许多软件还期望通过 init 系统 (systemd) 启动,并以 root 身份启动,然后在启动后放弃特权。 http://www.animeflvapkapp.com/

这篇文章对于了解容器的 root 安全性很有用。我了解了容器与内核的 root 行为一起工作的方式。

我认为这篇文章听起来合乎逻辑,但得出了不正确的结论。的确,当人们讨论“需要” root 访问权限时,首先想到的是 1024 以下的端口号和修改内核设置。但是,这篇文章不加批判地接受了“用户应该拥有执行其工作所需的最低访问权限”的观念。这种想法或最佳实践在今天被广泛接受,但其含义却很少被讨论。这种想法的增加可以而且确实会显着增加生产故障和安全事件的风险。

原因如下:当彻底调查生产故障时,通常会有许多促成因素 - 通常是四个、五个或更多。我的经验是,人为无知通常至少是其中两到三个因素的一部分。当“系统管理员”成为一种既定的职业道路时,程序员拥有 root 权限变得不那么普遍了——所有这些都是出于非常合乎逻辑的良好意图。这带来的意外后果是,作为开发人员群体,他们对自己的软件在生产环境中的实际行为了解较少。Linux 已经发展成为一个可观察性更高的操作系统,具有复杂的跟踪工具——其中许多工具需要 root 权限。与此同时,今天的多插槽、多核硬件功能更强大,也更复杂。然而,随着硬件变得越来越强大,很明显性能和安全性似乎仍然停滞不前。为了让应用软件充分利用拥有理想的开发人员,他们需要了解他们的代码如何与机器交互——虚拟机、容器和 Java(3 个巨大的积极发展)的普及都掩盖了这一点。不幸的是,遵循安全最佳实践可能会将我们带入不太安全的环境,在这些环境中,没有人了解整个系统。

只是为了进一步连接这些点... 这就是我所说的开发人员了解系统如何使用资源的意思
“应用程序 X 有 100 多个线程。其中三个是热线程,它们在一个核心上旋转——市场数据事件处理程序和两个工作线程。其余的大多数线程是每个连接一个线程的线程,它们在大部分时间都是冷的。然后有四个暖线程——记录器、持久器... 我们希望市场数据事件处理程序始终在插槽一上运行,因为市场数据 NIC 在第二个 PCI-X 插槽上,并且该线程可以保持 NUMA 本地。此应用程序的延迟比吞吐量更重要,因此我们不使用默认的 NIC 中断合并设置,...”

验证这些前提条件是否为真的最佳方法是使用 ethtool、perf-test(两者都需要 root 权限)。因此,问题不应该是“我们需要 root 访问权限吗?”,而应该是“拥有 root 访问权限是否有意义?”。我看到开发人员和 SA 不了解他们的系统行为所造成的损害,远远超过了在 root shell 中偶尔人为错误造成的损害。我认为应该审核 root 访问权限——关于主机最好的学习经验之一可能只是执行
sudo -s
history | more

回复 ,作者 Peter Booth

只是为了进一步连接这些点... 这就是我所说的开发人员了解系统如何使用资源的意思
“应用程序 X 有 100 多个线程。其中三个是热线程,它们在一个核心上旋转——市场数据事件处理程序和两个工作线程。其余的大多数线程是每个连接一个线程的线程,它们在大部分时间都是冷的。然后有四个暖线程——记录器、持久器... 我们希望市场数据事件处理程序始终在插槽一上运行,因为市场数据 NIC 在第二个 PCI-X 插槽上,并且该线程可以保持 NUMA 本地。此应用程序的延迟比吞吐量更重要,因此我们不使用默认的 NIC 中断合并设置,...”

验证这些前提条件是否为真的最佳方法是使用 ethtool、perf-test(两者都需要 root 权限)。因此,问题不应该是“我们需要 root 访问权限吗?”,而应该是“拥有 root 访问权限是否有意义?”。我看到开发人员和 SA 不了解他们的系统行为所造成的损害,远远超过了在 root shell 中偶尔人为错误造成的损害。我认为应该审核 root 访问权限——关于主机最好的学习经验之一可能只是执行
sudo -s
history | more

回复 ,作者 Peter Booth

知识共享许可协议本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
© . All rights reserved.