Docker Engine 安全性

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

  • 内核的内在安全性及其对命名空间和 cgroup 的支持
  • 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 守护程序。除非您选择加入 无根模式,否则此守护程序需要 `root` 权限,因此您应该了解一些重要细节。

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

这具有强大的安全意义:例如,如果您从 Web 服务器使用 Docker 来通过 API 预配容器,则应该比以往更加小心地进行参数检查,以确保恶意用户无法传递导致 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 访问。

如果您更喜欢 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”权限。例如,可以

  • 拒绝所有“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 用户,这有助于减轻容器突破的风险。此功能可用,但默认情况下未启用。

有关此功能的更多信息,请参阅命令行参考中的守护程序命令。有关在 Docker 中实现用户命名空间的更多信息,请参见这篇博文

结论

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

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

如果您想到使 Docker 更安全的方法,欢迎您在 Docker 社区论坛上提出功能请求、拉取请求或评论。