乐趣区

关于容器:关于容器镜像安全你做对了吗-IDCF

容器在近些年变得煊赫一时,提到容器就不能不提到镜像,如果说容器是云计算时代的核心内容之一,那么镜像就是容器这个外围的灵魂。所以镜像的平安也就显得尤为重要。
然而从上面的几组数据能够看到:镜像的平安问题却不那么令人乐观。

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_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” 命令和不执行此命令的镜像,其破绽数量和镜像体积都是不一样的。具体对比方下图。

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 和黑名单等机制来对镜像的拉取和推送等做权限管制等伎俩来进一步保障镜像的平安。限于篇幅,本文不再进一步深入探讨,感兴趣的,能够深刻进行钻研。

五、总结

没有相对的平安,也没有一劳永逸的平安,只有永不止步的口头。镜像扫描只是通过工具帮忙咱们来发现平安问题,而后去有指标的解决问题,属于一种医治伎俩;可能防患未然,预防问题的产生才是最重要的,所以,应该养成良好的习惯:从平安的角度登程,联合上述的最佳实际,来构建平安,标准的镜像。以 ” 预防为主,防治联合 ” 的形式来进一步确保镜像的平安。

起源: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 实际之继续测试》
退出移动版