有人说Docker Hub上三成的镜像包含漏洞?是吗?
到底Docker Hub上是否三成的镜像存在漏洞?通过漏洞计算发现确实有高比例漏洞对于官方镜像遵循Docker的安全指南如若是自创建镜像可找源仓库或自行处理。但我们发现这些漏洞中大部分是老镜像。面对漏洞镜像我们可以采取本地措施还可用Web安全审查进行检查如果想让Docker更加安全建议用dockerbench来评估。文中额外阐述了容器究竟有什么用。
这个数字太神奇了!并不是因为这个比例过高或者过低而是因为居然存在这个比例。既然存在(相对容易地)计算出这个比例的可能性也就意味着存在(相对容易地)改善这个比例的可能性。
声明我是Docker官方人员然而我写这篇文章并未受到公司批准所以最好带着批判的思维来读本文。
这个数字来源于BanyanOps的博客。
漏洞的计算
首先来看看怎么才能计算出原文中的数据。这过程比较简单
- 获取Docker注册表的一个列表
- 下载列表中的镜像
- 检查镜像中的漏洞
这步骤看起来似乎太简单我们稍微深入下细节。
列举镜像
列举官方的镜像是容易的。这些镜像是基于一个叫bashbrew的自动化系统构建而来的使用的都是公布于众的方法。顺便一提这意味着如果你想重建官方的镜像做起来也会很容易。(记住那些方法中涉及一些blobs或者tar包是在启动的时候用到的;因而有时你要多费一点力重建这些blobs或者tarbal)。
构建官方镜像的方法在Github上的docker-library仓库可以找到。
要列出其他的镜像(即属非官方的用户和机构所有的镜像)要困难点。Docker Hub目前没有提供什么方法来列举他们所以一个暂时可行的方法是搜索一个十分宽泛的关键字然后对其结果进行提取。当然这需要一些抓取的工作;抓取到的结果可能会漏掉一些用户的数据但是你拿到的结果已经会十分接近了。(虽然这么做肯定可行 我也听说新的注册表接口有一些十分好的特性可以让这一步完成起来容易点)。
下载镜像
下载镜像是一个繁琐的任务。如果你想安安静静的做这件事情运行一个docker的守护者进程然后执行docker pull username/imagename:tag即可。
如果你想拿到容器的文件系统的一个tar包也很容易只需要运行docker export username/imagename:tag就行。(记得把标准输出重定向到其他地方否则你的终端会抓狂的)
如果你不十分相信Docker的守护者进程你可以检查registry的接口(v1v2)并且通过接口来下载层然后把层文件重组成镜像文件。一些细节我想留给你自己去做但是层文件只是普通的tar包你只需要在彼此之上解包(需要保持正确的顺序)就可以重组成镜像文件。没有什么特别难的步骤;唯一需要留心的地方就是“留白”(whiteout)。“留白”是特殊的标记文件用来表明“此处曾经有文件存在于此但目前没有了”。换句话说如果一个层包含文件/etc/foo.conf但是之上的层把其删除了上一层就会包含一个/etc/.wh.foo.conf文件并且foo.conf不会出现在容器里面。这个文件相当于被留白文件加上了蒙板。
我后来发现了不起的Tianon已经为此写好了一个脚本如果感兴趣你可以去看看。
检查镜像
在这个阶段你有几件事情需要做。细节繁琐本文无法全部叙述;但是在一个全面的安全检查中下面这些步骤你可能需要做
- 运行yum-security或者类似的命令保证在此刻没有安全更新;
- 或者更好的做法是列举出所有安装的包及其版本然后检查软件包在该版本是否包含漏洞;
- 计算系统中的每一个文件的hash值然后去跟已知的有漏洞的文件的hash集合去做比较;
- 执行自动化工具(如chkrootkit)寻找可疑的文件;
- 运行一定数量的专门为某些漏洞而打造的漏洞测试。这些测试的目标是尝试利用某些漏洞然后告诉你“你的系统有漏洞因为我已经设法利用了这个漏洞”或者“我无法利用这个漏洞所以你的系统很可能没有此漏洞”。
事情到了容器的环境中变得很有趣因为用Docker来自动化这些步骤容易且便捷。例如你可以将你的漏洞分析工具包放在/tmp/toolkit中然后对于每一个镜像$I执行docker run -v /tmp/toolkit:/toolkit $I /toolkit/runall.sh。
(注意这里假设你的工具包是静态链接并且/或者是自包含的如不依赖你容器镜像中的任何地方因为这又可能让你的工具包收到蒙骗。我这里主要想说的是如果你想用一系列的测试来检查你的容器镜像你可以用容器让这个步骤变得很简单并且整个过程会更加的快速因为对于每一个测试你不需要单独做一个检查的机器的拷贝)
提升指标
那么在我们运行完这些测试然后发现出奇高比例的镜像包含有漏洞的包。我们怎么来改善这个指标呢?
对于官方的镜像最容易的方法是遵循Docker的安全指南。以后随着官方镜像数的增长Docker也会改善这个机制达到自动提示官方镜像的上游安全列表的效果。
对于非官方的镜像你可以检查镜像中的Author字段
- $ docker inspect --format '{{.Author}}' bin/ngrep
- Jerome Petazzoni
如果该镜像是自动构建出来的你可以找到其来源的仓库并且直接联系他们。
如果你直接受到了漏洞的影响想事情进展的更加快速你可以自己重建镜像并且/或者研究怎么才能修复这个漏洞然后提交一个包含相应修复的PR。这里的意图不是把安全的责任推卸到镜像的使用者身上而是让有意愿和有能力修复这些漏洞的人能对Docker镜像的安全做贡献。
在将来这些步骤会完善并且流式化。会有自动化的过程构建出来减少需要联系相关机构的烦琐然后尽量降低发布包含漏洞补丁版本的时间。
但是30%还是太高了对不对?
30%的“有漏洞的镜像”可能听起来非常多。我第一次听到也是这么想。但是如果你细看你会发现其中一大部分的镜像是老的镜像它们是__故意不被更新__的。
什么?__故意不被更新__?
是的并且对此有一些好的解释。第一个是(其中的一部分)照顾其他的媒介。有的发行版想XYZ想在CD/DVD网络安装VM镜像和容器都保持一致。第二个原因是(这也解释了第一个原因)可重复的构建。
设想你有一个服务器跑着12.04但是你用一个新的Ubuntu 12.04的版本(更别提14.04)却重现不出来。在更加深入的研究后发现这个问题只存在于那些在某特定时间安装的机器其版本是12.04.02。如果一个容器镜像有12.04.02的版本你可以重现出这个bug;否则你得从其他地方得到这个特定版本。这就是为什么Docker Hub有很多老的镜像它们保持着发行时的状态 - 同时包含当时发行时的安全问题。尽管这么说我们已经放置了安全警示说“历史的镜像 - 保持远离”所以我们比较希望这镜像在计算出这些安全的指标的时候不应该被包含进去。
让我们希望下次有人在计算安全指标的时候他们能意识到这一点。
在本地采取措施
我们可能跑着有漏洞的镜像!求救!怎么办怎么办?
事情没有其看起来那么糟。当你(或者其他人)做完对于这些镜像(官方的公开的私有的)的审查结果是一个镜像的列表(以一个唯一的hash 串)并且包含“PASS”或者“FAIL”的状态。(对于“FAIL”的镜像你可能想知道一些其为何不通过的细节如“好像有 ShellShock/CVE-2014-7187和其他漏洞”或者“包含软件包OpenSSL 1.0.1c / CVE-2014-0160”。)
Web规模的安全审查
你可以拿着这个列表然后与你本地的镜像做比较。这里就是事情变得有趣的地方。根据本地镜像和这个列表做一个简单而廉价的匹配结果你就马上能知道你是否运行着有漏洞的镜像。这可以很容易的扩展到成千上百万的主机。
这也意味着事情可以很好的解耦你的安全审查员不需要访问你的生产环境(甚至不需要访问你的开发环境中的系统)。他们甚至不需要要知道你运行着什么镜像他们只需大面积的对镜像做分析然后得出结果给你。你甚至可以从几个安全公司那里拿到结果然后比较他们的结果。
我的镜像在创建后修改过怎么办?
对于新手你不应该这么做。如果你像更新容器中的某些东西你应该创建一个新的镜像然后运行该新的镜像。好吧但是我已经这么做了该怎么办?
那真是什么都说不准但是至少我们能知道你这么做了。安全审查的一部分你可以在运行的容器上运行docker diff来知道是否他们被修改了。(通常docker diff是没有结果的。注意你已经用shell启动了一个容器或者在容器内执行过docker exec你可能会看到少许的更改。但是生产环境的容器不应该出现任何的更改结果。)
专业的Tip你甚至可以防止更改通过在容器中使用--read-only的标记来达到这一点。这会让容器的文件系统只读保证docker diff的结果为空。
如果你想用一条命令来检查所有的容器可以执行
- docker ps -q | xargs -I {} dockr diff {}
(感谢@diogomonica提供命令!)
我已经构建了自定义的容器怎么办
如果你构建了自己的容器我建议你把他们上传到一个仓库里面。如果这是一个公共的我们就会到最初讨论的情形。如果这是一个私有的让我们看下一个部分!
私有的镜像和注册表该怎么办?
如果你上传的是私有的镜像怎么办?如果你上传的地方是一个本地的注册表或者Docker Hub的企业版怎么办?
事情显然变得更加复杂了。你可能会看见有人告诉你“设想ABC有CVE-XYZ的漏洞”如果他们从来没有看到过镜像ABC。
这里是一些可能会发生的事情
安全提供商可以提供镜像的扫描器你可以用在自己的镜像上;
安全提供商可以更进一步将其集成到Docker的注册表中。这可以通过分配读权限(访问Docker Hub上的私有镜像)或者部署前置(on-prem)的安全扫描器(对于Docker Hub企业版的情形)来实现。在两个情形中都能达到一旦镜像上传就会被自动扫描的效果并且立即报告任何可能包含的漏洞。
结论
有两点我想强调因为我相信这能在安全领域产生好的结果。
得出数字是好的。一旦我们得到了量化的数据我们可以提升他们。Docker对于安全问题十分延严肃你可以很肯定我们会和社区及镜像的维护者一期来改善这些量化数据。
有类似这样的围绕着Docker和Docker Hub的生态环境和社区让他们成为一个树立标准的地方。正如Soloman在一些keynote里面指出的Docker里面最重要的一点不是技术而是让人在某事上达成一致。
后一点意味着Docker现在有足够的批评群体来校正横向的工具(包括安全审查)来让这个生态环境受益。其结果会是更加完善的安全机制让每一个人受益。
Docker注重安全
如果有Docker公司不在乎安全的印象那真相离你就太远了。正如上面指出的我们有一个负责的披露安全的政策并且我们总是很快的解决我们意识到的问题。没有哪个软件是没有bug的。Docker也是由人类编写出来的即时他们有些人十分的了优秀但是还是会犯错。重要的是我们对待安全报告的严肃态度如何我们解决这些问题的速度如何;我想我们在这些方面都一直做的很好。
如果你想让你的Docker的环境更加安全我推荐你看下dockerbench。我参与了其编写该软件包含了一个自动化的评估工具可以用来评估Docker的主机使用的是CIS Docker 1.6 Benchmark。它会检查很多事情(如是否SELinux和AppArmor是启用了的)然后生成一个报告。
这是Docker会推出或者参与的一大批软件的第一批目的是让你可以在没有Docker容器安全方面的Ph.d的证书的情形下或者在没有聘请一个Taylor Swift的情形下安全地运行Docker。
并且我们鼓励公开的讨论安全的担忧也不例外。Docker Library的仓库里面有一个十分有趣的有关于这个话题的讨论。
额外的提示
有人问我阐释下容器究竟为什么有用如果我们不反复的检查我们运行的所有的东西的来源。这里是一些例子
- 容器让我们可以在沙箱环境下测试一些危险的操作(如著名的curl ...| sh)并且可以查看到底能产生结果这得益与docker diff。
- 容器让我们可以在沙箱环境下测试一些危险的操作(比如一个商用软件的install.sh)并且可以查看到底能产生结果这得益与docker diff。
- 容器让我们可以在沙箱环境下测试一些危险的操作(如安装一个npmpipgem等等的包但是我们不清楚其来源)并且可以查看到底能产生结果这得益与docker diff。
- 容器让我们可以在沙箱环境下测试一些危险的操作(如安装一个debrpm或者其他的发行版的包)并且可以查看到底能产生结果这得益与docker diff。
- 容器让我们可以在沙箱环境下测试一些危险的操作(如安装一个危险的squid包)并且可以查看到底能产生结果这得益与docker diff。
- 我猜想你能看出这个模式。仅仅因为事情以一种熟悉的形式呈现给你并不代表它们是安全的。但是我们可以使用Docker来提升安全性。