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


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

文章插图
 
本文介绍了 12 个优化 Docker 镜像安全性的技巧 。每个技巧都解释了底层的攻击载体,以及一个或多个缓解方法 。这些技巧包括了避免泄露构建密钥、以非 root 用户身份运行,或如何确保使用最新的依赖和更新等 。
前言 
当你是刚开始使用 Docker 的新手时,你很可能会创建不安全的 Docker 镜像,使攻击者很容易借此接管容器,甚至可能接管整个主机,然后渗透到你公司的其他基础设施中 。
 
可以被滥用来接管你的系统的攻击向量有很多,例如:
 
  • 启动的应用程序(在你 Dockerfile 的 ENTRYPOINT 中指定)以 root 用户身份运行 。这样以来,一旦攻击者利用了一个漏洞并获得了 shell 权限,他们就可以接管 Docker 守护程序所运行的主机 。
  • 你的镜像是基于一个过时的和/或不安全的基础镜像,其中包含(现在)众所周知的安全漏洞 。
  • 你的镜像包含了一些工具(如 curl、apt 等),一旦攻击者获得了某种访问权,就可以通过这些工具将恶意软件加载到容器中 。
 
下面的各个章节讲解了能够优化你的镜像安全性的各种方法 。它们是按重要性/影响程度排序的,也就是说排名靠前的方法更重要 。
避免泄露构建密钥 
构建密钥是只在构建 Docker 镜像时需要的凭证(不是在运行时) 。例如,你可能想在你的镜像中包含某个应用程序的一个编译版本,这个应用的源代码是闭源的,并且其 Git 存储库是有访问保护的 。在构建镜像时,你需要克隆 Git 存储库(这需要构建密钥,例如该存储库的 SSH 访问密钥),从源代码构建应用程序,然后再删除源代码(和密钥) 。
 
“泄露“构建密钥是说你不小心把这种密钥烘焙到了你的镜像的某个层中 。这种情况很严重,因为拉取你的镜像的所有人都可以检索到这些机密 。这个问题源于这样一个事实,即 Docker 镜像是以纯粹的加法方式逐层构建的 。你在一个层中删除的文件只是被“标记”为已删除,但拉取你镜像的人们仍然可以使用高级工具访问它们 。
 
可以使用以下两种方法之一来避免泄露构建密钥 。
多阶段构建 
Docker 多阶段构建(官方文档)有许多用例,例如加快你的镜像构建速度,或减少镜像大小 。本系列的其他文章会详细介绍其他用例 。总之,你也可以通过多阶段构建来避免泄露构建密钥,如下所示:
 
  • 创建一个阶段 #A,将凭证复制到其中,并使用它们来检索其他工件(例如上述例子中的 Git 存储库)和执行进一步的步骤(例如编译一个应用程序) 。阶段 #A 的构建确实包含了构建的密钥!
  • 创建一个 #B 阶段,其中你只从 #A 阶段复制非加密的工件,例如一个已编译的应用程序 。
  • 只发布/推送阶段 #B 的镜像
BuildKit 的密钥背景知识 
如果你使用 docker build 进行构建,可以实际执行构建的后端选项不止一个 。其中较新和较快的后端是 BuildKit,你需要在 linux 上设置环境变量 DOCKER_BUILDKIT=1 来显式启用它 。注意,BuildKit 在 windows/macOS 的 Docker for Desktop 上是默认启用的 。
 
正如这里的文档所解释的(阅读它们以了解更多细节),BuildKit 构建引擎支持 Dockerfile 中的额外语法 。要使用构建密钥,请在你的 Dockerfile 中放入类似下面这样的内容:
 
RUN --mount=type=secret,id=mysecret,dst=/foobar <command to run>复制代码
当 RUN 语句被执行时,密钥将对这个构建容器可用,但不会将密钥本身(这里是:/foobar 文件夹)放入构建的镜像中 。你需要在运行 docker build 命令时指定密钥的源文件/文件夹(位于主机上)的路径,例如:
 
docker build --secret id=mysecret,src=https://www.isolves.com/it/cxkf/rongqi/2022-03-23/mysecret.txt -t sometag 复制代码
 
不过有一点需要注意:你不能通过 docker-compose up --build 来构建需要密钥的镜像,因为 Docker-compose 还不支持用于构建的--secret 参数,见 GitHub问题 。如果你依赖 docker-compose 的构建,请使用方法 1(多阶段构建) 。
题外话:不要推送在开发机上构建的镜像 
你应该一直在一个干净的环境中构建和推送镜像(例如 CI/CD 管道),其中构建代理会将你的存储库克隆到一个新目录 。


推荐阅读