DNSクエリの投げ方によって返ってくるDNSレコードの中身が変わるかもよって話
はじめに
非常に特定条件下の話ですが、ハマったので書き記しておきます。
複数のフルサービスリゾルバを多段で運用するケースを想定しています。
何が問題なの?
DNSサーバの設定およびスタブリゾルバからのDNSクエリの間隔次第で意図しないレコードを返してしまうという話です。
環境について
主に家庭内の名前解決の為に使用するDNSMasqと、広告ブロックの為のAdGuard Homeを併用しています。
AdGuard Homeが落ちても名前解決ができるように、DNSMasqの上位DNSサーバを複数指定していますが、ここの設定が曲者です。
DNSMasqの設定に起因する問題
DNSMasqのデフォルト設定では上位DNSサーバをラウンドロビンで参照してしまい意図した挙動になりません。したがって優先的にAdGuard Homeを参照するように設定を変更します。
resolv-file=/etc/resolv.dnsmasq
strict-order
nameserver 10.0.64.3
nameserver 172.16.0.1
これでresolv.dnsmasqの最上位に書かれたDNSサーバ(AdGuard Home)を優先的に使用してくれるようになります。
この状態でクライアントからnslookupコマンドで動作確認をします。ブロック対象のドメイン名を引くと、下記の通りちゃんとDNSMasq経由でAdGuard Homeによってブロックされた代替レコードが返ってきているのが分かります。
>nslookup foobar.net
サーバー: fe52ff681a3d.mydomain.com
Address: 172.16.0.10
名前: foobar.net
Address: ::
しかしこのようにnslookupでは意図した通りの結果になったにも関わらず、Webブラウザでページ読み込みをした際にブロックされるケースとそうでないケースがランダムで発生します。
DNSクエリの投げ方によってDNSMasqの挙動が変わる問題
ソースが読めないので困った時はパケットキャプチャ頼みです。対象のドメインに関する以下の通信が引っかかりました。
No. Time Source Destination Protocol Length Info
453 5.133549 172.16.0.186 172.16.0.10 DNS 70 Standard query 0x48cf A foobar.net
454 5.133707 172.16.0.186 172.16.0.10 DNS 70 Standard query 0x9087 AAAA foobar.net
506 5.159279 172.16.0.186 172.16.0.10 DNS 70 Standard query 0x9087 AAAA foobar.net
507 5.159300 172.16.0.186 172.16.0.10 DNS 70 Standard query 0x48cf A foobar.net
516 5.169019 172.16.0.10 172.16.0.186 DNS 86 Standard query response 0x48cf A foobar.net A 160.16.99.189
525 5.169658 172.16.0.186 172.16.0.10 DNS 70 Standard query 0xd3bd A foobar.net
527 5.169999 172.16.0.10 172.16.0.186 DNS 86 Standard query response 0xd3bd A foobar.net A 160.16.99.189
528 5.170262 172.16.0.186 172.16.0.10 DNS 70 Standard query 0x921d AAAA foobar.net
529 5.170496 172.16.0.10 172.16.0.186 DNS 70 Standard query response 0x921d AAAA foobar.net
よく見るとNo.516でAレコードが返ってくるまでに複数回のクエリが投げられているのが分かります。
この結果を見てから閃いたのですが、検証に使ったページはブロック対象のドメイン(今回はfoobar.net)からホスティングされている埋め込み広告を複数件使用していました。その為、ページを読み込んだ際に同一ドメインのDNSクエリが複数回送信されたのだと思われます。
しかしこうした挙動はリクエストを受け取るDNSMasq側との食い合わせの悪さからまたも意図しない挙動を誘発します。
When host retries 5 secs later, dnsmasq will find out previous unanswered struct forward record and try next in the upstream server list.
dnsmasq has a hardcoded timeout for struct forward record. But when this timeout happens, it just free up memory occupied and won't retry forwarding the query to another upstream server.
My conclusion is that there is no such setting in dnsmasq to "change the timeout of this switch in dnsmasq"
DNSMasqからすると未応答のDNSクエリがあるにもかかわらず間髪入れずにクライアントから同じドメインに対する問い合わせが来たため、仕様により代替のnameserver(今回はRT DNS)に問い合わせを投げます。最初に問い合わされたDNSクエリが先に返されれば何も問題ないのでしょうが、恐らくAdGuard HomeがDNSBL処理の為にいくらか応答が遅く、後だしのDNSクエリ応答に速度負けしているのだと思われます。
対策
とりあえずDNSMasqとAdGuard Homeの位置を入れ替えて対処しました。自分のネットワーク設計的にあまり好ましくありませんが背に腹は代えられません。
あとDNSMasqの向き先をRT DNSにするとインターネットのDNSレコードが引けなくなる問題が発生しています。原因はまだ分かってませんが※、ひとまずISPが提供しているDNSを直接指定する事で回避しています。
※ルーターに"[DNS] Received illegal request"というログが出力されてたのでEDNS非対応が原因かも?でもAdGuard Homeで有効化してないんですよね…
愚痴
WindowsクライアントのDNSスタブリゾルバキャッシュやブラウザのコンテンツキャッシュ、DNSMasqのキャッシュなど問題の切り分けをするのに厄介な要素が多くて疲れる…
今回のケースとは関係ありませんでしたが、WebブラウザのDoH設定がデフォルトで有効化されると切り分けがさらに難しくなりそうです。
Discussion