12 个优化 Docker 镜像安全性的技巧( 三 )


 
docker build --pull --no-cache <rest of the build command>复制代码
定期更新第三方依赖 
你编写的软件是基于第三方的依赖,也就是由其他人制作的软件 。这包括了:
 

  • 你的镜像下面的基础 Docker 镜像,或
  • 你作为自己应用程序的一部分使用的第三方软件组件,例如通过 pip/npm/gradle/apt/……安装的组件 。
 
如果你的镜像中的这些依赖过时了,就会增加攻击面,因为过时的依赖往往有可利用的安全漏洞 。
 
你可以定期使用 SCA(软件组件分析)工具来解决这个问题,比如Renovate Bot 。这些工具(半)自动将你声明的第三方依赖更新为最新版本,例如在你的 Dockerfile、Python 的 requirements.txt、NPM 的 packages.json 等文件中声明的列表 。你需要设计你的 CI 管道,使 SCA 工具所做的更改自动触发你的镜像的 re-build 。
 
这种自动触发的镜像重建对于处在只维护模式,但代码仍将被客户在生产环境中使用(客户希望它是安全的)的项目特别有用 。在维护期间,你不再开发新的特性,也不会构建新的镜像,因为没有新的提交(由你做出)来触发新的构建 。然而,由 SCA 工具做出的提交确实会再次触发镜像构建 。
 
你可以在我的相关博文中找到更多关于 Renovate bot 的细节 。
 
对你的镜像进行漏洞扫描 
即使你执行了上述建议,比如说你的镜像总是使用最新的第三方依赖,它仍然可能是不安全的(例如一个依赖已经被弃用的情况) 。在这种情况下,“不安全“意味着一个(或多个)依赖有已知的安全漏洞(在一些 CVE 数据库中注册) 。
 
出于这个原因,你可以给你的 Docker 镜像提供某种工具来扫描所有包含的文件,以找到这种漏洞 。这些工具有两种形式:
 
  1. 你显式调用的 CLI 工具(例如在 CI 管道中),比如说Trivy(OSS,在 CI 管道中非常容易使用,见 Trivy文档)、Clair(OSS,但设置和使用比 Trivy 更复杂),或Snyk(通过“docker scan“集成到 Docker CLI 中,见cheat sheet,但只有有限的免费计划!)
  2. 集成到你推送镜像的镜像注册中心的扫描器,如 Harbor(内部使用 Clair 或 Trivy) 。还有一些商业产品,如Anchore 。
 
因为这些扫描器是通用的,它们还试图覆盖一大堆包注册表,所以可能不会特别为你在自己项目中使用的编程语言或包注册表定制 。有时,你应该调查你的编程语言生态系统提供了哪些工具 。例如,对于 Python 来说就有一个专门针对 Python 包的安全工具 。
 
扫描你的 Dockerfile 是否违反了最佳实践 
有时,问题来自于你在 Dockerfile 中放置的语句,这些语句是不好的实践(但你没有意识到) 。为此可以使用诸如checkov、Conftest、trivy或hadolint等工具,它们是 Dockerfile 的 linter 。为了选择正确的工具,你需要查看它的默认规则/政策 。例如,hadolint 比 checkov 或 conftest 提供的规则更多,因为它是专门针对 Dockerfiles 的 。这些工具也是相互补充的,因此在你的 Dockerfiles 上运行多个工具(如 hadolint 和 trivy)确实是有意义的 。不过要做好准备,因为你需要维护“忽略文件“,在这个文件中的规则会被忽略——可能是由于误报而有意忽略它们,或者是你准备故意破坏规则 。
 
不要对 Docker Hub 使用 Docker 内容信任 
为了验证你使用的基础镜像确实是由该镜像背后的公司构建和推送的,你可以使用 Docker 内容信任(见官方文档)特性 。只需在运行 docker build 或 docker pull 时将 DOCKER_CONTENT_TRUST 环境变量设为“1“即可启用该特性 。Docker 守护进程将拒绝提取没有经过发布者签名的镜像 。
 
不幸的是,大约一年前开始社区就不再以这种方式签名镜像了 。就连 Docker Inc.也在 2020 年 12 月停止了签名官方 Docker镜像,也没有官方解释 。问题更大的是如果你使用“docker pull docker:latest”这样的命令,只会下载一个过时很久的镜像 。
 
你可以查看一下镜像签名的其他实现,比如说cosign(不过我还没试过) 。
 
扫描你自己的代码是否有安全问题 
安全问题通常来源于其他人的代码,也就是流行的第三方依赖 。因为它们应用广泛,所以在黑客那里是“有利可图“的 。然而,有时是你自己的代码在作怪 。例如,你可能不小心实现了 SQL 注入的可能性、堆栈溢出的错误,等等 。


推荐阅读