はじめに
Docker 18.09 で BuildKit
が正式に採用されました。BuildKit
によって、パフォーマンスの向上、ビルドキャッシュの改良、 鍵ファイルや SSH 秘密鍵の安全なマウントなどの改良がされました。今回は、BuildKit
の鍵ファイル、SSH 秘密鍵の安全なマウントに着目して、セキュアなビルドを試してみます。
環境
- macOS High Sierra Version 10.13.6
- Docker for Mac 2.0.0.3 (Engine: 18.09.2)
BuildKit を有効にする
環境変数から BuildKit
を有効にするには、DOCKER_BUILDKIT=1
を実行して有効にします。
$ export DOCKER_BUILDKIT=1
$ docker build .
デフォルトで有効にする場合は、/etc/docker/daemon.json
に以下の設定を記述して再起動することで有効になります。
本記事での環境は Docker for Mac なので、Docker for Mac の設定から Daemon - Advanced
を開いて設定を記述します。
{
"features": {
"buildkit": true
}
}
以降は、BuildKit
が有効である前提で進めます。
鍵ファイルのマウント
今まで Dockerfile 内からプライベートな Git リポジトリーや AWS などのクラウドサービスに接続するにはちょっとした工夫をする必要がありました。
まずは単純に鍵ファイルを COPY
命令でビルドした例をみてみましょう。
以下のような Dockerfile を用意します。
FROM ubuntu:18.04
COPY id_rsa /root/.ssh/
RUN apt-get update && apt-get install -y git
RUN ssh-keyscan -H github.com >> /root/.ssh/known_hosts \
&& git clone git@github.com:[ユーザー名]/[プライベートリポジトリ名].git
RUN rm -rf /root/.ssh/id_rsa
鍵ファイルを用意したディレクトリでビルドしてみます。
$ docker build -t sample .
ビルドした Docker イメージの履歴を表示してみます。
$ docker history sample
IMAGE CREATED CREATED BY SIZE COMMENT
4a841c8c9d87 2 minutes ago /bin/sh -c rm -rf /root/.ssh/id_rsa 0B
de2dc4ca7089 2 minutes ago /bin/sh -c ssh-keyscan -H github.com >> /roo… 22.6kB
16ced59e0844 2 minutes ago /bin/sh -c apt-get update && apt-get install… 118MB
207d964e6f0b 3 minutes ago /bin/sh -c #(nop) COPY file:53ba8782ad73011c… 1.68kB
47b19964fb50 3 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 3 weeks ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B
<missing> 3 weeks ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B
<missing> 3 weeks ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B
<missing> 3 weeks ago /bin/sh -c #(nop) ADD file:529264c6593975a61… 88.1MB
履歴を見ると鍵ファイルを削除する前のレイヤーが残っていることが分かります。
削除する前のレイヤーにアクセスできるということはパブリックなレジストリにプッシュしてしまうと、第三者が鍵ファイルにアクセスできてしまうことになります。
以前のバージョンでこのレイヤーへアクセスできないようにするには、マルチステージビルドや docker build --squash
を用いて不要なレイヤを削除する必要があります。
マルチステージビルドを試した場合の Dockerfile は以下のとおりです。
FROM ubuntu:18.04 as builder
COPY id_rsa /root/.ssh/
RUN apt-get update && apt-get install -y git
RUN ssh-keyscan -H github.com >> /root/.ssh/known_hosts \
&& git clone git@github.com:[ユーザー名]/[プライベートリポジトリ名].git
RUN rm -rf /root/.ssh/id_rsa
FROM ubuntu:18.04
COPY --from=builder /private-test /private-test
マルチステージビルドの Dockerfile をビルドします。
$ docker build -f Dockerfile.multi -t sample-multi .
マルチステージビルドした Docker イメージの履歴を表示してみます。
$ docker history sample-multi
IMAGE CREATED CREATED BY SIZE COMMENT
8ec225a84208 3 minutes ago /bin/sh -c #(nop) COPY dir:681f0d3bca9cd752e… 21.7kB
47b19964fb50 3 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 3 weeks ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B
<missing> 3 weeks ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B
<missing> 3 weeks ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B
<missing> 3 weeks ago /bin/sh -c #(nop) ADD file:529264c6593975a61… 88.1MB
COPY
したレイヤだけが表示されているので鍵ファイルにアクセスできないことが分かります。
次に Docker の experimental
を有効にして、 docker build --squash
を試した場合の Dockerfile は以下のとおりです。
FROM ubuntu:18.04
COPY id_rsa /root/.ssh/
RUN apt-get update && apt-get install -y git
RUN ssh-keyscan -H github.com >> /root/.ssh/known_hosts \
&& git clone git@github.com:[ユーザー名]/[プライベートリポジトリ名].git
RUN rm -rf /root/.ssh/id_rsa
docker build --squash
でビルドしてみます。
$ docker build --squash -f Dockerfile.squash -t sample-squash .
docker build --squash
した Docker イメージの履歴を表示してみます。
$ docker history sample-squash
IMAGE CREATED CREATED BY SIZE COMMENT
d447056e816f About a minute ago 118MB merge sha256:4a841c8c9d871dbe8f44c3624cb41bb3c0f65ff76b1b6ea29516d3cb945f0f4d to sha256:47b19964fb500f3158ae57f20d16d8784cc4af37c52c49d3b4f5bc5eede49541
<missing> 3 hours ago /bin/sh -c rm -rf /root/.ssh/id_rsa 0B
<missing> 3 hours ago /bin/sh -c ssh-keyscan -H github.com >> /roo… 0B
<missing> 3 hours ago /bin/sh -c apt-get update && apt-get install… 0B
<missing> 3 hours ago /bin/sh -c #(nop) COPY file:53ba8782ad73011c… 0B
<missing> 3 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 3 weeks ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B
<missing> 3 weeks ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B
<missing> 3 weeks ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B
<missing> 3 weeks ago /bin/sh -c #(nop) ADD file:529264c6593975a61… 88.1MB
docker build --squash
を利用するとマージされ不要なレイヤが削除されていることが分かります。
以上のように以前のバージョンでは注意して対応する必要がありました。
そこで BuildKit
では、RUN --mount=type=secret
命令を使用することでマウントできるようになりました。
試した例が以下の Dockerfile です。
# syntax = docker/dockerfile:experimental
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y git
RUN --mount=type=secret,id=ssh,dst=/root/.ssh/id_rsa \
ssh-keyscan -H github.com >> /root/.ssh/known_hosts \
&& git clone git@github.com:[ユーザー名]/[プライベートリポジトリ名].git
Dockerfile の新しい構文を利用するため、1 行目に # syntax = docker/dockerfile:experimental
を指定する必要があります。
そして、RUN --mount=type=secret
命令を用いて鍵ファイルをイメージに残す危険性を考えずにマウントできます。
id
には docker build 時に渡すキーとなる任意の同じ ID 、dst
には Docker コンテナ内のマウント先を指定します。dst
は、target
・destination
のいずれかでも動作します。
以下のコマンドでビルドします。
$ docker build --secret id=ssh,src=$HOME/.ssh/i d_rsa -f Dockerfile.secret -t sample-secret .
--secret
を有効にし、id
に Dockerfile の RUN --mount=type=secret
命令でも使用した同じ ID、src
にマウント元を指定します。src
は source
でも動作します。
RUN --mount=type=secret
命令を使用した Docker イメージの履歴を表示してみます。
$ docker history sample-secret
IMAGE CREATED CREATED BY SIZE COMMENT
f2f83f34ae1c 4 minutes ago RUN /bin/sh -c ssh-keyscan -H github.com >> … 22.6kB buildkit.dockerfile.v0
<missing> 30 hours ago RUN /bin/sh -c apt-get update && apt-get ins… 118MB buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 4 weeks ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B
<missing> 4 weeks ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0B
<missing> 4 weeks ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B
<missing> 4 weeks ago /bin/sh -c #(nop) ADD file:529264c6593975a61… 88.1MB
鍵ファイルが残っていないことが見て取れます。
実際にコンテナを起動して確認して見ると、コンテナの中には空のファイルが残っていることが分かります。
$ docker run --rm --name sample-secret -it -d sample-secret bash
$ docker attach sample-secret
root@9d4b16c1ba71:/# ls /root/.ssh/
id_rsa known_hosts
root@9d4b16c1ba71:/# cat /root/.ssh/id_rsa
root@9d4b16c1ba71:/#
SSH 秘密鍵のマウント
RUN --mount=type=secret
命令では、パスフレーズ付きの SSH 秘密鍵には対応できません。
そこで代わりに RUN --mount=type=ssh
命令が対応しています。
試した例が以下の Dockerfile です。
# syntax = docker/dockerfile:experimental
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y git
RUN --mount=type=ssh \
mkdir -p -m 0600 ~/.ssh \
&& ssh-keyscan -H github.com >> ~/.ssh/known_hosts \
&& git clone git@github.com:[ユーザー名]/[プライベートリポジトリ名].git
1 行目に # syntax = docker/dockerfile:experimental
を指定し、RUN --mount=type=ssh
命令を用いて SSH 秘密鍵をマウントします。
事前にパスフレーズ入力するため、ssh-agent
を実行して SSH 秘密鍵を登録します。
$ ssh-add ~/.ssh/id_rsa
Enter passphrase for /Users/[ユーザー名]/.ssh/id_rsa: [パスフレーズを入力する]
Identity added: /Users/[ユーザー名]/.ssh/id_rsa (/Users/[ユーザー名]/.ssh/id_rsa)
以下のコマンドでビルドします。
$ docker build --ssh default -f Dockerfile.ssh -t sample-ssh .
--ssh
を有効にし、default
を渡すことで ssh-agent
に接続し解決してくれます。
さいごに
BuildKit
を用いて鍵ファイルや SSH 秘密鍵をマウントしてイメージに認証情報を残さずビルドできることが分かりました。
リリースサイクルを考えると Docker 19.03 が控えているので次のリリースが楽しみです。
参考
Build Enhancements for Docker | Docker Documentation
Docker v18.09 新機能 (イメージビルド&セキュリティ) – nttlabs – Medium
Docker セキュリティ: 今すぐ役に立つテクニックから,次世代技術まで - SlideShare
Access Private Repositories from Your Dockerfile Without Leaving Behind Your SSH Keys - vsupalov
Docker Engine 18.09 から使える Build-time secrets を試してみた - はったりエンジニアの備忘録
GitHub - moby/buildkit: concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit