密钥写进Docker ENV会留在镜像:构建时改用secret mount

|作者: QUASA 编辑团队|2 分钟阅读
密钥写进Docker ENV会留在镜像:构建时改用secret mount

写入 Dockerfile ENV 的密钥会保留在镜像配置中;Docker 构建密钥文档建议构建时用 secret mount 传递凭证,而非使用 ENV 或 ARG。secret mount 只在使用它的构建指令执行期间提供秘密,不会因为指令结束而继续留在挂载位置。

容器启动后仍需读取的密码属于运行阶段。Compose secrets 规范允许从文件或宿主机环境变量取得秘密,但服务须显式声明访问权限。宿主机变量只是秘密的输入来源;若再把它写进服务的 environment,容器收到的仍是普通环境变量。

先按密钥的使用阶段选择入口

构建私有依赖、访问制品仓库或在构建期间认证接口,需要的是供构建指令短暂读取的凭证。构建完成后,应用不应再依赖这枚凭证。secret mount 限制了秘密的传入范围,但构建脚本若将其复制到生成文件、缓存或日志,最终产物仍可能泄露它。

应用在运行时连接数据库,或持续访问需要认证的接口,则必须在容器启动后取得凭证。构建时的挂载不会延续到运行中的容器;这类密码应交给获授权的服务,并由应用读取挂载文件。同一个项目若两个阶段都需要秘密,须分别配置构建访问和运行时访问,不能把其中一种授权当成另一种。

构建时:让 RUN 临时读取 secret mount

以下是使用无实际权限示例值的最小检查。Dockerfile 依次写入 FROM alpine 和 RUN --mount=type=secret,id=repo_token,required=true test -s /run/secrets/repo_token。repo_token 是秘密标识;test 只检查文件是否存在且非空,不会输出其内容。真正下载私有依赖时,应让下载命令直接读取这个挂载路径,并避免把凭证写进下载地址或生成文件。

在执行构建的宿主机或 CI 作业中设置 REPO_TOKEN,再运行 docker build --secret id=repo_token,env=REPO_TOKEN -t secret-demo .。这会将宿主机变量作为构建秘密提供给对应的 RUN 指令,Dockerfile 无需声明 ENV REPO_TOKEN。若秘密保存在文件中,可使用 --secret id=repo_token,src=文件路径;默认挂载路径仍是 /run/secrets/repo_token。

挂载只覆盖声明它的那条 RUN 指令,后续 RUN 不能继续读取该文件。构建命令或工具若在调试输出、报错信息中打印凭证,日志仍会泄露;将认证配置复制进应用目录,也会使镜像保留它。临时挂载解决的是秘密如何进入构建步骤,不能撤销构建步骤主动写出的内容。

运行时:用 Compose secrets 授权服务

另一个条件示例以宿主机 APP_TOKEN 为来源。compose.yaml 中可用两行顶层映射表达配置:services: {app: {image: alpine, secrets: [app_token]}} 和 secrets: {app_token: {environment: APP_TOKEN}}。前一行授权 app 服务,后一行指定秘密来源;单有顶层定义,不会自动授予服务访问权。

设置宿主机 APP_TOKEN 后,运行 docker compose run --rm app sh -c 'test -s /run/secrets/app_token',可通过退出状态检查挂载文件,而不显示秘密值。正式应用应读取 /run/secrets/app_token;如果镜像支持指向密码文件的配置项,可将该项设为这个路径。若秘密来自本地文件,则把顶层定义改为 secrets: {app_token: {file: ./app_token.txt}},并管理好该文件及其备份的访问权限。

Compose 使用指南分别展示了服务的 secrets 授权和构建配置中的 build.secrets。通过 docker compose build 构建时,还须在目标服务的 build.secrets 下列出秘密,Dockerfile 的 RUN 挂载才能使用它;仅配置服务的 secrets 解决的是运行时访问。以宿主机环境变量作为顶层秘密来源的写法适用于 Docker Compose,不适用于 docker stack deploy。

五个位置分别会暴露什么

下面的判断以示例命令没有主动复制或打印秘密为前提。文件挂载避免了把值放进普通容器环境变量,但能进入获授权容器并读取文件的人,仍可能取得秘密。

  • Dockerfile:直接写入 ENV 的字面值会出现在文件中;通过 ARG 再赋给 ENV,也会使值进入镜像环境配置。secret mount 指令只需写标识和挂载方式,无需写入秘密值。
  • 构建日志:示例中的 test 不输出文件内容。若脚本执行 cat、回显敏感参数,或让工具在报错时打印认证信息,日志仍可能包含秘密。
  • 镜像历史与配置:ENV 值保留在镜像环境配置中;ARG 虽不自动成为运行时环境变量,其值仍可能出现在构建历史或来源证明中。secret mount 不会自动把值写入镜像,但历史可以显示使用挂载的指令。
  • docker inspect:镜像或容器的 Config.Env 可显示设置过的环境变量。以 Compose secret 文件交付的值不会仅因挂载而成为 Config.Env 的值;挂载路径或元数据可见,也不等于文件内容受到所有容器内进程的隔离。
  • /run/secrets:构建秘密在对应 RUN 执行时提供;运行时 Compose secret 则在获授权的服务容器内作为文件提供。两处可能使用相似路径,生命周期和授权配置却不同。

用无权限示例值检查结果

构建完成后,执行 docker image inspect secret-demo --format '{{json .Config.Env}}' 和 docker history --no-trunc secret-demo,查找示例值是否出现在镜像环境或历史中。再检查构建脚本与产物目录,确认没有复制挂载文件或生成含凭证的配置。若发现值,须处理实际写入它的 ENV、ARG、RUN 或脚本;改用挂载参数本身不会清除已经生成的内容。

运行阶段可再次用 test -s 检查 /run/secrets/app_token 是否可读,再对服务容器执行 docker inspect,查看 Config.Env 中是否另有 APP_TOKEN。检查时只验证文件存在与访问权限,不在终端或 CI 日志中打印内容。构建完成后不再需要的凭证使用 BuildKit secret mount;运行期间持续需要的凭证交给获授权的 Compose 服务,并由应用读取文件。

相关阅读:

分享:

订阅我们的新闻通讯

将最新 Web3、AI 和加密货币新闻直接发送到您的邮箱。

0