DockerのBuildKitを使ってdocker build中で複数の秘密鍵を使用する

# はじめに
`Docker v18.09`以降ではBuildKit modeを使用することができます。
これは`export DOCKER_BUILDKIT=1`をホスト側で設定することで使用でき、docker bulidをセキュアに行う機能を提供しています。
本記事ではBuildKitが提供する機能の中でもssh接続に関するものについて簡単に紹介した上で、docker build中で複数の秘密鍵を利用する方法について説明します。

# BuildKitを使ったssh認証
例えば、docker build中でGithubのprivateリポジトリをcloneする場合、ホスト側からdocker側にSSH秘密鍵を渡す必要があります。
DockerfileにCOPY命令を記述して秘密鍵を渡すことでdocker側でもSSH接続できるようにはなりますが、この方法では後で鍵ファイルを削除しても削除する前のレイヤが残ってしまい安全ではありません。また、このように作成したdocker imageをパブリックなレジストリに公開してしまうと、第三者が秘密鍵にアクセスできる状態になってしまいます。
このあたりの挙動については[こちらの記事](https://qiita.com/takasp/items/56e1399a484ed5bfaade)が分かりやすかったので参考にされてください。

一方でBuildKitを使用した場合、SSH接続を伴うRUNコマンドのプロセスが走る間だけ、鍵の情報を読み取り専用でマウントされ、作成されたimageに秘密鍵の情報が残ることはありません。
BuiildKitを使用してssh認証情報を渡す方法は以下のようにとてもシンプルに行うことができます。

1. Dockerfile内でRUNする際に`--mount=type=ssh`を指定する
2. build時に`--ssh default`を指定する

```Dockerfile
FROM alpine
RUN --mount=type=ssh git clone git@github.com:private-repo.git
```

```terminal
DOCKER_BUILDKIT=1 docker build --ssh default -t test-image .
```

また、build時にdefaultを指定した場合は鍵情報として暗黙的に`$SSH_AUTH_SOCK`が使用されます。注意点としてdefaultを使用する際は事前にローカルPCでSSH agentに秘密鍵を追加しておく必要があります。

```terminal
eval $(ssh-agent)
ssh-add ~/.ssh/id_rsa
```

# 複数の秘密鍵を使用する
## 前提
複数の秘密鍵を使用する例としてdocker build中で複数のGithub privateリポジトリをcloneする場合を考えます。
前提として、この例ではdocker buildを行う環境として以下の2つが考えられるかと思います。

１つ目は自分のローカルのPCでbuildを行う場合, 2つ目はGithub ActionsやCloud BuildといったCI/CDでbuildを行う場合です。２つの違いとしては使用する認証情報にあります。

**【ローカルPCでのbuild】**自分のローカルPCでbuildを行う場合、`--ssh default`とすると自分のGithubアカウントに紐づく認証情報がbuild時に使用されます。
アカウントに紐づく認証情報の場合、権限のあるprivateリポジトリであれば全て参照できるので、1つの認証情報をbuild時に渡すだけで、複数privateリポジトリをcloneすることができます。　

```Dockerfile
FROM alpine
RUN --mount=type=ssh git clone git@github.com:private-repo-a.git && \
    git clone git@github.com:private-repo-b.git
```
```terminal
DOCKER_BUIDKIT=1 docker build --ssh default -t test .
```

**【CI/CDでのbuild】**CI/CDでbuildを行う場合はcloneしたいprivateリポジトリにdeploy keyとして公開鍵を設定し、CI/CD側にはそれぞれの秘密鍵を置くことになります。
この時、Githubリポジトリのdeploy keyには共通の公開鍵を設定することはできない仕様のため、それぞれのリポジトリごとに秘密鍵と公開鍵のペアが必要になります。
このため、BuildKitでdocker build時に使用する秘密鍵も複数必要になります。

## 解決法
BuildKitを使った`type=ssh`のマウントには[いくつかのオプション](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#:~:text=HOME/.aws/credentials-,RUN%20--mount%3Dtype%3Dssh,-This%20mount%20type)が提供されており、今回のケースではその中でも`id`オプションをRUNに設定して対応することができます。

`id`オプションをRUN時に設定すると、先ほど`--ssh default`とした部分で指定した秘密鍵を区別して使用することができます。

```Dockerfile
FROM alpine
RUN --mount=type=ssh,id=private-repo-a git clone git@github.com:private-repo-a.git
```
```terminal
DOCKER_BUIDKIT=1 docker build --ssh private-repo-a=~/.ssh/id_rsa_private_repo_a -t test .
```
複数の秘密鍵を使用する場合は`id`オプションを使うことで次のようにbuildを行うことができます。

```Dockerfile
FROM alpine
RUN --mount=type=ssh,id=private-repo-a git clone git@github.com:private-repo-a.git
RUN --mount=type=ssh,id=private-repo-b git clone git@github.com:private-repo-b.git
```
build時につける`--ssh`フラグを増やすことでdocker build中のRUNではそれぞれで使用したい認証情報を使うことができます。

```terminal
DOCKER_BUIDKIT=1 docker build --ssh private-repo-a=~/.ssh/id_rsa_private_repo_a --ssh private-repo-b=~/.ssh/id_rsa_private_repo_b -t test .
```

# まとめ
- BuildKitを使用するとdocker build時にセキュアに秘密鍵を使用することができる。
- BuildKitを使う場合、idを指定することで複数の秘密鍵を区別して使用することができる。

今回は`type=ssh`のマウント方法に着目しましたが、BuildKitには他にもいくつかのマウント方法が用意されています。詳しくは参考資料の方をご参照ください！

# 参考資料
- [BuildKit公式ドキュメント](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#run---mounttypessh)
- [BuildKitについて詳しく書いてある記事](https://medium.com/@tonistiigi/build-secrets-and-ssh-forwarding-in-docker-18-09-ae8161d066)
- [Docker の BuildKit を使ってセキュアなビルドを試す](https://qiita.com/takasp/items/56e1399a484ed5bfaade)