博客

信任谬误:我们的多租户Docker设置如何泄露数据(以及我们如何修复)

了解一个团队天真的Docker设置如何导致跨租户数据泄露,以及防止这种情况的分层隔离策略。

摘要

Docker容器默认情况下不是隔离的——它们共享主机内核,如果没有细致的配置,租户之间可能会相互干扰。本文通过一个真实场景,讲述一个多租户托管提供商发现客户容器由于共享网络和薄弱的安全默认设置而能够访问彼此数据库的故事。我们展示了修复漏洞的逐步更改:每个租户的用户定义网络、非root用户、丢弃的能力、只读文件系统和seccomp配置文件。一个常见的假设是容器天生提供强隔离;我们通过解释为什么虚拟机仍然提供更严格的边界以及何时考虑混合方法来挑战这一假设。结论强调隔离是一个分层的工作,而不是一个单一的复选框。

事件:当容器交流过多时

你在一台主机上设置了Docker来运行多个客户端网站。每个客户端都有自己的容器——一个整洁、隔离的环境,对吧?我们也是这么想的。直到一次常规安全审计发现客户端A的容器正在读取同一主机上客户端B容器的MySQL套接字。它们共享默认的bridge网络。更糟的是,容器以root身份运行,因此攻击者如果攻破一个容器,就可以篡改主机的Docker套接字或另一个容器的文件系统。这次漏洞并非复杂的利用;而是基本的配置错误。数据泄露了,信任瓦解了。

这种失败场景并不少见。许多团队认为Docker的命名空间和cgroups会自动隔离租户,但他们低估了默认情况下有多少逃生口仍然开放。默认的桥接网络在容器之间不提供网络隔离。以root身份运行给容器提供了比所需更多的权限。而且没有明确的资源限制,一个吵闹的邻居可能会耗尽其他容器的CPU或内存。

步骤1:停止共享单一网络

我们的第一个修复是给每个租户自己的用户定义Docker网络。这防止容器相互连接,除非你明确连接它们。我们创建了一个脚本,为每个租户启动一个专用网络,并将其应用容器附加到该网络。数据库容器位于同一个租户网络中,但我们也添加了一个仅用于租户内部通信的内部网络。不再有跨租户窥探。

我们还通过在同一租户网络上的单独容器中运行数据库来隔离数据库,使用单独的数据卷。这确保了即使攻击者侵入应用容器,他们也无法嗅探来自另一个租户的数据库流量。

有关网络隔离策略的更深入探讨,请参阅多租户托管的实用Docker隔离安全清单

步骤2:丢弃不必要的权限

默认情况下,Docker容器运行时有有限的Linux能力集,但仍然比大多数应用需要的多。我们的容器以root身份运行,允许内部进程执行挂载文件系统或更改内核参数等操作。我们改为在容器内以非root用户运行应用(使用Dockerfile中的USER指令),并丢弃除绝对必要之外的所有能力。对于一个典型的Web应用,可能只有NET_BIND_SERVICE(用于绑定到1024以下的端口)和CHOWN(用于写入目录)。我们还添加了--security-opt no-new-privileges以防止权限提升。

仅此一步就消除了许多常见的容器逃逸向量。攻击者即使攻破Web服务器,也无法安装软件包、修改系统二进制文件或访问主机的Docker套接字,因为进程缺少CAP_SYS_ADMINCAP_DAC_OVERRIDE能力。

步骤3:锁定文件系统

可写文件系统是常见的攻击面。我们对所有容器将根文件系统设为只读(--read-only),然后为需要写入权限的目录(如/tmp和应用缓存目录)挂载临时文件系统(tmpfs)。这阻止了攻击者修改应用代码或持久化恶意二进制文件。

此外,我们使用Docker的--mount选项仅在绝对必要时绑定挂载敏感目录(如Docker套接字),并且永远不在生产容器上这样做。原则:如果容器不需要写入某个路径,就将其设为只读。

步骤4:应用Seccomp和AppArmor配置文件

默认的seccomp配置文件已经阻止了许多危险的系统调用,但我们进一步自定义它们,仅白名单我们的应用实际需要的系统调用。这是一个权衡,因为它需要分析应用。更简单的方法是使用Docker的默认seccomp配置文件,然后在需要更严格规则时添加--security-opt seccomp=path/to/profile.json。类似地,AppArmor配置文件可以将容器进程限制在特定的文件路径和能力上。我们启用了AppArmor并使用了一个自定义配置文件,限制仅访问应用的数据目录。

关于这些加固步骤的全面指南,请参阅为多租户托管加固Docker容器:逐步隔离指南

相反观点:有时你需要虚拟机

无论多么加固,容器共享主机的内核。内核漏洞可以瞬间打破所有隔离。这就是为什么许多注重安全的平台在轻量级虚拟机内运行容器——每个租户有自己的内核。这增加了开销,但提供了容器单独无法提供的硬件级边界。如果你的租户处理信用卡数据或健康记录,混合方法(虚拟机内的容器)可能是正确的选择。不要假设容器隔离对你的威胁模型足够;评估数据的敏感性和监管要求。

有关隔离级别的更深入比较,请阅读设计多租户Docker架构:选择正确的隔离级别

结论:隔离是一个堆栈,不是一个开关

修复不是单一的改变——而是分层:网络隔离、有限权限、只读文件系统和系统调用过滤。即便如此,我们接受在共享内核容器中完美隔离是不可能的。对于最高安全性的租户,我们将他们迁移到专用主机。教训:不要信任默认设置。审计你的Docker设置,就好像漏洞已经发生。锁定的时机是在泄露之前,而不是之后。

Sources (5)