用 Dockerfile 构建镜像时用 build secrets 安全优雅地传递敏感数据
起因是这样的,我有一个 Go 写的 utils
库,但这个仓库目前是 private 的,于是在其他引用了这个库的项目构建时就需要解决鉴权访问的问题。
先来看看不使用私有库的 Go 项目构建的 Dockerfile:
# build api
FROM golang:1.23 AS builder
WORKDIR /src/app
COPY . ./
RUN --mount=type=cache,target=/go/pkg/mod \
make
# build the final image
FROM ubuntu:24.04
COPY --from=builder /src/app/bin/api /app/bin/api
而使用了私有库的话,就需要设置 GOPRIVATE
环境变量和鉴权。
在本地开发的时候我是用的 ssh
来访问 git 仓库的,所以在 ~/.gitconfig
追加如下配置即可:
[url "[email protected]:"]
insteadOf = https://github.com/
但是使用 Docker 构建的时候继续使用 ssh
就会略麻烦(使用 Deploy key)和不安全(使用个人账号 SSH key),那么细粒度的 Access Token 会是更好的选择。
因为 Access Token 是敏感数据,我们是不能直接写在 Dockerfile 里的,所以你可能会想到那就通过构建参数传进去:
+ARG GITHUB_TOKEN
+ENV TOKEN=$GITHUB_TOKEN
WORKDIR /src/app
COPY . ./
+RUN go env -w GOPRIVATE="github.com/YianAndCode/MY_PRIVATE_GO_PKG"
+RUN git config --global url."https://${TOKEN}:[email protected]/".insteadOf "https://github.com/"
RUN --mount=type=cache,target=/go/pkg/mod \
make
然后构建:
export GITHUB_TOKEN=xxx
docker build --build-arg GITHUB_TOKEN=${GITHUB_TOKEN} -t myapp:latest .
这时你会得到一个警告:
WARN: SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data
因为这样做别人在拿到镜像之后是可以看到你构建过程中的参数的,也不安全。其实在 Docker 官方文档有介绍 Build secrets,我们可以利用这个功能来实现更安全优雅地传入敏感数据。
首先来学习一下 build secrets
的基本知识:
1. 怎么传入 secrets
在执行 docker build
的时候通过 --secret
传入,格式:
# 通过文件传入
--secret id={ID},src=/path/to/secret_file
# 通过环境变量传入
--secret id={ID},env=SECRET_ENV_NAME
# 如果 id 跟环境变量名同名,可以简写
--secret id=SECRET_ENV_NAME
完整示例:
# 将环境变量 KUBECONFIG 为 secret 传入,secret id 是 kube
docker build --secret id=kube,env=KUBECONFIG .
2. Dockerfile 里怎么获取 secrets
前面的 id 是 Dockerfile 中用来获取 secret 的,在 Dockerfile 中需要先挂在:RUN --mount=type=secret,id={ID}
,默认会被作为文件放在 /run/secrets/{ID}
中:
RUN --mount=type=secret,id={ID} \
cat /run/secrets/{ID}
上面这个 Dockerfile 通过增加 --progress=plain
可以看到 cat
的输出:
HELLO="Hello world!" docker build --progress=plain --secret id=HELLO .
记得要把这个测试构建的无用镜像删掉
也可以通过 target
选项来指定文件位置或者通过 env
选项来使传入的 secret 作为环境变量:
# 指定文件
RUN --mount=type=secret,id={ID},target=/path/to/secret_file \
cat /path/to/secret_file
# 作为环境变量
RUN --mount=type=secret,id={ID},env=SECRET_ENV_NAME \
echo ${SECRET_ENV_NAME}
学习完 Build secrets
的知识之后,咱们把最开始构建 Go 项目的 Dockerfile 改下:
# build api
FROM golang:1.23 AS builder
WORKDIR /src/app
COPY . ./
RUN go env -w GOPRIVATE="github.com/YianAndCode/MY_PRIVATE_GO_PKG"
RUN --mount=type=secret,id=GITHUB_TOKEN,env=GITHUB_TOKEN \
git config --global url."https://${GITHUB_TOKEN}:[email protected]/".insteadOf "https://github.com/"
RUN --mount=type=cache,target=/go/pkg/mod \
make
# build the final image
FROM ubuntu:24.04
COPY --from=builder /src/app/bin/api /app/bin/api