Docker是基于Linux操作系统实现的应用虚拟化。运行在容器内的进程,跟运行在本地系统中的进程本质上并无区别,配置不合适的安全策略将可能给本地系统带来安全风险。因此,Docker的安全性在生产环境中是十分关键的衡量因素。
Docker容器的安全性,很大程度上依赖于Linux系统自身。目前,在评估Docker的安全性时,主要考虑下面几个方面:
- Linux内核的命名空间机制提供的容器隔离安全;
- Linux控制组机制对容器资源的控制能力安全;
- Linux内核的能力机制所带来的操作权限安全;
- Docker程序(特别是服务端)本身的抗攻击性;
- 其他安全增强机制(包括AppArmor、SELinux等)对容器安全性的影响;
- 通过第三方工具(如Docker Bench工具)对Docker环境的安全性进行评估。
命名空间隔离的安全
Docker容器和LXC容器在实现上很相似,所提供的安全特性也基本一致。当用docker run命令启动一个容器时,Docker将在后台为容器创建一个独立的命名空间。
命名空间提供了最基础也是最直接的隔离,在容器中运行的进程不会被运行在本地主机上的进程和其他容器通过正常渠道发现和影响。
例如,通过命名空间机制,每个容器都有自己独有的网络栈,意味着它们不能访问其他容器的套接字(socket)或接口。当然,容器默认可以与本地主机网络连通,如果主机系统上做了相应的交换设置,容器可以像跟主机交互一样和其他容器交互。启动容器时,指定公共端口或使用连接系统,容器可以相互通信了(用户可以根据配置来限制通信的策略)。
从网络架构的角度来看,所有的容器实际上是通过本地主机的网桥接口(docker0)进行相互通信,就像物理机器通过物理交换机通信一样。
那么,Linux内核中实现命名空间(特别是网络命名空间)的机制是否足够成熟呢?
与虚拟机方式相比,通过命名空间来实现的隔离并不是那么绝对。运行在容器中的应用可以直接访问系统内核和部分系统文件。因此,用户必须保证容器中应用是安全可信的(这跟保证运行在系统中的软件是可信的一样),否则本地系统将可能受到威胁,即必须保证镜像的来源和自身可靠。
Docker对镜像管理引入了签名系统,加强了对镜像安全性的防护,用户可以通过签名来验证镜像的完整性和正确性。
控制组资源控制的安全
控制组是Linux容器机制中的另外一个关键组件,它负责实现资源的审计和限制。
当用户执行docker run命令启动一个Docker容器时,Docker将通过Linux相关的调用,在后台为容器创建一个独立的控制组策略集合,该集合将限制容器内应用对资源的消耗。
控制组提供了很多有用的特性。它确保各个容器可以公平地分享主机的内存、CPU、磁盘IO等资源;
当然,更重要的是,通过控制组,可以限制容器对资源的占用,确保了当某个容器对资源消耗过大时,不会影响到本地主机系统和其他容器。
尽管控制组不负责隔离容器之间相互访问、处理数据和进程,但是它在防止恶意攻击特别是拒绝服务攻击(DDoS)方面是十分有效的。
对于支持多用户的服务平台(比如公有的各种Paas、容器云)上,控制组尤其重要。
例如,当个别应用容器出现异常的时候,可以保证本地系统和其他容器正常运行而不受影响,从而避免引发“雪崩”灾难。
内核能力机制
能力机制(Capability)是Linux内核一个强大的特性,可以提供细粒度的权限访问控制。
传统的Unix系统对进程权限只有根权限(用户id为0,即为root用户)和非根权限(用户非root用户)两种粗粒度的区别。
Linux内核支持能力机制,它将权限划分为更加细粒度的操作能力,既可以作用在进程上,也可以作用在文件上。
例如,一个Web服务进程只需要绑定一个低于1024的端口的权限,并不需要完整的root权限。那么它只需要被授权net_bind_service能力即可。此外,还有很多其他的类似能力来避免进程获取root权限。
默认情况下,Docker启动的容器被严格限制只允许使用内核的一部分能力,包括chown、dac_override、fowner、kill、setgid、setuid、setpcap、net_bind_service、net_raw、sys_chroot、mknod、setfcap、audit_write,等等。
使用能力机制对加强Docker容器的安全性有很多好处。通常,在服务器上会运行一堆需要特权权限的进程,包括ssh、cron、syslogd、硬件管理工具模块(例如负载模块)、网络配置工具,等等。容器跟这些进程是不同的,因为几乎所有的特权进程都由容器以外的支持系统来进行管理,例如:
- ssh访问被宿主主机上的ssh服务来管理;
- cron通常应该作为用户进程执行,权限交给使用它服务的应用来处理;
- 日志系统可由Docker或第三方服务管理;
- 硬件管理无关紧要,容器中也就无需执行udevd以及类似服务;
- 网络管理也都在主机上设置,除非特殊需求,容器不需要对网络进行配置。
从上面的例子可以看出,大部分情况下,容器并不需要“真正的”root权限,容器只需要少数的能力即可。
为了加强安全,容器可以禁用一些没必要的权限。包括:
- 禁止任何文件挂载操作;
- 禁止直接访问本地主机的套接字;
- 禁止访问一些文件系统的操作,比如创建新的设备、修改文件属性等;
- 禁止模块加载。
这样,就算攻击者在容器中取得了root权限,也不能获得本地主机的较高权限,能进行的破坏也有限。
不恰当地分配了内核能力,会导致容器内应用获取破坏本地系统的权限。例如,早期的Docker版本曾经不恰当的继承CAP_DAC_READ_SEARCH能力,导致容器内进程可以通过系统调用访问到本地系统的任意文件目录。
默认情况下,Docker采用白名单机制,禁用了必需的一些能力之外的其他权限,目前支持CAP_CHOWN、CAP_DAC_OVERRIDE、CAP_FSETID、CAP_FOWNER、CAP_MKNOD、CAP_NET_RAW、CAP_SETGID、CAP_SETUID、CAP_SETFCAP、CAP_SETPCAP、CAP_NET_BIND_SERVICE、CAP_SYS_CHROOT、CAP_KILL、CAP_AUDIT_WRITE等。
当然,用户也可以根据自身需求来为Docker容器启用额外的权限。
Docker服务端的防护
使用Docker容器的核心是Docker服务端。Docker服务的运行目前还需要root权限的支持,因此服务端安全性十分关键。
首先,必须确保只有可信的用户才可以访问到Docker服务。Docker允许用户在主机和容器间共享文件夹,同时不需要限制容器的访问权限,这就容易让容器突破资源限制。例如,恶意用户启动容器的时候将主机的根目录/映射到容器的/host目录中,那么容器理论上就可以对主机的文件系统进行任意修改了。事实上,几乎所有虚拟化系统都允许类似的资源共享,而没法阻止恶意用户共享主机根文件系统到虚拟机系统。这将会造成很严重的安全后果。因此,当提供容器创建服务时(例如通过一个Web服务器),要更加注意进行参数的安全检查,防止恶意的用户用特定参数来创建一些破坏性的容器。
为了加强对服务端的保护,Docker的REST API(客户端用来跟服务端通信的接口)使用本地的Unix套接字机制替代了原先绑定在127.0.0.1上的TCP套接字,因为后者容易遭受跨站脚本攻击。现在用户使用Unix权限检查来加强套接字的访问安全。
用户仍可以利用HTTP提供REST API访问。建议使用安全机制,确保只有可信的网络或VPN网络,或证书保护机制(例如受保护的stunnel和ssl认证)下的访问可以进行。此外,还可以使用HTTPS和证书来加强保护。
最近改进的Linux命名空间机制将可以实现使用非root用户来运行全功能的容器。这将从根本上解决了容器和主机之间共享文件系统而引起的安全问题。
目前,Docker自身改进安全防护的目标是实现以下两个重要安全特性:
- 将容器的root用户映射到本地主机上的非root用户,减轻容器和主机之间因权限提升而引起的安全问题;
- 允许Docker服务端在非root权限下运行,利用安全可靠的子进程来代理执行需要特权权限的操作。这些子进程将只允许在限定范围内进行操作,例如仅仅负责虚拟网络设定或文件系统管理、配置操作等。
更多安全特性的使用
除了能力机制之外,还可以利用一些现有的安全软件或机制来增强使用Docker的安全性,例如TOMOYO、AppArmor、SELinux、GRSEC等。
Docker当前默认只启用了能力机制。用户可以选择启用更多的安全方案来加强Docker主机的安全,例如:
- 在内核中启用GRSEC和PAX,这将增加更多的编译和运行时的安全检查;并且通过地址随机化机制来避免恶意探测等。启用该特性不需要Docker进行任何配置;
- 使用一些有增强安全特性的容器模板,比如带AppArmor的模板和Redhat带SELinux策略的模板。这些模板提供了额外的安全特性;
- 用户可以自定义更加严格的访问控制机制来定制安全策略。
此外,在将文件系统挂载到容器内部时候,可以通过配置只读(read-only)模式来避免容器内的应用通过文件系统破坏外部环境,特别是一些系统运行状态相关的目录,包括但不限于/proc/sys、/proc/irq、/proc/bus等等。这样,容器内应用进程可以获取所需要的系统信息,但无法对它们进行修改。
使用第三方检测工具
介绍了大量增强Docker安全性的手段。要逐一去检查会比较繁琐,已经有了一些进行自动化检查的开源工具,比较出名的有Docker Bench和clair。
Docker Bench
Docker Bench是一个开源项目,代码托管在https://github.com/docker/docker-bench-security。
该项目按照互联网安全中心(Center for Internet Security,CIS)对于Docker 1.11+的安全规范进行一系列环境检查,发现当前Docker部署在配置、安全等方面的潜在问题。
CIS Docker规范在包括主机配置、Docker引擎、配置文件权限、镜像管理、容器运行时环境、安全项等六大方面都进行了相关的约束和规定。
推荐大家在生产环境中使用Docker时,采用该规范作为部署的安全标准。
Docker Bench自身也提供了Docker镜像,采用如下命令,可以快速对本地环境进行安全检查。
$ docker run -it --net host --pid host --cap-add audit_control \
-v /var/lib:/var/lib \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/lib/systemd:/usr/lib/systemd \
-v /etc:/etc --label docker_bench_security \
docker/docker-bench-security
Initializing Sun Sep 7 03:34:36 UTC 2016[INFO] 1 - Host Configuration[WARN] 1.1 - Create a separate partition for containers[PASS] 1.2 - Use an updated Linux Kernel...
输出结果中,带有不同的级别,说明问题的严重程度。一般要尽量避免出现WARN或以上的问题。
clair
CoreOS团队推出的clair支持对容器的文件层进行扫描从而发现潜在漏洞,项目地址为https://github.com/coreos/clair。
可以通过如下命令快速体验:
$ curl -L https://raw.githubusercontent.com/coreos/clair/v1.2.2/docker-compose.yml -o $HOME/docker-compose.yml
$ mkdir $HOME/clair_config
$ curl -L https://raw.githubusercontent.com/coreos/clair/v1.2.2/config.example.yaml -o $HOME/clair_config/config.yaml
$ $EDITOR $HOME/clair_config/config.yaml # Edit database source to be postgresql://postgres:password@postgres:5432?sslmode=disable
$ docker-compose -f $HOME/docker-compose.yml up -d
小结
在使用Docker过程中,对于安全问题需要注意如下几方面:
- 在使用Docker容器运行应用的时候,一定要牢记容器自身所提供的隔离性其实并没有那么完善,需要加强对容器内应用的安全审查。要时刻牢记,容器即应用,原先保障应用安全的各种手段,都可以合理地借鉴利用。
- 采用专用的服务器来运行Docker服务端和相关的管理服务,如ssh监控和进程监控、管理工具nrpe、collectd等,并对该服务器启用最高级别的安全机制,而把其他业务服务都放到容器中去运行。
- 将运行Docker容器的机器划分为不同的组,互相信任的机器放到同一个组内;组之间进行安全隔离;同时进行定期的安全检查。
- 随着容器大规模地使用和集成,甚至组成容器集群。需要考虑在容器网络上进行必备的安全防护,避免诸如DDoS、ARP攻击、规则表攻击等网络安全威胁,这也是生产环境需要关注的重要问题。