1. 概述

在构建 Docker 镜像时,安全地处理敏感信息(如数据库凭据、API 密钥、Token 等)是非常关键的。将这些敏感信息硬编码进 Dockerfile 或嵌入到镜像中,都是非常不安全的做法。

通常,我们会使用文件的方式在构建过程中引入密钥(secret)。但本文将介绍一种相对少见但同样有效的方式:通过环境变量直接向 Docker Build 传入密钥

我们将一步步演示如何在 Docker 构建过程中使用 --secret 标志安全地引入一个密钥,并避免其泄露到最终的镜像中。

2. Docker 构建中的密钥管理挑战

在构建过程中,docker build 命令本身并没有提供一个安全传递敏感信息的机制,这就带来了密钥管理的难题:

问题一:直接嵌入镜像
将密钥直接写入镜像中,意味着任何能访问镜像的人都可以获取这些敏感信息,这严重违反了安全最佳实践。

问题二:使用 --build-arg 不安全
虽然 --build-arg 使用起来非常方便,但这些参数会被记录在镜像的元数据中,同样存在泄露风险。

问题三:使用临时文件容易出错
如果使用临时文件来传递密钥,一旦忘记删除这些文件,也可能导致敏感信息暴露。

推荐方式:使用 BuildKit 的 --secret 标志
从 Docker 18.09 开始,BuildKit 引入了 --secret 功能,可以让我们在构建过程中安全地使用密钥,且不会将其暴露到最终的镜像中。

3. 通过环境变量传入密钥

在开始操作前,请确保你的 Docker 版本 >= 18.09,并且已经启用了 BuildKit。同时,Dockerfile 必须使用 BuildKit 支持的语法格式。

接下来,我们一步步演示如何通过环境变量传入密钥。

3.1. 设置环境变量

首先,将密钥存储在一个环境变量中:

$ export MY_SECRET="super-secret-value"

⚠️ 注意:这里我们没有将密钥硬编码进任何脚本或 Dockerfile,而是通过环境变量来管理,提升了安全性。

3.2. 编写 Dockerfile

我们创建一个简单的项目目录结构:

$ tree sample_project
sample_project
└── Dockerfile

0 directories, 1 file

然后,编辑 Dockerfile 内容如下:

# syntax=docker/dockerfile:1.4

FROM ubuntu:20.04

# 使用密钥
RUN --mount=type=secret,id=my_secret cat /run/secrets/my_secret > /tmp/secret_output

CMD ["bash"]

逐行解释:

  • # syntax=docker/dockerfile:1.4:指定使用 BuildKit 的语法版本
  • RUN --mount=type=secret,id=my_secret:表示在构建时挂载一个名为 my_secret 的密钥文件
  • cat /run/secrets/my_secret > /tmp/secret_output:读取密钥内容并写入一个临时文件用于演示

⚠️ 注意:该密钥只在构建阶段可用,不会保留在最终的镜像中。

3.3. 构建镜像

使用 BuildKit 构建镜像,并通过 --secret 传入密钥:

$ DOCKER_BUILDKIT=1 docker build \
  --secret id=my_secret,env=MY_SECRET \
  -t my_image .

参数说明:

  • DOCKER_BUILDKIT=1:启用 BuildKit 构建器
  • --secret id=my_secret,env=MY_SECRET:将环境变量 MY_SECRET 的值作为 ID 为 my_secret 的密钥传入构建过程
  • -t my_image:给镜像打上标签 my_image
  • .:表示当前目录为构建上下文

3.4. 验证密钥是否正确使用

运行容器并查看是否成功读取密钥内容:

$ docker run --rm my_image cat /tmp/secret_output && echo
super-secret-value

✅ 输出结果表明密钥被成功读取并写入了 /tmp/secret_output 文件中。

⚠️ 再次强调:这个密钥只在构建阶段可用,不会出现在最终镜像中,因此非常安全。

4. 常见问题排查

虽然整个过程看起来很顺利,但在实际使用中可能会遇到一些常见问题。下面列出几个典型问题及解决方法。

4.1. 构建过程中密钥不可用

问题表现: 构建时报错提示找不到 /run/secrets/my_secret 文件。

可能原因:

  1. BuildKit 未启用
    确保命令前加上 DOCKER_BUILDKIT=1,否则 BuildKit 不会生效,--secret 也无法使用。

  2. 环境变量未设置或为空
    执行以下命令检查环境变量是否设置正确:

    $ echo $MY_SECRET
    super-secret-value
    
  3. ID 不匹配
    Dockerfile 中使用的 id=my_secret 必须与 --secret 参数中定义的 id 一致,否则密钥无法挂载。

4.2. 密钥意外保留在镜像中

问题表现: 最终镜像中仍然可以找到密钥内容。

可能原因:

  1. 在构建步骤中显式写入密钥文件
    例如:

    RUN echo "$MY_SECRET" > /app/config
    

    这样会将密钥写入镜像层中,无法清除。

  2. 使用 COPY 或 ADD 复制密钥文件
    例如:

    COPY secret_file /app/config
    

    如果 secret_file 包含敏感信息,它将被嵌入镜像中,存在泄露风险。

✅ 正确做法:

  • 使用 --mount=type=secret 临时挂载密钥文件
  • 只在构建阶段使用密钥,不在任何层中保留其内容

5. 总结

本文介绍了如何通过环境变量安全地向 Docker Build 传入密钥。我们使用 BuildKit 的 --secret 功能,实现了以下目标:

  • ✅ 将敏感信息从环境变量传入构建过程
  • ✅ 避免将密钥暴露到最终的镜像中
  • ✅ 通过验证和常见问题排查,确保流程安全可靠

这种做法在 CI/CD 流程中尤其有用,可以避免将密钥硬编码进脚本或镜像中,从而提升整体系统的安全性。

建议在所有需要构建阶段使用敏感信息的场景中,优先使用 BuildKit + --secret 的组合,避免踩坑。


原始标题:Adding Secret to Docker Build From Environment Variable