Pi-hole v6 はポート 53 で DNS クエリを受け取り、到着したパケットに応答してよいかを判断します。WireGuard ホストでは、この二番目の判断が、LAN から見てリゾルバが正常なのにクライアントがタイムアウトする理由であることがほとんどです。ポートは開いており FTL も動作していますが、10.90.0.1 からは何も返ってきません。応答が出る前に三つの要素が関係します: dns.interface、dns.listeningMode と、その両方の手前にあるファイアウォールです。

待ち受けソケットは誰が応答を受け取るかを決めない

FTL はポート 53 で UDP と TCP のリスナーを開きます。既定の設定ではソケットがワイルドカードであることが多く、そのため ss -lunp | grep :53 は、VPN クライアントを処理しているかどうかに関係なく 0.0.0.0:53 と [::]:53 を表示します。ワイルドカードソケットがパケットを受け入れ、その後 FTL が応答前にインターフェイスと送信元の規則を適用します。だから「ポートが待ち受けている」ことはトンネルクライアントについてほとんど何も証明しません。

有効だと思っているファイルではなく、実行中の設定を読んでください。pihole-FTL --config dns.interface と pihole-FTL --config dns.listeningMode は実際の値を出力し、コンテナや環境変数、デプロイ用の管理ツールが設定を所有している場合はホストのファイルと異なることがあります。Pi-hole FTL の設定リファレンス にモードの一覧と、どれがインターフェイスを基準にするかが書かれています。

LOCAL はサブネットを問い、SINGLE はインターフェイスを問う

既定の LOCAL は、送信元アドレスがホストのいずれかのインターフェイスに設定されたサブネットに属するクエリに応答します。10.90.0.1/24 の単純な wg0 はこのテストを通過します。10.90.0.2 のクライアントがトンネルのサブネット内にあるからです。ピアの向こう側にルーティングされたクライアントや、別のプレフィックスで NAT の背後に隠れたクライアントは、パケットが wg0 に届いてもこのテストを通過せず、ログにもクエリが現れません。

SINGLE は問いを変えます。dns.interface で指定したインターフェイスに到着するクエリを、ループバックを含めて受け入れ、送信元サブネットを無視します。連鎖した、あるいはルーティングされた VPN 構成で生き残るのはこのモードです。モードだけでは指示として不完全なので、両方のキーを一緒に設定します:

sudo pihole-FTL --config dns.interface "wg0"
sudo pihole-FTL --config dns.listeningMode "SINGLE"

BIND はソケットを変え、それには代償がある

BIND はワイルドカードソケットを絞り込みません。指定したインターフェイスに存在するアドレスだけにポート 53 をバインドするため、他の場所に届いたパケットはカーネルが拒否します。SINGLE より厳格で、その同義語ではありません。代償は起動時に現れます。FTL の起動時に wg0 が上がっていなければバインドするアドレスがなく、インターフェイスが現れて FTL が再起動するまで、リゾルバはリスナーなしで動く可能性があります。

起動順を制御できる場合、たとえば WireGuard インターフェイスの後に並べた systemd ユニットを使う場合は BIND を選んでください。カーネルに FTL が見る前に迷い込んだパケットを捨てさせたい場合も同じです。トンネルが頻繁に上下する場合や、FTL より後に起動するツールがインターフェイスを作る場合は SINGLE を選んでください。

ファイアウォールは二番目の関門

正しい FTL のモードでも、ホストのファイアウォールが wg0 のポート 53 を落とせば失敗します。両方のプロトコルで VPN の送信元範囲を許可し、インターネット側は閉じたままにします。inet filter テーブルの既存の input チェーンを使う nftables では、規則は次のようになります:

sudo nft add rule inet filter input iifname "wg0" ip saddr 10.90.0.0/24 udp dport 53 accept
sudo nft add rule inet filter input iifname "wg0" ip saddr 10.90.0.0/24 tcp dport 53 accept

トンネルが IPv6 を運ぶ場合は対応する IPv6 の規則を追加し、チェーンの drop ポリシーより前に置きます。ホストが ufw や firewalld を使う場合は、ツールを混在させず、その管理ツールで同じ一致を表現してください。インターネットルーターからポート 53 を転送しないでください。クライアントはトンネル経由でリゾルバの VPN アドレスに到達すべきです。

二つの経路は分かれたままです。クライアントのクエリは FTL で終わります。FTL 自身の上流への問い合わせはホストのルーティングテーブルに従って出ていき、トンネルを使うかどうかは場合によります。したがって、応答はするが有用なものを返さないリゾルバは上流の問題です。Web 管理画面と API は独自のポートで待ち受け、独自の制限が必要です。

応答と拒否を確認する

VPN クライアントから、両方のプロトコルでリゾルバのアドレスに問い合わせます: dig @10.90.0.1 example.com と dig +tcp @10.90.0.1 example.com。次に管理画面のクエリログで FTL がクエリを記録したことを確認します。ログ項目なしのクライアントのタイムアウトはファイアウォールかインターフェイスフィルターを指し、応答なしのログ項目は上流の解決を指します。

VPN の外にあるホストから、拒否される経路を試します。dig @10.90.0.1 example.com はタイムアウトし、ss -lunp はリゾルバ上でポート 53 が公開アドレスにバインドされていることを示さないはずです。サービスを一度再起動し、その後にホストを再起動して、wg0 が再び現れた後もリゾルバが応答することを確認してください。次の再起動までしか動かないクエリは、完了した変更ではありません。

関連記事