Docker 未来安全性

还没有读者喜欢这篇文章。
Shipping containers stacked in a yard

Lucarelli 通过 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 范围。如果您有数百个容器,则需要数百个范围。当容器主机之间进行共享存储时,这也成为一个问题。

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

用例

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

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

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

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

  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 中允许的所有旧网络,包括:业余无线电 X.25 (3)、IPX (4)、Appletalk (5)、Netrom (6)、Bridge (7)、ATM VPC (8)、X.25 (9)、业余无线电 X.25 PLP (11)、DECNet (12)、NetBEUI (13)、Security (14)、PF_KEY 密钥管理 API (15) 以及所有大于 AF_NETLINK (16) 的套接字调用。

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

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

调整 Seccomp

与功能和 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/syscall 限制将如何影响像 RancherOS 这样的东西 - 我认为 RancherOS 采用了理智的方法,在 docker 容器中 *完成所有事情*,包括 init/boot 过程。我可以想象这仍然需要访问模块加载和启用/禁用交换...

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

嗯,首先 RancherOS 运行了 2 个 docker 守护程序。

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

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

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

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

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

或者那只是使用旧的 LXC 后端?

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

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.