Docker内の開発ツールやAIサービスから外部へ接続するとき、コンテナごとにHTTP_PROXYやHTTPS_PROXYを設定する方法は手軽です。しかし、対応していないプログラム、名前解決を独自に行うバイナリ、UDPを使うアプリケーションでは、環境変数だけでは通信を捕捉できません。そこで有効なのが、ホスト上でMihomo(Clash系コア)を動かし、Dockerネットワークの通信をTUNとiptablesで透過的にプロキシへ流す構成です。
この方式では、コンテナ側のアプリケーションに個別のプロキシ設定を追加せず、宛先やルールに応じてClashがプロキシ接続または直接接続を選択できます。この記事では、Linuxホスト上のDockerを前提に、推奨構成、DNSの注意点、Composeへの組み込み、iptablesの確認方法、障害発生時の切り分けまでを実践的に解説します。なお、TUNとiptablesの具体的なオプションは、使用するMihomoのバージョンやホストOSによって差があるため、導入前に実行ファイルのドキュメントも確認してください。
Docker透過プロキシの基本構成
最初に理解しておきたいのは、Clashをコンテナの中に置くか、Dockerホスト上に置くかという違いです。単一ホスト上の複数コンテナをまとめて制御するなら、通常はホスト側でMihomoを実行するほうが管理しやすくなります。MihomoがホストのTUNインターフェースを作成し、iptablesのREDIRECTまたはTPROXYルールでDockerブリッジから出ていく通信をClashのポートへ転送します。
- アプリケーション層:コンテナ内のcurl、Python、Node.js、Git、AI SDKなど。プロキシ環境変数を設定していなくても対象にできます。
- Dockerネットワーク層:通常は
docker0またはComposeが作成するブリッジネットワークを経由します。ここがiptablesの主な対象です。 - Clash層:TUNでTCP・UDPを捕捉し、DNSとルールを適用して、指定したプロキシグループへ転送します。
- アップストリーム層:選択したノードへ接続します。プロキシ対象のDNSをどこで解決するかも、この層の安定性に影響します。
実運用では、ホスト自身の通信とDockerコンテナの通信を同じルールで処理する必要はありません。まずコンテナの通信だけを対象にして動作を確認し、問題がなければ必要に応じてホスト全体へ範囲を広げるほうが安全です。特にDocker内部の名前解決、ホストサービスへのアクセス、管理用ポートは、プロキシ対象から明示的に除外する設計が重要です。
CAP_NET_ADMINが必要です。iptablesを広範囲に変更すると、SSH接続やDockerのDNSまで停止することがあります。リモートサーバーで作業する場合は、復旧用の別セッションとロールバック手順を用意してください。MihomoのTUNとDNSを設定する
透過プロキシの中心はTUNモードです。Mihomoは仮想ネットワークインターフェースから流入したパケットを読み取り、宛先ドメインやIPアドレスに応じてルーティングします。Docker環境では、アプリケーションが接続先のIPだけを受け取ると、ドメインルールを適用できない場合があります。そのため、dns.enhanced-modeをfake-ipにする構成がよく使われます。
tun:
enable: true
stack: system
auto-route: true
auto-detect-interface: true
dns-hijack:
- any:53
dns:
enable: true
listen: 0.0.0.0:1053
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
nameserver:
- https://1.1.1.1/dns-query
- https://8.8.8.8/dns-query
fake-ip-filter:
- '*.lan'
- '*.local'
- 'localhost'
- '+.docker.internal'
- 'time.*.com'
stack: systemはホストのネットワークスタックを利用する設定です。環境によってはgvisorやmixedのほうが互換性の高い場合もありますが、まずはシンプルな構成で接続を確認してください。auto-routeはTUN用のルートを自動作成し、auto-detect-interfaceは実際の外部インターフェースを判定します。複数のNICやVPNを併用しているサーバーでは、自動判定が意図と異なることがあるため、インターフェース名を明示する検討も必要です。
DNSでは、Dockerの組み込みリゾルバーである127.0.0.11が特に重要です。コンテナ内のサービス名、たとえばdatabaseやredisはDocker内部DNSで解決されます。これらを無理に外部DNSへ送ると、Compose内の名前解決が壊れます。一方、外部ドメインの解決はMihomoへ渡したいので、内部名と外部名を分けて扱います。fake-ip-filterに内部ドメインを追加し、Docker内部の通信は実IPで解決させるとトラブルを減らせます。
iptablesでDocker通信を転送する実践手順
ここでは、まずMihomoの混合ポートを7890、Dockerブリッジのサブネットを172.17.0.0/16とする例を示します。実際の環境では、docker network inspect bridgeでサブネットを確認し、使用中のポートと一致させてください。REDIRECT方式はTCPの導入が比較的簡単ですが、UDPや元の宛先情報の扱いではTPROXYのほうが適する場合があります。
- DockerネットワークとMihomoの待受ポートを確認します。Mihomoが
127.0.0.1だけで待ち受けている場合、iptablesで転送しても到達できないため、必要に応じて待受アドレスを調整します。 - Dockerブリッジから出るTCP通信を専用チェーンへ送り、Mihomoの透過プロキシポートへREDIRECTします。
- ローカルネットワーク、Docker内部ネットワーク、Clash自身の接続は除外します。除外しないとループやサービス名解決の失敗が発生します。
- curl、Git、パッケージマネージャーなど複数のアプリケーションで通信を確認し、Clashの接続画面でルールとプロキシグループを確認します。
sudo iptables -t nat -N CLASH_DOCKER sudo iptables -t nat -A CLASH_DOCKER -d 127.0.0.0/8 -j RETURN sudo iptables -t nat -A CLASH_DOCKER -d 172.16.0.0/12 -j RETURN sudo iptables -t nat -A CLASH_DOCKER -d 192.168.0.0/16 -j RETURN sudo iptables -t nat -A CLASH_DOCKER -p tcp -j REDIRECT --to-ports 7890 sudo iptables -t nat -A PREROUTING -i docker0 -p tcp -j CLASH_DOCKER
上記は検証を目的とした最小例です。既存のチェーン名と重複させないこと、再実行時に同じルールを何度も追加しないことが重要です。Docker Composeが独自ブリッジを作る場合、入力インターフェースはdocker0ではなく、たとえばbr-xxxxxxxxxxxxになります。すべてのComposeネットワークを対象にするなら、ネットワークごとのインターフェースを確認してルールを追加してください。
UDP通信、QUIC、DNSの完全な透過処理まで必要な場合は、MihomoのTPROXYモードとポリシールーティングを検討します。ただし、TPROXYはmangleテーブル、fwmark、ip rule、専用ルートの設計が必要で、REDIRECTよりも複雑です。最初からすべてをTPROXY化するのではなく、TCPの動作を確認してからUDP要件を追加するのが現実的です。
Composeへの組み込みと運用設計
ClashをDockerコンテナとして動かす場合は、ネットワーク名前空間と権限の設計が重要です。TUNデバイスを使う構成では、コンテナに/dev/net/tunを渡し、通常はNET_ADMINなどの権限を追加します。ただし、Clashコンテナ自身がホストのiptablesを変更するのか、ホスト側のiptablesを管理するのかを混在させないでください。責任範囲を決めないと、再起動時にルールが消えたり、古いチェーンが残ったりします。
services:
mihomo:
image: metacubex/mihomo:latest
container_name: mihomo
restart: unless-stopped
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ./mihomo:/root/.config/mihomo
ports:
- "7890:7890"
- "1053:1053/tcp"
- "1053:1053/udp"
app:
image: your-app:latest
depends_on:
- mihomo
environment:
# Optional fallback for applications that support proxy variables
HTTP_PROXY: http://mihomo:7890
HTTPS_PROXY: http://mihomo:7890
NO_PROXY: localhost,127.0.0.1,.local,database,redis
ここで示した環境変数は透過プロキシの代替ではなく、対応アプリケーションに対する補助設定です。NO_PROXYにはComposeサービス名、ホスト名、内部ドメインを含めます。外部APIだけをプロキシしたい場合は、すべての通信を強制的に転送するより、アプリの環境変数とClashのルールを組み合わせたほうが予測しやすいこともあります。
運用時は、設定ファイルをイメージへ直接書き込まず、ホスト側のディレクトリをマウントしてバージョン管理してください。Clashの設定を変更したら、YAMLのインデントを確認し、ダッシュボードまたはログで設定の読み込み結果を確認します。ノード更新、Rule Provider更新、Dockerイメージ更新を同じタイミングで実施すると原因追跡が難しくなるため、変更は一つずつ行うのが安全です。
疎通確認と障害時の切り分け
透過プロキシが動かないときは、「アプリ」「DNS」「iptables」「Clashノード」のどこで止まっているかを分離します。まずコンテナ内からgetent hosts example.comを実行し、外部ドメインとCompose内部サービスの両方が解決できるか確認します。次にcurl -v https://example.comでTCP接続を試し、Clashの接続一覧に該当する接続が表示されるかを見ます。
docker network inspect bridge docker exec -it app getent hosts example.com docker exec -it app curl -v https://example.com sudo iptables -t nat -L CLASH_DOCKER -n -v sudo iptables -t nat -L PREROUTING -n -v ip addr show ip route docker logs mihomo --tail=100
- iptablesのパケットカウンターが増えない場合は、対象インターフェース、テーブル、プロトコル指定を確認します。
- カウンターは増えるがClashに接続が表示されない場合は、REDIRECT先のポートと待受アドレスを確認します。
- Clashには接続が表示されるが失敗する場合は、ノード、SNI、DNS、ルールの順に確認します。
- 外部サイトは開くが、Dockerサービス名だけ失敗する場合は、
127.0.0.11やfake-ip-filterの扱いを見直します。 - SSHやDocker全体が不通になった場合は、追加したチェーンを削除し、既存のDockerチェーンを手動で置き換えないようにします。
Dockerの透過プロキシは、単に一つの設定を貼り付ければ完成する機能ではありません。TUNがパケットを受け取り、iptablesが正しい通信だけを転送し、DNSがアプリケーションの期待する名前解決を維持し、Clashのルールが目的地に合った経路を選ぶことで初めて安定します。まずはTCPと一つのComposeネットワークから始め、ログとパケットカウンターを確認しながら対象範囲を広げてください。適切に分離して設計すれば、開発コンテナやAI関連ツールを、個別設定の負担を抑えながら安定したプロキシ環境で運用できます。
Clash でトラフィックを完全制御
Windows・macOS・Linux・Android・iOS 対応。柔軟なルール、すぐに使えます。