我经常被问及用于控制系统上容器进程行为的不同安全措施。其中大部分内容我在 Opensource.com 之前的文章中已经介绍过
以上几乎所有文章都关于控制系统上的特权进程在容器中可以做什么。例如:如何在容器中以 root 身份运行,又不允许其逃逸?
用户命名空间完全是为了在容器中运行特权进程,这样即使它们逃逸,在容器外部也不再具有特权。例如,在容器中我的 UID 是 0(零),但在容器外部我的 UID 是 5000。由于文件系统支持不足的问题,用户命名空间并没有像人们吹捧的那样成为万灵药。直到现在。
OpenShift 是红帽的容器平台,构建于 Kubernetes、Red Hat Enterprise Linux 和 OCI 容器之上,它具有出色的安全功能:默认情况下,不允许任何容器以 root 身份运行。管理员可以覆盖此设置,否则所有用户容器都将在不以 root 身份运行的情况下运行。这在多租户 OpenShift Kubernetes 集群中尤为重要,在这些集群中,单个集群可能为多个应用程序和多个开发团队提供服务。为每个集群运行单独的集群并不总是实际或可取的。遗憾的是,关于 OpenShift 最大的抱怨之一是用户无法轻松运行 docker.io 上提供的所有社区容器镜像。这是因为当今世界上绝大多数容器镜像都需要 root 权限。
为什么所有这些镜像都需要 root 权限?
如果您实际检查在系统上成为 root 用户的理由,它们其实非常有限。
修改主机系统
- 在系统上成为 root 用户的一个主要原因是更改系统的默认设置,例如修改内核的配置。
- 在 Fedora、CentOS 和 Red Hat Enterprise Linux 中,我们有系统容器的概念,这是一种特权容器,可以使用
atomic
命令安装在系统上。它们可以以完全特权模式运行,并允许修改系统以及内核。在系统容器的情况下,我们将容器镜像用作内容交付系统,而不是真正寻求容器隔离。系统容器更适用于核心操作系统主机服务,而不是大多数容器运行的用户应用程序服务。 - 在应用程序容器中,我们几乎从不希望容器内部的进程修改内核。默认情况下,这绝对不是必需的。
Unix/Linux 传统
- 操作系统软件供应商和开发人员早就知道以 root 身份运行进程是危险的,因此内核添加了许多 Linux 功能,允许进程以 root 身份启动,然后尽快放弃特权。大多数 UID/GID 功能允许诸如 Web 服务之类的进程以 root 身份启动,然后变为非 root 用户。这样做是为了绑定到 1024 以下的端口(稍后会详细介绍)。
- 容器运行时可以首先以非 root 用户身份启动应用程序。事实上,systemd 也可以做到这一点,但是过去 20 年中构建的大多数软件都假定它是以 root 身份启动并放弃特权的。
绑定到端口 < 1024
- 早在 20 世纪 60 年代和 70 年代,计算机还很少的时候,非特权用户无法绑定到 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 条评论