プロキシの内側から docker pull できない
Docker Hubから docker pull
しようとしたところ、以下のエラーでイメージのダウンロードが失敗したところから始まる。
Error response from daemon: Get https://registry-1.docker.io/v2/: remote error: tls: handshake failure
環境は社内プロキシの内側にあり、製品にはTLSの検閲を回避するオプションがある。
試しに検閲をOFFにすると pull
に成功するが、設定変更なしに実現できないか色々探っていた。
docker pull
したときのパケットをキャプチャし、 Wireshark で確認すると、以下の暗号スイートを Client Hello で送っていた。
Cipher Suites (8 suites) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a) Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014) Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
次に、このプロキシ製品でサポートされている暗号スイートを確認した。
openssl ciphers -v | awk '{print $1}' | xargs -n1 curl -ksv https://registry-1.docker.io/v2/ -x http://~<proxy>~/ --ciphers
AES256-SHA256 AES128-SHA SRP-RSA-AES-256-CBC-SHA SRP-AES-256-CBC-SHA RSA-PSK-AES256-CBC-SHA384 DHE-PSK-AES256-CBC-SHA384 RSA-PSK-AES256-CBC-SHA DHE-PSK-AES256-CBC-SHA AES256-SHA
うーん、対応してないですねぇ...
Docker ソースコードの確認
念のため Docker のソースコードも確認した。Docker 製品群のネットワーク接続は go-connections
というユーティリティにまとめられているらしいので、そちらのリポジトリから確認した。
GitHub - docker/go-connections: Utility package to work with network connections
Docker側が使っている go-connections
のバージョンが不明だが、2018年あたりのコミットの tlsconfig/config_client_ciphers.go
と tlsconfig/config.go
というファイルを確認すると、むちゃくちゃ絞られていた。
config_client_ciphers.go から抜粋 // Client TLS cipher suites (dropping CBC ciphers for client preferred suite set) var clientCipherSuites = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, }
config.go から抜粋 // Extra (server-side) accepted CBC cipher suites - will phase out in the future var acceptedCBCCiphers = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, }
後に気づいたのだが、 Issue にも普通に書かれていた。
多段プロキシ作戦
クライアントと社内プロキシの間にTLSプロキシを Squid で立てたら突破できるんじゃね?と考えたので試してみた。キャプチャを眺めたが、Squid はクライアントから送られた暗号スイートをそのまま使う動作をするようで、暗号スイートを変える(フォールバックというのか?)ようなことしてくれないので、この作戦は失敗に終わった。
Docker を古い暗号スイートに対応したい場合は、古いバージョンを利用するか、独自にビルドするしかなさそうだ。