Docker 安全
检查 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创建任意容器。
因此,REST API 端点(Docker CLI 用于与 Docker 守护进程通信)在 Docker 0.5.2 中发生了更改,现在使用 UNIX 套接字而不是绑定在 127.0.0.1 上的 TCP 套接字(后者容易出现如果您碰巧在虚拟机外部的本地计算机上直接运行 Docker,则会发生跨站点请求伪造攻击)。然后,您可以使用传统的 UNIX 权限检查来限制对控制套接字的访问。
如果您明确决定这样做,您还可以通过 HTTP 公开 REST API。但是,如果您这样做,请注意上述安全隐患。请注意,即使您有防火墙限制网络中其他主机对 REST API 端点的访问,该端点仍然可以从容器访问,并且很容易导致权限提升。因此,必须使用HTTPS 和证书来保护 API 端点的安全 。还建议确保只能从受信任的网络或 VPN 访问它。
如果您更喜欢 SSH 而不是 TLS,您也可以使用DOCKER_HOST=ssh://USER@HOST
或来代替。ssh -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 启动具有一组受限功能的容器。这意味着什么?
功能将二元“根/非根”二分法转变为细粒度的访问控制系统。只需要绑定到低于 1024 的端口的进程(如 Web 服务器)不需要以 root 身份运行:只需向它们授予该net_bind_service
功能即可。对于几乎所有通常需要 root 权限的特定领域,还有许多其他功能。这对于容器安全来说意义重大。
典型的服务器运行多个进程root
,包括 SSH 守护进程、
cron
守护进程、日志记录守护进程、内核模块、网络配置工具等。容器是不同的,因为几乎所有这些任务都是由容器周围的基础设施处理的:
- SSH 访问通常由 Docker 主机上运行的单个服务器管理
cron
必要时,应作为用户进程运行,专门为需要其调度服务的应用程序量身定制,而不是作为平台范围的设施运行- 日志管理通常也交给 Docker 或 Loggly 或 Splunk 等第三方服务
- 硬件管理是无关紧要的,这意味着您永远不需要
udevd
在容器内运行或等效的守护进程 - 网络管理发生在容器之外,尽可能地强制关注点分离,这意味着容器永远不需要执行
ifconfig
、route
或 ip 命令(当然,除非容器被专门设计为像路由器或防火墙一样运行)
这意味着在大多数情况下,容器根本不需要“真正的”root 权限* 因此,容器可以使用减少的功能集运行;这意味着容器内的“root”的权限比真正的“root”要少得多。例如,可以:
- 拒绝所有“挂载”操作
- 拒绝访问原始套接字(以防止数据包欺骗)
- 拒绝访问某些文件系统操作,例如创建新的设备节点、更改文件的所有者或更改属性(包括不可变标志)
- 拒绝模块加载
这意味着即使入侵者设法升级到容器内的 root,也很难造成严重损坏或升级到主机。
这不会影响常规网络应用程序,但会大大减少恶意用户的攻击向量。默认情况下,Docker 会删除除 所需功能之外的所有功能,采用允许名单而不是拒绝名单方法。您可以在Linux 联机帮助页中查看可用功能的完整列表 。
运行 Docker 容器的一个主要风险是,为容器提供的默认功能集和安装可能会提供不完全的隔离,无论是单独使用还是与内核漏洞结合使用。
Docker 支持添加和删除功能,允许使用非默认配置文件。这可能会通过删除功能使 Docker 变得更加安全,或者通过添加功能来降低安全性。用户的最佳实践是删除除其流程明确需要的功能之外的所有功能。
Docker Content Trust 签名验证
Docker 引擎可以配置为仅运行签名的映像。 Docker Content Trust 签名验证功能直接内置于dockerd
二进制文件中。
这是在 Dockerd 配置文件中配置的。
要启用此功能,可以在 中配置信任固定daemon.json
,从而只能拉取并运行使用用户指定的根密钥签名的存储库。
与以前的 CLI 相比,此功能为管理员提供了更多的洞察力来实施和执行图像签名验证。
有关配置 Docker 内容信任签名验证的更多信息,请转到 Docker 中的内容信任。
其他内核安全功能
功能只是现代 Linux 内核提供的众多安全功能之一。还可以通过 Docker 利用现有的知名系统,如 TOMOYO、AppArmor、SELinux、GRSEC 等。
虽然 Docker 目前仅启用功能,但它不会干扰其他系统。这意味着有很多不同的方法来强化 Docker 主机。这里有一些例子。
- 您可以使用 GRSEC 和 PAX 运行内核。这在编译时和运行时都增加了许多安全检查;得益于地址随机化等技术,它还击败了许多漏洞。它不需要特定于 Docker 的配置,因为这些安全功能适用于系统范围,独立于容器。
- 如果您的发行版附带了 Docker 容器的安全模型模板,您可以开箱即用。例如,我们发布了一个与 AppArmor 配合使用的模板,而红帽则为 Docker 提供了 SELinux 策略。这些模板提供了额外的安全网(尽管它与功能有很大重叠)。
- 您可以使用您最喜欢的访问控制机制定义自己的策略。
正如您可以使用第三方工具(包括特殊网络拓扑或共享文件系统)来增强 Docker 容器一样,也有工具可以强化 Docker 容器,而无需修改 Docker 本身。
从 Docker 1.10 开始,docker 守护进程直接支持用户命名空间。此功能允许将容器中的 root 用户映射到容器外部的非 uid-0 用户,这有助于降低容器突破的风险。此功能可用,但默认情况下未启用。
有关此功能的更多信息,请参阅 命令行参考中的daemon 命令。有关 Docker 中用户命名空间实现的更多信息,请参阅 这篇博客文章。
结论
默认情况下,Docker 容器非常安全;特别是当您在容器内以非特权用户身份运行进程时。
您可以通过启用 AppArmor、SELinux、GRSEC 或其他适当的强化系统来添加额外的安全层。
如果您想出让 docker 更安全的方法,我们欢迎在 Docker 社区论坛上提出功能请求、拉取请求或评论。