容器在近些年变得煊赫一时,提到容器就不能不提到镜像,如果说容器是云计算时代的核心内容之一,那么镜像就是容器这个外围的灵魂。所以镜像的平安也就显得尤为重要。
然而从上面的几组数据能够看到:镜像的平安问题却不那么令人乐观。
1) Linux OS的破绽呈逐年上涨趋势
从2008年的不到300+,回升至2018年的2000+。而且近两年呈指数级回升趋势。
2) 高危破绽数量呈逐年上涨趋势
从2014年的不到2000+,回升至2018年的4000+。
3) Docker 镜像普遍存在破绽
最受欢迎的镜像比方nginx,ubuntu,java等普遍存在破绽,且多达数十个。
4) 40%的受访者示意,没有在CI阶段采取任何平安伎俩。
因而,保障镜像平安,不仅是保障云平台平安的重要一环,更是DevSecOps治理体系中一个重要的话题。
一、镜像的特点
镜像的实质、镜像的起源以及镜像的制作独特造就了镜像的以下几个特点:
- 复杂性
镜像内容的复杂性(既包含一些OS的各种库、包,又蕴含一些Python或者Ruby等文件)、起源的复杂性(官网的、集体的)、Dockerfile 命令的复杂性(ADD和COPY的区别应用,CMD和ENTRYPOINT的区别应用,命令程序不同造就不同的镜像)使得镜像自身就变得非常复杂,如果思考平安因素,那就更加简单了。
- 便捷性
镜像的制作是十分不便的,只有会写Dockerfile,就能制作出镜像,或者也可将既有容器制作成一个镜像。而且镜像的应用也是十分不便的,一个命令(docker run)就能将一个动态的镜像转变为一个动静的容器,恰好是这种便捷性造成了镜像的另外一个特点,也就是上面的凌乱性。
- 凌乱性
人人可制作镜像,人人可治理镜像,也就使得镜像的品种、数量都是异样宏大的,而且针对某个特定性能的镜像可达几十个,甚至上百个,既有官网的,又有集体的,既有共有仓库的,又有公有仓库的。没有一个卓有成效的规范去标准的治理这些镜像,就使得镜像参差不齐,选用时较为艰难。
上述的特点也就使得镜像的平安变成了一个简单且容易被疏忽的点。然而再简单,也有伎俩去实现这个简单的工作,也就是上面要讲的镜像扫描。
二、镜像扫描在SDLC中的地位
镜像扫描是保障镜像平安的一个强有力伎俩,其通常产生在软件开发生命周期(SDLC: Software Development Life Cycle)的构建阶段,如下所示:
现有的镜像扫描大都是依赖于镜像仓库提供的扫描性能(内置镜像扫描工具),个别流程如下:
- 开发人员提交代码变更;
- 触发一个构建流程;
- 进行镜像构建(docker build);
镜像镜像推送(docker push); - 进行镜像扫描(利用镜像仓库内置扫描工具进行);
- 进行镜像部署(docker run)。
这种流程有诸多弊病:
- 扫描滞后
镜像的扫描依赖于镜像仓库自带的镜像扫描性能,只有镜像推送至镜像仓库才会进行镜像扫描,这种滞后的扫描会导致仓库中保留有破绽的镜像。在DevSecOps中,心愿做到的是:镜像仓库中存储的是平安的、可随时部署的镜像。
- 不能无效的终止CI/CD流程
成熟的DevSecOps CI/CD 应该是:当检测到镜像是不平安的,那么就应该立刻终止CI/CD流程,避免不平安的镜像被部署到环境中。这种依赖于镜像仓库自带扫描性能的滞后镜像扫描,没有方法做到这一点,因为镜像推送之后的扫描,和镜像的部署是同时进行的。
- 浪费资源
镜像是要占据肯定的磁盘空间的(有些镜像大小上百M,甚至上G),在镜像仓库中保留有大量有破绽的镜像,就会占据大量的磁盘空间,使仓库的应用成本上升。
三、镜像扫描前置
基于扫描滞后的有余,镜像扫描的前置就显得尤为重要了,将镜像扫描的操作前置(如下图绿色虚线方框所示):
让其位于镜像构建之后,镜像推送之前,这样一旦一个镜像扫描完结,如果其后果是平安的,那么就顺利进行后续的操作(推送至镜像仓库并进行镜像部署);如果扫描后果是不平安的,那么就立刻终止该CI/CD流程,并告诉相应的人员。这样就避免了不平安镜像推送至镜像仓库。保障镜像仓库中存储的镜像都是平安的,而且节俭了磁盘空间。同时,前置扫描对镜像的操作空间会变得更大,比方能够对镜像做一些黑白名单、RBAC(Role Based Access Control)权限管制等。
对于前置的镜像扫描操作,就须要借助一些现有的镜像扫描工具来帮忙咱们实现这个操作。这些工具既有大家耳熟能详的Clair,也有一些新贵比方Anchore,Trivy等,对于这些工具的具体应用和比照,会在前期有有一篇专门的文章来剖析。
镜像扫描只是帮忙咱们,发现镜像问题,而后解决镜像问题,这只是镜像问题的医治伎俩,然而对于镜像问题,最好是要做到避免问题产生,也就是在构建镜像的时候,尽可能的依据一些最佳实际来构建一些尽量平安的镜像,这也就是咱们接下来要讲的镜像平安的一些最佳实际。这样通过预防为主,防治联合(防:依据最佳实际构建平安镜像;治:嵌入CI/CD中的前置镜像扫描)的办法来确保镜像的平安。
四、容器镜像平安最佳实际
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的三分之一不到。
4.2 增加非root用户,以最小受权用户运行容器
容器是和宿主机共享内核的,如果以root用户启动容器,也就意味着容器是有root权限去操作宿主机,这样就使得宿主机的受攻击面增大,潜在威逼系数进步。因而,应该指定一个非root的特定用户,而后以此特定用户来启动容器。如果是通过Dockerfile的形式来构建镜像,能够在Dockerfile中增加如下代码,来增加一个位于特定group的特定用户,并以此特定用户来启动容器:
FROM your_base_imageRUN groupadd -r devsecops && useradd -r -g devops devsecopsUSER 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"命令和不执行此命令的镜像,其破绽数量和镜像体积都是不一样的。具体对比方下图。
4.5 对Dockerfile进行扫描
能够用Dockerfile扫描工具(比方Hadolint)来对Dockerfile来进行扫描,以发现Dockerfile中的不标准写法或者一些可能引起平安问题的构建命令。比方用Hadolint扫描如下的Dockerfile:
FROM alpineRUN 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和黑名单等机制来对镜像的拉取和推送等做权限管制等伎俩来进一步保障镜像的平安。限于篇幅,本文不再进一步深入探讨,感兴趣的,能够深刻进行钻研。
五、总结
没有相对的平安,也没有一劳永逸的平安,只有永不止步的口头。镜像扫描只是通过工具帮忙咱们来发现平安问题,而后去有指标的解决问题,属于一种医治伎俩;可能防患未然,预防问题的产生才是最重要的,所以,应该养成良好的习惯:从平安的角度登程,联合上述的最佳实际,来构建平安,标准的镜像。以"预防为主,防治联合"的形式来进一步确保镜像的平安。
起源:CIO Talk
作者:马景贺
马景贺,人称小马哥,曾做过LTE 4G网络协议开发,后转向DevOps,对于Cloud Native DevSecOps进行布道,喜爱钻研docker,kubernetes,istio等Cloud Native相干技术,乐于分享,经营着DevOps SIG公众号。在大连DevOps社区活动上,举办DevOps Workshop流动。
参考资料
- https://snyk.io/blog/10-docke...
- https://boxboat.com/2020/04/2...
- https://github.com/aquasecuri...
- https://anchore.com/blog/diff...
- https://malikashish8.github.i...
- https://github.com/arminc/cla...
- https://livingwithagility.org...
- https://medium.com/better-pro...
- https://github.com/anchore/an...
- https://0x1.gitlab.io/securit...
- https://github.com/anchore/
- https://github.com/chaitin/xray
- https://www.stackrox.com/post...
- https://github.com/hadolint/h...
5月每周四晚8点,【冬哥有话说】品质与测试专场。公众号留言“品质”可获取地址
- 0506 朱少民 《如何最大化软件测试效力》
- 0513 陈琦 《数据驱动测试》
- 0520 陈霁 《没错,去QA是提高质量最无效的办法!》
- 0527 施慧斌 《DevOps实际之继续测试》