未来的 Docker 安全性

还没有读者喜欢这个。
Shipping containers stacked in a yard

Lucarelli via Wikimedia Commons。CC-BY-SA 3.0

当我开始在 Opensource.com 上撰写关于 Docker 安全性系列文章时,我声明“容器并不包含”。

Red Hat 和 Docker 的主要目标之一是使这个说法不再那么正确。我在 Red Hat 的团队正在继续尝试利用其他安全机制来提高容器的安全性。以下是我们正在努力实施的一些安全功能,以及它们在未来可能如何影响 Docker 和容器。

用户命名空间

用户命名空间是一种内核命名空间,它应该使我们能够在主机和容器之间获得更好的隔离。

基本思想是您可以创建一个 UID 范围(例如,60,000-61,000),您可以将其映射到用户命名空间中作为 0-1000。您也可以对 GID 执行此操作。内核会将容器内部的 UID 0 视为容器外部的 UID 60,000。文件或进程上任何不在映射范围内的 UID 都将被视为 UID=-1 并且在容器中不可访问。这包括整个基础镜像。如果您想使用带有用户命名空间的基础镜像,则需要将基础镜像中的所有 UID 更改为新的 Root 用户。用户命名空间的另一个问题是,以 UID 0 拥有的文件挂载到容器中的卷在容器内将不可访问。您必须对容器中所需的所有内容执行 chown,使其归 UID 范围所有。

chown -R 60000:60000 /var/lib/content

用户命名空间的另一个问题是,如果您想使用它们来隔离容器,则需要为每个容器设置不同的 UID 范围。如果您有数百个容器,则需要数百个范围。当在容器主机之间进行共享存储时,这也成为一个问题。

用户命名空间的一个很酷的地方是它们允许使用命名空间化的 capabilities(能力)。如果您将容器放入用户命名空间中,它不再需要真正的系统 capabilities。这意味着我们可以调整代码,在容器启动用户命名空间时删除所有系统 capabilities。它还允许我们从 SELinux 标签中删除所有 capabilities。

用例

我至少看到了用户命名空间的三个不同的潜在用例。

  1. 提高容器之间的一般隔离,以便我们可以关闭容器的所有 Linux capabilities。这样做可以加强系统免受容器攻击的安全性,但不一定能提高容器之间的隔离。在这种模式下,我设想我们将为 DOCKERROOT 选择一个 UID,然后设置所有容器以使用它。例如,如果 DOCKERROOT 是 UID=2,我将为 UID0=2 和 GID0=2 设置映射,然后将大于 2 的所有 UID 映射到它们自身。例如,3-MAX_UID=3-MAX_UID,我们将对 GID 执行类似的操作。通过这样做,我们消除了容器攻击 root 的能力。对于卷挂载来说也简单得多。

    我建议也许我们可以尝试默认只在用户命名空间内使用 capability drop(能力删除),例如,通过将 UID 0-65,0000 映射到 UID 0-65,0000。然后,如果您将 root 拥有的文件卷挂载到容器中,它将起作用,但容器外部的进程将没有任何 capabilities。 通过这样做,我们可以以一种理智的方式试验使用用户命名空间。

  2. OpenShift 方法:容器内的所有文件都映射到单个 UID/GID 对。系统上的每个用户都获得不同的 UID。 这样做的主要原因是当用户容器需要进程以内核 capability 运行时。 否则,使用用户命名空间几乎没有增加什么。

  3. 每个容器都从其他每个容器获得单独的 UID 范围映射。这使您能够运行大量容器,并使用 UID 隔离来分隔容器。但这会使复杂性急剧增加。卷挂载变得非常头疼。为了使它工作,我建议我们添加 -v /SRC/DEST:U,这将在挂载期间将 /SRC 的 UID:GID chown 为容器的默认 UID。

但是,我并不是建议这三个用例可以一起使用。 我已经看到向内核提出的建议,允许在加入容器时“重新映射 UID”,即使可能使用绑定挂载,但我将把它留给内核人员,看看是否可行,并听取安全人员关于这是否是一个好主意的意见。

用户命名空间已在此点合并到 libcontainer 中,并且正在准备补丁以使其可以在 Docker 中运行。

Seccomp

此处和其他地方描述的所有容器隔离模式的问题之一是,它们都依赖内核进行隔离。与气隙计算机甚至虚拟机不同,容器内的进程可以直接与主机内核对话。如果主机内核存在容器可以访问的内核漏洞,则它们可能能够禁用所有安全措施并突破容器。

x86_64 Linux 内核有 600 多个系统调用,其中任何一个中的错误都可能导致权限提升。一些系统调用很少被调用,应该从容器内的访问中消除。

Seccomp 是由 Google 开发的,用于从进程中删除系统调用。Google 在 Chrome 浏览器内部使用它来执行插件。由于插件往往是从互联网下载的不受信任的内容,因此您确实需要控制插件的安全性。

我的同事 Paul Moore 决定构建一个 C 库来简化 syscall 树的管理,从而使 seccomp 更易于使用。Libseccomp 现在用于 qemu、systemd、lxc 工具和其他一些工具中。

我们还为 libseccomp 编写了一个 Go 绑定,我们正在努力将其放入 libcontainer 中,以从容器中删除系统调用。

我们建议从容器中删除以下系统调用列表:kexec_load、open_by_handle_at、init_module、finit_module、delete_module、iopl、ioperm、swapon、swapoff、sysfs、sysctl、adjtimex、clock_adjtime、lookup_dcookie、perf_event_open、fanotify_init 和 kcmp。

我们希望获得有关要删除的其他系统调用的建议。 我们还希望删除 Linux 中允许的所有旧网络,包括:Amateur Radio X.25 (3)、IPX (4)、Appletalk (5)、Netrom (6)、Bridge (7)、ATM VPC (8)、X.25 (9)、Amateur Radio X.25 PLP (11)、DECNet (12)、NetBEUI (13)、Security (14)、PF_KEY 密钥管理 API (15) 以及所有大于 AF_NETLINK (16) 的套接字调用。

放入系统调用过滤器的另一个效果是,默认情况下,它会删除所有其他架构的系统调用。例如,默认情况下,您将无法使用启用 seccomp 的容器调用 i386 系统调用。我们希望将其作为合并后的默认设置。

通过消除上述系统调用和其他架构的系统调用,我们可以将内核上的攻击面缩小一半以上。

调整 Seccomp

与 capabilities 和 SELinux 标签类似,我们还在 Docker 中构建了在命令行消除其他系统调用的能力。

docker run -d --security-opt seccomp:allow:clock_adjtime ntpd

这将允许系统调用返回到容器中。

docker run -d --security-opt seccomp:deny:getcwd /bin/sh

同样,这将消除容器查看其当前工作目录的能力。Red Hat 的 Matt Heon 有一个简短的视频展示了 seccomp 的实际应用。您也可以从此处下载视频文件。

我们从要阻止的系统调用黑名单开始,但对于真正有冒险精神的人来说,您可以从关闭所有系统调用并添加一些回来开始。

docker run -d --security-opt seccomp:deny:all --security-opt seccomp:allow:getcwd /bin/sh

实际上,您需要更多的系统调用才能使其工作。系统调用的拒绝将显示在 /var/log/audit/audit.log 中,就像 SELinux 错误一样,或者在 /var/log/messages 中(如果审计未运行)。

未来的 Docker

我们将继续研究可以添加的其他安全功能。如果 Linux 内核中出现或改进了新的安全功能,我们希望能够在容器中利用这些功能。

我们开始研究的另一个领域是容器的管理。目前,如果您可以连接到网络上 Docker 的套接字或端口,您可以做任何您想做的事情。可悲的是,您可以轻松地破坏系统的安全性,这就是为什么我们关闭了非 root 用户对 /run/docker.socket 的访问权限。我们开始研究添加授权,以便管理员可以证明他是特定用户。我们还在研究添加适当的日志记录,以便我们可以记录哪个管理员使用特权将容器运行到 syslog/journalctl 中。最后,我们希望添加基于角色的访问控制 (RBAC),以便管理员可以控制其他管理员可以做什么。例如

  • 管理员 1 仅允许启动/停止以下容器。
  • 管理员 2 允许在镜像 foobar 上创建非特权容器。
  • 管理员 3 允许运行超级特权容器。

结论

当这些安全功能完全实施后,Docker 容器将更能抵抗主机系统上的安全风险。目标始终是提高容器的包含能力。

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

4 条评论

我想知道 seccomp/限制系统调用将如何影响 RancherOS 之类的东西 - 我认为 RancherOS 采取了理智的方法,即在 docker 容器中 *完成所有事情*,包括 init/启动过程。我可以想象这仍然需要访问模块加载和启用/禁用交换......

所以归结为这一点:您是否可以为特定容器禁用此功能(为使用 --privileged 启动的容器禁用它,或添加您可以启用的某些 capabilities)

好吧,首先 RancherOS 运行着 2 个 docker 守护进程。

一个用于“系统”容器,一个用于“用户”容器。

如果我没记错的话,系统容器已经在 run 命令中传递了一些特殊选项。甚至不同系统容器的不同选项,我现在不记得了。

但我很确定目标是允许运行容器的人员或进程指定任何选项。

回复 作者 Bart M. (未验证)

所以我猜我错了,认为 Docker 已经支持 seccomp?

还是只在旧的 LXC 后端中?

是的,lxc-backend 支持 seccomp。只需传递 --lxc-conf lxc.seccomp=<您想要的策略>

© . All rights reserved.