Docker環境で動くClashを単なるHTTPプロキシとして使うだけなら、アプリケーションごとにHTTP_PROXYやHTTPS_PROXYを設定すれば十分です。しかし開発用サーバー、CIランナー、複数のコンテナで構成されたサービスでは、すべてのプロセスがプロキシ環境変数に対応しているとは限りません。パッケージマネージャー、Git、DNSクライアント、サブプロセスが個別に通信するため、設定漏れがあると一部のリクエストだけが直接接続されます。この記事では、Mihomoを中心にDocker上のClashを透過プロキシとして動作させるためのYAML設定を組み立て、コンテナ通信の捕捉、DNSリークの抑制、ルール不一致の確認、パッケージ取得タイムアウトの切り分けまで実践的に解説します。

Docker透過プロキシの基本設計

透過プロキシでは、アプリケーションがプロキシの存在を意識する必要がありません。コンテナが外部へTCP接続を開始すると、Dockerホストのiptablesまたはnftablesルールが宛先をClashの透過ポートへリダイレクトします。Clashは接続元と宛先を読み取り、通常のルールエンジンでプロキシ、直接接続、拒否のいずれかを選択します。

Dockerで最初に決めるべきなのは、Clashコンテナのネットワーク方式です。一般的には次の2方式があります。

  • hostネットワーク:Clashがホストのネットワーク名前空間を直接利用します。iptables連携が分かりやすく、LinuxサーバーやCI専用ホストで扱いやすい方式です。
  • bridgeネットワーク:Clashを独立したコンテナとして動かします。構成を分離できますが、ホストとコンテナ間の転送、ポート公開、戻りパケットの経路を追加で設計する必要があります。

透過プロキシのデバッグを優先するなら、最初はnetwork_mode: hostを推奨します。Docker DesktopのmacOSやWindowsではLinuxのホストネットワークと挙動が異なるため、以下の例は主にLinuxホストを対象にしています。特にCI環境では、実行ランナーの権限、Docker-in-Dockerの有無、ホスト側ファイアウォールの管理方法を事前に確認してください。

重要:iptablesのルールを無計画に追加すると、Clash自身の接続まで再びClashへリダイレクトされるループが発生します。Clashの実行ユーザーまたはUID、Dockerネットワーク、ローカル宛先を必ず除外してください。

Mihomo向けYAMLの中核設定

透過プロキシ用の設定では、通常のHTTP混合ポートに加えてredir-portまたはtproxy-portを有効にします。TCPだけを扱うならREDIRECT方式が比較的簡単です。UDPやDNSを含む幅広い通信を捕捉する場合は、Linuxカーネルとファイアウォールが対応していることを確認したうえでTProxyを選択します。

Docker向けMihomo基本設定
mixed-port: 7890
redir-port: 7892          # TCPのREDIRECT用
tproxy-port: 7893         # TCP/UDPのTProxy用
allow-lan: true
bind-address: '*'
mode: rule
log-level: info
ipv6: false

external-controller: '127.0.0.1:9090'
secret: 'change-this-secret'

profile:
  store-selected: true
  store-fake-ip: true

allow-lanとbind-addressは、別コンテナやLAN上のクライアントからClashへ接続させる場合に必要です。ただし、外部コントローラーまで0.0.0.0で公開する必要はありません。管理APIは127.0.0.1:9090に限定し、Dockerホストから操作する構成のほうが安全です。

DNSは透過プロキシの安定性を左右します。ドメインベースのルールを確実に適用したい場合は、MihomoのDNSリスナーをコンテナの外部から利用できるようにし、fake-ipを基本にします。Dockerの内蔵DNSである127.0.0.11を無条件に上流へ転送すると、名前解決だけがホスト側へ流れてDNSリークの原因になることがあります。

透過プロキシ向けDNS設定
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'
  fallback:
    - 'tls://1.1.1.1:853'
  fake-ip-filter:
    - '*.lan'
    - '*.local'
    - 'localhost.ptlogin2.qq.com'
    - '+.stun.*.*'

sniffer:
  enable: true
  override-destination: true
  sniff:
    HTTP:
      ports: [80, 8080]
    TLS:
      ports: [443, 8443]

fake-ip-filterには、ローカルサービスやSTUNのように実IPを必要とする通信だけを追加します。除外範囲を広げすぎると、ドメインベースのルーティングやDNSリーク対策が弱くなります。社内ドメインは+.corp.exampleのように明示的に登録し、検証後に対象を増やすのが安全です。

Dockerネットワークとファイアウォール連携

YAMLを用意しただけでは、コンテナの通信はClashへ届きません。Dockerホスト上で、対象の送信元ネットワークから出るTCP通信をClashの透過ポートへ転送します。まずはREDIRECT方式でHTTPとHTTPSを確認し、正常に動作してからTProxyやUDPへ拡張すると、原因を切り分けやすくなります。

docker-compose.ymlの例
services:
  clash:
    image: metacubex/mihomo:latest
    container_name: clash
    network_mode: host
    cap_add:
      - NET_ADMIN
      - NET_RAW
    volumes:
      - ./config:/root/.config/mihomo
    restart: unless-stopped

NET_ADMINはルーティングや透過処理に必要になる場合がありますが、コンテナへ過剰な権限を与えることにもつながります。実際に必要な権限はイメージと実行方式で異なるため、検証環境で確認し、本番では不要な権限を削減してください。CIでDockerソケットを共有している場合は、Clashの設定変更権限が他のジョブへ波及しないよう、専用ランナーを使うことをおすすめします。

iptablesを使う場合の概念的なルールは次のようになります。ここで172.18.0.0/16は例示的なDockerネットワークであり、実際の値はdocker network inspectで確認してください。

TCP REDIRECTの例
# 転送対象ネットワークの例
iptables -t nat -N CLASH
iptables -t nat -A CLASH -d 127.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -d 10.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -d 172.16.0.0/12 -j RETURN
iptables -t nat -A CLASH -d 192.168.0.0/16 -j RETURN
iptables -t nat -A CLASH -p tcp -j REDIRECT --to-ports 7892
iptables -t nat -A OUTPUT -p tcp -m owner --uid-owner 1000 -j RETURN
iptables -t nat -A PREROUTING -s 172.18.0.0/16 -p tcp -j CLASH

上の例ではプライベートアドレスを除外しています。これにより、Docker内部サービス、データベース、サービスディスカバリへの接続が意図せずプロキシへ送られることを防ぎます。一方、社内ネットワーク上の特定ドメインだけをプロキシしたい場合は、アドレス帯を一括除外するのではなく、ルールと経路を分けて設計してください。ホスト自身の通信を対象にする場合は、ClashプロセスのUID除外がないとリダイレクトループになりやすい点にも注意が必要です。

ルール設計とコンテナ除外

透過プロキシでは、アプリケーションが送信する接続先を正確に把握しづらいため、ルールの順序が重要です。ローカルネットワーク、Docker内部ネットワーク、プロキシ対象、その他の直接接続という順に整理すると、意図しない経路を減らせます。GEOIP,CN,DIRECTのような広いルールを上位に置くと、プロキシしたいCDNやAPIまで直接接続になる可能性があります。

コンテナ環境向けルール例
proxy-groups:
  - name: PROXY
    type: select
    proxies:
      - Auto
      - DIRECT
  - name: Auto
    type: url-test
    use:
      - nodes
    url: 'https://www.gstatic.com/generate_204'
    interval: 300

rules:
  - DOMAIN-SUFFIX,localhost,DIRECT
  - 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,proxy-sites,PROXY
  - MATCH,DIRECT

no-resolveは、IP-CIDRルールに到達した際に不要なDNS逆引きを避けるための指定です。ルールプロバイダーを利用する場合は、コンテナ内の保存先が書き込み可能であること、起動直後に外部URLへ到達できることを確認します。CIでは毎回空のボリュームから起動するとルール取得がビルド開始を遅らせるため、プロバイダーのキャッシュを永続化するか、ジョブ開始前に明示的な更新ステップを置くと安定します。

DNSリークとタイムアウトの診断

設定が正しいかは、Clashのログだけでなく、コンテナ内部、ホストの接続テーブル、ダッシュボードの接続一覧を組み合わせて確認します。まず対象コンテナで名前解決とHTTPS接続を個別にテストしてください。

段階的な診断コマンド
# コンテナのDNS設定を確認
docker exec app cat /etc/resolv.conf

# 名前解決と接続を分離して確認
docker exec app getent hosts example.com
docker exec app curl -v --connect-timeout 10 https://example.com/

# Clashコンテナのログを確認
docker logs -f clash

# ホストで透過ポートの待受を確認
ss -lntup | grep -E '7892|7893|1053'

# ルールカウンターとNAT経路を確認
iptables -t nat -vnL CLASH

DNSリークを調べるときは、/etc/resolv.confに記載されたサーバーだけで判断してはいけません。アプリケーションが独自のDoH、DoT、DNS over QUICを使う場合、53番ポートのリダイレクトだけでは捕捉できません。Clashの接続一覧でDNS関連の外向き接続を確認し、必要に応じて既知の外部DNS宛てUDP/TCP 53をClashへ転送します。DoHを使うアプリは、アプリ側で無効化するほうがルール管理しやすい場合もあります。

パッケージ取得がタイムアウトする場合は、次の順番で切り分けます。第一に、Clashの外部プロキシノード自体がテストURLへ接続できるかを確認します。第二に、パッケージリポジトリのドメインがルールプロバイダーに含まれているかを調べます。第三に、リポジトリがIPv6を返しているのにClash側でIPv6を無効化していないか、あるいは逆にIPv6経路だけが不完全ではないかを確認します。最後に、TLS検証エラー、MTU不足、企業ネットワークの証明書差し替えをログから確認します。

ルール不一致の見つけ方:ダッシュボードの接続詳細で、宛先ドメイン、マッチしたルール、使用されたプロキシグループを確認します。表示がIPアドレスだけなら、DNSがClashを通っていない、またはfake-ipの対応範囲外である可能性があります。まずDNS経路を直してからルールを修正すると、推測による設定変更を減らせます。

本番運用前の確認事項

開発環境で通信が通った後も、CIや共有サーバーでは可用性と安全性を確認する必要があります。Clashの管理APIをインターネットへ公開せず、設定ファイルにプロキシ認証情報を平文で保存する場合は、ファイル権限を限定してください。ノード設定を環境変数や秘密情報管理サービスから生成する方法も有効です。

  • DockerネットワークのCIDRと、除外対象のプライベートアドレスを記録する。
  • Clash自身、DNS、ルールプロバイダー更新の通信がリダイレクトループにならないことを確認する。
  • HTTP、HTTPS、DNS、Git、パッケージマネージャーを別々にテストする。
  • ノード障害時にfallbackまたはurl-testが期待どおり切り替わるか確認する。
  • コンテナ再起動後もiptablesルール、fake-ip状態、ルールプロバイダーが復元されるようにする。
  • ログを無期限に保存せず、CIの秘密情報やAuthorizationヘッダーが出力されない設定にする。

透過プロキシは、環境変数を配布するだけの方法よりも導入作業が増えます。しかし、プロキシ非対応のツールを含むビルドチェーン全体を一貫して制御でき、コンテナごとの設定漏れも減らせます。まずはREDIRECTとTCP通信で小さく検証し、DNS、UDP、TProxyを順番に追加するのが最も安定した進め方です。Docker上のClashを継続的に利用するなら、YAML、ファイアウォールルール、診断コマンドを同じリポジトリで管理し、環境ごとの差分を明示しておくと、障害対応とCIの再現性が大きく向上します。

はじめる

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

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

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