dnssec-enable no;でのBINDの挙動についてメモを兼ねて。
DNSSECではRRSIGリソースレコードに
・署名対象の名前
・署名対象のリソースタイプ・クラス
・署名対象のオリジナルの(コンテンツサーバでの)TTL
・RRSIG自体の有効期間、鍵のハッシュなど
を載せ、電子署名で保護している。
RRレコードと、対応するRRSIGレコードを受け取る事ができ、対応する公開鍵を信頼しているならば、
「現在時刻と照らしあわせてRRSIGレコードが有効期間内である事」を確認し、さらに、「Aレコードの元々のTTLがRRSIGの主張するオリジナルTTLだったと仮定して、現在時刻と照らしあわせて署名に矛盾がない」ことを確認することで、改竄されていないことが証明できる。
(公開鍵はルートから権威の木構造で証明するが署名原理は同じなので割愛。NSECも割愛)
キャッシュサーバから受け取るRRSIGレコードのTTLは、時間経過にともなって減少しているが、RRSIGには元のTTLが明記されているので、無矛盾を確認できる、したがって、キャッシュサーバが全てのレコードをフィルタすることなく(TTL以外変化させずに)透過的に返答してくれるなら、経路上のキャッシュサーバがDNSSEC検証していなくても、検証は可能になる。
例えば、あるQNAMEでQTYPE=Aで問い合わせしてAレコードしか得られなかった場合、QTYPE=RRSIGレコードで問い合わせすればRRSIGレコードが全て得られるはずなので、その中から必要なQTYPE(ここではA)に対応するRRSIGレコードで検証を行えばいい。
BINDの実装では、この手続を行うクライアントで問題が生じる。
前提として、DNSプロトコルのEDNS0という拡張に更にオプションで、DOビットというフラグがあり、DNSSEC対応しているクライアント(リゾルバ側)はこれをセットしてクエリすることで、サーバー側はQTYPEに対応するRRSIGレコードを追加で返すことになっている。
BINDは再帰問い合わせを許容する場合、オプションで
dnssec-enable no;
と設定すると、DOビットをセットしたクエリに対しても、RRSIGを付加しない。
一方、dnssec-enable noにもかかわらず、応答のために外部に問い合わせる際にはDOビットをセットして問合せ、RRSIGなどのDNSSEC用レコードが付加されていればキャッシュする。
このサーバーに対して、QTYPE=RRSIGとしてDNSSEC対応クライアントが問い合わせると、BINDは一部のRRSIGだけ返してしまうようだ。
たとえば同じQNAMEにAレコードとTXTレコードがあり、TXTレコード用のRRSIGレコードだけがキャッシュに有った場合、Aレコードを検証したいクライアントはRRSIGでクエリしても必要なRRSIGレコードが得られない。
通常DNSキャッシュサーバからのレスポンスでは、NAME・TYPEが一致するレコードは全てTTLが一致し、キャッシュ上のデータは全か無かしかない。例えば本来3つあるNSレコードのうち、1つしかキャッシュに残っていない、ということは少なくともDNSSECの署名においては想定されていない。(権威サーバの設定でTTLがそもそも違う場合、同時に対応するRRSIG署名も消えるはずなので良いが、同じTTLのレコードが一つだけ消えたりした場合検証失敗してしまう。これはDNSポイゾニングを想定した動作)
現在のBIND9.9.2の実装では、付随的に受け取った部分的なRRSIGレコードをキャッシュしてしまうため、QTYPE=RRSIGなクエリに対して、部分的なRRSIGレコードを返してしまう。バリデータはTTLが異なる他のTYPE用のRRSIG RRから、必要なTYPE用のRRSIG RRがあるかもしれないことを検出できるが、BINDのキャッシュを外から制御できるわけではないので他の経路を探す必要がある。
BINDのこの(恐らく不正な)挙動がなければ、DNSSEC非対応キャッシュサーバを経由してもDNSSECは機能するはずであり、『DNSSECはキャッシュサーバが対応しないと利用できない』というのはBINDの実装を前提とした問題とおもわれる。
指摘頂いて最初、RRSIGは部分的な応答が許される特殊なレコードなのでRRSIGクエリは禁止されているのか思っていたが、RRSIG自体は普通のRRであって、digなども+nodnssecであってもRRSIGクエリタイプを認識する。DNSSEC以前の古いキャッシュサーバを透過するためにもそう有るべきだ。
BIND側はdnssec-enable no;な場合再帰問い合わせでDOフラグをがセットするべきではないし、DOフラグによって付加的に得られた、部分的なRRSIGレコードはキャッシュするべきでない。
少なくとも、QTYPE=RRSIGのクエリに対しては、改めて問い合わせするよう実装しなければならないはず。
このへんはRFC読みなおすことにする。
追記
例えばCDビットセットかつDOビットがセットされtruncatedでないNOERRORなレスポンスにRRSIG RRが付いていなければ、RRSIGが存在しないと分かる。一方、問い合わせ先がDNSSEC非対応だった場合、DOビットは単に無視されるのでNOERRORでRRが見つかり、RRSIGは返されないが、RRSIGが存在しないのかは不明になってしまう。この場合、バリデータ側はRRSIGが無かったとネガティブキャッシュし検証失敗と判断するべきか、あるいは他の方法を試して良いのか?
個人的には否定応答ではないのだから、キャッシュしないほうが正しいと思う。少なくとも、明示的にネガティブキャッシュするべきとは書いていないし、「バリデータは同じレスポンスメッセージに含まれるRRSIGしか検証に利用してはいけない」といった類の要件も見つけられなかった。とすると、賢いバリデータはDNSSEC非対応な経路を検出してDOフラグ拡張を使わず明示的にクエリしたり、他のキャッシュサーバを試したりするようなフォールバック動作をするかもしれない。
RRSIGはそのRRSIGレコード自体の有効性をも検証できる(でなければ意味が無い)ので、最初のQTYPE≠RRSIGなクエリでえた目的とするTYPEと、他の経路で得られたRRSIGを組み合わせて検証することが出来る。
2012年12月18日
この記事へのコメント
コメントを書く
この記事へのトラックバック


