使用 Azure DevOps 构建管道将机密传递给容器时,Docker BuildKit 返回“/run/secrets/<secret-id>:没有这样的文件或目录”

Posted

技术标签:

【中文标题】使用 Azure DevOps 构建管道将机密传递给容器时,Docker BuildKit 返回“/run/secrets/<secret-id>:没有这样的文件或目录”【英文标题】:Docker BuildKit returning '/run/secrets/<secret-id>: No such file or directory' when passing secrets to container using Azure DevOps build pipeline 【发布时间】:2022-01-21 06:39:11 【问题描述】:

我对 docker 和 DevOps 还是很陌生。我正在尝试将秘密传递给我的 Docker 容器以在构建中使用。我读到使用 BuildKit 可以提高安全性,因为秘密不会被放入容器中。在遵循本文底部评论https://***.com/a/70309396 的步骤后,我遇到了问题,这似乎完美地解决了我的问题,并且看起来非常简单明了,但是我不确定出了什么问题?我可以使用 --build-arg 将其他环境传递到构建中(我没有将它们包含在 yaml 代码 sn-p 中),但是这种方法似乎在 Dockerfile 阶段对我来说是破坏(我认为)。

我的构建管道将值存储为一个秘密,当我将秘密回显到文件 $(Pipeline.Workspace)/direct_line_secret.txt 并将其 cat 如下所示时,我得到 *** 隐藏值在管道构建中按预期返回(第二行从底部)。

Starting: Store direct line secret
==============================================================================
Task         : Bash
Description  : Run a Bash script on macOS, Linux, or Windows
Version      : 3.195.0
Author       : Microsoft Corporation
Help         : https://docs.microsoft.com/azure/devops/pipelines/tasks/utility/bash
==============================================================================
Generating script.
========================== Starting Command Output ===========================
/usr/bin/bash /home/vsts/work/_temp/070f6401-2a4d-4656-b674-4fadcce142ea.sh
***
Finishing: Store direct line secret

这是我用于创建此脚本和 docker build 任务的 yaml 文件。

trigger:
- main

resources:
- repo: self

variables:
  dockerfilePath: '$(Build.SourcesDirectory)/Dockerfile'
  tag: '$(Build.BuildId)'
  DOCKER_BUILDKIT: 1

  # Agent VM image name
  vmImageName: 'ubuntu-latest'

stages:
- stage: Build
  displayName: Build and push stage
  jobs:
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)
    variables:
      DOCKER_BUILDKIT: 1
    steps:
    - bash: |
        echo $DIRECT_LINE_SECRET > $SECRET
        cat $SECRET
      displayName: Store direct line secret
      env:
        DIRECT_LINE_SECRET: $(nextPublicDirectLineSecret)
        SECRET: $(Pipeline.Workspace)/direct_line_secret.txt

    - task: Docker@2
      displayName: Build image
      inputs:
        command: build
        repository: $(imageRepository)
        dockerfile: $(dockerfilePath)
        containerRegistry: $(dockerRegistryServiceConnection)
        arguments: --secret id=dl_secret,src=$(Pipeline.Workspace)/direct_line_secret.txt --build-arg [...]
        tags: |
          $(tag)

[...]

这是我的 Dockerfile


# Build target base #
#####################
FROM node:lts-alpine AS base
WORKDIR /app
ARG NODE_ENV=production \
    API_BASE \
    SIGNALR_BASE \
    DIRECT_LINE_SECRET
ENV PATH=/app/node_modules/.bin:$PATH \
    NODE_ENV="$NODE_ENV" \
    NEXT_PUBLIC_API_BASE_URL=$API_BASE \
    NEXT_PUBLIC_SIGNALR_BASE_URL=$SIGNALR_BASE
RUN apk --no-cache add curl
COPY package.json yarn.lock /app/
EXPOSE 3000

# Build target dependencies #
#############################
FROM base AS dependencies
# Install prod dependencies
RUN yarn install --production && \
    # Cache prod dependencies
    cp -R node_modules /prod_node_modules && \
    # Install dev dependencies
    yarn install --production=false

# Build target development #
############################
FROM dependencies AS development
COPY . /app
CMD [ "yarn", "dev" ]

# Build target builder #
########################
FROM base AS builder
COPY --from=dependencies /app/node_modules /app/node_modules
COPY . /app
RUN --mount=type=secret,id=dl_secret 
RUN export NEXT_PUBLIC_DIRECT_LINE_SECRET=$(cat /run/secrets/dl_secret)
RUN yarn build && \
    rm -rf node_modules

# Build target production #
###########################
FROM base AS production
COPY --from=builder /app/public /app/public
COPY --from=builder /app/.next /app/.next
COPY --from=builder /app/next.config.js /app/next.config.js
COPY --from=dependencies /prod_node_modules /app/node_modules
CMD [ "yarn", "start" ]

HEALTHCHECK --interval=5s --timeout=5s --retries=3 \
    CMD curl --fail http://localhost:3000 || exit 1

我最初将这两条线放在顶部 RUN 线的下方,但认为它们被调用得太早了?移低时我仍然遇到同样的问题......

RUN --mount=type=secret,id=dl_secret 
RUN export NEXT_PUBLIC_DIRECT_LINE_SECRET=$(cat /run/secrets/dl_secret)

这是我在 docker build 任务的构建管道中遇到的错误

[...]

#12 [builder 3/5] RUN --mount=type=secret,id=dl_secret
#12 sha256:8a1115368ff855c4abf1043ef21d89bc00cfaa8759ba13fc73644e5df74a668b
#12 DONE 0.3s

#13 [builder 4/5] RUN export NEXT_PUBLIC_DIRECT_LINE_SECRET=$(cat /run/secrets/dl_secret)
#13 sha256:0e752980cf8a0ad0065c0ecc815a4f42a9c06aff4dc6de47eef68b9f01805e96
#13 0.306 cat: can't open '/run/secrets/dl_secret': No such file or directory
#13 DONE 0.3s

[...]

阅读有关 BuildKit 的文档时,它说像这样DOCKER_BUILDKIT=1 docker build 运行 docker build,或者设置变量,正如我在上面链接的帖子中看到的那样(我什至设置了两次)确保它没有在错误的地方),但这是我的 docker 命令在查看这些管道构建日志时的样子 /usr/bin/docker build -f /home/vsts/work/1/s/Dockerfile --label com.azure.dev.image.system.teamfoundationcollectionuri=https://dev.azure.com/[...] 我不知道它是否应该在构建命令前面有DOCKER_BUILDKIT=1,这可能是它不起作用的原因吗?

我不确定构建有什么问题,是否是文件未创建的情况,或者只是我设置错误?我花了几个小时试图研究这个问题,但没有找到太多适用的信息,并且进行了 100 次编辑,但无济于事。也许这是一件相当简单的事情,但我真的很急于这个周末把它整理好,我不知道接下来要尝试什么。我不确定如何浏览 DevOps 上的文件以查看是否有任何文件。

任何帮助将不胜感激!

【问题讨论】:

【参考方案1】:

--mount 命令必须在您希望使用密钥的同一层中使用。您当前在一个RUN 命令中指定--mount,然后尝试在另一个RUN 命令中cat 安装的密钥。

如果您将 Dockerfile 更改为使用单个 RUN 命令,则挂载的密钥将可用:

RUN --mount=type=secret,id=dl_secret \
  export NEXT_PUBLIC_DIRECT_LINE_SECRET=$(cat /run/secrets/dl_secret)

Dockerfile 前端的当前语法可以在这里找到:https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#run---mounttypesecret

【讨论】:

这确实解决了文件不存在的问题,但不幸的是该值仍然未定义:( 如果它解决了标题中概述的“没有这样的文件或目录”问题,我可以建议接受答案吗?这可能会帮助其他有同样问题的人。我相信您的 Dockerfile 现在是正确的,需要修复 CI 模板才能正确传递变量。 我已接受答案,谢谢。您对修正模板有什么建议吗? Azure 管道的输出会很有帮助。我将首先验证 SECRET 是否正在写入文件。不过,我通常只会使用 ENV 变量,将其写入文件并从文件中读取似乎很麻烦。

以上是关于使用 Azure DevOps 构建管道将机密传递给容器时,Docker BuildKit 返回“/run/secrets/<secret-id>:没有这样的文件或目录”的主要内容,如果未能解决你的问题,请参考以下文章

如何将变量传递给 azure devops 管道中的 SqlAzureDacpacDeployment@1 任务

在 azure 后端存储中使用状态文件将 terraform 的输出传递到 Azure Devops Pipeline

是否可以在 Azure DevOps 的构建管道期间下载文件?

azure devops 构建管道将超时减少到 30 分钟

在 azure-devops 中的发布管道期间创建标签

Azure DevOps 中的多个 YAML 构建管道