バイパスルーティングとして使う Armbian 上で、sing-box と WireGuard を同時に稼働させている。このルーター自身は社内ドメインを解決できるのに、その IP を DNS サーバーに指定した下流クライアントは毎回タイムアウトする。原因は DNS のマッチングルールではなく、sing-box が上位 DNS へ送るトラフィックが誤った出力インターフェースを選んでいたことにあった。

本稿のアドレスはすべて書き換えた架空の値であり、実際のネットワーク設定ではない。プライベートネットワークのトポロジ関係とプロトコルの挙動はそのまま保持している。

トポロジと症状

役割例示アドレス用途
下流クライアント192.168.10.20DNS クライアント
バイパスルート LAN192.168.10.254 / eth0sing-box DNS の待ち受けアドレス
バイパスルート WireGuard10.77.0.11 / wg0トンネルクライアント
社内 DNS10.77.0.1:53WireGuard ピア側の DNS

トポロジは次の構成になる。

クライアント 192.168.10.20
  │ DNS クエリ
  ▼
バイパスルート 192.168.10.254:53 (sing-box DNS 受信)
  │ 期待: wg0 から 10.77.0.1:53 へ問い合わせ
  ▼
WireGuard ピア側 DNS 10.77.0.1:53

社内テストドメインは service.home.arpa(例示)を使用した。観測された症状は以下のとおり。

  • ルーター自身ではこのドメインを正常に解決できる
  • 下流クライアントがルーターの LAN IP を明示的に DNS サーバーに指定すると、一貫してタイムアウトする
  • sing-box は UDP 53 を待ち受けしており、対応する受信側ルーティングルールは正常と確認済み

第 1 ステップ: 受信リクエストが届いていることを確認する

バイパスルート上で下流クライアントの DNS トラフィックをキャプチャする。

sudo tcpdump -i eth0 -nn -K -l \
  'host 192.168.10.20 and udp port 53'

同じ DNS Transaction ID を持つリクエストが数秒間隔で再送されているのに、対応する成功レスポンスは確認できない。つまりクライアントは確かにクエリをルーターへ渡しており、処理チェーンは最後まで到達していない。

待ち受けポートを確認する。

sudo ss -lnup | grep ':53 '

sing-box がバイパスルートの LAN IP 上で UDP 53 を待ち受けていることを確認できる。

第 2 ステップ: LAN と WireGuard の出力を分けて観察する

最初からすべての DNS トラフィックを出力しないこと。ターミナルを 2 つ開き、経路ごとに分けて観察する。

# LAN: この下流クライアントのみ
sudo tcpdump -i eth0 -nn -K -l 'host 192.168.10.20 and udp port 53'
# WireGuard: 対象の DNS のみ
sudo tcpdump -i wg0 -nn -K -l 'host 10.77.0.1 and udp port 53'

その後、下流クライアントから明示的なクエリを 1 回発行する。

nslookup -type=A service.home.arpa 192.168.10.254

今回のケースでは、下流のリクエストは確かに LAN に到達したが、sing-box の DNS 上位への新規クエリは WireGuard IP ではなく LAN アドレスをソースとして eth0 から送信されていた。

重要な区別: DNS ルールが「どの上位サーバーを選ぶか」と、OS / アプリケーションが「そのサーバーにどのインターフェースで接続するか」は別の話である。ルールが正しくヒットしていても、実際の送信経路が間違っていればリクエストはタイムアウトし得る。

第 3 ステップ: sing-box の DNS 上位にインターフェースをバインドする

障害発生時の上位設定は次のようなものだった。

{
  "type": "udp",
  "tag": "internal-dns",
  "server": "10.77.0.1"
}

修正後は次のようになる。

{
  "type": "udp",
  "tag": "internal-dns",
  "server": "10.77.0.1",
  "bind_interface": "wg0"
}

bind_interface はその DNS サーバーに対するネットワークダイヤルに作用し、指定したインターフェースを使用させる。TUN・ポリシルート・WireGuard を併用する環境では、OS の自動経路選択だけに依存するより明確になる。

設定ファイルを確認して再起動する(自分の設定パスに合わせて変更すること)。

sudo sing-box check -c /etc/sing-box/config.json
sudo systemctl restart sing-box

その後、再度クエリを行い wg0 でキャプチャする。

sudo tcpdump -i wg0 -nn -K -l 'host 10.77.0.1 and udp port 53'

wg0 上でリクエストと対応するレスポンスが確認できるはずである。今回のケースでは、bind_interface を追加したおかげで下流の DNS クエリが正常に戻った。

付随する現象: A レコードの成功後に NXDOMAIN が発生する

調査中に別の種類の症状にも遭遇した。ある社内名称が CNAME 経由で単一ラベルのホスト名に解決され、A クエリはアドレスを正常に返すが、終端ホスト名に対する AAAA クエリが NXDOMAIN を返す。これは別途切り分けて調査すべき別の名前解決の異常であり、今回の下流クエリのタイムアウトの原因とみなしてはならない。

同種の症状に遭遇した場合は、次を個別に実行する。

dig @<DNS サーバーアドレス> service.home.arpa A
dig @<DNS サーバーアドレス> service.home.arpa AAAA

そして、CNAME が意図しない単一ラベル名称を指していないか、ローカルリゾルバと上位 DNS の結果が一致しているかを確認する。

トラブルシューティングの要点

  1. クライアント → バイパスルート受信 → 上位への出力 → 上位レスポンス → クライアントへのレスポンス の区間ごとにパケットキャプチャする。
  2. 検証するのはパケットの実際の出力インターフェースと送信元 IPであり、DNS 設定の server フィールドだけでは判断できない。
  3. クライアントのリクエストは受け取ったが対応するレスポンスが見えない場合は、まず上位への出力経路を確認する。すでに正常と確認済みのルーティングルールを直ちに書き換える必要はない。
  4. tcpdump が bad udp cksum を表示した場合、ローカル送信時の NIC チェックサムオフロードの結果である可能性がある。-K を付けるとキャプチャ時のチェックサム検証を無効化できる。
  5. home.arpa はホームネットワーク向けの専用ドメインである。実際の公開ドメイン名、デバイス名、社内アドレス、トポロジ構成をそのまま公開ブログへ載せないこと。

結論: 最終的な障害箇所は、sing-box が社内 DNS へ送る上位 UDP クエリが WireGuard を通らず LAN インターフェースから送信されていたことにある。該当する DNS 上位サーバーに bind_interface: "wg0" を設定することで解消した。