Docker 引擎安全

在审查Docker安全性时,有四个主要领域需要考虑:

  • 内核的固有安全性及其对命名空间和cgroups的支持
  • Docker守护进程本身的攻击面
  • 容器配置文件中存在的漏洞,无论是默认配置,还是用户自定义配置时。
  • 内核的“强化”安全特性以及它们如何与容器交互。

内核命名空间

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

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

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

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

控制组

控制组(Control Groups)是Linux容器的另一个关键组件。它们实现了资源统计和限制。它们提供了许多有用的指标,但也帮助确保每个容器获得其公平的内存、CPU、磁盘I/O份额;更重要的是,确保单个容器不会通过耗尽这些资源之一而使系统崩溃。

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

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

Docker守护进程攻击面

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

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

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

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

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

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

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

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

Linux 内核能力

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

能力将二元的“root/non-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 Engine 可以配置为仅运行已签名的镜像。Docker Content Trust 签名验证功能直接内置在 dockerd 二进制文件中。
这是在 Dockerd 配置文件中配置的。

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

此功能为管理员提供了比之前通过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社区论坛上提出功能请求、拉取请求或评论。