GeekFactory

int128.hatenablog.com

プロキシの内側でDockerを使う

プロキシの内側でDockerを使おうとすると以下の問題に直面します。

  1. docker pullでプロキシ経由でイメージを取得させる必要がある。
  2. docker runでアプリ実行時にプロキシ設定を与える必要がある。
  3. docker buildでビルド実行時にプロキシ設定を与える必要がある。

1はデーモンの起動時に環境変数http_proxyを与えることで解決できます。また、2はdocker runの引数に-e http_proxy=...を与えることで解決できます。3はDockerfileに env http_proxy ... を追記することで解決できますが、Dockerfileやイメージのポータビリティが低下します。ポータビリティが必要ない場合はこれが現実的な解決法になると思います。

本稿では、一風変わった解決法として、Dockerホストに透過型プロキシを設置する方法を説明します。この方法では2と3の問題を解決できます。

前提

  • プロキシサーバは x.x.x.x:9090 とします。
  • docker0ネットワークは 172.17.0.0/16 で、ホスト側のIPアドレスは 172.17.42.1 とします。(デフォルト設定)

設計

コンテナから外部にアクセスする際、DNATを適用してホストのSquidを経由するようにします。例として、コンテナ内のcurlGoogleにアクセスする場合を以下に示します。

curl http://www.google.com/
|
| DNAT rule:
| Rewrites destination of the packet to 172.17.42.1:9090
|
172.17.42.1:9090
|
| Squid:
| Proxies the request to x.x.x.x:9090
|
x.x.x.x:9090
|
|
www.google.com

構築

ホストのiptablesを設定します。

iptables -t nat -A PREROUTING -s 172.17.0.0/16 ! -d 172.17.0.0/16 -p tcp --dport 80 -j DNAT --to 172.17.42.1:9090

docker0ネットワークがないとエラーになるので、起動スクリプトで設定する方がよいと思います。systemdの例(/etc/systemd/system/docker-proxy-dnat.service)を以下に示します。

[Unit]
Description=Apply DNAT rule for transparent proxy
After=docker.service
 
[Service]
Type=oneshot
ExecStart=/usr/sbin/iptables -t nat -A PREROUTING -s 172.17.0.0/16 ! -d 172.17.0.0/16 -p tcp --dport 80 -j DNAT --to 172.17.42.1:9090
ExecStop=/usr/sbin/iptables -t nat -D PREROUTING -s 172.17.0.0/16 ! -d 172.17.0.0/16 -p tcp --dport 80 -j DNAT --to 172.17.42.1:9090
RemainAfterExit=yes
 
[Install]
WantedBy=multi-user.target

Squidをインストールして、/etc/squid/squid.confを設定します。

acl docker src 172.17.0.0/16
http_access allow docker
http_access allow localhost
 
http_port 172.17.42.1:9090 intercept
cache_peer x.x.x.x parent 9090 0
never_direct allow all
 
visible_hostname linux
forwarded_for off
request_header_access X-FORWARDED-FOR deny all
request_header_access Via deny all
request_header_access Cache-Control deny all

試験

コンテナ内でcurlコマンドを実行してみます。

docker run --rm centos:latest curl www.google.com

HTMLが表示されればOKです。

制約

HTTPSは透過的にプロキシできないため、従来通りhttps_proxyを設定する必要があります。MITMすることでプロキシできなくもないですが、証明書の問題があるので難しいです。

上記のSquid設定は最小限のものなので、セキュリティ設計に応じて改良が必要です。また、Squidプロセスの監視も必要です。


Dockerプロキシ問題を解決する方法の一つとしてお役に立てば幸いです。 なお、本稿の内容は以下のGistにも書いています。

Transparent proxy for Docker containers