关于Docker的安全方面,主要有以下这几个方面值得我们考虑:

  • 内核固有的的安全性及对namespace和cgroups的支持方面
  • docker守护进程自身的安全问题
  • 无论是默认情况还是用户定制的情况下,docker容器的配置文件存在漏洞
  • 内核“硬化“的安全特性以及他们与容器间交互的方式存在问题

Kernel Namespaces

docker容器和LXC容器很相似,它们都有类似的安全特性。当你用 docker run命令运行一个容器时,docker就会创建一系列的namespace和control groups。

namespace提供了一种最为直接和简单的隔离方式:运行在容器中的进程并不会发现有什么区别,受到的影响也几乎较小。

每一个容器都有自己的网络:这意味着一个容器在使用sockets和其他容器的接口时,不需要有很高的权限。当然,如果宿主机有相应的设置,容器可以互相通过各自的网络来进行交互—就像与外部的主机交互一样。当你给你的容器指定了公共的端口或使用links,那么容器之间就可以进行ip的访问了。而且容器间互相可以ping通,发送/接收UDP封包,还可以建立TCP的连接,但是如果必要的话还可以稍微做一些限制。以一个网络架构师的观点来看,docker宿主机上的所有容器都被设置在网桥的接口上,这就像物理机通过Ethernet交换器来连接一样。

那么代码是如何成熟地提供内核命名空间和私有网络的呢?内核命名空间在 2.6.15 和 2.6.26 的时候被引进。早在2008年7月(5年前,内核2.6.26版本发布),内核的代码在许多生成系统中就可以被运用和审核了。其实命名空间代码的设计和灵感很久之前就有了,实际上namespace(命名空间)是为了重新实现 OPenVZ 这样的功能,只有一这样的方式,namespa技术才可能被主流的内核所合并。OPenVZ最初发布与2005年,所以它的设计实现方式还是比较成熟的。

Control Groups

Control Groups是实现Linux容器技术的另一个重要组件。它们能实现资源的计算和资源的限制,同时它们也提供了许多非常有用的度量标准,并且确保了每一个容器可以获得相对平等的内存,CPU和磁盘的I/O,更重要的是,一个单独的容器并不能通过耗尽以上资源中的一个来使整个系统瘫痪。

因此,尽管它们不起到防止一个容器访问或影响到另一容器的数据和进程的作用,但对于一些拒绝服务的攻击,它们也是必不可少的。对于多租户来说,Control Groups尤为重要,比如一些公共的或私有的PaaS服务,当某些应用执行非法的动作时,Control Groups可以保证一个一致的时间(和性能)。

Control Groups也已经出来很长一段时间了:它开始于2006年,最初的合并是在 2.6.24内核中。

Docker Daemon Attack Surface

当运行docker容器(和应用)时都需要运行docker守护进程,而这个守护进程的运行则需要一个root权限,所以你应该在这留意一下细节。

首先,让受信任的用户操作你的docker守护进程。特别地,docker允许你在docker宿主机和一个guest容器中分享一个目录,并且没有限制你的访问权限。这就意味着在你的主机上运行一个容器时,原来的/host目录将会变成根目录。并且这个容器将毫无限制的改变你的文件系统,这就类似于虚拟系统如何允许文件系统资源共享。没有什么能阻止你和虚拟机分享你的root文件系统(或root设备)。

这具有很强的安全性含义:比如,如果从通过API的web服务器提供的docker创建容器时,在检查参数时你应该比平时更加小心,一确保恶意用户不能通过某些某些参数来让docker创建任意的容器。

由于这个原因,REST API终端(通过docker CLI 来和docker守护进程进行通信)在docker 0.5.2 时就做了一些修改,而且现在用Unix Socket来替代TCP Socket绑定到127.0.0.1(后者易发生跨站点脚本攻击,如果你碰巧直接在你的本机运行docker,当然虚拟机上除外),然后,你可以使用传统的Unix权限检查来限制control socket的访问。

你也可以通过Http来公开REST API,一旦你这样做了,你就要留意上述提到的安全问题,你应该确保它只能通过一个受信任的网络或VPN来访问到,同时受保护的安全通道和客户端SSL证书也可以,你也可以使用 HTTPS 和 一些相关的证书来让它更加的安全。
对于某些输入操作来说docker 守护进程还是比较脆弱的,比如用docker load从其他磁盘加载镜像文件,或是用docker pull从网上下载镜像,在改善docker社区时这一直是一个焦点,特别是pull操作时存在的安全隐患。虽然load和pull操作有些重叠了,但应注意的是docker load是一种用于备份和恢复的机制,当前并不考虑将它用于加载镜像的一种安全机制。由于从docker 1.3.2版本开始,docker镜像才从基于Linux/Unix平台中的chroot子进程来提取,这是迈向特权分离的重要一步。

最终,我们可以预料到:docker守护进程将会限制特权,以授权的方式操作一些安全的子进程,并且每个都有自己的受限制的Linux功能,虚拟网络的配置,文件系统的管理等。也就是说,以后很有可能docker engine本身会在容器中运行。
最后,如果你在服务器上运行docker,建议服务器去掉其他服务,专门运行docker服务。当然,你也可以保留一些你喜欢的管理工具如SSH服务,进程管理工具(如:NRPE,collectd)等等。

Linux Kernel Capabilities

在默认情况下,docker启动的容器的功能会有一些限制。这意味着什么呢?

这是一个root或非root 二分法粒度管理的访问控制系统。比如web服务这样的进程只需要绑定到一个低于1024的端口,而不需要root权限,它们只需要被授权net_bind_service功能就可以了。当然还有其他许多功能,但几乎所有的功能都需要root权限。
这对容器的安全性来说意味着很多,让我们来看看:

通常你的服务器(物理机或虚拟机)都需要以root权限运行一堆进程,包括SSH, cron, syslogd,硬件管理工具(如加载模块),网络配置工具(如DHCP, WPA, or VPNs)等等,而容器却不同,几乎所有的任务都是由围绕容器的基础设施来处理的:

  • SSH的访问通常由docker主机上的一个单独的服务器来管理
  • cron,必要时可以作为用户进程来运行,并利用它的调度服务来专门的应用于某个应用程序,而不是作为一个平台级的设施。
  • 日志服务也通常交给docker来管理,或者是第三方的服务,比如Loggly 或 Splunk
  • 硬件的管理是无关紧要的,这意味着你不必运行udevd 或在容器中运行类似于守护进程的进程来管理硬件
  • 网络的管理是在容器的外面,这就是说容器中没有必要执行ifconfig,route 或 ip 命令(除了一个容器被专门设置为路由器或防火墙)

在大多数情况下,容器根本不需要“真实的”root权限,因此,容器拥有的功能是被削减过的,就是说容器中的root权限小于真实的root权限,例如以下几个方面:

  • 取消所有的“mount”操作
  • 取消了访问 raw sockets的权限(防止数据包的欺骗)
  • 取消了一些文件系统的权限,比如创建一个新的设备节点,改变文件的所有者,或是改变一些属性(包括不可改变的标志)
  • 取消了模块的加载
  • 还有其他一些方面

这意味着即使入侵者在容器中将权限升级到root权限,他也很难对主机造成巨大的伤害。

然而这些都不会影响常规的web apps,一些恶意的用户会发现他们容器中可以支配的技术大大的缩水了!在默认的情况下,docker舍弃了大多数功能除了它所需要的,它会将需要的例如白名单,不需要的例如黑名单,Linux manpages你可以在这篇文章中找到docker拥有的所有功能。

在运行docker 容器时首要的风险是容器的默认功能和挂载可能会提供不完全的隔离,无论是独立使用或是与内核漏洞组合使用。

同时,docker还提供了添加和删除的功能,并且允许使用非默认的配置文件。这在删除docker的某些功能时可能会让docker更安全,或者在添加某些功能时导致docker的安全性降低。最佳的做法是用户可以保留运行他们的应用必要的功能,删除其他的功能。

Other Kernel Security Features

Capabilities 是现代Linux内核提供的许多安全功能之一,