是什么让 Docker 比虚拟机或裸机更安全?

信息安全 虚拟化 码头工人 隔离
2021-08-14 21:42:38

我最近与 Docker 专家讨论了 Docker 与虚拟机的安全性。当我告诉我从不同的来源读到,在 Docker 容器中运行的代码比在虚拟机中运行的代码更容易从中逃脱时,专家解释说我完全错了,Docker 机器是与虚拟机或裸机相比,在防止恶意代码影响其他机器方面实际上安全。

尽管他试图解释是什么让 Docker 容器更安全,但他的解释对我来说太技术性了。

据我了解,“操作系统级虚拟化重用虚拟机之间的内核空间”,如本网站的不同答案中所述。换句话说,来自 Docker 容器的代码可以利用内核漏洞,而这在虚拟机中是不可能做到的。

因此,与虚拟机或裸机隔离相比,在容器/机器中运行的代码会故意尝试逃逸并感染/损坏其他容器/机器的上下文中,什么可以使使用 Docker 本质上更安全?假设 Docker 配置正确,它可以防止此处描述的四类攻击中的三类。

4个回答

不,Docker 容器并不比 VM 更安全。

引用丹尼尔·夏皮拉的话

仅在 2017 年,就发现了 434 个 linux 内核漏洞,正如您在这篇文章中所看到的,内核漏洞对于容器化环境可能是毁灭性的。这是因为容器与主机共享相同的内核,因此仅信任内置的保护机制是不够的。

1. 来自容器的内核漏洞利用

如果有人利用容器内的内核漏洞,他们就会在主机操作系统上利用它。如果此漏洞允许代码执行,它将在主机操作系统上执行,而不是在容器内。

如果此漏洞允许任意内存访问,则攻击者可以更改或读取任何其他容器的任何数据。

在虚拟机上,这个过程更长:攻击者必须同时利用虚拟机内核、管理程序和主机内核(这可能与虚拟机内核不同)。

2. 资源匮乏

由于所有容器共享相同的内核和相同的资源,如果对某些资源的访问不受限制,则一个容器可以将其全部用完,从而使主机操作系统和其他容器处于饥饿状态。

在 VM 上,资源由管理程序定义,因此没有 VM 可以拒绝主机操作系统使用任何资源,因为管理程序本身可以配置为限制使用资源。

3. 集装箱突围

如果容器内的任何用户能够使用某种漏洞利用或错误配置逃离容器,他们将有权访问主机上运行的所有容器。发生这种情况是因为运行 docker 引擎的用户就是运行容器的用户。如果任何exploit在主机上执行代码,它将在docker引擎的权限下执行,因此它可以访问任何容器。

4.数据分离

在 docker 容器上,有一些资源没有命名空间:

  • SELinux
  • Cgroups
  • /sys, /proc/sys,下的文件系统
  • /proc/sysrq-trigger, /proc/irq,/proc/bus
  • /dev/mem,/dev/sd*文件系统
  • 内核模块

如果任何攻击者可以利用这些元素中的任何一个,他们将拥有主机操作系统。

虚拟机操作系统将无法直接访问任何这些元素。它将与管理程序对话,管理程序将对主机操作系统进行适当的系统调用。它将过滤掉无效调用,增加一层安全性。

5. 原始套接字

/var/run/docker.sock如果没有正确保护,任何容器都可以安装默认的 Docker Unix 套接字 ( )。如果某个容器挂载了这个套接字,它可以关闭、启动或创建新的镜像。


如果正确配置和保护它,您可以使用 docker 容器实现高级别的安全性,但它会低于正确配置的 VM。无论使用多少加固工具,VM 总是更安全。裸机隔离甚至比虚拟机更安全。一些裸机实现(例如 IBM PR/SM)可以保证分区是分开的,就好像它们在单独的硬件上一样。据我所知,没有办法逃避 PR/SM 虚拟化。

说 VM 或 Docker 比另一个更安全是一种过度简化。

VM提供硬件虚拟化;虚拟机管理程序模拟硬件,以便来宾内核认为它在自己的机器上运行。这种类型的虚拟化更容易相互隔离。如果您对虚拟化的主要关注是隔离(您实际上并不需要虚拟机相互交互),那么虚拟机的安全性将大大简化。

Docker 提供内核/操作系统虚拟化,Docker 使用内核命名空间对内核和操作系统进行虚拟化,让来宾认为它运行在自己的操作系统实例上。操作系统虚拟化为如何保护容器之间的互连提供了更大的灵活性。如果您对虚拟化的主要关注要求您互连容器,那么 Docker 提供了以虚拟机不可能或过于繁琐的方式定义这些共享规则的能力。

极大的灵活性伴随着极大的风险;docker 比 VM 更容易配置错误,而 docker 资源共享的灵活性也为实现和配置错误创造了更多机会。但是,如果您设法正确配置共享权限并假设 Docker 或内核中没有实现错误,那么 Docker 提供比硬件虚拟化更细粒度的共享,并可能为您提供更好的整体安全性。

例如,套接字共享。使用 Docker,您可以通过共享套接字文件轻松地在两个容器之间创建共享命名套接字,并且您可以在容器之间定义安全权限(使用任何现有的安全模块:传统的 Unix 权限、功能、SELinux、AppArmor、seccomp 等)套接字端点,以便 docker/kernel 强制执行哪些应用程序以及如何访问套接字端点。使用虚拟机,您可以通过设置一个套接字链来通过 TCP 将数据传送到套接字来共享一个更麻烦的套接字。但是,管理程序的可见性非常有限,并且无法控制对套接字端点的访问,因为对这些套接字端点的权限是由来宾内核应用的。

另一个例子是文件夹共享。使用容器,您可以通过设置共享挂载来共享文件夹,并且由于 Docker/内核强制执行容器使用的文件权限,来宾系统无法绕过该限制。对于虚拟机,如果您想共享文件夹,您必须让一台机器运行网络文件服务器或 Samba 服务器或 FTP 服务器,并且管理程序对共享几乎没有可见性,并且无法强制执行共享权限。此处的其他移动部件(文件服务器)也可能有其自身的漏洞和配置错误问题需要考虑。

TL;DR:使用 VM 进行隔离,使用容器进行受控共享。

正如您正确指出的那样,Docker 使用“操作系统级虚拟化”。您可以将此(如果您是 *nix 粉丝)视为chroot.

通过利用操作系统内置的特性和功能,Docker 充当容器的指挥器。该软件的操作系统视图由 Docker 决定。

所有容器都使用相同的内核。例如,如果我能够在一个容器中引起内核恐慌(想想“蓝屏死机”),那么所有其他容器都会受到影响。

配置似乎比基于硬件的解决方案更为重要。一切都有效地在同一个共享空间中。想象一下把野生捕食者放在它的天然食物来源旁边。如果您没有在捕食者周围放置足够坚固的围栏,或者每次离开围栏时都忘记关闭大门,您可能会想象会发生什么。

虽然肯定是一个轻量级的解决方案,但我当然不会在运行受信任代码的同时运行任何未知代码。

恶意代码必须确定一种将其权限提升到 root/Administrator 级别的方法,以便以任何有意义的方式逃避容器。

在虚拟机中,管理程序会受到攻击,而不是内核。这可能被证明更安全,因为“容器”之间存在更高级别的隔离,但会带来更高的管理开销。

据我了解,没有什么比“裸机”或基于硬件的解决方案更安全的了。我倾向于说 Docker 的安全性较低。就每个软件一个容器而言,这可以证明是一个不同的故事。

如果你不确定真实世界的例子,我会看看 OpenVZ。它以与 Docker 类似的风格使用操作系统级别的虚拟化,但使用了经过修改的内核。

如果我们只是将空白 VM 与空白 Docker 容器进行比较,我同意 ThoriumBR 的回答。然而,应该注意的是,正确配置您的系统(例如在Red Hat 的 Atomic Host中)可以减轻许多这些因素,甚至消除一些因素。

此外,自从 Docker 启动以来,您一方面可以指望他回答中提到的各种漏洞的数量,所有这些都可以通过SELinux等进一步的层来缓解。我们也开始看到基于虚拟机管理程序的 OCI 兼容运行时,如果您真的偏执并愿意承受性能损失,您可以使用它来代替 runc。

我还要指出,软件中的绝大多数漏洞并不存在于 VM 具有安全优势的内核/驱动程序空间,而是存在于 Docker 容器具有优势的应用层,因为它们可以更容易地创建单个进程攻击面。您可以使用单个静态链接的可执行文件构建可用的 Docker 容器,该可执行文件以非 root 用户身份运行,资源和功能有限。你不能制造一个攻击面那么小的虚拟机。

底线是您必须查看整个安全图景。Docker 容器可以非常安全,但您必须查看整个软件供应链,并确保正确配置 docker 和主机。虚拟机有自己的一套优势和劣势。您不能只比较两者“开箱即用”并做出决定。您需要一个流程来强化任一解决方案。