DNSリークの見分け方:名前解決と接続を分けて考える

Webサイトをプロキシ経由で開けても、ドメインの名前解決までプロキシ経由とは限りません。アプリが先にルーターやインターネットサービスプロバイダー(ISP)のDNSへ問い合わせてから、HTTPS接続をClashに渡す場合があります。一方、ドメイン名をそのままプロキシに渡し、接続先で名前解決する場合もあります。Webサイト上では同じように見えても、DNS情報が外部に伝わる範囲は異なります。原因を調べる際は、アプリの名前解決方法、通信の捕捉状況、上流DNSへの通信経路をそれぞれ確認してください。

この記事では、本来プロキシまたは指定した暗号化DNSリゾルバーを使うはずの問い合わせが、意図せずローカルネットワークなど想定外のリゾルバーへ送信されることを「DNSリーク」とします。中国本土向けのサイトを設定どおり直接接続し、ローカルDNSで名前解決するのはルーティング方針によるものです。検出ページにローカルのリゾルバーが表示されたというだけで、障害とは判断できません。まず、プロキシ対象ドメインだけを保護するのか、ノードのドメイン名など必要な問い合わせを除いて、すべてのアプリのDNSを指定した経路に通すのかを決めましょう。

よくある4つの迂回経路

  • システムプロキシだけを使用:システムプロキシに従わないアプリでは、UDP 53の問い合わせがルーターへ直接送信される場合があります。
  • ブラウザーのセキュアDNS:ブラウザーが独自のDoHサービスへHTTPSリクエストを送信し、ClashのDNSモジュールを経由しない場合があります。その接続がプロキシを通るかどうかは、通信の捕捉範囲とルールによって決まります。
  • 複数のDNS設定:サブスクリプション、クライアントのオーバーライド、OSのネットワーク設定が同時に適用され、編集したファイルが実際の設定に反映されていない場合があります。
  • IPv6または別のVPN:別のネットワークアダプター、IPv6のルート、VPNのDNS設定が、通信の捕捉対象外の経路を作っている場合があります。

検出手順:テストサイト・コアのログ・OSの問い合わせを照合する

検出を始める前に、クライアント名、コアの種類、システムプロキシの状態、TUNの状態、現在のモードを記録してください。この記事で紹介するrespect-rulesやproxy-server-nameserverの設定例は、これらの項目に対応したmihomoコア向けです。旧バージョンのClashにはそのまま適用できません。GUIクライアントとコアのバージョンは別々に確認し、クライアントの「バージョン情報」画面と起動ログを参照してください。

手順1:同じネットワークで条件をそろえて比較する

  1. ほかのVPN、通信高速化ツール、プロキシ拡張機能をいったん無効にし、ブラウザーのセキュアDNSが有効かどうかを記録します。
  2. Clashの通信捕捉を一時的に無効にし、dnsleaktest.comでExtended testを実行します。リゾルバーのIPアドレス、ネットワークの運営元、検出時刻を記録してください。
  3. 確認する設定を有効にします。ブラウザーの設定は変えず、同じテストをもう一度実行してください。browserleaks.com/dnsで照合する方法もあります。
  4. 次に、ブラウザーのセキュアDNSを一時的に無効にして、もう一度テストします。「ブラウザー独自の名前解決経路」と「OS・コアの名前解決経路」を切り分けられます。テスト後は用途に応じて設定を戻してください。

検出サイトでは通常、ブラウザーからランダムなサブドメインへアクセスし、権威DNS側から再帰リゾルバーを確認します。結果に表示されるIPアドレスはリゾルバーの出口であり、プロキシノードのIPアドレスとは限りません。パブリックDNSでは、リゾルバーの所在地とプロキシの出口の所在地が異なることはよくあります。Anycastや転送方式を使うサービスでは、複数のアドレスが表示されることもあります。国名だけを比べず、運営元と想定した通信経路を確認しましょう。

手順2:DNSモジュールが応答しているか確認する

以下の問い合わせ先は、この記事の例で使うローカル待受ポート1053です。確認できるのはコアのDNS機能だけで、OSやブラウザーがこのポートを使っていることの証明にはなりません。fake-ipが有効で、対象ドメインがフィルターされていない場合、Aレコードの問い合わせには通常、198.18.0.0/16の範囲の仮想アドレスが返されます。

# Windows PowerShell またはコマンドプロンプト
nslookup -port=1053 example.com 127.0.0.1

# macOS / Linux(digが必要)
dig @127.0.0.1 -p 1053 example.com A

続いてクライアントの「ログ」画面を開き、ログレベルを一時的にdebugにして、DNS問い合わせとWebアクセスをもう一度行います。その後、「接続」画面で対象ドメイン、適用されたルール、最終的なポリシーを確認してください。ログの形式はコアのバージョンによって異なります。名前解決の失敗、接続タイムアウト、名前解決のループに関する記録があるかを確認しましょう。調査後はinfoに戻し、ドメイン情報を過剰に記録しないようにしてください。

手順3:キャッシュと実際の迂回を切り分ける

設定変更後も以前のリゾルバーが表示される場合は、まず設定を再読み込みし、OSのDNSキャッシュを消去してからテスト用ブラウザーを再起動します。Windowsではipconfig /flushdns、systemd-resolvedを使うLinuxではresolvectl flush-cachesを実行できます。これらのコマンドですべてのアプリ内キャッシュが消えるわけではありません。ランダムなドメインを使う検出テストも再実行してください。

さらに確認する場合は、物理ネットワークアダプターでパケットをキャプチャし、Wiresharkの表示フィルターdns || tcp.port == 853 || udp.port == 853で従来型DNS、DoT、DoQに関連する通信を調べます。DoHは通常443番ポートを使うため、このフィルターだけでは特定できません。宛先アドレスとコアの接続記録も確認してください。ネットワークアダプター上でプロキシサーバーとの接続が見えるのは正常です。すべての送信通信をリークと判断しないでください。

設定項目の見方:nameserver、fallback、respect-rules

項目役割確認ポイント
enableコアのDNSモジュールを有効にする。有効にしたうえで、アプリの問い合わせがモジュールに届くことを確認する。
listenローカルDNSの待受先を設定する。127.0.0.1:1053を指定しても、OSのDNS設定は自動で変わりません。
enhanced-modefake-ipなどの処理モードを選択する。アドレスの対応付けと上流DNSへの接続経路は別の設定。
nameserverデフォルトの上流DNSを指定する。プロトコルと接続可否に加え、最終的に直接接続かプロキシ経由かを確認する。
fallback別の上流DNSを設定する。フィルター条件と組み合わせて応答を選ぶ。メインの上流DNSが失敗したときだけ問い合わせる設定とは限らない。
fallback-filterIPアドレスやドメイン名などの条件で応答を絞り込む。応答の絞り込みは、もう一方の上流DNSへの問い合わせを防ぐものではない。
respect-rulesDNS上流への接続にルーティングルールを適用する。trueにするだけでは不十分。ノードのドメイン名解決も設定し、ルールの順序を確認する。
proxy-server-nameserverプロキシサーバーのドメイン名を専用に解決する。プロキシ接続の確立前に、そのプロキシを使ってノードを名前解決するループを防ぐ。
default-nameserver上流DNS自身のドメイン名を解決する。ブートストラップ用の名前解決であり、通常の通信で使うデフォルトの上流DNSではない。プライバシー上の扱いも確認する。

たとえば、ISPのUDP DNSと海外のfallbackを同時に設定している場合、最終的に海外のDNS応答が採用されても、ISPに問い合わせが届いていないとは限りません。ローカルの暗号化されていないDNSへ業務ドメインを送らないことが目的なら、fallback-filterだけを調整するのではなく、不要な平文DNSの上流を先に削除してください。

nameserver-policyを使うと、特定のドメインに別の上流DNSを指定できます。既存のサブスクリプションにこの項目が残っている場合、ドメインによっては変更したデフォルトのnameserverを使わないことがあります。設定を調べる際は、追加した設定だけでなく実際に読み込まれた設定を確認してください。暗号化DNSは端末からDNSサービスまでの問い合わせ内容を保護しますが、DNSサービス自体はドメイン名を処理し、確認できます。

mihomoのfake-ip設定例:上流DNSとプロキシ経路を明示する

以下は既存のmihomo設定に統合するDNS設定例であり、サブスクリプション全体ではありません。上流DNSにはCloudflareのDoHをIPアドレス形式で指定し、DNSサービスのドメイン名を解決するためのブートストラップ問い合わせを減らしています。使用前に、そのDNSサービスが利用中のネットワークポリシーに適合していること、プロキシノードから接続できることを確認してください。既存設定にfallbackやnameserver-policyがある場合も確認が必要です。この設定を貼り付けても、既存項目が自動で削除されるわけではありません。

# DNS設定例:既存の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"
    - "localhost"
  respect-rules: true
  nameserver:
    - https://1.1.1.1/dns-query
  proxy-server-nameserver:
    - https://1.1.1.1/dns-query

ここでipv6: falseを指定するのは、まずコアDNSのAAAAレコード応答を切り分け対象から外すためです。OSのIPv6を無効にする設定ではなく、ほかのアプリがIPv6アドレスを取得するのを防ぐものでもありません。IPv6が必要な場合は、通信の捕捉と出口が対応していることを確認してから設定を戻し、IPv6の経路を個別にテストしてください。

業務用DNSの上流接続を実際のプロキシポリシーに通す

respect-rules: trueを設定すると、業務用DNSの上流接続にルールが適用されます。中国本土向けの直接接続、その他の直接接続、最終的な直接接続ルールがある場合は、この上流DNSをどの経路に通すか明示してください。以下のルールは既存のrulesリストの前方に追加し、広範囲のIPルールやMATCHより先に適用されるようにします。例では既存のポリシーグループ名をPROXYとしています。名前が異なる場合は実際のグループ名に変更し、2つ目のrulesキーは追加しないでください。

# ルール例:既存のrulesリストに追加し、ほかのルールは残す
- IP-CIDR,1.1.1.1/32,PROXY,no-resolve

「プロキシ」画面で、PROXYにプロキシノードが選択され、DIRECTになっていないことも確認してください。上記のルールは業務用DNSの上流接続に適用されます。一方、proxy-server-nameserverはプロキシの確立に必要なノードのドメイン名を解決する経路です。まだ確立していない同じプロキシに依存させることはできません。この例ではノード名のブートストラップに、直接接続の暗号化DoHを使います。ローカルネットワークからDNSサービスへの接続は確認でき、DNSサービスにはノードのドメイン名が渡ります。

fake-ip-filterには必要なドメインだけ追加する

実IPアドレスが必要なLAN機器や特定のアプリは、fake-ip-filterに追加できます。フィルター対象には実際のアドレスが返されますが、それだけで直接接続になるわけではなく、DNSリークを防ぐスイッチでもありません。特定の機器の問題を直すためにすべてのドメインをフィルターへ追加すると、fake-ipでドメイン名の対応関係を保つメリットが失われます。

TUNとブラウザーの設定:DNS問い合わせを捕捉対象にする

システムプロキシの影響を受けるのは、主にその設定に従うアプリです。UDP DNSを幅広く捕捉できるわけではありません。より多くのアプリの通信を捕捉するには、システム権限がある環境でTUNを利用できます。Windowsでは通常、クライアントのサービスモードまたは管理者権限が必要です。macOSやLinuxでも、適切な認証や権限設定が必要です。DNSのリダイレクトを確認する前に、仮想ネットワークアダプターが正常に作成されていることを確認しましょう。画面上のスイッチをオンにしただけで通信が捕捉されているとは限りません。

# TUNの任意設定例:上記のdnsセクションと併用
tun:
  enable: true
  stack: system
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53
    - tcp://any:53

この設定は、TUNに入る通常のUDP・TCP 53番ポートの問い合わせをコアDNSに転送するためのものです。実際の動作はOS、ルーティング、コアの対応状況によって異なります。OSのDNS設定では通常、サーバーのIPアドレスしか指定できず、127.0.0.1:1053のようにポート番号付きのアドレスは指定できません。TUNを使わず、OSからローカルのDNSリゾルバーを直接使うには、別途53番ポートで待ち受ける構成を用意し、権限とポートの競合に対処する必要があります。

  • Chrome:「設定」→「プライバシーとセキュリティ」→「セキュリティ」→「セキュア DNS を使用する」。診断時はいったん無効にして、検出結果を比較してください。
  • Firefox:「設定」→「プライバシーとセキュリティ」→「DNS over HTTPS」。保護レベルと指定プロバイダーを確認してください。項目名はバージョンによって多少異なる場合があります。
  • Android:システムの「設定」で「プライベート DNS」を確認してください。メニューの場所はメーカーによって異なります。暗号化された問い合わせは、通常のUDP 53番ポートのリダイレクト対象にはなりません。

ブラウザーのDoHやOSのDoTは独立した暗号化接続を使うため、dns-hijackでコアの通常のDNS問い合わせに変換することはできません。これらの機能は有効なままでも構いませんが、接続が想定したプロキシ経路を通るか確認してください。診断のために無効にする方法はありますが、長期的な対策はそれだけではありません。

変更後にインターネットへ接続できない場合:依存関係を順に確認

症状最初に確認する項目対処方法
ローカルの1053番ポートへの問い合わせがタイムアウトする設定の読み込みエラー、DNSの有効状態、ポートの競合。まずローカルのDNS問い合わせを成功させてから、Web上の検出結果を確認する。
ノードにドメイン名を使うと接続できないproxy-server-nameserverに接続できるか。ノードのブートストラップ用名前解決を修正し、名前解決とプロキシ接続の相互依存を避ける。
ノードには接続できるが、ドメインへのアクセスがタイムアウトするDoH上流へのルートと、ポリシーグループの現在の選択先。上流DNSへの接続にプロキシが適用され、プロキシからDNSサービスに接続できることを確認する。
一部のLANアプリだけ動作しないfake-ipが返されているか、実際のLANアドレスが必要か。必要最小限のフィルターとLAN向けの迂回ルールを追加する。
検出結果に想定外のリゾルバーが表示されるブラウザーのDoH、プライベートDNS、ほかのVPN、IPv6。一つずつ無効にして比較し、迂回経路を特定してから有効にする方法を決める。

一度に変更するのは1つの層だけにします。まず上流DNS、次にルール、続いてTUN、最後にブラウザーやOS独自の名前解決を確認してください。ノードの変更、TUNの有効化、OSのDNS変更を同時に行うと、どの設定が効いたのか判断しにくくなります。TUNを有効にした後、端末全体がインターネットに接続できなくなった場合は、まずTUNを無効にしてネットワークを復旧し、デフォルトルート、権限、ほかのVPNとの競合を確認してください。異なるDNSアドレスを重ねて追加しないでください。

最終確認リスト

  1. ローカルの1053番ポートへの問い合わせで想定した仮想アドレスが返り、コアのログに継続的な名前解決タイムアウトがない。
  2. 業務用DoH接続に指定したプロキシポリシーが適用され、ノードのドメイン名解決には独立して使えるブートストラップ経路がある。
  3. Web上の検出結果に表示されるDNSサービスが想定どおりである。「リゾルバーのIPアドレスがノードのIPアドレスと一致すること」は条件にしない。
  4. ブラウザーのセキュアDNS、OSのプライベートDNS、IPv6、LANアプリを個別にテストした。
  5. クライアントの再起動後とサブスクリプション更新後にもテストし直し、オーバーライド設定が保持されていることを確認した。

DNSリーク対策で重要なのは、特定の項目をtrueにすることではありません。通信の捕捉、アドレスの対応付け、上流DNS、ルーティングが、検証できる一連の経路を構成していることです。ネットワーク、ノード、クライアントのコアを変更した場合は、同じ手順で改めて比較してください。ポート、ポリシーグループ、起動時の設定については、応用マニュアルのポート解説とヘルプセンターのトラブル対処をご覧ください。