DNS漏れとリゾルバーの場所の違いを見分ける

Clashでプロキシを有効にしているのに、DNS検出サイトに契約中のインターネットサービスプロバイダーが表示される場合、DNSの問い合わせが端末のネットワークから直接送信されている可能性があります。ただし、複数の国やリゾルバーが表示されても、必ずしもDNS漏れとは限りません。まず、ローカルで名前解決するドメインと、プロキシ経由で解決するドメインを決め、設定を一つずつ確認しましょう。

この記事の設定例はClash Meta(mihomo)カーネルを対象としています。旧版Clash、一部のモバイル向けクライアント、ほかのプロキシツールでは対応する設定項目が異なります。変更前に、クライアントのカーネル情報または実行ログでカーネル名を確認し、現在の設定をバックアップしてください。クライアント名だけでrespect-rulesや以下のTUN設定に対応していると判断しないでください。

確認結果考えられる状態次に行うこと
ページの出口IPがプロキシノードのものWeb通信はプロキシ経由だが、DNSの経路は未確認DNSの拡張テストを続ける
リゾルバーに契約中のプロバイダーが表示されるOS、ブラウザー、カーネルの上流DNSが直接接続している可能性ログとポート53のパケットキャプチャを確認する
リゾルバーにパブリックDNSサービスが表示される問い合わせがパブリック再帰リゾルバーに到達しているそのリゾルバーへの通信経路を確認する
想定どおり国内外のリゾルバーが表示されるドメインごとの名前解決を意図的に分けている可能性表示数ではなく、設定したポリシーに沿っているかを確認する

基準を作る:ノード、ブラウザー、検出条件を固定する

3つの条件で結果を記録する

  1. プロキシをオフ:グローバルIP、プロバイダー名、DNS検出結果を記録し、ローカルネットワークの基準にします。
  2. システムプロキシだけをオン:同じノードを使い、TUNをオフにして、通常の検出と拡張検出を再実行します。システムプロキシを使うのは、プロキシ設定に従うアプリが中心です。OS全体のDNSが自動的にプロキシ経由になるわけではありません。
  3. TUNをオン:ノード、ルール、ブラウザーはそのままにし、DNSハイジャックを有効にして再検出します。リゾルバーのアドレス、時刻、プロバイダーが表示されたかを記録してください。

DNS leak testやBrowserLeaksのDNS検出ページを利用できます。毎回テストを新たに実行し、前回表示された結果を新しい結果と取り違えないようにしてください。2回続けて検査し、異常が1回だけなら、まずブラウザーのキャッシュ、ノードの切り替え、設定の再読み込みを確認します。

ブラウザー独自のDNSを一時的に無効にする

デスクトップ版Chromeでは通常、「設定」→「プライバシーとセキュリティ」→「セキュリティ」→「セキュア DNS を使用する」から設定できます。Firefoxでは通常、「設定」→「プライバシーとセキュリティ」→「DNS over HTTPS」にあります。テスト中はブラウザー独自のDoHをオフにしてください。項目名や場所はバージョンによって異なる場合があります。修正後に再び有効にする場合は、DoH接続の経路を別途確認してください。

シークレットウィンドウを開いても、セキュアDNSが自動的に無効になるわけではありません。ブラウザーがHTTPSで独自のリゾルバーに接続する場合、通常はポート443を使用します。TUNがこの接続を取り込んでいても、dns-hijackのポート53向けルールでカーネルのDNS問い合わせに置き換わるわけではありません。

  • WindowsでOSのDNSキャッシュを消去するには、ipconfig /flushdnsを実行します。
  • systemd-resolvedを使うLinuxでは、resolvectl flush-cachesを実行できます。ほかのディストリビューションでは、実際に使用している名前解決サービスに応じて操作してください。
  • クライアントのDNSキャッシュも消去するか、クライアントを再起動してから、検出ページを閉じて開き直します。OSのキャッシュを消すだけでは、すべてのアプリのキャッシュは消去されません。

fake-ipの役割:ドメインとの対応を保つ機能であり、問い合わせを自動で隠すものではない

enhanced-mode: fake-ipを設定すると、対象ドメインに合成アドレスが返されます。よく使われるアドレス範囲は198.18.0.1/16です。アプリがこのアドレスに接続すると、mihomoが対応するドメインを復元して、ルールに基づく処理と出力先の選択を行います。そのため、端末上の名前解決結果が198.18で始まるのは通常の動作であり、接続先サーバーが実際にそのアドレス範囲にあるという意味ではありません。

fake-ipが変えるのは、クライアントからアプリに返す応答です。その後、実際の名前解決が必要か、どのリゾルバーを使うか、DNSの問い合わせが直接接続とプロキシ接続のどちらを通るかは、接続方法、出力プロトコル、DNS設定によって異なります。fake-ipを有効にするだけで、上流DNSへの問い合わせがすべてプロキシ経由になるわけではありません。

フィルターリストは互換性のための例外に限る

LAN内のデバイス検出、プリンター、実IPアドレスが必要な一部のアプリでは、fake-ip-filterへの追加が必要になる場合があります。たとえば+.lanや+.localは、LAN内のドメインを調べる際の候補になります。ただし、これらの名前を解決できるローカルDNSサービスが別途必要です。フィルターに追加しても、mDNSが自動で有効になったり、ルーターが自動で見つかったりするわけではありません。

一つのアプリの問題を解決するために、すべてのドメインをフィルターリストに追加しないでください。まず問題が起きているドメインを特定し、必要最小限の例外を追加して、そのドメインの名前解決経路を再確認します。実IPアドレスが返ることとDNS漏れは別の問題です。

設定例:ドメイン別の名前解決と上流への接続

以下はmihomoのdnsとtunの設定例です。サブスクリプション全体の設定ではありません。元の設定にあるノード、プロキシグループ、ルールは残してください。同じ名前の項目がある場合は統合し、YAMLのトップレベルにdnsを重複して追加しないでください。サブスクリプションの更新で変更が上書きされないよう、クライアントのローカルオーバーライドまたは設定のマージ機能を使って保存することをおすすめします。

dns:
  enable: true
  listen: 127.0.0.1:1053
  ipv6: false
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  fake-ip-filter:
    - '+.lan'
    - '+.local'
  respect-rules: true
  default-nameserver:
    - 223.5.5.5
    - 119.29.29.29
  proxy-server-nameserver:
    - https://dns.alidns.com/dns-query
  nameserver:
    - https://1.1.1.1/dns-query
  nameserver-policy:
    '+.cn':
      - https://dns.alidns.com/dns-query

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53
    - tcp://any:53
項目設定例での役割
default-nameserverDNSサーバーのドメイン解決に使う。すべての通信先ドメインに対するデフォルトの上流ではない
proxy-server-nameserverプロキシノードのドメインを個別に解決し、プロキシ接続前の名前解決が循環するのを防ぐ
nameserver名前解決ポリシーに一致しない通常のドメイン問い合わせを処理する
nameserver-policy問い合わせるドメインに応じてリゾルバーを選ぶ。Web接続に使うプロキシノードは指定しない
respect-rulesDNS上流への接続にルーティングルールを適用する。実際に選ばれた出力先の確認も必要

この設定例で確認が必要な点

  • 上流への接続:通常のドメインをプロキシ経由で名前解決する場合、1.1.1.1:443への接続がDIRECTではなく、想定したプロキシグループに振り分けられていることを確認してください。直接接続になっている場合は、既存ルールの順序と振り分け先を調整します。respect-rules: trueを設定するだけでは不十分です。
  • 中国国内向けの名前解決:+.cnが対象にするのは、該当するドメインだけです。「中国国内のWebサイトすべて」を表すわけではありません。中国国内のサービスでも.comを使う場合があり、中国国外のサービスでも.cnを使うことがあります。必要に応じて、実際のドメインに合わせた詳細なルールを追加してください。
  • 初期問い合わせ:この例では、ローカルから到達できる初期問い合わせ用リゾルバーと、ノードのドメインを解決するリゾルバーを残しています。これらの問い合わせは直接送信される可能性があります。ノードのドメインを含むすべてのDNS問い合わせをローカルネットワーク経由にしない場合は、到達可能な初期問い合わせ経路を別途設計してください。この設定例をそのまま使うことはできません。
  • DoHへの接続:HTTPSは通信内容を暗号化しますが、接続がプロキシ経由になるとは限りません。現在のネットワークから該当リゾルバーへ直接接続できない場合は、先にルーティングを見直すか、到達可能な上流DNSに変更してください。

TUNでDNSをハイジャックする:ポート53が対象。すべての名前解決方式を処理するわけではない

この例ではUDPとTCPの両方について、ポート53のハイジャックを設定しています。対象のパケットが実際にTUNへ入ることが前提です。別のVPN、静的ルート、アプリにバインドされたインターフェースなどがTUNを迂回する場合、このルールは適用されません。auto-routeはルートの自動設定に、auto-detect-interfaceは出力インターフェースの検出に使います。どちらもOSの権限と実際のルーティングテーブルを確認してください。

待ち受けポートとハイジャック先を混同しない

listen: 127.0.0.1:1053は、カーネルがローカルDNSの待ち受け先を用意する設定です。dns-hijackは、TUNに入った指定のDNS問い合わせをカーネルに渡します。TUNのDNSハイジャックを有効にしても、システムDNSを「127.0.0.1:1053」に設定する必要はありません。多くのOSでは、システムDNSの設定画面にポート番号を指定できません。

ループバックアドレスだけで待ち受ければ、外部デバイスからのアクセスを制限できます。LAN内のデバイス向けにDNSを提供する場合は、待ち受けアドレス、ファイアウォール、ゲートウェイを別途設計してください。端末のDNS漏れを直すためだけに、すべてのインターフェースへ公開しないでください。

  • DNS over TLS:一般的にはTCPポート853を使うため、上記のポート53向けルールには一致しません。
  • DNS over HTTPS:通常はHTTPSのポート443を使います。通常のネットワーク接続と同様に、出力先を確認してください。
  • AndroidのプライベートDNS:切り分けの際は、OSの「プライベートDNS」設定でカスタムプロバイダーをいったんオフにし、クライアントのDNSをテストしてください。
  • IPv6:dns.ipv6: falseは、カーネルがAAAA問い合わせを処理するかどうかを設定します。OSのIPv6を無効にしたり、既存のIPv6接続を自動で取り込んだりする設定ではありません。

再テストと切り分け:ローカルの入口から上流への接続まで追う

まず設定が実際に反映されているか確認する

  1. オーバーライドを保存して設定を再読み込みし、ログに未知の項目、YAMLのインデントミス、ポートのバインドエラーがないか確認します。
  2. TUNアダプターと関連サービスが起動しているか確認します。Windowsでは通常、クライアントのサービスや適切な権限が必要です。画面上のスイッチをオンにしただけで、ルートが設定されたとは限りません。
  3. 一時的にログの詳細度を上げ、テスト中にドメイン、ルール、出力先の記録を確認します。切り分けが終わったら、普段使うログレベルに戻してください。
  4. キャッシュを消去し、同じノードで検出を再実行して、最初の基準結果と比較します。

digをインストールした端末からカーネルの待ち受け先へ直接問い合わせると、DNSモジュールが応答するか確認できます。

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

fake-ipモードでは、Aレコードの問い合わせに合成アドレスが返ることがあります。設定例のようにDNSのIPv6を無効にすると、AAAA問い合わせでアドレスが返らない場合があります。これらのコマンドはカーネルへ直接問い合わせるため、待ち受け先の動作だけを確認できます。OSからの問い合わせがTUNに取り込まれるかは確認できません。通常のアプリから接続し、ルーティングを観察してください。

パケットキャプチャで残っている迂回経路を確認する

TUNの仮想ネットワークアダプターだけでなく、実際の物理ネットワークアダプターでもパケットをキャプチャします。Wiresharkでは以下の表示フィルターを使えます。853番ポートは一般的な暗号化DNS接続を見つける補助になりますが、すべての暗号化名前解決方式を検出できるわけではありません。

udp.port == 53 || tcp.port == 53 || tcp.port == 853

端末からプロバイダーのDNSへ通常のドメイン問い合わせが送信されている場合は、OSの名前解決サービス、ブラウザー、ほかのVPN、TUNの除外ルートを確認してください。初期問い合わせ用リゾルバーやノードのドメインへの問い合わせだけが見える場合は、前述の設定範囲に照らして想定どおりか判断します。DoHはポート443を使うため、このフィルターで何も表示されないだけで、すべて修正済みと判断することはできません。

よくある質問と完了の判断基準

fake-ipを有効にしても検出サイトにプロバイダーが表示される場合は?

まずテストの問い合わせがカーネルに届いているか確認します。届いている場合は、上流DNS接続の出力先と、既存のfallbackを確認してください。届いていない場合は、ブラウザー独自のDNS、OSのプライベートDNS、TUNのルートを優先して確認します。fake-ipのアドレス範囲を何度も変更しても、このような経路の問題は通常解決しません。

nameserver-policyを設定すると、対象サイトが自動で直接接続されますか?

いいえ。nameserver-policyが決めるのは、どのリゾルバーにドメインを問い合わせるかです。Webサイトへの接続先は、rules、モード、プロキシグループの設定によって決まります。「DNSサーバーへの接続の出力先」と「Webサイトへの接続の出力先」を個別に確認し、同じ接続として扱わないでください。

ネットワークを切り替えると、なぜ問題が再発しますか?

Wi-Fiと有線接続を切り替えると、ゲートウェイ、インターフェース、OSのDNSが変わります。スリープから復帰した後に古いルートが残ることもあります。TUNサービス、デフォルトインターフェース、ローカルDNSの待ち受け先をもう一度確認し、キャッシュを消去して再テストしてください。クライアントを終了するとOSの名前解決ができなくなる場合は、まずOSのDNS設定とルートを戻し、通常のネットワーク接続ができることを確認してから設定を読み込んでください。

修正完了と判断できる状態は?

  • プロキシ経由で解決するテスト対象ドメインについて、上流への接続が想定したプロキシ出力先に振り分けられている。
  • 通常のアプリによるポート53の問い合わせがカーネルに入り、物理ネットワークアダプター上にポリシーに反する平文のDNS問い合わせが現れない。
  • ブラウザーのDoH、OSのプライベートDNS、IPv6の経路をそれぞれ確認している。いずれか一つの入口を無効にしただけでは不十分です。
  • サブスクリプションの更新、クライアントの再起動、ネットワークの切り替え後にも再検出し、オーバーライドとルーティングが有効なままである。

DNSを保護するには、名前解決の経路を把握でき、再テストで確認できる状態にすることが重要です。fake-ipはドメインとの対応を保ち、nameserver-policyはリゾルバーを選び、TUNのDNSハイジャックは問い合わせの入口をそろえます。最後に、ルーティングルールで上流への接続先を決めます。この4つが整って初めて、検出結果を正しく判断できます。