【DNS入門 第4回】DNSメッセージとトランスポート ― UDP/TCP、EDNS、応答コードを読み解く

DNS入門 第4回 DNSメッセージとトランスポート DNS サーバー

「DNSって、UDPの53番だけ開けておけばいいんですよね?」

実はこれ、よくある誤解です。そして「普段は問題ないのに、一部のドメインだけ引けない」「DNSSECを有効にしたら、ときどき名前解決に失敗する」といった、原因の分かりにくい障害の入口にもなっています。

第3回までは、キャッシュDNSがどう答えを探すかという「流れ」を見てきました。第4回では視点を変えて、DNSのメッセージの中身と、それがUDP・TCPでどう運ばれるかを見ていきます。digの出力が読めるようになるための土台でもあります。

▶ この回の内容は、YouTube の動画(約15分)でも見られます(記事の最後にも埋め込んであります)。


DNSメッセージの構造

DNSの問い合わせと応答は、どちらも同じ形式のDNSメッセージでやり取りされます。

DNSメッセージのヘッダー・質問・回答・権威・追加の5つの部分と、digの表示のHEADER・QUESTION SECTION・ANSWER SECTION・AUTHORITY SECTION・ADDITIONAL SECTIONの対応を示す図
部分入るもの
ヘッダー(12バイト)問い合わせと応答を対応づけるID、フラグ、応答コード(RCODE)、各セクションの件数
質問問い合わせた名前・タイプ・クラス。応答にもそのまま写される
回答答えのレコード
権威委任先のNSレコード、「存在しない」ときのSOAレコード
追加NSのホスト名に対応するグルーのA/AAAAレコード、EDNSのOPT疑似レコード

digの出力の「QUESTION SECTION」「ANSWER SECTION」などは、この5つの部分をそのまま表示したものです(第8回)。問い合わせでは回答・権威は空で、追加にOPTだけが入ることが多いです。問い合わせも応答も同じ形で、違うのはフラグと各部分の中身です。

ポイント
・メッセージはヘッダー・質問・回答・権威・追加の5部分
・権威には委任先のNSや「無い」ときのSOAが入る
・追加にはグルーやEDNSのOPT疑似レコードが入る


ヘッダーのフラグ

ヘッダーのフラグは、障害の切り分けでとてもよく見る情報です。

フラグ名前意味見るべき場面
QRQuery/Response0=問い合わせ、1=応答応答には必ず付く
AAAuthoritative Answer権威DNSサーバー自身の答えキャッシュからの答えか、権威の答えか
TCTrunCation応答が入りきらず切り詰めたTCPでやり直す合図
RDRecursion Desired再帰問い合わせを希望するスタブリゾルバーは通常1
RARecursion Available再帰問い合わせを受け付ける権威専用サーバーは0
ADAuthentic DataDNSSECの検証に成功した検証するリゾルバーの応答(第9回)
CDChecking DisabledDNSSECの検証をしないでほしい検証失敗の切り分け(dig +cd、Resolve-DnsName -DnssecCd)

実際のdigの出力で見ると、こうなります。

$ dig www.example.jp                          # キャッシュDNSに聞く
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

$ dig @ns1.example.jp www.example.jp +norec   # 権威DNSに直接聞く
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

キャッシュDNSの答えにはaaが付かず、権威DNSに直接聞くとaaが付きます。「権威の答えと、キャッシュの答えが違う」という切り分けで、最初に見る情報です。flags行の後ろの数字は、各セクションの件数です。

なお、PowerShellのResolve-DnsNameはフラグを表示しないため、フラグを見るときはLinuxなどのdigを使います。


UDPとTCP

DNSは53番ポートを使い、UDPとTCPの両方で通信します。

リゾルバーがUDPで問い合わせ、TC=1の応答を受けたら同じ問い合わせをTCPでやり直す流れと、クライアント・キャッシュDNS・権威DNSの間の両方のファイアウォールでUDP 53とTCP 53を許可する必要があることを示す図

通常の問い合わせはUDPで行います。1往復で済むため、軽く速いのが利点です。

応答がUDPに入りきらないとき、サーバーはヘッダーのTCビットを立てた応答を返し、クライアントは同じ問い合わせをTCPでやり直します。これをTCPフォールバックと呼びます。ゾーン転送(第7回)もTCPです。

TCPは「たまに使う予備」ではない

ここが冒頭の誤解のポイントです。

  • RFC 7766(2016年):すべての汎用的な実装に、UDPとTCPの両方への対応を求める
  • RFC 9210(2022年):運用者に、TCPを遮断しないことを求める

ファイアウォールでは、クライアントとキャッシュDNSの間、キャッシュDNSと権威DNSの間の両方で、UDP/53とTCP/53を許可します。片方でもTCP/53を閉じると、大きな応答だけが失敗するという、原因の見えにくい障害になります。

ポイント
・通常はUDP、入りきらなければTCビットを見てTCPでやり直す
・TCP対応は必須(RFC 7766)、遮断しない(RFC 9210)
・両区間でUDP/53とTCP/53を許可する


TCPフォールバックの誕生経緯と「512の壁」

なぜこんな仕組みになっているのか、歴史を少しさかのぼってみます。

1987年のRFC 1034/1035で512バイト上限、1989年のRFC 1123でTCPフォールバック、EDNS(0)、2016年のRFC 7766、2020年のFlag Day、2022年のRFC 9210までの変遷を示す年表
  • 1983年(RFC 883)、1987年(RFC 1034/1035):DNSはUDPとTCPのどちらかで通信し、UDPメッセージは最大512バイトまでと定めました。512の根拠はRFC 791の「すべてのホストが受け取れるデータグラム576バイト(データ512+ヘッダー64)」で、必ず1パケットで届くサイズを上限にしたのです。
  • 1989年(RFC 1123):問い合わせは必ず最初にUDPで送り、応答が切り詰められていたらTCPでやり直す、と定めました。小さなデータを頻繁にやり取りするDNSで、処理の負担を抑えるためです。これがTCPフォールバックです。
  • 2010年(RFC 5966):TCP対応を必須にし、「最初にUDP」を「UDPを先に送るべき(SHOULD)」に緩和しました。
  • 2016年(RFC 7766、5966を置き換え):TCPから始めてもよい(MAY)と明記しました。2022年のRFC 9210で運用上の要件が追加されています。

「512の壁」

UDPで試して切り詰められてからTCPで出し直すため、1回の名前解決で往復が倍になります。DNSSECやIPv6の普及で応答が大きくなり、この非効率が本格的に問題になりました。これが「512の壁」です。そこで登場したのが、次に説明するEDNS(0)です。

実際に体験してみることもできます。+noednsで512バイトの制限に戻すと、大きなTXTやMXの応答でTCPフォールバックが起きます。

$ dig +noedns example.jp TXT
;; Truncated, retrying in TCP mode.
(中略)
;; SERVER: 192.0.2.53#53(192.0.2.53) (TCP)

PowerShellではResolve-DnsName -TcpOnlyで最初からTCPで問い合わせられます。パケットキャプチャでも、TCビットの立ったUDP応答の直後にTCPの接続が始まる様子を確認できます。


EDNS(0)とOPT疑似レコード

EDNS(0)(RFC 6891)は、追加セクションのOPT疑似レコードでDNSを拡張する仕組みです。

クライアントが「1232バイトまでUDPで受け取れる」と申告し、サーバーが512バイト超でもUDPで返すことと、OPT疑似レコードのCLASS・TTL・RDATAの各フィールドの使い方を示す図

「このサイズまでUDPで受け取れる」と申告すると、サーバーは512バイトを超えてもUDPで返します。面白いのは、既存のレコードの枠を借りて、拡張の情報を運んでいる点です。

OPTのフィールド通常のレコードでの意味OPTでの使い方
NAME名前ルート(空)
TYPEタイプ41(OPT)
CLASSクラス(IN)受け取れるUDPの大きさ(例:1232バイト)
TTLキャッシュする時間拡張RCODE・EDNSのバージョン(0)・DOビット(DNSSECの要求)
RDATA値オプション(COOKIE、EDE、NSID、Client Subnet、Padding 等)

digでは、次のように表示されます。

$ dig www.example.jp
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232

2019年2月1日のDNS Flag Day 2019で、主要なリゾルバーはEDNSに正しく応答しないサーバーへの回避策を取りやめました。つまり、EDNSをまともに扱えない古い権威DNSサーバーは、この時点で名前解決に失敗するようになっています。なお、Resolve-DnsNameではOPTの中身は表示されません。


フラグメントとDNS Flag Day 2020

EDNS(0)で大きなUDP応答を送れるようになると、今度はIPフラグメントが問題になりました。

4096バイトの大きなUDP応答がIPで3つに分割され、UDPヘッダーのない2つ目以降の断片がFW/NATで落とされることと、Flag Day 2020の推奨である1232バイトの計算を示す図

経路のMTU(イーサネットで通常1500バイト、VPNなどではもっと小さい)を超えるUDPの応答は、IPのレベルで分割されます。2つ目以降の断片にはUDPのヘッダーが無いため、ファイアウォールやNATで落とされやすく、断片の偽造によるキャッシュポイズニングの手口も知られています。

そこで2020年10月1日のDNS Flag Day 2020では、EDNSのバッファーサイズを1232バイトにすることが推奨されました。

1232の計算
1280(IPv6の最小MTU)− 40(IPv6ヘッダー)− 8(UDPヘッダー)= 1232

この大きさなら、ほぼすべての経路で分割されずに届きます。入りきらない応答はTCPで運ぶため、TCP/53の許可が前提です。BINDやUnbound(1.12.0以降)は既定値を1232にしています。

ポイント
・大きなUDPはIPで分割され、断片が落とされやすい
・推奨バッファーは1232バイト(1280−48、Flag Day 2020)
・入りきらない応答はTCPで運ぶ。TCP/53の許可が前提


ファイアウォールと経路の機器で起きる問題

ここまでの話を、現場で見かける症状に落とし込むと次のようになります。

症状よくある原因対策
大きな応答(DNSSEC、長いTXT、多数のMX)だけ失敗するTCP/53を閉じているUDP/53とTCP/53の両方を許可する
512バイトを超えるUDP応答だけ届かない古い機器のDNS検査の上限が512バイトのまま(例:Cisco ASAの message-length maximum 512)EDNSの申告値に合わせる設定へ変更(ASAの message-length maximum client auto 等)
特定のドメインだけ、ときどきタイムアウトする分割されたUDPの断片を落としているバッファーを1232にし、TCPを許可する
FORMERRが返る、EDNSが通らないルーター等のDNSプロキシ・ALGがOPTを理解しない、削除するDNSプロキシ・ALGを無効にする、機器を更新する
戻りの応答が落ちる戻りの通信を送信元ポート53固定で許可している送信元ポートはランダム(RFC 5452)。ステートフルに許可する

ファイアウォールでUDPの応答が落ちると、クライアントには応答が返らずタイムアウトになります。REFUSEDなどの応答コードが返るのは、DNSサーバー自身が応答したときだけです。

経路の対応状況は、dig +bufsize=1232と+bufsize=4096の比較(Windows標準のコマンドには無い機能のため、Linuxなどで行う)や、ISCのEDNS Compliance Tester(ednscomp.isc.org)で確かめられます。


DNS Cookies

UDPは接続の手順が無いため、送信元のIPアドレスを偽った問い合わせや、偽の応答を送り込まれやすいという弱点があります。DNS Cookies(RFC 7873、2016年)は、EDNSのオプションでこの弱点を軽く補う仕組みです。

クライアントがクライアントCookie(C)を付けて問い合わせ、サーバーがサーバーCookie(S)を付けて返し、以後はCとSの組で互いが本物の相手かを確かめ、送信元を偽装した攻撃者は本物と扱われないことを示す図
  1. クライアントは問い合わせにクライアントCookie(8バイト)を付ける
  2. サーバーは応答にサーバーCookieを付けて返す
  3. 次からは両方を付けて送る

これにより、サーバーは「以前に本当にこのアドレスとやり取りした相手か」を、クライアントは「応答が自分の問い合わせへの本物の応答か」を確かめられます。送信元を偽った反射攻撃や、経路外からの偽の応答を減らせます。

RFC 9018(2021年)はサーバーCookieの作り方を統一し、複数の製品を混ぜたエニーキャスト(第7回)でも使えるようにしました。digでは; COOKIE:の行で確認できます。


応答コード(RCODE)

RCODEは、障害対応で最初に見る情報です。

値名前意味よくある原因
0NOERROR正常に処理した答えが0件でもNOERROR(下のNODATA)
1FORMERR問い合わせの形式を解釈できないEDNSのOPTを理解できない古いサーバー・DNSプロキシ
2SERVFAILサーバー側で処理に失敗した権威DNSに到達できない、DNSSECの検証失敗
3NXDOMAIN名前そのものが存在しない名前の誤り、未登録、問い合わせ先の誤り
4NOTIMP要求された機能を実装していない対応していない種類の問い合わせ
5REFUSEDポリシーにより拒否したACLで拒否、権威DNSに担当外の名前を聞いた
(なし)NODATARCODEではなく状態の呼び名名前はあるが、そのタイプが無い(NOERROR・回答0件)

特にNODATAはRCODEではない点に注意してください。RCODEはNOERRORで、回答が0件、権威セクションにSOAが入る状態を指します。「NOERRORなのに答えがない」ときは、名前はあるけれどそのタイプのレコードが無い、ということです。

なお、6〜10(YXDOMAIN等)は動的更新(第7回)で使い、16以上はEDNSで拡張されたコードです。応答の型ごとのdigの見え方は第8回で扱います。


拡張エラー(EDE)

SERVFAILは「サーバー側で何か失敗した」としか言っていないので、それだけでは原因が分かりません。そこで登場したのがEDE(Extended DNS Errors、RFC 8914、2020年)です。SERVFAILなどの理由を、EDNSのオプションで返します。

INFO-CODE名前意味
3Stale Answer期限切れのキャッシュで答えた(serve-stale)
6DNSSEC BogusDNSSECの検証に失敗した
7Signature Expired署名の有効期限が切れている
9DNSKEY MissingDSに対応するDNSKEYが無い
15/17Blocked/Filteredリゾルバーのポリシーで遮断した
18Prohibited問い合わせ元が許可されていない
22No Reachable Authority権威DNSサーバーに到達できない
23Network Error権威DNSとの通信でエラーが起きた

検証用のドメインdnssec-failed.orgを公開リゾルバーに問い合わせると、次のようにEDEが返ってきます(抜粋)。

;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 34492
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
; EDE: 9 (DNSKEY Missing): (no SEP matching the DS found for dnssec-failed.org.)

これなら、原因が「DNSSECの失敗」なのか「権威に届かない」のかを、応答だけで見分けられます。SERVFAILを見たら、まずEDEを確認する習慣をつけましょう。


考えてみよう

  1. あなたの環境のファイアウォールで、DNSのTCP/53は許可されていますか。クライアント⇔キャッシュDNS、キャッシュDNS⇔権威DNSのそれぞれで確認してみましょう。
  2. 「一部のドメインだけ、ときどき名前解決に時間がかかる」と報告を受けました。この記事の内容から、どんな原因が考えられますか。
  3. 社内のキャッシュDNSがSERVFAILを返したとき、原因の手がかりを増やすには、どの情報(フラグ、EDE、別のリゾルバーの結果)を見ればよいでしょうか。

ヒント:dig +bufsize=512、+tcp、+norec、+cdのようにオプションを変えて結果を比べると、どの段階で落ちているかが見えてきます。PowerShellでは-TcpOnly、-NoRecursion、-DnssecCdが同じ役割です。


まとめ

  • DNSメッセージはヘッダー・質問・回答・権威・追加の5部分。digの表示はそのまま対応している
  • aaは権威の答え、TCはTCPでやり直す合図、adはDNSSEC検証成功
  • DNSはTCPも必須。両区間でUDP/53とTCP/53を許可する
  • EDNSのバッファーは1232バイト。超える分はTCPで運ぶ
  • NODATAはRCODEではない。SERVFAILを見たらEDEで理由を確かめる

次回からは、メッセージの中身であるリソースレコードの種類と書き方を、前編・後編の2回に分けて見ていきます。


動画で見る

この回の内容を、YouTube の動画(約15分)でも解説しています。

連載の全回は、YouTube の再生リスト「DNS入門」にまとめています。

連載「DNS入門」全12回

全回の目次と、読む順番・目的別の読み方は、DNS入門の入口のページにまとめています。

  1. DNSとは?名前解決の全体像と3つの登場人物
  2. ドメイン名の階層と委任
  3. 名前解決の流れ
  4. DNSメッセージとトランスポート(この記事)
  5. リソースレコード 前編
  6. リソースレコード 後編
  7. 権威DNSサーバーの運用
  8. コマンドで調べる
  9. DNSのセキュリティ
  10. クラウド・社内環境のDNS
  11. DNSトラブルシュート
  12. DNS設計演習
seichan

プロフィール詳細は「プロフィール」をご覧ください。
AWS Certified Solutions Architect – Professional (https://www.credly.com/badges/fd8feb88-6f1f-42ff-a94b-445bb6661cb8/public_url)
Nutanix Certified Professional - Multicloud Infrastructure 6 (https://www.credly.com/badges/5c081bef-a425-42e4-809c-85138e676630/public_url)
Palo Alto Networks System Engineer (PSE) - Hardware Firewall Professional (https://www.credly.com/badges/69dc3880-4a51-47eb-8843-47ba6ea7d013/public_url)

seichanをフォローする
DNS サーバー
シェアする
seichanをフォローする

コメント

タイトルとURLをコピーしました