Docker Engine 安全

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

  • 内核的内在安全性及其对命名空间和 cgroups 的支持
  • 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)上尤其重要,以保证即使某些应用程序开始行为不端,也能有一致的正常运行时间(和性能)。

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

Docker 守护进程攻击面

使用 Docker 运行容器(和应用程序)意味着需要运行 Docker 守护进程。除非你选择使用无根模式 (Rootless mode),否则该守护进程需要 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,而不是在 VM 中,则容易受到跨站请求伪造攻击)。然后,你可以使用传统的 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 平台上的一个 chrooted 子进程中提取,这是朝着权限分离更广泛努力的第一步。从 Docker 1.10.0 开始,所有镜像都通过其内容的加密校验和来存储和访问,从而限制了攻击者与现有镜像发生冲突的可能性。

最后,如果你在服务器上运行 Docker,建议只在该服务器上运行 Docker,并将所有其他服务移到由 Docker 控制的容器内。当然,保留你喜欢的管理工具(可能至少有一个 SSH 服务器)以及现有的监控/监督进程,如 NRPE 和 collectd,是没问题的。

Linux 内核能力

默认情况下,Docker 以一组受限的能力 (capabilities) 启动容器。这是什么意思?

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

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

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

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

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

这意味着即使入侵者成功在容器内提升到 root 权限,也很难造成严重损害或升级到宿主机。

这不会影响常规的 Web 应用,但会大大减少恶意用户的攻击途径。默认情况下,Docker 会移除除所需能力之外的所有能力,采用白名单而非黑名单的方法。你可以在 Linux 手册页中看到可用能力的完整列表。

运行 Docker 容器的一个主要风险是,默认赋予容器的能力和挂载集可能提供不完整的隔离,无论是独立地还是与内核漏洞结合使用时。

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

Docker 内容信任签名验证

Docker Engine 可以配置为只运行经过签名的镜像。Docker 内容信任签名验证功能直接内置于 dockerd 二进制文件中。
这在 Dockerd 配置文件中进行配置。

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

此功能为管理员提供了比以前使用 CLI 执行和强制镜像签名验证时更多的洞察力。

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

其他内核安全特性

能力只是现代 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 用户,这有助于减轻容器逃逸的风险。此功能可用但默认未启用。

有关此功能的更多信息,请参考命令行参考中的 daemon 命令。关于在 Docker 中实现用户命名空间的更多信息,可以在这篇博客文章中找到。

结论

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

你可以通过启用 AppArmor、SELinux、GRSEC 或其他适当的加固系统来增加一层额外的安全保护。

如果你有任何让 Docker 更安全的想法,我们欢迎在 Docker 社区论坛上提出功能请求、拉取请求或评论。

© . This site is unofficial and not affiliated with Kubernetes or Docker Inc.