docker build --build-arg http_proxy=... で認証プロキシを設定すると危険だよ、という話

# はじめに
`docker build --build-arg http_proxy=...` で認証プロキシを設定すると危険だよ、という情報があまり広まっていない気がしたので書いておく。

# なにが問題か？
[Dockerのドキュメント](https://docs.docker.com/engine/reference/builder/#arg)より引用です。

> 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.

そう、`--build-arg`で渡した環境変数はイメージには保存されていないから安全:smile:  と**思ってはいけない**。`docker history`で参照できるメタデータにはバッチリ渡した環境変数が**保存されている**のである。
例えば`docker build --build-arg http_proxy=http://foo:bar@proxy.example.org` のようにプロキシの認証情報を設定してビルドしていると、そのイメージをpullした環境で`docker history`するとプロキシ接続に利用したユーザID/パスワードが丸分かりになっちゃう、ということ。

この話、[Qiitaで docker build-arg proxy で検索](https://qiita.com/search?utf8=%E2%9C%93&sort=&q=docker+build-arg+proxy)すると引っかかる記事では、触れられているものがなさそうで不安になった。
また、Qiita以外の日本語情報をググって探してみると、teratailに [docker build で指定する http_proxy が履歴に残らないようにしたい](https://teratail.com/questions/66850) というズバリそのものの質問があった。ぱっと目についたのはこの1件くらいと、気にしている人はかなり少なそうな印象。

# で、どうすればいい？

いくつか対策はある。

## 対策1: Dockerをバージョンアップする

この問題、[本家のISSUE](https://github.com/moby/moby/issues/30721)でも扱われて実は新しめのDockerだと既に解決済みになっている。具体的には、[Exclude “default” build-args from image history](https://github.com/moby/moby/pull/31584)というプルリクエストにより、下記のデフォルトのbuild-argsはhistoryから除外されるようになった。

* `HTTP_PROXY`
* `http_proxy`
* `HTTPS_PROXY`
* `https_proxy`
* `FTP_PROXY`
* `ftp_proxy`
* `NO_PROXY`
* `no_proxy`

Dockerのリリースノートを見ると、

* Docker CEの場合は **[17.05.0-ce (2017-05-04)](https://docs.docker.com/release-notes/docker-ce/#17050-ce-2017-05-04)** 以上
* Docker EEの場合は **[17.06.1-ee (2017-08-16)](
https://docs.docker.com/enterprise/17.06/#17061-ee)** 以上

ならこの修正が入っており大丈夫そう。ただし、リリースノートの下記記載のとおり、Dockerfileの中に `ARG http_proxy` みたいに書いてしまうと駄目なので注意。

>The values of default build time arguments (e.g HTTP_PROXY) are no longer displayed in docker image history unless a corresponding ARG instruction is written in the Dockerfile. [#31584](https://github.com/moby/moby/pull/31584)

というわけで、新しいDockerに移行できるなら、これが一番簡単な対策方法。
(もちろんコンテナの再ビルドは必要。あと、過去のコンテナイメージはすでにpullされて広まっている可能性があるので、やってしまった方はパスワード変更をまず先にすべきでしょう)

## 対策:2 認証なしのプロキシを中継用に構築する
これは、[teratailの回答](https://teratail.com/questions/66850) で示されていた方法の1つだが、認証なしのプロキシをDockerと認証プロキシの間に配置し、そこから認証プロキシに接続するようにするという方式。これだと、`--build-arg`で渡すプロキシ情報には認証情報は入れなくて済む(プロキシを使っていることは分かってしまうが)。

自分は試していないが、[認証ありHTTPプロキシを認証なしでプロキシするDockerfile](https://qiita.com/megmogmog1965/items/5f38a3d1b281a3005383)でsquidを使って簡単にこのような中継プロキシを構築できるかも。

## 対策:3 透過プロキシを利用する

これも、[teratailの回答](https://teratail.com/questions/66850) で示されていた方法の1つ。Dockerホスト側で透過プロキシを構築して、透過的に認証プロキシを経由させるという方法。これだと、`--build-arg`でプロキシ情報は一切渡さなくて済む。Dockerのバージョンを上げれない場合はこの方法が個人的にはよいと思う。

なお、先日書いた [プロキシとの戦いに疲れたのでgoで透過プロキシを作ってみた](https://qiita.com/wadahiro/items/7fa852b7217940e6ef26) で紹介している透過プロキシを使うことで簡単に対応できる。

# まとめ

* Docker CE 17.05.0-ce未満を使っている場合、`docker build --build-arg http_proxy=...` で認証プロキシを設定するのは今すぐ止めよう (パスワード変更も忘れずに)。
* Docker CE 17.05.0-ce以上にバージョンアップできる場合はそれでOK。コンテナの再ビルドは忘れずに。
* バージョンアップできない場合は、透過プロキシ等を使って、少なくとも直接認証プロキシ情報を `--build-arg`には設定しないようにすること。


# おまけ

プロキシ関連で、[Docker CE 17.07.0-ce (2017-08-29)](https://docs.docker.com/release-notes/docker-ce/#17070-ce-2017-08-29) 以上だと、下記のリリースノート内容のとおり、`$HOME/.docker/config.json` でプロキシ情報を設定できるようになっているので、`--build-arg`で毎回渡す手間を省けるようになっている。

> Add support for proxy configuration in config.json [docker/cli#93](https://github.com/docker/cli/pull/93)

なお、この設定は`docker build`だけでなく、`docker run`時にも有効だそうで。設定方法は[ドキュメント](https://docs.docker.com/engine/userguide/networking/#configure-the-docker-client)に書かれているが、残念ながら[実装と違うというISSUE](https://github.com/docker/docker.github.io/issues/4686)が報告されており、正しくは以下のように設定する必要がある模様。

```
{
  "proxies":
  {
    "default":
    {
      "httpProxy": "http://127.0.0.1:3001",
      "noProxy": "*.test.example.com,.example2.com"
    }
  }
}
```