DNSの名前解決・通信の振り分け・DNSハイジャックを区別する

Webページが開けない原因は、必ずしもノードの障害とは限りません。ドメイン名を解決できていない、誤ったIPアドレスが返っている、あるいはアプリがクライアントのDNSを使っていない可能性があります。切り分けるには、DNSリクエストの受け口、アドレスを返す上流DNS、接続に適用されるプロキシルールの3点を確認しましょう。

  • DNSの名前解決:dnsの設定で、コアが問い合わせるDNSサーバーと応答の処理方法を指定します。
  • 通信の振り分け:rulesとプロキシグループで、接続にDIRECT、プロキシノード、その他の出力先のどれを使うか決まります。DNSが海外のIPアドレスを返しても、接続が自動的にプロキシ経由になるわけではありません。
  • DNSハイジャック:この記事では、TUNコアが指定ポートのDNS通信を取り込み、内蔵DNSに転送する機能を指します。通信事業者によるDNS応答の改ざんとは別のものです。

変更前に、現在動作している設定をエクスポートしてください。サブスクリプションから取得した設定は、クライアントのオーバーライド、拡張設定、マージ機能を使うのがおすすめです。サブスクリプション生成ファイルを直接編集すると、次回更新時に変更が上書きされることがあります。一度に変更するのは1項目にし、再読み込み後に最終的な適用設定を確認しましょう。

nameserverとdefault-nameserverの役割

nameserverは、通常のドメイン名問い合わせに使う上流DNSサーバーのリストです。IPアドレスだけを指定すると通常のDNSで、デフォルトのポートは53です。https://はDoHで通常TCP 443、tls://はDoTで通常TCP 853を使用します。暗号化プロトコルはDNSサーバーまでの問い合わせ通信を保護しますが、DNSサーバーへの接続性を保証するものではなく、通信の振り分けにも代わりません。

項目主な用途確認ポイント
enable内蔵DNSを有効化trueにしても、リクエストが実際にコアへ届いているか確認が必要
listenローカルDNSの待ち受け先を指定アドレス・ポートとシステムDNSの設定が合っているか
default-nameserverホスト名を使う上流DNSの名前解決に使用現在のネットワークから到達できるIPアドレスを優先
nameserver通常のドメイン名問い合わせを処理上流DNSへの接続性・応答内容・遅延
nameserver-policy指定したドメインのDNSサーバーを選択ルールの適用範囲が広すぎないか、イントラネットのドメインが誤ったDNSに送られていないか

上流DNSのホスト名も事前に名前解決が必要

https://dns.alidns.com/dns-queryを使う場合、コアはまずdns.alidns.comのIPアドレスを知る必要があります。これがブートストラップDNSの役割です。default-nameserverを、すべての問い合わせが失敗した際の予備DNSリストと混同しないでください。fallbackとは役割が異なります。

また、127.0.0.1:1053のように、コア自身の待ち受けアドレスを指定しないでください。上流への問い合わせが同じDNS入口に戻り、ループする可能性があります。別のローカルDNSサービスを指定する場合も、そのサービスがリクエストをClashへ戻していないか確認してください。

fallbackは単なるタイムアウト時の予備ではない

fallbackを設定すると、対象となる問い合わせでは通常、メインDNSと予備DNSへの問い合わせが並行して行われ、fallback-filterに基づいて採用する応答が決まります。「nameserverの失敗を5秒待ってからfallbackに問い合わせる」という動作ではありません。実際の処理順はキャッシュ、ドメイン別の設定、コアの実装にも左右されます。

fallback-filterによる応答の判定

  • geoip: true:GeoIP判定を有効にします。geoip-code: CNと組み合わせると、メインDNSが指定地域に属さないIPアドレスを返した場合、fallbackの応答が採用されます。
  • ipcidr:メインDNSの応答が指定したIPアドレス範囲に含まれる場合、fallbackの応答を使います。例の240.0.0.0/4は、通常のインターネット接続先として使うべきでない予約アドレス範囲の判定に使います。
  • domain:指定したドメインには、fallback側のDNSルールを直接適用します。メインDNSの応答が信頼できないと確認済みのドメインを、少数だけ指定する用途に適しています。

GeoIP判定には、コアで利用できる地理情報データが必要です。判定するのはIPアドレスの地域であり、応答の正しさではありません。海外のIPアドレスでもCDNの正常な応答の場合があり、国内のIPアドレスだから正しいとも限りません。このフィルターを有効にする前に、地理情報データが使えることを確認し、fallbackへの接続性に依存する問い合わせが増える点も理解しておきましょう。

mihomoで使えるDNS設定例

以下はトップレベルのdnsセクションのみで、ノード、サブスクリプション、プロキシグループは含みません。既存の動作する出力設定は残し、DNSセクション全体を置き換えるか、オーバーライドでマージしてください。同じYAMLファイル内にトップレベルのdns:を2つ作らないでください。この例では、システムで既に使われている53番ポートを避けるため、ローカルの1053番ポートを使用します。

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"

  default-nameserver:
    - 223.5.5.5
    - 119.29.29.29

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

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

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

この設定例を使う際の注意点

例にあるパブリックDNSは設定例として選んだもので、すべてのネットワークから直接接続できるとは限りません。1.1.1.1:443に接続できない場合は、このfallback設定を有効にしないでください。いったんfallbackとfallback-filterを削除し、メインDNSが正常に動作することを確認してから、接続できると確認済みの予備DNSを追加しましょう。

dns.ipv6: falseは、内蔵DNSが通常のAAAAレコードを返さないようにする設定です。OSのIPv6を無効にするものではなく、すべてのアプリによるIPv6接続を防ぐものでもありません。IPv4とIPv6の両方を使う場合は、IPv6のルート、ノード、TUNの対応状況を確認してから有効にしてください。

Fake-IPモードでは、コアがアプリに198.18.0.0/16の範囲にある仮想IPアドレスを返し、その対応情報を使ってドメイン名を保持できます。この範囲のアドレスが表示されても、通常はDNS汚染ではありません。実際の名前解決や接続の流れは、出力方式とルールによって異なります。fake-ip-filterに指定したドメインには仮想応答を使いませんが、適切な実DNSサーバーは引き続き必要です。フィルターを設定しても、パブリックDNSがイントラネットのホスト名を解決できるようになるわけではありません。

dns-hijackはdnsではなくTUNセクションに設定する

listen: 127.0.0.1:1053を設定しただけでは、システムのDNSリクエストが自動的にこのポートへ送られることはありません。通常、システムDNSの設定欄ではIPアドレスしか指定できず、1053のような任意のポートは指定できません。テストにはポートを直接指定できますが、普段使いにはTUNによる取り込み、システムの転送サービス、その他の明示的なDNS接続方法が必要です。

以下は独立したトップレベルのtunセクションで、dnsと同じ階層に置きます。クライアントがすでにTUN設定を生成している場合は、必要な項目だけをマージしてください。同じ名前のセクションを重複して作らないでください。

tun:
  enable: true
  stack: mixed
  auto-route: true
  auto-detect-interface: true
  dns-hijack:
    - any:53
    - tcp://any:53
  • any:53は、TUNに入る通常のUDP DNSリクエストを取り込む設定です。2つ目の項目でTCP 53も対象にします。
  • auto-routeはルートを自動設定する項目です。ただし、実際に取り込める範囲はOSの権限、ネットワークインターフェース、プラットフォームごとの実装に制限されます。
  • Windowsでは通常、クライアントのサービスまたは管理者権限が必要です。macOSでは該当するネットワークコンポーネントの許可が必要で、AndroidではシステムのVPN権限を使って通信経路を設定します。
  • クライアントにTUNの切り替え項目がある場合は、画面上の状態と実行ログの両方を確認し、YAMLの設定と画面上のオーバーライドが競合していないか確認してください。

DNS上流への接続にプロキシを使う:まずノードのドメイン名解決を設定

DoHを使っても、問い合わせが必ずプロキシ経由になるとは限りません。mihomoではrespect-rulesを有効にすると、DNS上流への接続にルーティングルールを適用できます。有効にする場合は、プロキシノード自身のドメイン名を解決するproxy-server-nameserverも設定してください。設定しないと、「DNSへの接続にプロキシが必要なのに、プロキシへの接続にはDNSが必要」という依存関係のループが起きやすくなります。

dns:
  respect-rules: true
  proxy-server-nameserver:
    - https://dns.alidns.com/dns-query

これは既存のDNSセクションに追加する項目であり、前節の設定全体を置き換えるものではありません。プロキシノードのドメイン名を解決するDNSサーバーは、プロキシ接続を確立する前から利用できる必要があります。有効にした後はrulesを確認してください。DoHの接続先がDIRECTに一致すれば直結し、プロキシグループに一致すれば、そのグループ内のノードも利用可能でなければなりません。

イントラネットのドメインには専用のDNSサーバーを指定

社内ドメイン、NAS、ルーターの管理用ドメインは、通常イントラネットのDNSサーバーに解決させます。nameserver-policyで実際のイントラネットDNSを指定できますが、アドレスは現在のネットワーク環境で確認してください。他の環境のゲートウェイアドレスをそのまま使わないでください。また、TUNによる53番ポートの取り込みで、この上流DNSへのリクエストが再びコアに戻らないか確認し、ループを防ぎましょう。

固定の端末が少数であれば、明示的なhostsマッピングを使う方法もあります。イントラネットのドメイン1つを解決するために、すべてのパブリックドメインをLANルーターのDNSに切り替えないでください。逆に、パブリックDoHで社内専用のドメインを解決できるとも限りません。

4段階で確認し、タイムアウト・DNS汚染・取り込み失敗を切り分ける

手順1:設定が実際に読み込まれたか確認する

再読み込み後にログを確認し、YAMLのインデント、項目の型、ポートの競合、未対応の設定項目を先に解消します。YAMLのインデントにはスペースを使い、Tabは使いません。起動ログに待ち受け失敗が表示されたら、まず1053番ポートを他のプロセスが使用していないか確認してください。画面では変更成功と表示されてもログの動作が変わらない場合は、クライアントのサブスクリプションオーバーライドが手動設定を置き換えていないか確認しましょう。

手順2:内蔵DNSに直接問い合わせる

macOSまたはLinuxでdigを使える場合は、待ち受けアドレスとポートを指定して問い合わせます。1つ目のコマンドでAレコードを、2つ目でAAAAレコードの挙動を確認します。Fake-IPモードで1つ目のコマンドが仮想IPアドレスを返しても、ローカルのDNS入口が応答したことを示すだけで、fallbackが正常に動作している証明にはなりません。

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

Windowsでは、nslookupの対話モードでポートを指定できます。以下のコマンドを1行ずつ入力してください。終了するにはexitを入力します。

nslookup
server 127.0.0.1
set port=1053
set type=A
example.com

手順3:システムのDNSリクエストがコアに届いているか確認する

TUNを有効にしたうえで、サーバーを指定せずにシステムのDNS問い合わせを実行し、コアのログを確認します。1053番ポートへの直接問い合わせが成功し、システムからの問い合わせだけ失敗する場合は、nameserverを変更し続けるのではなく、ルート、TUNの許可、アプリ独自のDNS設定を優先して確認してください。ブラウザーでページが開けても十分な証拠にはなりません。独自のDoHやキャッシュを使っている可能性があります。

手順4:名前解決と接続を個別に確認する

以前に問題があったドメインを選び、DNSエラー、適用されたルール、最終的な出力先を確認します。短時間の調査であれば、一時的にログレベルを上げてもかまいません。作業後は元に戻し、ログを共有する前にサブスクリプションURL、トークン、ノードの認証情報を削除してください。上流DNSを変更した後は、関連キャッシュを削除するかコアを再起動し、同じドメインへの初回と2回目以降の問い合わせを比較します。キャッシュヒットをDNSサーバーの速度向上と取り違えないようにしましょう。

症状優先して確認する項目次の対応
ローカルの1053番ポートへの問い合わせがタイムアウトするDNSの有効化、待ち受けアドレス、ポートの競合まずDNSの入口を直し、その後で外部DNSを確認する
コアに問い合わせは届くが、上流DNSがタイムアウトする443番または853番ポートへの接続性、ブートストラップDNS、出力ルール接続できると確認済みの上流DNSを1つだけ残す
一部のドメインでfallbackを有効にすると遅くなるGeoIPフィルターと予備DNSへの接続性フィルター条件を確認してから、予備DNSをテストする
Fake-IPが返るが接続できないマッピング、TUNルート、ルール、ノードの状態仮想IPをエラーと決めつけず、接続ログをたどって確認する
パブリックサイトは開くが、イントラネットのドメインは解決できないイントラネットDNSとドメイン別ルールイントラネットのドメインには、到達可能な内部DNSを指定する

安定して設定を調整するには、まずnameserverを1つ正常に使える状態にし、次にシステムDNSの接続を確認します。その後fallbackのフィルターを追加し、最後にDNS上流への接続に使うプロキシルートを設定します。各段階を個別に確認すれば、問題がDNSの入口、上流への通信、その後の接続のどこで発生しているか判断できます。