Dockerfileでプライベートリポジトリからレポジトリをclone

Dockerfileでプライベートリポジトリからレポジトリをclone。 わかります??

以下の記事を参考にしました。 vsupalov.com

通常このように書けばいいと思われがち。
というか自分はこれでいいと思っていた。

ARG SSH_PRIVATE_KEY
RUN mkdir /root/.ssh/
RUN echo "${SSH_PRIVATE_KEY}" > /root/.ssh/id_rsa
# [...]
RUN rm /root/.ssh/id_rsa

やっていることはいたってシンプル。
SSH_PRIVATE_KEYとしてコンテナ作成時に秘密鍵を渡すだけ。

甘い。甘すぎるよkimoton。

何がまずいの??

試しに上記のやばいDockerfileを使用して、以下のようにイメージを作成してみる。
SSH_PRIVATE_KEYdocker buildコマンドの実行時に--build-arg引数として渡すか、docker-compose.ymlのbuildセクションに記述することでコンテナに渡すことができます。

$ docker build --build-arg SSH_PRIVATE_KEY=testtesttest -t kimoton/yabai .

Sending build context to Docker daemon  2.048kB
Step 1/6 : FROM centos
 ---> 1e1148e4cc2c
Step 2/6 : ARG SSH_PRIVATE_KEY
 ---> Running in fa59663a1496
Removing intermediate container fa59663a1496
 ---> 1bb440bceb04
Step 3/6 : RUN mkdir /root/.ssh/
 ---> Running in eb0f4f35b42e
Removing intermediate container eb0f4f35b42e
 ---> b1dc93116422
Step 4/6 : RUN echo "${SSH_PRIVATE_KEY}" > /root/.ssh/id_rsa
 ---> Running in 7370ef573596
Removing intermediate container 7370ef573596
 ---> f12c7d9e29a9
Step 5/6 : RUN rm /root/.ssh/id_rsa
 ---> Running in ba2e6a4797e8
Removing intermediate container ba2e6a4797e8
 ---> df0c2ddc75c9
Step 6/6 : CMD tail -f /dev/null
 ---> Running in 4a34c83ba114
Removing intermediate container 4a34c83ba114
 ---> 9d62a050705f
Successfully built 9d62a050705f
Successfully tagged kimoton/yabai:latest

ここでdocker historyを打ってみると、、

$ docker history kimoton/yabai
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
9d62a050705f        3 minutes ago       /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "tail…   0B
df0c2ddc75c9        3 minutes ago       |1 SSH_PRIVATE_KEY=testtesttest /bin/sh -c r…   0B
f12c7d9e29a9        3 minutes ago       |1 SSH_PRIVATE_KEY=testtesttest /bin/sh -c e…   13B
b1dc93116422        3 minutes ago       |1 SSH_PRIVATE_KEY=testtesttest /bin/sh -c m…   0B
1bb440bceb04        3 minutes ago       /bin/sh -c #(nop)  ARG SSH_PRIVATE_KEY          0B
1e1148e4cc2c        5 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>           5 weeks ago         /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B
<missing>           5 weeks ago         /bin/sh -c #(nop) ADD file:6f877549795f4798a…   202MB```

SSH_PRIVATE_KEY=testtesttestが丸見え。

さらに言えば、実体まで残ってます。

$ docker run -it f12c7d9e29a9 cat /root/.ssh/id_rsa
testtesttest

この鍵ファイルを含むイメージを、不特定多数のユーザが参照可能なレジストリにpushした日にはもう大惨事です。
GitやS3などのアクセス権限を奪取される恐れがあります。絶対にやめましょう。

要するに単にRUN rmしても無駄なんですね。
dockerイメージはマルチレイヤで構成されている & デフォルトで過去のレイヤは保持されるため、セキュリティー的にまずいものは意図的に消さねばなりません。

じゃあどうするか。

第1の選択肢。押しつぶす。

※これは微妙な解です。微妙な解を知りたくない人は次の項目まで飛ばしてください。

squashって押しつぶすって意味なんですね。

Docker 1.13から、--squashという引数が実験的に使用できるようになりました。
このオプションを使用するには、Experimentalモードにしなければなりません。

Docker for WindowsをDockerデーモンに使用している場合、のSettings > DeamonからExperimental featuresにチェックを入れてください。 f:id:kimoppy126:20190111090648p:plain

通常のDockerデーモンを使用している場合、以下を参考にしてください。 qiita.com

docker versionコマンドを実行した際にDocker Engineが
Experimental: trueとなっていればOKです。

$ docker version
Client:
 Version:           18.09.0
 API version:       1.39
 Go version:        go1.10.4
 Git commit:        4d60db4
 Built:             Wed Nov  7 00:49:01 2018
 OS/Arch:           linux/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:     true

この--squashオプションは、複数レイヤからなるイメージを、オリジナルと最新のステージのレイヤのみを残すことで、イメージのサイズを小さくするのに使われます。
参考:docker buildのsquashオプションを試す - Qiita

これを使用してDockerイメージを作ると、、

docker history kimoton/squash
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
707a78e1ce12        About an hour ago                                                   0B                  merge sha256:d94c9209e30829d49f6e18c84330a24b99ef43593d91b72358e74df48d6d62d9 to sha256:1e1148e4cc2c148c6890a18e3b2d2dde41a6745ceb4e5fe94a923d811bf82ddb
<missing>           About an hour ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "tail…   0B
<missing>           About an hour ago   |1 SSH_PRIVATE_KEY=testtesttest /bin/sh -c r…   0B
<missing>           About an hour ago   |1 SSH_PRIVATE_KEY=testtesttest /bin/sh -c e…   0B
<missing>           About an hour ago   |1 SSH_PRIVATE_KEY=testtesttest /bin/sh -c m…   0B
<missing>           About an hour ago   /bin/sh -c #(nop)  ARG SSH_PRIVATE_KEY          0B
<missing>           5 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>           5 weeks ago         /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B
<missing>           5 weeks ago         /bin/sh -c #(nop) ADD file:6f877549795f4798a…   202MB

見事にcredentialな情報が入っている不要な中間レイヤが消えました! これで中間レイヤに入ってid_rsaを見に行くことはできなくなります。

しかし実行時に渡しているSSH_PRIVATE_KEY=testtesttestは相変わらず見えてしまっています。。 さらに、この方法は毎回忘れずにこの引数を使う必要があるという何とも微妙な解決策となっています。

マルチステージビルド

Docker17.05以上ではマルチステージビルドという機能が使えます。
この機能を使うと、複数のイメージを連鎖的に使用し、あるイメージの生成物を別のイメージへと引き継ぐことができます。 なお、最後に作成したイメージ以外は記録が残らず、ほかのイメージはそのイメージを作成する手助けをするために使用されます。
すなわち、中間イメージの1つにSSH鍵を渡しそのイメージの中で依存ライブラリや必要なデータのダウンロードを行いコンパイルを行ってしまった上で、最終的に使用するイメージに必要なディレクトリだけを渡せば、イメージサイズを節約しながら、安全にcredentialなデータを扱うことができます。

例えば以下のようなDockerfileを使用します。

このDockerfileを使用して作成したDockerイメージを見てみると、、
当たり前ですが、最終的に作成したイメージ内の~/.ssh/以下にはファイルが存在しません。

$ docker run -it kimoton/multistage ls ~/.ssh/
ls: cannot access /home/kimoton/.ssh/: No such file or directory

また、このイメージではcredentialな情報を示した引数は使用されていないため、docker historyコマンドで参照されてしまう恐れもありません。 実際にdocker historyコマンドを実行した結果は以下のようになります。

$ docker history kimoton/multistage
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
032b29cab9f5        33 seconds ago                                                      18kB                merge sha256:49dabbbee1dd809ce19e20971c2968c56cf0dd24cf3ebf3f5d81c1e112d11997 to sha256:1e1148e4cc2c148c6890a18e3b2d2dde41a6745ceb4e5fe94a923d811bf82ddb
<missing>           33 seconds ago      /bin/sh -c #(nop) COPY dir:429b124a8f606daaa…   0B
<missing>           5 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>           5 weeks ago         /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B
<missing>           5 weeks ago         /bin/sh -c #(nop) ADD file:6f877549795f4798a…   202MB

イメージサイズを大幅に節約できる上にcredentialなデータを安全にできるという何ともかっちょいいビルドが行えるんですね。

Secure Mount

Docker 18.09から、Secure Mountという機能が加わったみたいです。
今はこちらを使う方が良いのかもしれません。

medium.com

今度試してみます。。