まずDNSリークかどうかを切り分ける

DNSリークとは、ドメイン名の問い合わせが本来通るはずのClashまたはmihomoのDNS処理経路に入らず、OS、ルーター、ブラウザ内蔵のセキュアDNS、あるいは接続事業者のリゾルバーによって直接処理される状態を指します。Web通信がプロキシを経由していても、DNS問い合わせだけがローカルネットワークから送信されることがあります。その結果、外部の観測者に問い合わせ先を知られたり、誤った名前解決、DNS汚染、ルール判定の異常、同じサイトがアプリによって異なる挙動を示したりする可能性があります。

ローカルの通信事業者が提供するDNSサーバーが表示されたからといって、必ずしも設定が失敗しているとは限りません。まず期待する動作を明確にしましょう。設定のnameserverにローカルのパブリックDNSを指定しているなら、検出ページにその事業者が表示されるのは正常です。指定したDoH、DoT、またはリモートリゾルバーを使う想定なのに、ルーターのアドレス、回線事業者のリゾルバー、社内ネットワークのDNSが繰り返し表示される場合は、詳しく確認してください。

よくある症状

  • ブラウザでは正常にアクセスできるのに、端末のcurl、ソフトウェア更新ツール、ゲームプラットフォームから同じドメインに接続できない。
  • プロキシノードを切り替えても、DNS検出結果にローカル回線事業者のリゾルバーが表示され続ける。
  • ルールでドメインマッチングを使っているのに、ログにはIPアドレスしか表示されず、DOMAIN-SUFFIXルールが期待どおりに機能しない。
  • TUNを有効にすると一部サイトで誤った地域のコンテンツが表示され、TUNを無効にすると元に戻る。
  • 同じドメインをブラウザ、nslookup、Clashのログで確認すると、異なるアドレスが返される。
  • fake-ipを設定しているのに、システムの問い合わせが198.18.0.0/16の範囲ではなく、パブリックIPを直接返す。

まず基準状態を記録し、すぐに設定を変更しない

  1. 現在のクライアント、カーネルのバージョン、使用中の設定ファイル名を記録します。
  2. 動作モードがルール、グローバル、ダイレクトのどれかを確認し、TUNとシステムプロキシの有効・無効も記録します。
  3. ブラウザとシステムのDNSキャッシュを消去してから、テストを2回繰り返します。
  4. ブラウザ、コマンドライン、単独で動作するアプリをそれぞれテストし、1つのアプリの挙動をシステム全体の結論と混同しないようにします。
  5. ClashのログにDNSリクエスト、ルールマッチング、接続先が記録されているか確認します。

システムプロキシだけを有効にしても、OSの通常のDNS問い合わせが必ずしも横取りされるとは限りません。HTTPまたはSOCKSプロキシが処理するのはアプリの接続であり、システムのリゾルバーはネットワークアダプターに設定されたDNSサーバーへUDP 53のリクエストを送信することがあります。TUNモードとDNSハイジャックを組み合わせれば、より多くのアプリをカバーできますが、TUN、ルーティング、DNSの待受アドレス、ハイジャックルールが実際に有効であることが前提です。

ブラウザの検出ページとコマンドラインで相互に確認する

ブラウザの検出ページを正しく使う

一般的なDNSリークテストページを開いたら、まず標準テストを実行し、続けて拡張テストを行います。テスト前に他のプロキシ拡張機能を無効にし、ブラウザのウィンドウを1つだけ残して検出ページを再読み込みします。結果には通常、リゾルバーのIP、ネットワーク組織、地域が表示されます。地域がプロキシノードと一致するかだけでなく、設定したDoH、DoT、通常のDNSサービスと照合してください。

ブラウザが独自のセキュアDNSを使用している可能性があります。Chrome、Edge、FirefoxはいずれもOSのリゾルバーを迂回し、ブラウザに指定されたDoHサービスへ直接問い合わせられます。そのため、ブラウザの検出ページで分かるのはその時点のブラウザの経路だけであり、システム全体がClashに処理されていることを単独で証明するものではありません。

  • Chromium系ブラウザでは「設定」→「プライバシーとセキュリティ」→「セキュリティ」→「セキュアDNSを使用する」を確認します。
  • Firefoxでは「設定」→「プライバシーとセキュリティ」→「DNS over HTTPS」を確認します。
  • ClashのDNS処理をテストする場合は、ブラウザのセキュアDNSを一時的に無効にし、検証後に実際の構成に応じて再度有効にするか判断します。
  • ブラウザのDoHを有効にしたままにする場合は、独立した名前解決経路として扱い、Clashのdnsセクションの検証には使用しないでください。

ClashのDNS待受ポートへ直接問い合わせる

設定にlisten: 127.0.0.1:1053と記述している場合、digをインストールしたWindows、macOS、Linuxから、そのポートへ直接問い合わせできます。

dig @127.0.0.1 -p 1053 example.com A
dig @127.0.0.1 -p 1053 example.com AAAA

fake-ipモードでは、Aレコードに通常198.18.0.0/16内の予約アドレスが返されます。たとえば198.18.0.23です。この結果は対象サイトの実際のアドレスではなく、カーネルがドメインとの対応関係を作るための仮想アドレスです。そのアドレスへの接続が発生すると、カーネルは元のドメインを復元し、ルールに従ってプロキシ方針を選択します。

設定がenhanced-mode: redir-hostの場合、通常は実際のパブリックIPが返されます。この場合、198.18.x.xが表示されるかどうかだけでリークを判定せず、ログ、パケットキャプチャ、上流リゾルバーの結果を組み合わせて経路を確認してください。

システムが現在使用しているDNSを確認する

Windows PowerShellでは、各ネットワークアダプターのDNSアドレスを確認できます。

Get-DnsClientServerAddress |
  Where-Object {$_.ServerAddresses.Count -gt 0} |
  Format-Table InterfaceAlias, AddressFamily, ServerAddresses

結果が192.168.1.1のようなルーターのアドレスのままでも、システムプロキシだけを使用している場合はよくあります。TUNを有効にした後にループバックアドレスへ変更する必要があるかどうかは、クライアントの実装によって異なります。多くのmihomo GUIクライアントは、ネットワークアダプターのDNSを恒久的に変更するのではなく、DNSハイジャックでUDP 53を処理します。クライアントの挙動を理解しないまま、すべてのネットワークアダプターを127.0.0.1に手動変更しないでください。カーネル終了後、システム全体で名前解決できなくなる可能性があります。

Linuxでsystemd-resolvedを使用している場合は、次のコマンドを実行できます。

resolvectl status
resolvectl query example.com

macOSでは、システムリゾルバーの優先順位を確認できます。

scutil --dns
dscacheutil -q host -a name example.com

パケットキャプチャで53番ポートへの直接接続を確認する

検出結果がまだ明確でない場合は、テスト中にDNSトラフィックをキャプチャします。Linuxでは次のコマンドで従来型のUDPおよびTCP DNSを監視できます。

sudo tcpdump -ni any 'port 53'

Windowsでは標準搭載のpktmonを使用できます。ネットワーク解析ツールでdnsまたはudp.port == 53のフィルターを設定しても構いません。テスト問い合わせを実行した後、物理ネットワークアダプターからルーターや通信事業者のDNSへ直接パケットが送信されていれば、従来型DNSが想定した経路を迂回しています。DoHは443番ポート、DoTは853番ポートを使用するため、53番ポートだけのフィルターでは検出できません。対象IP、プロセス、Clashのログも合わせて判断してください。

fake-ip、redir-host、DNSハイジャックを理解する

fake-ipの処理フロー

  1. アプリがシステムにドメインの名前解決を要求します。
  2. 問い合わせがClashのDNSリスナーへ送られるか、TUNのDNSハイジャックルールによって捕捉されます。
  3. カーネルが198.18.0.0/16内の仮想アドレスを返し、その仮想アドレスとドメインの対応関係を保存します。
  4. アプリがその仮想アドレスへ接続し、TUNまたは透過プロキシ層が接続を捕捉します。
  5. カーネルが元のドメインを復元し、ドメインルールを適用したうえで、プロキシグループを通じて実際の接続を確立します。

この方式では、ローカルで先に名前解決することによるルール情報の欠落を減らし、ドメイン単位で振り分けやすくなります。ただし、上流DNSが不要になるわけではありません。カーネルはプロキシサーバーのドメイン、ダイレクト接続先、一部の除外ドメインなどを解決する必要があるため、default-nameservernameserverproxy-server-nameserverとルールの動作を正しく設定する必要があります。

redir-hostが適する範囲

redir-hostはアプリに実際のIPを返すため、互換性を把握しやすい一方、ドメインと接続の関連付けがスニッフィング、キャッシュ、マッピングに依存しやすくなります。多数のドメインルール、TUNによる処理、複雑な振り分けを使う場合、mihomoでは通常fake-ipが適しています。LAN機器の検出、プリンター、ゲームのログイン、企業内ネットワーク、実際のDNS結果に依存するアプリで問題が起きた場合は、fake-ip全体を無効にするのではなく、まず特定のドメインをfake-ip-filterに追加してください。

fake-ip-filterに入れるべきもの

フィルターリストのドメインは仮想アドレス処理をスキップします。実際に発生した障害を基準にし、範囲の広すぎるワイルドカードは避けてください。よくある出発点は次のとおりです。

fake-ip-filter:
  - "*.lan"
  - "*.local"
  - "localhost"
  - "time.*.com"
  - "time.*.gov"
  - "ntp.*.com"
  - "+.stun.*.*"
  - "+.stun.*.*.*"

*.localはmDNSやLANサービスの検出に使われます。NTPやSTUNなどのプロトコルも、特殊な名前解決や直接アドレスへの接続に依存することがあります。mihomoのバージョンやクライアント内蔵テンプレートによっては、既定のフィルター項目がすでに含まれています。サブスクリプション設定を統合する際は、最終的に有効な設定を先に確認し、カスタムリストでクライアント側の互換ルールを上書きしないようにしてください。

DNSハイジャックは悪意のある改ざんではない

TUN設定のdns-hijackは、指定した宛先へのDNSリクエストをカーネルの処理へリダイレクトする設定です。たとえばany:53は、任意のアドレスへ送られる53番ポートのリクエストを捕捉します。これは、アプリがシステムDNSを迂回して固定サーバーへ直接問い合わせる問題に対処するものです。ブラウザのDoHはHTTPSを使用し、宛先ポートは443なので、any:53では捕捉できません。

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53
    - tcp://any:53

カーネルのバージョンによって、フィールドの形式やプロトコル対応が異なる場合があります。変更前に、クライアントの「設定」→「カーネル」または「概要」で実際にmihomoが使用されているか確認し、設定検証機能で構文をチェックしてください。サブスクリプションの元ファイルだけを編集しても不十分です。更新時に変更が上書きされる可能性があるため、クライアントが提供するオーバーライド、マージ、Mixin機能を優先してください。

出発点として使えるmihomoのDNS設定

以下の設定は、各フィールドの関係を理解するための例です。待受ポートは1053にして、システムですでに使われている53番ポートとの直接衝突を避けています。実際の処理方法は、クライアントのTUNとDNSハイジャックの設定によって決まります。

dns:
  enable: true
  listen: 127.0.0.1:1053
  ipv6: false
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16

  default-nameserver:
    - 223.5.5.5
    - 119.29.29.29

  nameserver:
    - https://dns.alidns.com/dns-query
    - https://doh.pub/dns-query

  fallback:
    - tls://1.1.1.1:853
    - https://1.1.1.1/dns-query

  fallback-filter:
    geoip: true
    geoip-code: CN
    ipcidr:
      - 240.0.0.0/4

  fake-ip-filter:
    - "*.lan"
    - "*.local"
    - "localhost"
    - "time.*.com"
    - "time.*.gov"
    - "ntp.*.com"
    - "+.stun.*.*"
    - "+.stun.*.*.*"

default-nameserver:上流サーバー自身のドメインを解決

default-nameserverは、DoHやDoTの上流サーバー、その他の基礎接続に必要なドメインを解決するために使います。通常は直接アクセスできるIP形式のDNSを指定します。すべてのサービスドメインに対する唯一のリゾルバーではありません。DoHのアドレスをドメイン形式で記述したのに、起動時に使えるリゾルバーがないと循環依存が起きることがあります。DoHを使うにはまずDoHのドメインを解決する必要がありますが、その解決を担うサーバーが存在しないためです。

nameserver:主要な名前解決経路

nameserverは、通常のドメイン問い合わせに使う主要な上流サーバーです。例ではHTTPS DNSを使用し、デバイスとリゾルバー間の問い合わせ内容を暗号化します。2つの上流サーバーは可用性のための冗長構成であり、リストの順番どおりに必ず1つずつ問い合わせるという意味ではありません。実際の並列処理、キャッシュ、結果の選択はカーネルの実装とバージョンによって異なります。

通常のUDP DNSは223.5.5.5のようにも記述できますが、問い合わせ内容は従来型DNSとして送信されます。DoTはtls://1.1.1.1:853のように記述し、DoHは完全なHTTPSアドレスを指定します。設定の読み込みに成功しても、ネットワークが対象ポートへの接続を許可しているとは限りません。ログでタイムアウト、TLSハンドシェイクの失敗、証明書エラーがないかも確認してください。

fallbackとfallback-filter:結果に応じて予備の名前解決を選ぶ

fallbackは別のリゾルバー群を提供し、fallback-filterは予備の結果を採用する条件を決めます。例のgeoip: truegeoip-code: CNはIP地理データベースを使って結果を判定します。240.0.0.0/4などの特殊なアドレス範囲は、異常な結果を除外する条件として使えます。

この仕組みはGeoIPデータの正確性に依存します。CDNやAnycastのアドレス、新しく割り当てられたネットワーク範囲は誤分類される可能性があるため、地域判定を絶対的な基準にしないでください。あるサイトがfallbackを有効にしたときだけ異常になる場合は、まず2つのリゾルバーがそれぞれどのアドレスを返すか確認し、その後にドメイン方針やフィルター条件を調整します。

ipv6:ネットワーク環境に応じて明示的に有効化する

例ではipv6: falseを使用しています。ローカル環境に安定したIPv6がない、プロキシノードがIPv6に対応していない、または切り分け範囲を先に狭めたい場合に適しています。ローカルとプロキシ経路がIPv6を完全にサポートしているならtrueに変更し、TUNルート、プロキシの出口、AAAA問い合わせも確認してください。DNSでAAAAの応答を無効にするだけでは、すべてのIPv6迂回問題を解決できません。システムに既存のIPv6接続やアプリ内蔵の名前解決も個別に確認する必要があります。

DNSの迂回を順番に修正する

ステップ1:最終的に有効な設定を確認する

サブスクリプションの内容、クライアントのオーバーライド、実行時設定が組み合わさって最終設定になることがあります。クライアントの「設定」→「現在の設定」または「設定」→「実行中の設定を表示」を開き、dns.enableenhanced-mode、待受アドレス、TUN設定が実際に存在するか確認します。メニュー名はクライアントによって多少異なりますが、確認すべきなのはカーネルが実際に読み込んだ設定であり、サブスクリプションのダウンロード先にある元のYAMLではありません。

ステップ2:ポートが待ち受けているか確認する

Windowsでは次のコマンドを実行できます。

Get-NetTCPConnection -LocalPort 1053 -ErrorAction SilentlyContinue
Get-NetUDPEndpoint -LocalPort 1053 -ErrorAction SilentlyContinue

macOSまたはLinuxでは次のコマンドを実行できます。

lsof -nP -iTCP:1053 -iUDP:1053
ss -lntup | grep 1053

待受結果が表示されない場合は、まずカーネルのログを確認します。よくある原因は、YAMLのインデントエラー、ポートの使用中、編集後の設定がクライアントに読み込まれていないこと、セキュリティソフトによるポートへのバインド阻止です。別のポート、たとえば1054に変更する場合は、直接問い合わせるコマンドとクライアントの処理設定も同時に変更してください。

ステップ3:直接問い合わせとシステム問い合わせを分けて検証する

  1. dig @127.0.0.1 -p 1053を使い、ClashのDNS自体が応答できることを確認します。
  2. サーバーを指定しないdig example.comまたはnslookup example.comを使い、システムの既定経路を確認します。
  3. TUNを有効にした状態と無効にした状態でそれぞれテストし、返されたアドレスを記録します。
  4. ブラウザの検出ページを開き、ブラウザのセキュアDNSが結果に影響していないか確認します。
  5. カーネルのログにテスト対象のドメイン、最終的に適用されたルール、ポリシーが表示されるか確認します。

Clashへの直接問い合わせは正常なのに、システムの既定問い合わせがルーターを経由し続ける場合、問題はnameserverそのものではなく、処理の引き継ぎ部分にあります。TUNが実際に起動しているか、必要なシステム権限を取得しているか、DNSハイジャックが存在するか、現在のネットワークアダプターが除外されていないかを確認してください。直接問い合わせもタイムアウトする場合は、まずDNSリスナーまたは上流接続を修正します。

ステップ4:キャッシュを消去して再テストする

WindowsでシステムのDNSキャッシュを消去します。

ipconfig /flushdns

macOSでは次を実行できます。

sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

Linuxのキャッシュ機構はディストリビューションによって異なります。systemd-resolvedを使用している場合は、次を実行できます。

sudo resolvectl flush-caches

その後、ブラウザを完全に終了して再起動します。Webページを更新するだけでは、ブラウザ独自のホストキャッシュや既存の接続は消去されません。長時間接続を維持するチャット、ゲームプラットフォーム、同期ツールも、プロセスを終了してから再起動してください。

ステップ5:ブラウザやアプリ内蔵のDoHを処理する

コマンドラインとシステムの問い合わせがすでにClashへ入っているのに、ブラウザの検出結果だけ別のリゾルバーを表示する場合は、まずブラウザのセキュアDNSを確認します。一部のセキュリティソフト、企業向けクライアント、モバイルアプリにもDoHが内蔵されています。これらのHTTPSリクエストは通常のWebトラフィックとしてClashを通る場合もあれば、ルールによってダイレクト接続される場合もありますが、いずれにしてもClashのローカルDNSモジュールには入りません。

対処方法は2つあります。アプリ内蔵のDoHを無効にして、システムからClashへ一元的に処理させる方法と、アプリのDoHを残して明確なルーティングルールを設定する方法です。前者は一元管理とトラブルシューティングに向き、後者はアプリごとに独立した名前解決方針が必要な場合に適しています。把握していない上流サーバーを複数同時に管理すると、アプリやドメインによって検出結果が変わるため避けてください。

よくある誤設定と修正方法

nameserverだけ記述し、DNSを有効にしていない

dns:
  enable: false
  nameserver:
    - https://dns.alidns.com/dns-query

enable: falseの場合、後続の上流設定から想定したローカルDNSサービスは構築されません。trueに変更し、待受ポートと処理方法を確認してください。

待受アドレスをLANに公開する

listen: 0.0.0.0:1053はすべてのインターフェースで待ち受けます。LAN内の機器からこのマシンをDNSサーバーとして使わせる必要がある場合に限って検討し、ファイアウォールで送信元も制限してください。単独で使う場合は127.0.0.1:1053へのバインドを優先し、不要なLANからのアクセスを減らします。

fake-ipの返り値を汚染結果と誤認する

198.18.x.xはfake-ipが正常に使用する仮想アドレス範囲です。このアドレスが表示されたら、hostsへ書き込んだり、すぐにダイレクトルールへ追加したりせず、アプリの接続とルールマッチングを続けて確認してください。特定のアプリに互換性の問題がある場合は、該当ドメインを正確にfake-ip-filterへ追加します。

複数のプログラムで53番ポートを同時に使用する

ローカルの広告ブロッカー、仮想マシンソフト、コンテナサービス、他のプロキシツールが53番ポートを待ち受けている可能性があります。同じアドレスとプロトコルで2つのプログラムが同じポートを安定して使用することはできません。Clashを1053で待ち受けさせ、前段のDNSから転送する方法や、競合するサービスを停止してClashに一元管理させる方法があります。変更後はUDPとTCPの両方で待ち受け状態を確認してください。

システムプロキシをグローバルなDNS処理と誤解する

システムプロキシが主に影響するのは、HTTPプロキシ設定に対応したアプリです。DNS、ゲームの通信、コマンドラインツール、システムプロキシを参照しないプログラムは、引き続き直接接続する可能性があります。より広範囲を処理するにはTUNを使用し、自動ルート、ネットワークアダプターの検出、DNSハイジャック、システム権限がすべて有効であることを確認してください。TUNも単純な「より強力なプロキシ」スイッチではありません。ネットワークスタックの経路が変わるため、切り分けでは一度に1つの項目だけを変更します。

修正後の確認リスト

  • カーネルのログに、DNS上流のタイムアウト、待受失敗、設定フィールドのエラーがない。
  • dig @127.0.0.1 -p 1053が通常の遅延で結果を返す。
  • fake-ipモードで、テスト対象のドメインが198.18.0.0/16の範囲のアドレスを返す。
  • TUNを有効にすると、システムの既定問い合わせに対応するドメインがClashのログで確認できる。
  • 物理ネットワークアダプターのキャプチャに、想定外のUDPまたはTCP 53への直接問い合わせが表示されない。
  • ブラウザのセキュアDNSの状態が現在の構成と一致し、把握していない独立経路が存在しない。
  • DNS検出ページに表示される名前解決サービスが、nameserverfallbackの設計に一致する。
  • LAN、NTP、STUN、企業内ネットワークのドメインに互換性の問題がある場合、正確なfake-ip-filterルールで処理されている。
  • サブスクリプション更新後もオーバーライドが保持され、クライアントとデバイスを再起動しても結果が変わらない。

DNSリークの切り分けでは経路を分解することが重要です。まずClashのDNSが応答できるか確認し、次にOSの問い合わせがカーネルへ入っているかを確認し、最後にブラウザやアプリに独立したDoHがないか調べます。検出ページ、コマンドライン、ログ、パケットキャプチャの結果を相互に照合してください。nameserverを1つ変更するだけでは、通常、処理の引き継ぎ部分の問題は解決しません。待受、ハイジャック、上流サーバー、アプリの挙動がそろって初めて、名前解決経路は安定します。