Docker 引擎安全性

在审查 Docker 安全性时,需要考虑四个主要方面:

  • 内核的内在安全性及其对命名空间和控制组的支持
  • Docker 守护进程本身的攻击面
  • 容器配置文件中的漏洞,无论是默认设置,还是由用户自定义时。
  • 内核的“加固”安全功能及其与容器的交互方式。

内核命名空间

Docker 容器与 LXC 容器非常相似,并且它们具有相似的安全特性。当你使用 docker run 启动一个容器时,Docker 在幕后为该容器创建了一组命名空间和控制组。

命名空间提供了第一种也是最直接的隔离形式。在容器内运行的进程无法看到,更不用说影响在另一个容器或主机系统中运行的进程。

每个容器都有自己的网络栈,这意味着容器无法获取另一个容器的套接字或接口的特权访问权限。当然,如果主机系统相应地设置了,容器可以通过各自的网络接口相互交互——就像它们可以与外部主机交互一样。当您为容器指定公共端口或使用链接时,容器之间允许IP流量传输。它们可以相互ping、发送/接收UDP数据包,并建立TCP连接,但如有必要可以限制这些操作。从网络架构的角度来看,给定Docker主机上的所有容器都位于桥接接口上。这意味着它们就像通过一个通用以太网交换机连接的物理机器;不多也不少。

提供内核命名空间和私有网络的代码有多成熟?内核命名空间是在 内核版本2.6.15和 2.6.26之间 引入的。这意味着自2008年7月(2.6.26版本发布日期)以来,命名空间代码已在大量生产系统中得到实践和审查。不仅如此:命名空间代码的设计和灵感甚至更早。命名空间实际上是以一种可以合并到主流内核的方式重新实现 OpenVZ 功能的努力。而OpenVZ最初发布于2005年,因此其设计和实现都相当成熟。

控制组

控制组是 Linux 容器的另一个关键组件。它们实现了资源统计和限制功能。它们提供了许多有用的指标,同时也确保每个容器能够公平地获得内存、CPU、磁盘 I/O 的份额;更重要的是,确保单个容器不会因为耗尽这些资源中的任何一个而导致系统崩溃。

因此,虽然它们在防止一个容器访问或影响另一个容器的数据和进程方面不起作用,但对于抵御某些拒绝服务攻击至关重要。它们在多租户平台上尤为重要,如公共和私有PaaS,以确保即使某些应用程序开始出现异常行为,也能保持一致的正常运行时间(和性能)。

控制组(Control Groups)也已经存在一段时间了:代码始于2006年,最初合并到内核2.6.24中。

Docker 守护进程攻击面

使用 Docker 运行容器(和应用程序)意味着运行 Docker 守护进程。除非您选择加入 Rootless 模式,否则该守护进程需要 root 特权,因此您应该注意 一些重要细节。

首先,只有受信任的用户才应该被允许控制您的 Docker 守护进程。这是某些强大的 Docker 功能的直接后果。具体来说,Docker 允许您在 Docker 主机和来宾容器之间共享一个目录;并且它允许您这样做 而不限制容器的访问权限。这意味着您可以 启动一个容器,其中 /host 目录是您主机上的 / 目录 ;并且容器可以无限制地修改您的主机文件系统 。这类似于虚拟化系统 允许文件系统资源共享的方式。没有什么能阻止您与 虚拟机共享您的根文件系统(甚至您的根块设备)。

这具有强烈的安全影响:例如,如果您通过API从Web服务器配置Docker以创建容器,您应该比平时更加小心地进行参数检查,以确保恶意用户无法传递精心构造的参数,导致Docker创建任意容器。

因此,REST API 端点(由 Docker CLI 用于与 Docker 守护进程通信)在 Docker 0.5.2 中发生了变化,现在使用 Unix 套接字而不是绑定在 127.0.0.1 上的 TCP 套接字(后者如果您直接在本地机器上运行 Docker,而不是在虚拟机中,容易受到跨站请求伪造攻击)。然后,您可以使用传统的 Unix 权限检查来限制对控制套接字的访问。

如果您明确决定这样做,您也可以通过 HTTP 暴露 REST API。 但是,如果您这样做,请注意上述的安全隐患。 请注意,即使您有防火墙来限制网络中其他主机对 REST API 端点的访问, 该端点仍然可以从容器访问,并且很容易导致权限提升。 因此,必须使用 HTTPS 和证书 来保护 API 端点。 不允许在没有 TLS 的情况下通过 HTTP 暴露守护进程 API, 这样的配置会导致守护进程在启动时提前失败,请参阅 未认证的 TCP 连接。 还建议确保只能从受信任的网络或 VPN 访问它。

如果您更喜欢使用 SSH 而不是 TLS,也可以使用 DOCKER_HOST=ssh://USER@HOSTssh -L /path/to/docker.sock:/var/run/docker.sock 代替。

守护进程也可能容易受到其他输入的影响,例如从磁盘加载镜像时使用 docker load,或从网络加载时使用 docker pull。从 Docker 1.3.2 开始,镜像现在在 Linux/Unix 平台上通过 chroot 子进程提取,这是更广泛的权限分离工作的第一步。从 Docker 1.10.0 开始,所有镜像都通过其内容的加密校验和进行存储和访问,限制了攻击者与现有镜像发生冲突的可能性。

最后,如果您在服务器上运行 Docker,建议在该服务器上仅运行 Docker,并将所有其他服务移至 Docker 控制的容器中。当然,保留您喜爱的管理工具(可能至少包括一个 SSH 服务器)以及现有的监控/监督进程(如 NRPE 和 collectd)也是可以的。

Linux内核功能

默认情况下,Docker 以受限的权限集启动容器。这意味着什么?

功能将二元的“root/非root”对立转化为细粒度的访问控制系统。只需要绑定1024以下端口的进程(如Web服务器)无需以root身份运行:它们只需被授予net_bind_service功能即可。还有许多其他功能,几乎覆盖了所有通常需要root权限的特定领域。这对容器安全意义重大。

典型服务器通常以 root 身份运行多个进程,包括 SSH 守护进程、 cron 守护进程、日志守护进程、内核模块、网络配置工具 等。容器则不同,因为几乎所有这些任务都由容器周围的基础设施处理:

  • SSH 访问通常由运行在 Docker 主机上的单个服务器管理
  • cron,如有必要,应作为用户进程运行,专为需要其调度服务的应用程序定制,而不是作为平台范围的设施
  • 日志管理通常也交给Docker,或者交给像Loggly或Splunk这样的第三方服务
  • 硬件管理无关紧要,意味着您永远不需要 在容器内运行 udevd 或等效守护进程
  • 网络管理发生在容器之外,尽可能强制分离关注点,这意味着容器永远不需要执行 ifconfigroute 或 ip 命令(当然,除非容器被专门设计为像路由器或防火墙一样工作)

这意味着在大多数情况下,容器根本不需要“真正的”root 特权* 因此,容器可以在减少的 能力集下运行;这意味着容器内的“root”比真正的“root”拥有的特权 少得多。例如,可以:

  • 拒绝所有“挂载”操作
  • 拒绝访问原始套接字(以防止数据包欺骗)
  • 拒绝访问某些文件系统操作,例如创建新的设备节点、更改文件所有者或修改属性(包括不可变标志)
  • 拒绝模块加载

这意味着即使攻击者设法在容器内提升到 root 权限,也很难造成严重损害,或者提升到主机权限。

这不会影响常规的 Web 应用程序,但会显著减少恶意用户的攻击向量。默认情况下,Docker 会丢弃所有能力,除了 那些 需要的, 这是一种允许列表而非拒绝列表的方法。您可以在 Linux 手册页 中查看可用能力的完整列表。

运行 Docker 容器的一个主要风险是,默认分配给容器的权限集和挂载点可能提供不完整的隔离,无论是单独使用,还是与内核漏洞结合使用时。

Docker 支持添加和移除功能,允许使用非默认配置文件。这可以通过移除功能使 Docker 更加安全,或者通过添加功能使其安全性降低。用户的最佳实践是移除所有功能,除非这些功能是其进程明确需要的。

Docker 内容信任签名验证

Docker 引擎可以配置为仅运行已签名的镜像。Docker 内容信任签名验证功能直接内置在dockerd二进制。
This is configured in the Dockerd configuration file.

要启用此功能,可以在 daemon.json 中配置信任固定, whereby 只有使用用户指定的根密钥签名的仓库才能被拉取和运行。

此功能为管理员提供了比以前通过CLI执行和执行镜像签名验证更深入的洞察。

有关配置 Docker 内容信任签名验证的更多信息,请访问 Docker 中的内容信任

其他内核安全功能

Capabilities 只是现代 Linux 内核提供的众多安全功能之一。还可以利用现有的、众所周知的系统,如 TOMOYO、AppArmor、SELinux、GRSEC 等,与 Docker 一起使用。

虽然 Docker 目前仅启用功能,但它不会干扰其他系统。这意味着有许多不同的方法可以加固 Docker 主机。以下是几个示例。

  • 您可以运行带有 GRSEC 和 PAX 的内核。这会在编译时和运行时添加许多安全检查;此外,得益于地址随机化等技术,它还能抵御许多漏洞利用。由于这些安全功能适用于整个系统,与容器无关,因此不需要针对 Docker 进行特定配置。
  • 如果您的发行版附带了适用于 Docker 容器的安全模型模板, 您可以直接使用它们。例如,我们 提供了一个适用于 AppArmor 的模板,而 Red Hat 附带了适用于 Docker 的 SELinux 策略。这些模板提供了额外的安全保障(尽管它与功能特性有很大重叠)。
  • 您可以使用您最喜欢的访问控制机制来定义您自己的策略。

正如您可以使用第三方工具来增强 Docker 容器,包括特殊的网络拓扑或共享文件系统,也存在一些工具可以在无需修改 Docker 本身的情况下加固 Docker 容器。

从 Docker 1.10 开始,Docker 守护进程直接支持用户命名空间。此功能允许将容器内的 root 用户映射到容器外的非 uid-0 用户,这有助于减轻容器逃逸的风险。此功能可用,但默认未启用。

请参阅 守护进程命令 以获取有关此功能的更多信息。 有关 Docker 中用户命名空间实现的更多信息,请参阅 这篇博客文章

结论

Docker 容器默认情况下非常安全;特别是如果您在容器内以非特权用户身份运行进程。

您可以通过启用 AppArmor、SELinux、GRSEC 或其他适当的强化系统来增加一层额外的安全性。

如果您有让Docker更安全的方法,我们欢迎功能请求、拉取请求或在Docker社区论坛上发表评论。