ステートレスな VPN エッジファイアウォールで DNS 応答が失敗する理由
VPN ホストの DNS クエリへのインバウンド応答が持つのは送信元ポート 53 であり、宛先ポート 53 ではない。ルールは実際のリゾルバーと経路上のエッジに絞り込む。
トンネルは確立しており、クライアントは VPN ホストに到達できるのに、ホスト側では何も名前解決できない。ホスト自身の外向きクエリはインターフェースをきれいに出ていくので、障害は戻り経路にある。上流のエッジが、ホストが送ったばかりの内容を記憶しないまま受信トラフィックをフィルタしているのだ。トンネルのポートを許可しても、このやり取りはまったく記述できない。DNS クエリへの応答は別のフローであり、エッジがステートレスなら、戻ることを許可したものは何もない。
二つのやり取りは同じやり取りではない
タプルを書き出せば間違いは明らかだ。要求の宛先ポートが応答の送信元ポートになる。ポート 53 のインバウンドルールを追加するとき、多くの人が思い描くのとは逆である。
query: 203.0.113.10:53018 -> 198.51.100.53:53
reply: 198.51.100.53:53 -> 203.0.113.10:53018宛先ポート 53 を要求する受信ルールが記述するのは、ホスト上で動く DNS リスナー宛てのトラフィックであり、まったく別のサービスだ。これらのアドレスはドキュメント用の例であり、送信元ポートはクエリごとにクライアントが選ぶので、ルールにコピーして動き続けることを期待できる値ではない。
ステートレスなエッジとステートフルなホストファイアウォール
ステートレスなエッジフィルタは、背後に接続テーブルを持たず、各受信パケットをルール一覧と照合する。正常に出ていった外向きクエリは、その受信応答に対する許可を一切作らない。機器側にクエリが起きた記録がないからだ。ステートフルなホストファイアウォールは逆で、接続追跡が許可されたやり取りの戻りトラフィックを認識し、応答用のルールなしで通過させる。どちらの境界も一般的な経路に存在する、そして両方が目的のパケットを許可しなければならない。
比較が重要なのは、二つの機器の失敗の仕方が違うからだ。応答を拒否するステートフルなファイアウォールはドロップを記録する。ステートレスなエッジは黙って拒否するので、ホストは単にタイムアウトする。
許可はポート番号ではなくプロトコルを中心に組み立てる
ルールは実際のリゾルバーのアドレス、宛先ホスト、製品が対応する受信ポートのポリシーに絞る。DNS の TCP 経路も忘れないこと。大きな応答や切り詰め時のフォールバックはやり取りを TCP に移すので、UDP だけのルールでは対応できない。エッジが定義済みフローに対してステートフル検査をサポートするなら、手書きの応答ルールより優先する。ステートフルな経路はクライアントが選んだポートを自動的に追従するからだ。
次に、経路上のどの製品が実際にトラフィックをフィルタしているかを確認する。文書化された IPv4 のエッジ範囲は IPv6 のポリシーを定めないし、プロバイダー内部のトラフィックは同じフィルタを一度も通らないかもしれない。
任意の送信元ポート 53 を許可しても解決にならない理由
送信元ポート 53 と名乗るものをすべて受け入れるルールは、送信者がヘッダーフィールドに書いた内容をそのまま受け入れるルールだ。ポート番号は送信者を認証せず、ペイロードが有効な応答であることも証明しない。したがってこのルールは、名前解決をより確実にすることなくホストの露出を広げる。アクセスリストを簡単にするために無効化するのではなく、送信元の制限とクライアント自身の応答検証(送信元ポートのランダム化を含む)を維持すること。
リゾルバーが自分で管理するサービスなら、そのアドレスに限定すれば十分だ。公開リゾルバーなら、承認したアドレスに限定し、ルールのコメントに記しておく。
正しい境界で、正しい方向に検証する
同じクエリをホスト側とエッジ側で取得し、パケット数ではなくタプルを比較する。両側で見えるのにホスト側で応答が見えないクエリは受信フィルタを指す。ホスト側だけで見えるクエリはルーティングかリゾルバー自体を指す。UDP のみの許可なら TCP クエリで繰り返すこと。最初のテストでは見えない隙間がそこにある。
これを修正しても、ホストが本当に必要とする DNS のやり取りが完了するだけだ。ホストを公開リゾルバーにするわけでも、転送された VPN フローを許可するわけでも、無関係なトンネル問題の原因が DNS だったと証明するわけでもない。