我经常被问及用于控制系统上容器进程行为的不同安全措施。我在 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 权限仍然需要进行大量的教育。如果没有教育,聪明的管理员也可能做出错误的决定。
5 条评论