この記事で分かること
Docker コンテナから外向き通信を行うたびに、各アプリケーションへ個別のプロキシ設定を追加するのは、短期的には簡単でも運用が続くほど複雑になります。開発用コンテナ、CI ジョブ、自宅 NAS 上のサービス、定期バックアップ、イメージ取得処理などが増えると、環境変数の設定漏れやプロキシの切り替え忘れが起きやすくなります。本稿では、Clash と Docker ネットワークを組み合わせ、コンテナの外向き通信を一元的に制御する透過プロキシ構成を、Mihomo 系コアを利用するクライアントを前提に整理します。
中心となる考え方は、コンテナ内のアプリに HTTP_PROXY や HTTPS_PROXY を一つずつ設定するのではなく、Docker ホストまたは LAN 上の Clash に通信を集約し、ドメイン単位のルール分流で PROXY と DIRECT を決めることです。これにより、パッケージレジストリや GitHub などの開発系通信、国内サービスや社内ネットワーク、コンテナ間通信を用途ごとに分けられます。単に「すべてをプロキシへ送る」構成ではなく、DNS の扱い、ルールの優先順位、ログの確認まで含めて設計することが重要です。
なお、透過プロキシは利用環境のネットワーク管理権限を必要とします。会社や学校のネットワーク、共有サーバー、第三者の通信を管理する環境では、管理者の承認と利用規約を確認してください。ここでは個人の開発環境や自宅 NAS のように、自分で管理できる Docker ホストを想定します。
Docker と Clash の接続方式を選ぶ
最初に決めるべきなのは、Clash がどこで動作し、コンテナがどのアドレスへ接続するかです。デスクトップ上の Clash Verge Rev をそのままコンテナから参照する構成と、Docker ホスト上または LAN ルーター上の Mihomo に集約する構成では、安定性と管理範囲が異なります。特に macOS や Windows では、ホストの 127.0.0.1 はコンテナ自身の loopback ではないため、アドレスの取り違えに注意が必要です。
| 構成 | 向いている用途 | 注意点 |
|---|---|---|
| Docker ホスト上の Clash | 開発 PC、単一サーバー、検証環境 | コンテナからホストのプロキシポートへ到達できるようにする必要がある |
| LAN 上の Clash / Mihomo | NAS、複数の Docker ホスト、家庭内サービス | ファイアウォール、待受アドレス、LAN 内アクセス制御を確認する |
| ルーターや OpenClash | 端末ごとの設定を省き、ネットワーク全体を管理する場合 | DNS、IPv6、TPROXY、既存 VPN との競合を慎重に設計する |
| コンテナごとの環境変数 | 一部の CLI や特定サービスだけをプロキシ化する場合 | 透過プロキシではなく、アプリが環境変数を正しく読む必要がある |
実務では、まず Docker ホスト上の一つの検証コンテナだけを対象にし、通信ログと名前解決を確認してから NAS や複数サービスへ広げる方法が安全です。Clash の混合ポートを例として 7890、透過処理用のポートを 7892 としますが、実際のポート番号はクライアントの設定に合わせて変更してください。
Clash を Docker ホストで動かす場合は、コンテナから参照できるインターフェースで待ち受ける必要があります。127.0.0.1 のみで待ち受ける設定は、ホスト上のブラウザからは使えても、ブリッジネットワーク上のコンテナからは利用できません。一方、無条件に 0.0.0.0 で公開すると、LAN 外からプロキシポートを悪用される危険があります。ホストのファイアウォールで Docker ブリッジや LAN セグメントからのアクセスだけを許可する設計が望ましいです。
Clash プロファイルの基本設定
透過プロキシでは、Clash 側が受信した接続をどのルールへ送るかが結果を決めます。コンテナがアクセスする代表的な宛先をあらかじめ分類し、最後の MATCH を安易に PROXY にしないことがポイントです。社内 Git、プライベートレジストリ、Docker の内部ネットワークまで外部ノードへ送ると、認証失敗や意図しない情報流出につながる可能性があります。
次の例は考え方を示す最小限の YAML です。プロキシグループ名やルールプロバイダーの URL は、利用している購読設定に合わせて置き換えてください。
mixed-port: 7890
redir-port: 7892
allow-lan: true
bind-address: "*"
mode: rule
log-level: info
dns:
enable: true
enhanced-mode: fake-ip
nameserver:
- https://1.1.1.1/dns-query
- https://dns.google/dns-query
fallback:
- https://9.9.9.9/dns-query
rules:
- DOMAIN-SUFFIX,local,DIRECT
- IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
- IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
- IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
- RULE-SET,developer-sites,PROXY
- DOMAIN-SUFFIX,example.internal,DIRECT
- MATCH,DIRECT
プライベートレジストリや社内 API のドメインがある場合は、一般的なルールプロバイダーより上に明示的な DIRECT ルールを置きます。逆に GitHub、各種パッケージレジストリ、コンテナイメージの取得先など、確実にプロキシへ送る宛先は専用のルールプロバイダーへまとめると更新が容易です。ルールの順番は上から評価されるため、広い DOMAIN-SUFFIX を先に置いてしまうと、後ろに追加した例外へ到達しません。
ルールプロバイダーを利用する場合は、提供元のメンテナンス状況と内容を確認してください。大量のドメインを無条件に登録すると、国内 CDN や認証サービスまでプロキシへ送られ、速度低下やログイン障害の原因になります。最初は必要なドメインだけを小さなプロバイダーに分け、ログで実際の接続先を確認しながら追加する方が再現性の高い運用になります。
実際に Docker コンテナから接続する
ここでは、Docker ホスト上の Clash の混合ポートへコンテナの HTTP 通信を渡す手順を紹介します。これはアプリケーションがプロキシ環境変数を読む方式であり、完全な透過プロキシとは異なりますが、最初の検証として非常に有効です。通信が正しくプロキシを通ることを確認した後、プロセスが環境変数を無視する場合に TUN や REDIRECT を検討します。
ステップ 1:ホスト側の到達アドレスを確認する
Linux の Docker では、通常のブリッジネットワークからホストへ接続するために、専用のホスト名を登録できます。Compose なら host.docker.internal を利用できる環境が多いですが、Linux のバージョンや Docker の設定によって挙動が異なります。まずコンテナから名前解決と TCP 接続を別々に確認してください。ホストの LAN アドレスを使う場合は、Clash がそのアドレスで待ち受け、ファイアウォールがポートを許可している必要があります。
docker run --rm \
--add-host=host.docker.internal:host-gateway \
curlimages/curl:latest \
curl -I -x http://host.docker.internal:7890 https://example.com
この確認で接続できない場合、ノードやルールを変更する前に、待受アドレス、ポート、Docker からホストへの経路を確認します。Clash のログに接続記録が出ないなら、リクエストはまだ Clash へ到達していません。ログが出ているのに TLS 接続で失敗するなら、次にルールや上流ノードを調べます。
ステップ 2:Compose にプロキシ環境変数を渡す
開発用サービスであれば、Compose の共通設定にプロキシを定義できます。ただし、コンテナ間通信やデータベース接続までプロキシへ送らないよう、NO_PROXY を必ず用意します。サービス名、Docker の内部サブネット、ホスト名、社内ドメインを用途に応じて列挙してください。
services:
worker:
image: your-worker:latest
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
HTTP_PROXY: http://host.docker.internal:7890
HTTPS_PROXY: http://host.docker.internal:7890
ALL_PROXY: http://host.docker.internal:7890
NO_PROXY: localhost,127.0.0.1,worker,db,redis,.internal,10.0.0.0/8
networks:
- appnet
db:
image: postgres:16
networks:
- appnet
networks:
appnet:
すべてのソフトウェアが同じ変数名を読むとは限りません。大文字と小文字の両方を確認する実装もあれば、ALL_PROXY を SOCKS 用として扱う実装もあります。HTTP プロキシへ接続する場合は、アプリケーションの仕様に合わせて不要な変数を削除してください。また、認証情報をプロキシ URL に直接書くと Compose ファイルやプロセス一覧へ残ることがあるため、秘密情報として管理し、ログへ出力しないようにします。
ステップ 3:コンテナ内から直接検証する
HTTP ヘッダーだけを見る curl -I は、DNS、TCP、TLS、HTTP のどの層まで進んだかを切り分ける入口になります。プロキシなしとプロキシありを比較し、Clash の接続ログに同じ宛先が現れるか確認してください。プロキシありだけ失敗する場合は、プロキシポートの種類が合っていない、または HTTP と SOCKS の指定を取り違えている可能性があります。
イメージ取得そのものは Docker デーモンが実行するため、コンテナ内の環境変数だけでは docker pull に反映されません。Docker デーモンの systemd 設定や Docker Desktop のプロキシ設定が別途必要です。アプリの外向き通信とイメージ取得の通信を同じものだと考えず、前者は Compose、後者はデーモン側というように設定場所を分けて管理してください。
本格的な透過プロキシと DNS の扱い
アプリケーションがプロキシ環境変数を無視する、バイナリが独自の名前解決を行う、複数のサービスをまとめて管理したい、といった場合は、Mihomo の TUN や Linux の REDIRECT / TPROXY が候補になります。TUN は仮想インターフェースへパケットを取り込みやすく、アプリごとの設定を減らせる一方、コンテナのルーティング、権限、カーネル機能、IPv6 の扱いが複雑になります。
Docker ホスト上で TUN を使う場合、Clash コンテナやホストプロセスに必要なデバイス権限を与え、ルートテーブルを壊さないように検証します。特権コンテナを常時運用するのは攻撃面を広げるため、可能なら専用ホスト、最小権限、管理ポートの LAN 内限定、設定ファイルの読み取り専用化を組み合わせてください。NAS の管理画面やバックアップ通信まで誤ってプロキシへ流すと、アクセス速度だけでなく管理可用性にも影響します。
DNS リークも見落とされやすい問題です。コンテナがホストや ISP の DNS を直接利用し、名前解決だけが別経路へ出ると、通信本体を PROXY にしても問い合わせ先やドメイン情報が外へ漏れる可能性があります。Fake-IP、Redir-Host、TUN の DNS モードは環境によって相性が異なるため、Docker 内の固定 IP、サービスディスカバリー、社内 DNS と衝突しない方式を選んでください。内部ドメインは専用の nameserver へ送り、外部ドメインだけ Clash の DNS へ渡す設計が扱いやすいです。
db、redis、host.docker.internal などの内部名をプロキシ用 DNS へ問い合わせる構成は避け、内部ネットワークと外部名前解決を分離してください。
IPv6 を有効にしたまま IPv4 だけを想定していると、アプリが AAAA レコードを選んで Clash のルールを経由しないことがあります。問題の再現時には一時的に IPv6 を無効化して挙動を比較し、最終的には IPv6 も TUN やルーター側で一貫して制御するか、不要なら明確に無効化します。
ルール、ログ、障害の切り分け
透過プロキシの調整では、最初から設定を大きく変更するより、通信を一つずつ観察する方が早く解決できます。Clash のログで確認したいのは、接続元 IP、宛先ホスト、使用されたルール、選択されたプロキシグループ、接続結果です。コンテナからの接続がログに存在しない場合は、Docker の経路やファイアウォールが問題であり、ルールの書き換えでは解決しません。
| 症状 | 考えられる原因 | 確認方法 |
|---|---|---|
| ログに何も出ない | プロキシポートへ到達していない、環境変数が読まれていない | コンテナからホストのポートへ TCP 接続し、プロセスの環境変数を確認する |
| 内部 API まで PROXY になる | NO_PROXY の不足、またはルールの例外が後ろにある |
サービス名と CIDR を追加し、ルールの上から順番を見直す |
| 名前解決だけ失敗する | DNS モードの衝突、内部 DNS の未登録、IPv6 の経路差 | コンテナ内で getent hosts や nslookup を比較する |
| 接続が途中で切れる | ノードの混雑、長時間接続のタイムアウト、TUN と既存 VPN の競合 | Clash の接続時間、再接続回数、上流ノードをログで比較する |
ルールのテストでは、最初に DOMAIN-SUFFIX で対象を限定し、動作が確認できてからルールプロバイダーへ移します。GEOSITE や大規模なカテゴリールールは便利ですが、どのドメインが含まれているか分からないまま採用すると、想定外の通信まで同じポリシーへ入ります。エラー時刻とコンテナ名を記録し、Clash のログ、アプリのログ、Docker のイベントを同じ時系列で見れば、断続的な障害でも原因を追いやすくなります。
運用を安定させるには、設定変更前に現在の YAML と Compose ファイルを保存し、変更は一項目ずつ行います。プロファイルを更新した後は、設定構文の検証、Clash の再読み込み、代表的な外部 URL と内部サービスの確認を順番に実施します。ログレベルを常時 debug にすると機密性のある URL や大量の接続情報が残るため、通常は info とし、必要な時間だけ詳細ログへ切り替えるのが安全です。
アプリごとに環境変数を埋め込む方式は、対応ソフトが明確で導入も早い反面、設定漏れやサブプロセスへの継承差が起こりやすく、別の HTTP クライアントへ移行した際に再調整が必要です。一方、単純な全体 VPN は導入が容易でも、社内 Git や LAN 内 API まで遠回りになり、除外ルールの説明が難しくなることがあります。Clash は Docker の外向き通信をまとめながら、ルールプロバイダー、DNS 制御、接続ログ、DIRECT と PROXY の明示的な分割を同じプロファイルで管理できるため、開発サーバーや自宅 NAS の段階的な運用に向いています。個別設定の増加や全体 VPN の除外管理に悩んでいるなら、まず検証用コンテナからこの構成を試すと、環境に合う分流を確認しながら導入できます。