Docker Engine 18.09 から使える Build-time secrets を試してみた

Posted on

Docker Engine 18.09 から Build-time secrets という機能が使えるようになりました。この機能を使えば docker build するときに secrets (API key, credentials, etc) を安全に取り扱えるようになります。

Integrate secrets in your Dockerfile and pass them along in a safe way. These secrets do not end up stored in the final image nor are they included in the build cache calculations to avoid anyone from using the cache metadata to reconstruct the secret.

docker build--build-arg オプションを使えば引数で ARG を渡せますが、この方法で渡した ARGdocker history コマンドで簡単に見えてしまいます。公式ドキュメントにも secrets を渡す用途には使わないように警告されています。

Warning: It is not recommended to use build-time variables for passing secrets like github keys, user credentials etc. Build-time variable values are visible to any user of the image with the docker history command.

これまではワークアラウンドで回避するしかありませんでしたが、やっと secrets を安全に渡すオフィシャルな方法が提供されました。

Build-time secrets を試してみる

試した Docker 環境は次のとおりです。

$ docker version
Client: Docker Engine - Community
 Version:           18.09.0
 API version:       1.39
 Go version:        go1.10.4
 Git commit:        4d60db4
 Built:             Wed Nov  7 00:47:43 2018
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.0
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.4
  Git commit:       4d60db4
  Built:            Wed Nov  7 00:55:00 2018
  OS/Arch:          linux/amd64
  Experimental:     false

今回は AWS credentials を使って S3 にアクセスしてみます。 S3 にあるプライベートなファイルをビルド時にダウンロードしてくるイメージです。

Dockerfile はこんな感じです。

# syntax = docker/dockerfile:1.0.0-experimental
FROM python:3.7-alpine

RUN pip install --quiet awscli

RUN --mount=type=secret,id=credentials,dst=/root/.aws/credentials \
aws s3 cp --region ap-northeast-1 s3://docker-build-secret/hello_world.txt .

まだ stable チャンネルの Dockerfile では有効になっていないので、1 行目のコメントで experimental チャンネルの syntax であることを宣言しています。詳しい説明は New Docker Build secret information を参照してください。

docker build するときは --secret オプションで idsrc を指定します。 BuildKit はまだオプトインの機能なので DOCKER_BUILDKIT=1 を指定する必要があります。

$ DOCKER_BUILDKIT=1 docker build -t docker-build-secret --progress=plain \
--secret id=credentials,src=$HOME/.aws/credentials .

実行すると S3 にアクセスできていることが確認できました。

#8 [3/3] RUN --mount=type=secret,id=credentials,dst=/root/.aws/credentials ...
#8       digest: sha256:389724b447a83d008ea7d2a8ab88f3ceb9a4fe58dc58af68b3a62996c7f74251
#8         name: "[3/3] RUN --mount=type=secret,id=credentials,dst=/root/.aws/credentials aws s3 cp --region ap-northeast-1 s3://docker-build-secret/hello_world.txt ."
#8      started: 2018-12-28 07:50:57.1181584 +0000 UTC
download: s3://docker-build-secret/hello_world.txt to ./hello_world.txt
#8    completed: 2018-12-28 07:51:00.7738157 +0000 UTC
#8     duration: 3.6556573s

Build-time secrets の仕組みを知る

Build-time secrets の仕組みを知るために、ビルドされた Docker イメージを調べてみます。

docker history でイメージの履歴を見ても credentials の情報はもちろん残っていません。

$ docker history docker-build-secret
IMAGE               CREATED              CREATED BY                                      SIZE                COMMENT
8f70e4d781aa        About a minute ago   RUN /bin/sh -c aws s3 cp --region ap-northea…   206kB               buildkit.dockerfile.v0
<missing>           About a minute ago   RUN /bin/sh -c pip install --quiet awscli # …   60.9MB              buildkit.dockerfile.v0
<missing>           47 hours ago         /bin/sh -c #(nop)  CMD ["python3"]              0B
...

Dockerfile の中で /root/.aws/credentials のパスにマウントしましたが、Docker イメージの中には空のファイルだけが残っていました。

$ docker run -ti --rm docker-build-secret /bin/sh
/ # ls -l /root/.aws/credentials
-rwxr-xr-x    1 root     root             0 Dec 28 07:50 /root/.aws/credentials
/ # cat /root/.aws/credentials
/ #

また、同じ Dockerfile の中でも他の RUN コマンドからは参照できませんでした。たとえば、次の Dockerfile の最後の RUN コマンドは空のファイルを参照するだけです。

# syntax = docker/dockerfile:1.0.0-experimental
FROM python:3.7-alpine

RUN pip install --quiet awscli

RUN --mount=type=secret,id=credentials,dst=/root/.aws/credentials \
aws s3 cp --region ap-northeast-1 s3://docker-build-secret/hello_world.txt .

RUN cat /root/.aws/credentials

公式ドキュメントには詳しい仕組みが載っていませんが、BuildKit のソースコードを読んでみると tmpfs をマウントして実現していました。

まとめ

これまで secrets を安全に扱うには multi-stage build を使ったり、イメージの履歴に残らない特別な build-args を使っていましたが、Build-time secrets はそれらのワークアラウンドをすべて解決してくれます!

BuildKit について詳しく知りたい方は、こちらのスライドがよくまとまっていました。