在构建容器镜像时,有的时候需要在Dockerfile中使用一些敏感信息,例如下面的Dockerfile:

1
2
3
4
5
FROM python:3
......
WORKDIR /usr/src/app
RUN  pip3 install -r /usr/src/app/requirements.txt -i https://username:password@pypi.company.com/simple
......

这是构建一个python应用镜像的Dockerfile,在从私有pip源安装依赖时需要在Dockerfile中用到私有源仓库的用户名和密码。如果像上面这个Dockerfile这样写的话,用户名和密码就会暴露在构建出的镜像中,并随着镜像的分发泄露出去。

在使用buildkit构建容器镜像时,可以使用secret解决这个问题。先看一下buildctl build命令的选项:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
buildctl build --help
NAME:
   buildctl build - build

USAGE:

  To build and push an image using Dockerfile:
    $ buildctl build --frontend dockerfile.v0 --opt target=foo --opt build-arg:foo=bar --local context=. --local dockerfile=. --output type=image,name=docker.io/username/image,push=true


OPTIONS:
   --output value, -o value  Define exports for build result, e.g. --output type=image,name=docker.io/username/image,push=true
   --progress value          Set type of progress (auto, plain, tty). Use plain to show container output (default: "auto")
   --trace value             Path to trace file. Defaults to no tracing.
   --local value             Allow build access to the local directory
   --frontend value          Define frontend used for build
   --opt value               Define custom options for frontend, e.g. --opt target=foo --opt build-arg:foo=bar
   --no-cache                Disable cache for all the vertices
   --export-cache value      Export build cache, e.g. --export-cache type=registry,ref=example.com/foo/bar, or --export-cache type=local,dest=path/to/dir
   --import-cache value      Import build cache, e.g. --import-cache type=registry,ref=example.com/foo/bar, or --import-cache type=local,src=path/to/dir
   --secret value            Secret value exposed to the build. Format id=secretname,src=filepath
   --allow value             Allow extra privileged entitlement, e.g. network.host, security.insecure
   --ssh value               Allow forwarding SSH agent to the builder. Format default|<id>[=<socket>|<key>[,<key>]]
   --metadata-file value     Output build metadata (e.g., image digest) to a file as JSON

注意到buildctl build有一个--secret的选项,选项值的格式是id=secretID,src=filepath。 表示在使用buildctl构建镜像时可以通过--secret将外部的一个文件以指定的id名传递到Dockerfile中。 那么在Dockerfile中如何使用外部传入的secret呢?

Dockerfile的RUN指令支持一个--mount的选项,可以secret挂载进去,具体用法RUN --mount=type=secret,id=secretID,secret将以文件的形式默认挂载到构建容器环境中的/run/secrets/secretID,而镜像构建结束后这个文件将不再存在。使用这种方式就可以避免将敏感信息构建到镜像中。

本文一开始的python应用镜像构建例子中可以修改如下:

1
2
RUN  --mount=type=secret,id=pypiSecret  secretFile=`cat /run/secrets/pypiSecret` && \
  pip3 install -r /usr/src/app/requirements.txt -i https://$secretFile@pypi.company.com/simple

使用buildctl的--secret将外部的pypiSecret文件传递进去:

1
2
3
4
5
buildctl build --frontend dockerfile.v0 
 --local 'context=./' \
 --local 'dockerfile=./' \
 --output 'type=image,push=true,"name=your.harbor.com/project/pyapp:latest"' \
 --secret 'id=pypiSecret,src=/somedir/pypiSeceretFile'

注意Dockerfile的RUN指令还可以定制secret文件的挂载位置,默认是/run/secrets,可以使用RUN指令--mount选项的dst选项值修改:

1
RUN --mount=type=secret,id=mysecret,dst=/foobar cat /foobar

总结

secret是buildkit和Dockerfile中专门用来处理容器镜像构建中所需敏感数据传递问题的解决方案。 Dockerfile中的RUN指令除了支持RUN --mount=type=secret外,mount的type还支持cache,bind,tmpfs,ssh,这里不再展开介绍。

参考