Dockerコンテナから外部サービスへ接続するとき、ホスト側ではClashが正常に動作しているのに、コンテナ内のapt、Docker Registry、Git、APIクライアントなどだけがタイムアウトすることがあります。原因は、コンテナのネットワーク名前空間とホストのルーティングが分離されているためです。ホストのブラウザをClashに接続しただけでは、コンテナの通信まで自動的にプロキシされません。

この記事では、Mihomoを中心に、Dockerホストとコンテナの通信を透過プロキシ化するための考え方を解説します。TUNモード、DockerのHTTPプロキシ、iptablesによるリダイレクト、DNSの扱いを比較し、実際に動作確認できるYAMLとコマンドを紹介します。特定の構成をそのままコピーするのではなく、ホストのOS、Dockerネットワーク、Clashの実行方式に合わせて段階的に切り分けることが重要です。

Dockerの透過プロキシ化で理解すべき基本

Dockerコンテナの通信経路は、一般的に「コンテナ内のアプリケーション → Dockerブリッジ → ホストのネットワークスタック → 外部ネットワーク」という順番になります。通常のClash設定では、アプリケーションがHTTP_PROXYHTTPS_PROXYを参照して初めてプロキシが利用されます。しかし、これらの環境変数に対応していないアプリケーションや、DNSリクエスト、Dockerデーモン自身の通信は別途設定が必要です。

透過プロキシは、アプリケーションにプロキシの存在を意識させず、ルーティング層でTCPまたはUDP通信をClashへ渡します。MihomoのTUNモードを使う場合は、仮想ネットワークインターフェースが作成され、OSのルーティングによって対象トラフィックがMihomoへ送られます。iptables方式では、Dockerブリッジから出る接続をREDIRECTまたはTPROXYでClashの受信ポートへ転送します。

  • HTTPプロキシ方式:設定が簡単で、アプリごとの動作確認に適しています。ただし、対応していない通信は対象外です。
  • TUN方式:TCP、UDP、DNSを一元的に処理しやすく、コンテナを含むホスト全体の透過プロキシ化に向いています。
  • iptables方式:Linuxで細かな制御が可能です。Dockerチェーンとの順番、除外、権限管理を正しく設計する必要があります。
  • hostネットワーク方式:コンテナがホストのネットワーク名前空間を共有します。検証は簡単ですが、ポート競合と隔離性の低下に注意してください。
重要:Dockerコンテナの全通信を転送すると、Clash自身、DNS、Docker Registry、監視エージェントまでプロキシ対象になる可能性があります。ループを防ぐため、Clashの実行ユーザー、ホストのLANセグメント、コンテナ用サブネット、プロキシサーバーのIPを明示的に除外してください。

Mihomo TUNモードを使う推奨構成

新しいLinux環境で最初に検討したいのはMihomoのTUNモードです。TUNはアプリケーションごとにプロキシ変数を設定する必要がなく、ドメインルール、IPルール、DNS処理をClash側に集約できます。Dockerコンテナだけでなく、ホスト上のsystemdサービスやCLIツールも同じルールで処理できる点が利点です。

Dockerコンテナ内でMihomoを実行する場合は、仮想インターフェースの作成とルーティング変更に必要な権限を付与します。ホスト側のMihomoを利用する場合は、コンテナのデフォルトゲートウェイからClashの受信ポートへ到達できるように設定します。まずはホスト上で動作させる構成のほうが、ネットワーク障害の原因を追いやすくなります。

Mihomo TUN設定例
mixed-port: 7890
allow-lan: true
bind-address: '*'

tun:
  enable: true
  stack: mixed
  device: Mihomo
  auto-route: true
  auto-detect-interface: true
  strict-route: true
  dns-hijack:
    - any:53
    - tcp://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://dns.google/dns-query
  fake-ip-filter:
    - '*.lan'
    - '*.local'
    - 'localhost.ptlogin2.qq.com'

strict-routeは、TUNが作成したルート以外へ意図せず通信が逃げることを抑制します。ただし、ホストのVPN、仮想マシン、Kubernetes、複数のNICと組み合わせると、起動後に管理用SSHまで経路変更される場合があります。リモートサーバーで設定を変更するときは、コンソールアクセスや既存SSHセッションを確保し、まずauto-route: falseで検証するのが安全です。

Docker Composeで動作を確認する手順

いきなり全コンテナを透過プロキシ化するのではなく、検証用コンテナを一つ起動し、DNS、TCP接続、HTTPS接続の順に確認します。LinuxホストのDockerブリッジからホストへ接続するため、Composeではhost.docker.internalを明示的に追加すると便利です。

  1. Mihomoをホスト上で起動し、mixed-portまたは専用HTTPポートがLANから到達可能であることを確認します。
  2. ホストのファイアウォールで、DockerブリッジのサブネットからClashポートへの接続を許可します。
  3. 検証用コンテナからホスト名解決、プロキシ経由のHTTPS、直接接続を個別に試します。
  4. Clashの接続ログまたはダッシュボードで、実際にどのルールとプロキシグループが選択されたかを確認します。
  5. 問題がなければ、対象サービスを一つずつ本番ネットワークへ移行します。
検証用 compose.yaml
services:
  netcheck:
    image: curlimages/curl:latest
    extra_hosts:
      - "host.docker.internal:host-gateway"
    environment:
      HTTP_PROXY: http://host.docker.internal:7890
      HTTPS_PROXY: http://host.docker.internal:7890
      NO_PROXY: localhost,127.0.0.1,.local
    command: ["sh", "-c", "sleep 3600"]

コンテナ内で次のコマンドを実行し、プロキシの効果を確認します。プロキシ環境変数を使う方法は透過方式ではありませんが、Clashポート、認証、外向き接続を検証する最初のテストとして非常に有効です。

コンテナ内の接続テスト
docker compose run --rm netcheck \
  curl -I --max-time 15 https://example.com

docker compose run --rm netcheck \
  curl -s https://ifconfig.me

docker exec -it <container-name> sh
cat /etc/resolv.conf
ip route
wget -S -O /dev/null https://registry-1.docker.io/v2/

HTTPレスポンスが返っても、期待した経路を通っているとは限りません。Clashの接続画面で宛先ドメインを確認し、外部IP表示サービスで出口IPを確認してください。また、Dockerイメージの取得はアプリケーションコンテナではなくDockerデーモンが実行するため、docker pullだけ失敗する場合は別の設定が必要です。

iptablesでDockerブリッジをリダイレクトする

TUNを使えない環境や、対象ポートを厳密に限定したい環境ではiptables方式を選択できます。TCPの透過転送だけならREDIRECTが比較的扱いやすく、元の宛先情報を維持したい高度な構成ではTPROXYとポリシールーティングを利用します。後者はMihomoの設定、カーネルモジュール、ファイアウォールの設計を同時に合わせる必要があります。

以下は概念を確認するための例です。実際のインターフェース名、Dockerサブネット、Clashのリダイレクトポートは環境に合わせて変更してください。iptablesのルールを誤ると、プロキシ自身の接続が再びリダイレクトされ、接続ループが発生します。

TCP REDIRECTの例
# 例: Docker bridge 172.17.0.0/16、Mihomo redirect-port 7892
sudo iptables -t nat -N MIHOMO_DOCKER
sudo iptables -t nat -A MIHOMO_DOCKER -d 127.0.0.0/8 -j RETURN
sudo iptables -t nat -A MIHOMO_DOCKER -d 10.0.0.0/8 -j RETURN
sudo iptables -t nat -A MIHOMO_DOCKER -d 172.16.0.0/12 -j RETURN
sudo iptables -t nat -A MIHOMO_DOCKER -d 192.168.0.0/16 -j RETURN
sudo iptables -t nat -A MIHOMO_DOCKER -p tcp -j REDIRECT --to-ports 7892
sudo iptables -t nat -A PREROUTING -s 172.17.0.0/16 -p tcp -j MIHOMO_DOCKER

この例ではプライベートアドレスを除外しています。LAN上のデータベース、Docker内部サービス、Kubernetes APIなどをプロキシへ送らないためです。必要に応じて、Docker DNS、メタデータサービス、ホストゲートウェイ、プロキシサーバーのIPも除外してください。iptables-persistentやnftablesを使う場合は、再起動後にもルールが同じ順序で復元されるか確認します。

REDIRECTとTPROXYの選び方:単純なTCP通信をClashへ送るだけならREDIRECTから始めると切り分けが容易です。UDP、元の宛先IPを必要とするアプリ、複雑な策略ルーティングまで扱う場合はTPROXYまたはTUNを検討してください。Docker環境では、まずTUNで動作確認し、要件がある部分だけiptablesへ分離すると保守しやすくなります。

DNS、ルーティング、Docker固有の障害を切り分ける

透過プロキシで最も多い障害は、プロキシそのものではなくDNSと経路の不一致です。コンテナが取得したドメインのIPへ直接接続している場合、Clashのドメインルールが適用されないことがあります。TUNではdns-hijackとfake-ipを組み合わせ、コンテナのDNSリクエストをClashへ集約します。iptables方式ではDNSのUDP・TCP 53番ポートが別途処理されるため、HTTPS DNSやコンテナ内の固定DNS設定も確認してください。

症状確認ポイント対処の方向
docker pullだけ失敗Dockerデーモンのproxy設定、RegistryへのDNS、証明書daemon.jsonまたはsystemd drop-inで別途設定
コンテナは名前解決できない/etc/resolv.conf、Docker DNS、ClashのlistenアドレスDNSポートの到達性とfake-ip設定を確認
接続が無限にタイムアウトiptablesのループ、Clash自身のUID、戻りルート除外ルールとログを確認し、一度TUNを停止
一部のLANサービスだけ失敗RFC1918宛先がプロキシへ送られていないかDIRECTルールとiptables除外を追加
HTTPSだけ証明書エラー時刻、CA証明書、TLS検査、プロキシのSNIコンテナの時刻同期とCAストアを確認

Dockerネットワークを追加した場合、サブネットが172.16.0.0/12に含まれるとは限りません。Composeのネットワーク設定で10.20.0.0/16などを指定しているなら、Clashのルールとiptablesの除外範囲にも反映してください。特に複数のComposeプロジェクトを運用するサーバーでは、ネットワークが増えるたびに手動ルールが古くなるため、nftablesのセットや管理スクリプトで一元化すると安全です。

ログを見るときは、次の順番で確認すると効率的です。まずコンテナのip routecat /etc/resolv.confで基礎的な経路を確認し、次にホストのtcpdumpでDockerブリッジからパケットが出ているかを確認します。その後、Clashの接続ログでリクエストの到着、ルールマッチ、プロキシ接続の三段階を追います。

Linuxでの診断コマンド
docker network inspect bridge
docker exec -it <container-name> ip route
docker exec -it <container-name> cat /etc/resolv.conf

sudo ss -lntup | grep -E '7890|7892|1053'
sudo tcpdump -ni docker0 port 53 or port 443
sudo iptables -t nat -vnL --line-numbers

本番運用での安全性とパフォーマンス

Clashのallow-lan: truebind-address: '*'は、Dockerブリッジから接続するために便利ですが、ホストの全インターフェースへプロキシポートを公開します。クラウドサーバーではセキュリティグループとホストファイアウォールで接続元を制限し、管理用APIのexternal-controller127.0.0.1へバインドしてください。APIをLANへ公開する場合も、強力なsecretを必ず設定します。

ルールは、最初にLANやDocker内部ドメインをDIRECTとして定義し、その後にプロキシ対象、最後にFINALを置く構成が分かりやすいです。DNSでは、社内ドメインやサービスディスカバリをfake-ipから除外し、外部ドメインはClashのDNSで解決します。不要なログを常時有効にすると高負荷環境でディスクとCPUを消費するため、障害調査後はログレベルを戻してください。

  • Dockerサブネット、ホストLAN、VPNインターフェースのアドレス範囲を台帳化する。
  • Clashの管理APIとプロキシポートを同じ公開範囲にしない。
  • プロキシサーバー、DNS、Docker Registryへの経路を個別に監視する。
  • コンテナ再作成後もDNS、ルート、環境変数が期待どおりになるか自動テストする。
  • iptablesやnftablesを変更する前に、復旧用のコンソールアクセスを確保する。

よくある質問

TUNとiptablesはどちらを選ぶべきですか?

新規構築なら、DNSとUDPもまとめて扱えるTUNが第一候補です。特定ポートだけを転送したい、既存のファイアウォール設計に組み込みたい、あるいはTUNを利用できない場合はiptablesを選びます。iptablesは自由度が高い反面、Dockerチェーン、戻りルート、除外対象を自分で管理しなければなりません。

コンテナの環境変数を設定したのにdocker pullが失敗します。

docker pullは通常、コンテナ内のシェルではなくホスト上のDockerデーモンが実行します。そのため、ComposeのHTTP_PROXYはDockerデーモンに影響しません。systemdの環境設定やDockerデーモン用のproxy設定を使い、設定後にデーモンを再起動してから再試行してください。

fake-ipで社内サービスやゲームが動かなくなりました。

実IPやUDPの直接通信を必要とするドメインをfake-ip-filterへ追加します。*.lan*.local、STUN関連ドメインなどを環境に合わせて除外し、必要なら該当ドメインをDIRECTルールへ配置します。除外を増やしすぎるとDNSリークや誤接続につながるため、ログを見ながら最小限にしてください。

透過プロキシのループはどう見つけますか?

Clashのプロセス自身が作る外向き接続をiptablesで再度リダイレクトしていないか確認します。ClashのUID、プロキシサーバーのIP、管理用ネットワークをRETURN対象にし、iptablesのパケットカウンターとClashログを同時に観察してください。切り分け中はiptablesを外してHTTPプロキシまたはTUNだけで接続し、段階的に機能を戻す方法が安全です。

Dockerの透過プロキシ化は、YAMLを数行追加するだけで完了する設定ではありません。ホストのルート、コンテナのDNS、Dockerデーモン、Clashのモードを分けて検証すれば、イメージ取得失敗や外部通信の遅延も原因を絞り込めます。まずはHTTPプロキシで接続性を確認し、次にMihomo TUN、必要な場合だけiptablesへ進む順番なら、複雑な本番環境でも安定して運用できます。

はじめる

Clash でトラフィックを完全制御

Windows・macOS・Linux・Android・iOS 対応。柔軟なルール、すぐに使えます。

無料ダウンロード セットアップガイドを見る →