Xrayの高度なルーティングとDNS漏洩対策をJSONで実装する実践ガイド

ルーティングと DNS を別々に考えない

Xray の通信振り分けを安定させるには、ルーティングルールだけでなく、ドメイン名をどのタイミングで IP アドレスへ変換するかまで設計する必要があります。ブラウザが example.com へ接続するとき、最初に DNS 問い合わせが発生し、その後に得られた宛先情報をもとに TCP や TLS の通信が始まります。Xray の外側で OS やルーターが DNS を処理していると、プロキシ接続は成功していても、名前解決だけが別経路へ出てしまうことがあります。

この状態が一般に DNS 漏洩と呼ばれます。ウェブページは表示できるため、通常の接続テストだけでは気付きにくい点が問題です。たとえば、プロキシ経由でサイト本体へ接続していても、問い合わせ先が自宅ルーターや ISP の DNS のままなら、アクセスしたドメインの情報がその DNS サーバーに渡ります。対策の基本は、DNS の問い合わせ先、ルーティングの判定材料、実際の接続経路を同じ設計図の中で管理することです。

ただし、DNS 設定を Xray に追加するだけで、すべての DNS 漏洩が自動的に解消されるわけではありません。システム全体を TUN で接管しているのか、アプリが独自に DoH や DoT を使っているのか、IPv6 が別経路で有効になっているのかによって結果は変わります。まず自分の利用モードを確認し、そのうえで JSON の順序と役割を整理するのが近道です。

内蔵 DNS の JSON を組み立てる

Xray の DNS 設定は、通常トップレベルの dns オブジェクトに記述します。最初に決めるのは、どの DNS サーバーへ問い合わせるか、どのドメインをどのサーバーへ送るか、応答をどのようにルーティングへ渡すかという3点です。単に複数のサーバーを並べるのではなく、用途ごとに役割を分けると、後から障害を切り分けやすくなります。

たとえば、国内向けドメインと国外向けドメインで問い合わせ先を変えたい場合、dns.servers にサーバーを登録し、サーバー項目の domains で対象を指定します。サーバーの指定には通常の IP アドレスだけでなく、DoH の URL やローカル DNS も利用できます。設定項目の対応状況は Xray-core のバージョンによって異なるため、クライアントが同梱するコアのバージョンと公式仕様を合わせて確認してください。

queryStrategy は A レコードと AAAA レコードのどちらを問い合わせるかに関わります。IPv4 のみで運用する環境なら UseIPv4、IPv6 も安定して使える環境なら UseIP など、実際のネットワークに合った値を選びます。IPv6 を使えない回線で AAAA レコードを優先すると、接続待ちが長くなったり、失敗した IPv6 経路から別の通信が漏れたりすることがあります。IPv6 を無効にしたい理由があるなら、DNS と OS の両方で方針をそろえることが重要です。

特定のドメインを固定 IP へ解決したい場合は hosts を使えますが、常用の DNS 代わりにするものではありません。サービス側の IP が変わると古い値が残り、突然接続できなくなる可能性があります。テスト用のドメイン、社内ホスト、明確な名前解決要件に限定し、広範囲のドメインを手作業で固定する運用は避けた方が安全です。

domainStrategy とルールの判定順

ルーティングの中心は routing オブジェクトです。ここで domainStrategyrules を組み合わせ、ドメイン、IP、ポート、ネットワーク種別などを基準に outbound を選びます。よく使われる AsIs は、受け取ったドメイン情報をそのまま使い、必要がなければ名前解決を追加で行いません。一方、IPIfNonMatch はドメインルールに一致しない場合に解決を試み、得られた IP を使って IP ルールを再判定します。

この違いは DNS 漏洩対策と直接関係します。IPIfNonMatch を選んだ場合、Xray がルーティング判定のために DNS 解決を行うことがあります。その問い合わせが意図した内蔵 DNS を通っているかを確認しないまま、外側の名前解決へ任せると、ルールを高度化したつもりで実際には漏洩経路を増やすことになります。ドメインルールだけで十分な構成なら AsIs を検討し、IP 判定が必要な構成では DNS 経路まで検証してください。

rules は上から順番に評価され、最初に一致したルールが適用されます。したがって、特定ドメインを専用 outbound へ送るルール、広告や不要な接続を遮断するルール、最後に通常通信を送るルールという順番が分かりやすいです。広い条件のルールを先頭に置くと、後ろの細かい例外に到達しません。自作ルールを追加するときは、現在のルールの前後関係を確認し、意図した条件が本当に評価される位置へ置きます。

sniffing と routeOnly の実用的な使い分け

接続先が IP アドレスとして渡されると、ドメインベースのルールだけでは判定できないことがあります。TLS の Server Name Indication、HTTP の Host、QUIC などからドメイン情報を読み取る機能が sniffing です。受信設定の sniffing.enabled を有効にし、必要なプロトコルを destOverride に指定すると、宛先 IP だけでは分からない通信をドメインルールへ戻せる場合があります。

ただし、sniffing は万能な復号機能ではありません。暗号化された内容をすべて読めるわけではなく、プロトコルやアプリの実装によって取得できる情報は異なります。また、読み取ったドメインを実際の接続先として置き換える動作と、ルーティング判定にだけ使う動作は区別が必要です。設定項目に routeOnly が使える場合は、まずルール判定だけに利用し、接続先そのものを不用意に書き換えない構成から始めると安全です。

routeOnly の考え方は、特に IP 直打ちの通信や CDN を利用するサービスで役立ちます。見えている IP は実際のサービス名と一致しないことがあり、接続先を強制的に置き換えると TLS 検証やアプリの接続処理を壊す可能性があります。まず「ドメイン情報をルールに利用するだけ」に限定し、必要性を確認してから destOverride の動作を広げてください。

sniffing を有効にした後は、通常のブラウザだけでなく、ソフトウェア更新、メッセージアプリ、動画アプリなども確認します。アプリによっては SNI を送らない、独自プロトコルを使う、QUIC と TCP を切り替えるといった違いがあります。ひとつのサイトが開いたからといって、すべての通信が同じルールで処理されているとは限りません。

DNS 漏洩を減らすための実装手順

実際の設定では、まず Xray の内蔵 DNS に問い合わせ先を集約します。次に、ルーティング用の DNS 解決と、アプリから直接発生する DNS 通信を分けて考えます。前者は Xray の dnsdomainStrategy の範囲です。後者は OS の DNS、ブラウザの安全な DNS、アプリ独自の DoH、TUN の DNS リダイレクトなどが関係します。Xray の JSON だけを修正しても、ブラウザが独自 DoH を使えば別経路が残ることがあります。

  1. 内蔵 DNS のサーバーと問い合わせ対象を決める
  2. IPv4 と IPv6 の利用方針を決め、queryStrategy と OS 側を合わせる
  3. domainStrategy を選び、追加解決が必要なルールだけに限定する
  4. sniffing は必要な受信設定で有効にし、最初は routeOnly を優先する
  5. ブラウザの独自 DNS、OS の手動 DNS、他の VPN やプロキシを確認する
  6. 通常接続、IP 直打ち、IPv6、DoH 利用アプリを個別に検証する

DNS の転送をプロキシ経由にしたい場合は、DNS サーバーへ送る通信をどの outbound で処理するかも確認します。内蔵 DNS が動いていても、問い合わせ先への通信が直通なら、目的に対して不十分なことがあります。逆にすべてを無条件にプロキシへ送ると、国内サービスの名前解決が遅くなったり、ルーティングループを作ったりする場合があります。DNS 用の outbound と通常通信の outbound を分ける設計では、DNS 自身を再び DNS ルールへ送り返さないよう注意してください。

DNS 漏洩の確認には、ブラウザの DNS テストだけでなく、クライアントのログ、OS の DNS キャッシュ、ルーターの問い合わせ履歴を組み合わせます。テスト中はブラウザのキャッシュを消し、同じドメインを何度も開いて結果を比較します。TUN を使う場合は、TUN を有効にした状態と無効にした状態を分けて確認し、片方だけで漏れるなら接管範囲や DNS リダイレクトの問題を疑います。

設定検証と障害の切り分け

複雑な JSON を一度に変更すると、どの項目が原因か分からなくなります。最初は動作確認済みの最小設定を保存し、DNS、ルール、sniffing、TUN の順に一つずつ追加してください。JSON の構文エラーは、余分なカンマ、引用符の閉じ忘れ、配列とオブジェクトの括弧違いで起きます。編集後はクライアントの設定検証機能や Xray-core のチェック機能を使い、起動前にエラーを消します。

「接続できるが、想定した outbound に入らない」場合は、ルールの順序、ドメインの表記、ポート条件、domainStrategy を確認します。ルーティングログを一時的に詳しくし、実際に受け取った宛先がドメインなのか IP なのか、sniffing で何が取得されたのかを確認すると判断しやすくなります。ログを見ずにルールを増やすと、同じ条件を重複させるだけになりがちです。

「名前解決だけ失敗する」場合は、DNS サーバーへ到達できるか、DoH の URL が正しいか、証明書検証に使う時刻が正しいかを見ます。UseIPv4 にすると改善するなら、IPv6 経路または AAAA 応答が原因かもしれません。「ブラウザは開くが特定アプリだけ漏れる」場合は、そのアプリの独自 DNS、QUIC、VPN 権限、システムプロキシ非対応を確認します。

設定を変更するたびに、変更点、テストしたドメイン、使用したモード、ログの結果を簡単に記録しておくと、元へ戻す判断が早くなります。最終的な JSON は、複雑さを増やすことより、どの DNS が使われ、どのルールが先に一致し、どの outbound が接続を担当するかを説明できることが重要です。必要な通信だけを分類し、未一致通信の扱いを明確にし、定期的に Xray-core とクライアントの互換性を確認すれば、ルーティングと DNS 漏洩対策を無理なく維持できます。