关于容器镜像安全,你作对了吗? | IDCF

容器在近些年变得煊赫一时,提到容器就不能不提到镜像,若是说容器是云计算时代的核心内容之一,那么镜像就是容器这个核心的灵魂。因此镜像的安全也就显得尤其重要。
可是从下面的几组数据能够看到:镜像的安全问题却不那么使人乐观。java

1) Linux OS的漏洞呈逐年上涨趋势

从2008年的不到300+,上升至2018年的2000+。并且近两年呈指数级上升趋势。nginx

2) 高危漏洞数量呈逐年上涨趋势

从2014年的不到2000+,上升至2018年的4000+。git

3) Docker 镜像广泛存在漏洞

最受欢迎的镜像好比nginx,ubuntu,java等广泛存在漏洞,且多达数十个。github

4) 40%的受访者表示,没有在CI阶段采起任何安全手段。

所以,保证镜像安全,不只是保障云平台安全的重要一环,更是DevSecOps治理体系中一个重要的话题。docker

1、镜像的特色

镜像的本质、镜像的来源以及镜像的制做共同造就了镜像的如下几个特色:数据库

  • 复杂性

镜像内容的复杂性(既包括一些OS的各类库、包,又包含一些Python或者Ruby等文件)、来源的复杂性(官方的、我的的)、Dockerfile 命令的复杂性(ADD和COPY的区别使用,CMD和ENTRYPOINT的区别使用,命令顺序不一样造就不一样的镜像)使得镜像自己就变得很是复杂,若是考虑安全因素,那就更加复杂了。ubuntu

  • 便捷性

镜像的制做是很是方便的,只要会写Dockerfile,就能制做出镜像,或者也可将既有容器制做成一个镜像。并且镜像的使用也是很是方便的,一个命令(docker run)就能将一个静态的镜像转变为一个动态的容器,偏偏是这种便捷性形成了镜像的另一个特色,也就是下面的混乱性。vim

  • 混乱性

人人可制做镜像,人人可管理镜像,也就使得镜像的种类、数量都是异常庞大的,并且针对某个特定功能的镜像可达几十个,甚至上百个,既有官方的,又有我的的,既有共有仓库的,又有私有仓库的。没有一个行之有效的标准去规范的管理这些镜像,就使得镜像参差不齐,选用时较为困难。segmentfault

上述的特色也就使得镜像的安全变成了一个复杂且容易被忽略的点。然而再复杂,也有手段去完成这个复杂的工做,也就是下面要讲的镜像扫描。安全

2、镜像扫描在SDLC中的位置

镜像扫描是保证镜像安全的一个强有力手段,其一般发生在软件开发生命周期(SDLC: Software Development Life Cycle)的构建阶段,以下所示:

image.png

现有的镜像扫描大都是依赖于镜像仓库提供的扫描功能(内置镜像扫描工具),通常流程以下:

image.png

  • 开发人员提交代码变动;
  • 触发一个构建流程;
  • 进行镜像构建(docker build);
    镜像镜像推送(docker push);
  • 进行镜像扫描(利用镜像仓库内置扫描工具进行);
  • 进行镜像部署(docker run)。

这种流程有诸多弊端:

  • 扫描滞后

镜像的扫描依赖于镜像仓库自带的镜像扫描功能,只有镜像推送至镜像仓库才会进行镜像扫描,这种滞后的扫描会致使仓库中保存有漏洞的镜像。在DevSecOps中,但愿作到的是:镜像仓库中存储的是安全的、可随时部署的镜像。

  • 不能有效的终止CI/CD流程

成熟的DevSecOps CI/CD 应该是:当检测到镜像是不安全的,那么就应该当即终止CI/CD流程,防止不安全的镜像被部署到环境中。这种依赖于镜像仓库自带扫描功能的滞后镜像扫描,没有办法作到这一点,由于镜像推送以后的扫描,和镜像的部署是同时进行的。

  • 浪费资源

镜像是要占据必定的磁盘空间的(有些镜像大小上百M,甚至上G),在镜像仓库中保存有大量有漏洞的镜像,就会占据大量的磁盘空间,使仓库的使用成本上升。

3、镜像扫描前置

基于扫描滞后的不足,镜像扫描的前置就显得尤其重要了,将镜像扫描的操做前置(以下图绿色虚线方框所示):

image.png

让其位于镜像构建以后,镜像推送以前,这样一旦一个镜像扫描结束,若是其结果是安全的,那么就顺利进行后续的操做(推送至镜像仓库并进行镜像部署);若是扫描结果是不安全的,那么就当即终止该CI/CD流程,并通知相应的人员。这样就防止了不安全镜像推送至镜像仓库。保证镜像仓库中存储的镜像都是安全的,并且节省了磁盘空间。同时,前置扫描对镜像的操做空间会变得更大,好比能够对镜像作一些黑白名单、RBAC(Role Based Access Control)权限控制等。

对于前置的镜像扫描操做,就须要借助一些现有的镜像扫描工具来帮助咱们完成这个操做。这些工具既有你们耳熟能详的Clair,也有一些新贵好比Anchore,Trivy等,关于这些工具的具体使用和对比,会在后期有有一篇专门的文章来分析。

镜像扫描只是帮助咱们,发现镜像问题,而后解决镜像问题,这只是镜像问题的治疗手段,然而对于镜像问题,最好是要作到防止问题发生,也就是在构建镜像的时候,尽量的根据一些最佳实践来构建一些尽可能安全的镜像,这也就是咱们接下来要讲的镜像安全的一些最佳实践。这样经过预防为主,防治结合(防:根据最佳实践构建安全镜像;治:嵌入CI/CD中的前置镜像扫描)的方法来确保镜像的安全。

4、容器镜像安全最佳实践

4.1 尽可能选择轻量的基础镜像

每一个镜像都有一个基础镜像,也就是在 Dockerfile中的 FROM 中指定的镜像,通常这个基础镜像就是一个Linux发行版,好比alpine,ubuntu等。在为一个新项目选取基础镜像的时候,应该考虑这个项目的运行是否须要一个全量的操做系统(包含各类库),若是说alpine就能能知足要求,就不要选择ubuntu这个相对庞大的操做系统,做为新项目的基础镜像。不一样量级的操做系统,除了有镜像大小的区别,更重要的是,操做系统包含的文件系统越多,攻击面也就越大,包含的漏洞也就越多。

用Anchore对 openjdk:8-jdk-alpine和openjdk:8-jdk进行扫描,结果显示openjdk:8-jdk有128个漏洞,而openjdk:8-jdk-alpine却只有48个。并且openjdk:8-jdk-alpine的镜像大小仅为openjdk:8-jdk的三分之一不到。

image.png

4.2 添加非root用户,以最小受权用户运行容器

容器是和宿主机共享内核的,若是以root用户启动容器,也就意味着容器是有root权限去操做宿主机,这样就使得宿主机的受攻击面增大,潜在威胁系数提升。所以,应该指定一个非root的特定用户,而后以此特定用户来启动容器。若是是经过Dockerfile的方式来构建镜像,能够在Dockerfile中添加以下代码,来添加一个位于特定group的特定用户,并以此特定用户来启动容器:

FROM your_base_image
RUN groupadd -r  devsecops && useradd -r  -g devops devsecops
USER devsecops
......(your other dockerfile steps)

4.3 不要将敏感信息暴漏在镜像中

不要将token,密码,ssh key等敏感信息,存储在镜像中,一旦镜像被推送至公共镜像仓库,那么就会形成上述敏感信息的泄漏。结果将是灾难性的。若是在镜像中必需要用到一些敏感信息,能够采用docker提供的secret功能,或者经过多阶段构建来完成。具体的使用能够参考docker官网。

4.4 不要安装不须要的包

不少镜像中都经过"apt-get update && apt-get install xxx" 或者"yum update && yum install xxx"的方式来安装软件包,可是切记:只安装与应用程序运行有关的包,不要安装非必须的包。好比在数据库镜像中安装vim。安装的包越多,镜像的复杂性就越高,依赖也越多,不只致使容器镜像的体积变大,还使得镜像的受攻击面变大,安全性下降。

当以ubuntu:16.04为基础镜像时,执行 "apt-get update && apt-get install vim telnet curl -y"命令和不执行此命令的镜像,其漏洞数量和镜像体积都是不同的。详细对好比下图。

image.png

4.5 对Dockerfile进行扫描

能够用Dockerfile扫描工具(好比Hadolint)来对Dockerfile来进行扫描,以发现Dockerfile中的不规范写法或者一些可能引发安全问题的构建命令。好比用Hadolint扫描以下的Dockerfile:

FROM alpine
RUN apk add busybox-extras

能够看到以下输出结果:

/dev/stdin:1 DL3006 Always tag the version of an image explicitly
/dev/stdin:2 DL3018 Pin versions in apk add. Instead of `apk add <package>` use `apk add <package>=<version>`
/dev/stdin:2 DL3019 Use the `--no-cache` switch to avoid the need to use `--update` and remove `/var/cache/apk/*` when done installing packages

结果提示,基础镜像(alpine)应该要指定tag(DL3006提示所示),安装包时(apk add) 要指定包(busybox-extras)的版本(DL3018提示所示)。

4.6 不要选取未知来源且没有维护的镜像

当选取基础镜像时,不要选取来源未知(不知道这个镜像的做者是何方神圣,不知道Dockerfile的内容,不知道基础镜像的内容)、中止维护(最后一次更新是几个月甚至几年前,这段时间积累了多少问题,天知道)的镜像。由于不能肯定镜像中包含的内容是不是安全的。更不要仅以镜像被拉取的次数、Star数量做为指标来决定是否使用镜像,由于这个次数是能够被刷上去的。必定要尽可能选取官方的、Dockerfile内容明确的,更新频繁的镜像,若是没有符合条件的镜像,能够尝试本身制做镜像。

上述几点只是构建安全、规范镜像的一部分手段,本文只是列出来一些重要,并且常见的。其余还有诸如使用镜像签名来防止镜像被篡改、构建受信的镜像仓库并经过RBAC和黑名单等机制来对镜像的拉取和推送等作权限控制等手段来进一步保证镜像的安全。限于篇幅,本文再也不进一步深刻探讨,感兴趣的,能够深刻进行研究。

5、总结

没有绝对的安全,也没有一劳永逸的安全,只有永不止步的行动。镜像扫描只是经过工具帮助咱们来发现安全问题,而后去有目标的解决问题,属于一种治疗手段;可以未雨绸缪,预防问题的发生才是最重要的,因此,应该养成良好的习惯:从安全的角度出发,结合上述的最佳实践,来构建安全,规范的镜像。以"预防为主,防治结合"的方式来进一步确保镜像的安全。

来源:CIO Talk

做者:马景贺

马景贺,人称小马哥,曾作过LTE 4G网络协议开发,后转向DevOps,对于Cloud Native DevSecOps进行布道,喜欢研究docker,kubernetes,istio等Cloud Native相关技术,乐于分享,运营着DevOps SIG公众号。在大连DevOps社区活动上,举办DevOps Workshop活动。

参考资料

5月每周四晚8点,【冬哥有话说】质量与测试专场。公众号留言“质量”可获取地址

  • 0506 朱少民 《如何最大化软件测试效能》
  • 0513 陈琦 《数据驱动测试》
  • 0520 陈霁 《没错,去QA是提升质量最有效的方法!》
  • 0527 施慧斌 《DevOps实践之持续测试》
相关文章
相关标签/搜索