Mike Zhang

DNS DevOps CISSP CISA Security+ 摄影 程序员 北京

RFC2308 DNS查询的否定缓存

18 Nov 2018 » rfc, dns

摘要

RFC1034中介绍了如何去缓存否定响应,然而存在一个根本的缺陷,不允许将缓存的响应发送给其他的解析器使用,从而大大的降低了缓存的效率。本篇文档根据经验来解决这个问题,并替换RFC1034的4.3.4章节。

否定缓存是一个DNS规范中的可选部分,处理不存在的资源记录或者域名的缓存内容,可以用来缩减DNS否定查询的响应时间,同时可以减少解析器和权威域名服务器之间的网络流量。如果所有的解析器都实施了否定缓存,则可以减少大量的互联网上DNS查询,因此基于这些前提,否定缓存不应该是被认为是可选的部分。

1. 术语

否定缓存: 用来存储某些记录不存在的信息。我们可以存储一个记录对应于某一个值,同时也可以存储一个记录不存在。当一个记录不存在的时候,查询不能给出响应的情况称之为否定缓存。

QNAME: 查询字段的名称, 或者解析为一个CNAME记录或者一个CNAME记录链中最后的CNAME记录对应的数据字段,且最后的这个CNAME记录不会解析为另外一个CNAME。实施应该注意的是CNAME记录在响应中的顺序,第一个具有查询记录的标签,而后面的每一个都包含前一个记录的标签(当有多个CNAME纪录时),这种方式也减轻了接收者的工作。其他的记录(比如SIG签名记录)可以分散在CNAME记录中。

NXDOMAIN: DNS数据包中RCODE=NAME ERROR错误的另一种表达方式。

NODATA: 一个伪RCODE代表了域名针对给定的类别下是有效的,但是针对给定的类型并不存在对应的记录,可以从响应中推断出是否是一个NODATA响应(一般返回记录为NOERROR,但是没有任何返回记录)

FORWARDER: 一个名称服务器,被用来去解析响应而不是直接迭代查询权威服务器。一个Forwarder一般要么具有很好的互联网访问条件,妖魔维护一个比较大的缓存池提供给域名服务器用来共享数据。如何定义或者识别是否是Forwarder不在本文档中介绍,但是如果被作为Forwarder则查询需要设置其RD比特位。

本文档默认读者已经阅读和理解了RFC1034,RFC1035以及RFC2065的相关内容

2. 否定响应

最常见的否定响应描述了一个特殊的资源记录是不存在的,第一部分处理的就是这种场景,其他的否定响应可以被用来描述域名服务器的失效,可参考第7部分。

2.1 域名错误NameError

域名错误NameError(NXDomain),可在查询的响应RCODE中被设置,代表了查询域名错误,指的是QNAME对应的域名记录不存在。响应字段可能具有SIG或者CNAME记录,权威字段可能包含有SOA记录以及SIG记录。因此不管是否响应NS或者SOA记录都可以通过RCODE来判断是否是一个NXDOMAIN的错误,还是一个引用类型。

~/mikezhang.cc(master ✗) dig not-exist-domain.com
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096

;; QUESTION SECTION:
;not-exist-domain.com.		IN	A

;; AUTHORITY SECTION:
com.			900	IN	SOA	a.gtld-servers.net. nstld.verisign-grs.com. 1542764754 1800 900 604800 86400

引用类型的示例(注意和NODATA类型的区别,见后文), 这里A类型是不存在的,但是存在一个CNAME记录类型,但是不知道这个CNAME记录是否存在的信息,除非使用返回的NS1.XX服务器去进一步查询对应的信息。

Header:
 	RDCODE=NOERROR
Query:
 	AN.EXAMPLE. A
Answer:
 	AN.EXAMPLE. CNAME TRIPPLE.XX.
 Authority:
 	XX. NS NS1.XX.
Additional:
 	NS1.XX. A 127.0.0.2

NXDOMAIN根据权威Authority字段的内容可以分为以下四种类型,查询某一个具体的域名,且A类型不存在,存在一个CNAME记录,但是CNAME记录不存在的情况。

 NXDOMAIN RESPONSE: TYPE 1.   
 Header:
     RDCODE=NXDOMAIN
 Query:
     AN.EXAMPLE. A
 Answer:
     AN.EXAMPLE. CNAME TRIPPLE.XX.
 Authority:
     XX. SOA NS1.XX. HOSTMASTER.NS1.XX. ....
     XX. NS NS1.XX.
     XX. NS NS2.XX.
 Additional:
     NS1.XX. A 127.0.0.2
     NS2.XX. A 127.0.0.3
     
 NXDOMAIN RESPONSE: TYPE 2.   
 Header:
      RDCODE=NXDOMAIN
 Query:
      AN.EXAMPLE. A
 Answer:
   	  AN.EXAMPLE. CNAME TRIPPLE.XX.
 Authority:
      XX. SOA NS1.XX. HOSTMASTER.NS1.XX. ....
 Additional:
      <empty>
      

NXDOMAIN RESPONSE: TYPE 3
Header:
   RDCODE=NXDOMAIN
Query:
   AN.EXAMPLE. A
Answer:
   AN.EXAMPLE. CNAME TRIPPLE.XX.
Authority:
   <empty>
Additional:
   <empty>

NXDOMAIN RESPONSE: TYPE 4
Header:
   RDCODE=NXDOMAIN
Query:
   AN.EXAMPLE. A
Answer:
   AN.EXAMPLE. CNAME TRIPPLE.XX.
Authority:
   XX. NS NS1.XX.
   XX. NS NS2.XX.
Additional:
   NS1.XX. A 127.0.0.2
   NS2.XX. A 127.0.0.3

当没有CNAME记录出现的时候,NXDOMAIN指的是查询域名资源记录不存在

2.1.1 针对NameError的特殊处理

本章节主要处理实施针对NXDOMAIN的否定响应的一些问题。当前大量的解析器不能正确的检查和处理所有形式的NXDOMAIN。 一些解析器将类型1与引用响应相混淆。为了缓解这一问题,推荐权威服务器仅仅发送类型2的NXDOMAIN响应:包含SOA记录,且不包含任何NS记录。如果一个非权威的域名服务器发送了一个类型1的NXDOMAIN响应,将导致一个不必要的权威服务器的查询过程。尽管这是一个不期望的行为,但当这个服务器作为一个Forwarder的时候这并非一个致命的异常问题。但是如果解析器作为一个Forwarder将这样的请求传递到另外的解析器,就必须去禁止发送类型1的NXDOMAIN,而是使用类型2.

一些解析器在权威响应AA比特位没有设置的情况下会不停的进行操作,循环查询直到超出最大的查询次数后返回一个SERVFAIL错误。当你的域名服务器被这样的解析器作为一个Forwarder的时候就会出现问题,这样的情况下,NXDOMAIN响应其中的AA比特位必须被强制设置,不过如果一直设置AA状态也不会有问题,这也作为BIND自从4.9.3后的默认行为。

2.2 No Data

NODATA指代的是一个RCODE为NOERROR但是没有相关的查询对应记录的响应。权威字段将包含SOA记录,可能不会存在相关NS记录。由于响应的RCODE并不包含这样的类别,因此NODATA响应必须从响应中通过算法来判断,在某些情况下,检测NODATA类型后可能需要发送另一个查询请求。

权威字段中除了NS或者SOA记录外还可能包含NXT或者SIG资源记录,响应字段中可能存在CNAME和签名记录。

可以通过区分SOA记录是否在权威字段中出现,或者NS记录在权威字段中的缺失来判断是否是一个NODATA类型或者一个引用类型。根据权威字段的内容,可以将NODATA类型分为三类,

   NODATA RESPONSE: TYPE 1.
   Header:
       RDCODE=NOERROR
   Query:
       ANOTHER.EXAMPLE. A
   Answer:
       <empty>
   Authority:
       EXAMPLE. SOA NS1.XX. HOSTMASTER.NS1.XX. ....
       EXAMPLE. NS NS1.XX.
       EXAMPLE. NS NS2.XX.
   Additional:
       NS1.XX. A 127.0.0.2
       NS2.XX. A 127.0.0.3

   NO DATA RESPONSE: TYPE 2.
   Header:
       RDCODE=NOERROR
   Query:
       ANOTHER.EXAMPLE. A
   Answer:
       <empty>
   Authority:
       EXAMPLE. SOA NS1.XX. HOSTMASTER.NS1.XX. ....
   Additional:
       <empty>

   NO DATA RESPONSE: TYPE 3.
   Header:
       RDCODE=NOERROR
   Query:
       ANOTHER.EXAMPLE. A
   Answer:
       <empty>
   Authority:
       <empty>
   Additional:
       <empty>

这些例子,不像是NXDOMAIN例子,这些查询都没有CNAME记录,但是对于CNAME的情况NODATA应该是指的是最后的CNAME记录不包含对应的记录,如下面:

$ dig www.icann.org txt 
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.icann.org.			IN	TXT

;; ANSWER SECTION:
www.icann.org.		3580	IN	CNAME	www.vip.icann.org.

;; AUTHORITY SECTION:
vip.icann.org.		60	IN	SOA	gtm1.lax.icann.org. hostmaster.gtm1.lax.icann.org. 2018082708 10800 3600 604800 60
2.2.1 针对NODATA的特殊处理

当前大量的解析器不能正确的检查和处理所有形式的NODATA。 一些解析器将类型1与引用响应相混淆。为了缓解这一问题,推荐权威服务器仅仅发送类型2的NODATA响应:包含SOA记录,且不包含任何NS记录。对于非权威服务器发送类型1的NODATA响应将导致不必要的查询。如果一个服务器被其他的解析器作为FORWARDER使用,对于非权威的NODATA响应可能有必要去设置不去发送类型1的响应。

一些域名服务器当响应内容中包含CNAME的时候,可能忘记设置RCODE到NXDOMAIN。 如果明确需要NXDOMAIN或者NODATA响应,解析器必须重新使用QNAME域名进行查询。

3. 来自权威服务器的否定响应

当权威服务器回复一个NXDOMAIN或者NODATA的时候,需要明确的在权威字段中给出SOA记录,否定响应依赖于这个记录进行缓存,TTL被设置为SOA记录中的MINIMUM字段的值和SOA本身的TTL的最小值,用来描述一个解析器缓存多久的否定响应。与SOA记录相关联的签名记录的TTL应当根据SOA的TTL进行调整.

;; AUTHORITY SECTION:
icann.org.              1799    IN      SOA     sns.dns.icann.org. noc.dns.icann.org. 2018112101 10800 3600 1209600 3600
icann.org.              1799    IN      RRSIG   SOA 7 2 3600 20181211195540 20181121003055 2424 icann.org. LKzsfClQTyyxqApidb7iYJu04nTQBCwSCPM+45Jk05v2j2w4rPsHcEBR hGMYGW0tZxxjSr5gBcisDZDuTb4Cf13YtzuXma8fdLlwQ4HwBt7CWK6C P0boZcHpocmKSIxswgTCV1XyeksOhkjSdk7AQAUf/HWH7g9D2lW8s2SG GOs=
f4t82ork4m0jdlub2vjm6m2vocfobapk.icann.org. 3599 IN NSEC3 1

4. SOA中的Minimum字段

SOA中的Minimum字段具有三个不同的含义,资源记录的最小TTL, 对于没有TTL的资源记录的缺省值以及否定响应的缓存时间。

尽管第一个含义是作为最初定义使用,最小的TTL这个含义却从来没有在实践中使用过,因此已经被废弃了。

第二个含义作为缺省的TTL资源仅仅在主服务器上,对于通过区传输到其他的节点后每个记录明确的增加了TTL内容。当一个服务器不要求RR显式包含TTL时候,应该提供一个机制从中获取缺少的TTL值,而不是从SOA记录中获取Minumum字段的值,这种机制依赖于具体实现。

RFC1035第五部分中定义的区文件格式扩展包含了下面的指令:

 $TTL <TTL> [comment]

这个指令之后的资源记录,在记录中不包含对应TTL值的时候,将默认设置为这个TTL的值, 签名记录的TTL来自于记录本身的TTL时间(RFC2065 4.5部分)。

最后一条含义代表是否定缓存时间,这也是SOA minimum字段的最新的定义。

5. 缓存否定响应

否定响应和正常的响应一样具有生存时间TTL,由于没有像资源记录那样的具有TTL字段来设置缓存时间,TTL必须使用另外的方式进行设置。方式就是通过在权威记录中包含一个SOA记录,这个SOA记录中包含了一个SOA.Minumum字段来设置缓存时间。这个TTL和正常缓存的TTL使用方式相同,逐步减少到0,代表了缓存的否定响应不应被重新使用。

一个导致NXDOMAIN的否定响应,基于查询的QNAME和QCLASS来缓存内容,任何其他具有相同QNAME和QCLASS的查询都应该返回相同的内容。

如果一个否定响应不包含SOA记录则不应该被缓存,即便是设置较短的TTL,也没有办法阻止否定响应在服务器之间循环查询(客户端的行为)

尽管DNS是一个树形结构,但是错误配置也会导致循环查询的出现。比如两个服务器彼此设置为对方为Forwarder服务器。如果当前没有一个TTL来不断的倒计时否定缓存记录,服务器收到的下一个相同的否定缓存响应应当重新设置TTL。

对于解析器来讲,合理限制否定缓存的时间是必要的,因为协议支持的缓存时间高达68年之久!限制时间不应该比普通应答缓存的时间还要长,而且应该是可调节的。一般设置1-3小时左右是比较合理的设置,超过1天的否定缓存会被认为是存在问题。

6. 来自于缓存的否定响应

当一个服务器在响应查询的时候,如果遇到缓存中包含对应的否定响应,必须增加缓存的SOA记录到响应消息的权威字段,同时包含一个递减的TTL时间(代表将在缓存中存储多久), 这允许NXDOMAIN/NODATA的响应实现正确的超时。如下面的记录中,查询第二次时出现的已经开始倒计时的TTL时间。

> dig not-exist.com

;; QUESTION SECTION:
;not-exist.com.			IN	A

;; AUTHORITY SECTION:
not-exist.com.		3594	IN	SOA	ns1.cloudns.net. support.cloudns.net. 2014031805 7200 1800 1209600 3600

与来自缓存的其他应答一样,否定缓存也应当在响应中包含一个隐含的引导信息, 这使得解析器能够定位一个权威数据源。一个隐含的引导信息一般包含一个NS记录信息指向权威服务器,NXDOMAIN类型1和4响应以及NODATA的类型1都包含此类隐含的信息。

7. 其他否定响应

缓存其他的否定响应并没有在任何的RFC中提及到,也没有办法去描述这些响应的TTL信息,要注意实现的时候保证不会出现任何的循环查询的出现。

7.1 ServerFailuer服务器错误(可选)

服务器失效主要有两类,一种是服务器本身检测到区文件的错误配置,可能由于该服务器被列为某一个区的权威,但是本身并非该区的权威,或者属于权威服务器但由于部分原因无法获得区数据(区文件不存在或者包含错误信息,或其他的这个区的权威服务器没有响应或者不愿意提供相关应答)第二类则是服务器需要从其他地方获得应答,但是由于网络故障或者服务器不响应或者响应ServerFail等问题无法获得应答。

解析器可以缓存一个ServeFail的响应,但是必须不能超过5分钟的时间,且必须缓存的是针对包含<查询域名,类型和类以及对应服务器IP>的缓存信息。

7.2 死掉的或者不可达的服务器(可选)

死掉的或者不可达的服务器是那些无法响应任何查询的服务器,或者通过传输层通信显示该服务器已不存在或者不可达。一个服务器如果超过120秒无响应任何查询则可以被认为是死掉的或者不可达的服务器。

传输层通信可以是:

  • ICMP错误信息显示主机网络或者端口不可达
  • TCP重置
  • IP栈错误等

一个服务器可能缓存一个死掉的服务器的信息,但是缓存时间必须不能超过5分钟,且必须缓存的是针对包含<查询域名,类型和类以及对应服务器IP>的缓存信息,除非传输层已经显示该服务器不可用时候,可以设置所有到这个服务器的查询均应用该缓存信息。

8 针对RFC1034的修改

否定响应对于解析器来讲,不是可选部分。如果一个解析器缓存普通记录,则必须同时缓存否定响应记录

非权威否定缓存可以被缓存

否定响应中的权威字段SOA记录信息必须被缓存,域名错误NXDOMAIN缓存依赖<查询名称, 查询类别>,NODATA则依赖于<查询名称, 查询类型,查询类别>

一个缓存SOA记录必须增加到响应信息中。 同时不允许简单的提取一个正常的缓存SOA,增加到否定缓存记录中去,要严格区分正常的SOA记录查询的缓存,和一个否定缓存获得的SOA缓存,

增加一个$TTL指令到区文件中用于代表缓存时间字段。

9 否定缓存历史

该部分主要是介绍DNS中的否定缓存产生的相关历史,不包含任何关于否定缓存的技术规范内容。介绍了早期在选择否定缓存机制和具体实施的一些问题,同时提到了Bind在早期选择否定缓存时候的一些方式。作者提到,整体而言,当前关于否定缓存的设计是合理的,区管理员可以设置否定应答的时间,解析器也可以配置自己的限制时间. 经过十几年的运行,想不出来任何的改进措施了。

10 实例

下面的例子基于一个签名的区,查询域名为WWW.XX.EXAMPLE, 十分钟后重新进行查询,需要注意的是

  1. 10分钟间隔后 XX.EXAMPLE的NS记录已经过期。
  2. 签名的TTL没有在区文件中显式的设置,使用的是记录RRSet的TTL

        $TTL 86400
        $ORIGIN XX.EXAMPLE.
        @       IN      SOA     NS1.XX.EXAMPLE. HOSTMATER.XX.EXAMPLE. (
                                1997102000      ; serial
                                1800    ; refresh (30 mins)
                                900     ; retry (15 mins)
                                604800  ; expire (7 days)
                                1200 ) ; minimum (20 mins)
                IN      SIG     SOA ...
          1200  IN      NXT     NS1.XX.EXAMPLE. A NXT SIG SOA NS KEY
                IN      SIG     NXT ... XX.EXAMPLE. ...
           300  IN      NS      NS1.XX.EXAMPLE.
           300  IN      NS      NS2.XX.EXAMPLE.
                IN      SIG     NS ... XX.EXAMPLE. ...
                IN      KEY     0x4100 1 1 ...
                IN      SIG     KEY ... XX.EXAMPLE. ...
                IN      SIG     KEY ... EXAMPLE. ...
        NS1     IN      A       10.0.0.1
                IN      SIG     A ... XX.EXAMPLE. ...
          1200  IN      NXT     NS2.XX.EXAMPLE. A NXT SIG
                IN      SIG     NXT ...
        NS2     IN      A       10.0.0.2
                IN      SIG     A ... XX.EXAMPLE. ...
          1200  IN      NXT     XX.EXAMPLE. A NXT SIG
                IN      SIG     NXT ... XX.EXAMPLE. ...

查询的最初响应如下:

        Header:
            RDCODE=NXDOMAIN, AA=1, QR=1, TC=0
        Query:
            WWW.XX.EXAMPLE. IN A
        Answer:
            <empty>
        Authority:
            XX.EXAMPLE.      1200 IN SOA NS1.XX.EXAMPLE. ...
            XX.EXAMPLE.      1200 IN SIG SOA ... XX.EXAMPLE. ...
            NS2.XX.EXAMPLE.  1200 IN NXT XX.EXAMPLE. NXT A NXT SIG
            NS2.XX.EXAMPLE.  1200 IN SIG NXT ... XX.EXAMPLE. ...
            XX.EXAMPLE.     86400 IN NS  NS1.XX.EXAMPLE.
            XX.EXAMPLE.     86400 IN NS  NS2.XX.EXAMPLE.
            XX.EXAMPLE.     86400 IN SIG NS ... XX.EXAMPLE. ...
        Additional
            XX.EXAMPLE.     86400 IN KEY 0x4100 1 1 ...
            XX.EXAMPLE.     86400 IN SIG KEY ... EXAMPLE. ...
            NS1.XX.EXAMPLE. 86400 IN A   10.0.0.1
            NS1.XX.EXAMPLE. 86400 IN SIG A ... XX.EXAMPLE. ...
            NS2.XX.EXAMPLE. 86400 IN A   10.0.0.2
            NS3.XX.EXAMPLE. 86400 IN SIG A ... XX.EXAMPLE. ...

10分钟后的查询响应

        Header:
             RDCODE=NXDOMAIN, AA=0, QR=1, TC=0
         Query:
             WWW.XX.EXAMPLE. IN A
         Answer:
             <empty>
         Authority:
             XX.EXAMPLE.       600 IN SOA NS1.XX.EXAMPLE. ...
             XX.EXAMPLE.       600 IN SIG SOA ... XX.EXAMPLE. ...
             NS2.XX.EXAMPLE.   600 IN NXT XX.EXAMPLE. NXT A NXT SIG
             NS2.XX.EXAMPLE.   600 IN SIG NXT ... XX.EXAMPLE. ...
             EXAMPLE.        65799 IN NS  NS1.YY.EXAMPLE.
             EXAMPLE.        65799 IN NS  NS2.YY.EXAMPLE.
             EXAMPLE.        65799 IN SIG NS ... XX.EXAMPLE. ...
         Additional
             XX.EXAMPLE.     65800 IN KEY 0x4100 1 1 ...
             XX.EXAMPLE.     65800 IN SIG KEY ... EXAMPLE. ...
             NS1.YY.EXAMPLE. 65799 IN A   10.100.0.1
             NS1.YY.EXAMPLE. 65799 IN SIG A ... EXAMPLE. ...
             NS2.YY.EXAMPLE. 65799 IN A   10.100.0.2
             NS3.YY.EXAMPLE. 65799 IN SIG A ... EXAMPLE. ...
             EXAMPLE.        65799 IN KEY 0x4100 1 1 ...
             EXAMPLE.        65799 IN SIG KEY ... . ...

11 安全问题

本篇文档所介绍的内容在DNS的数据使用方面相对于以前没有引入任何额外的安全威胁。否定响应缓存可能因为设置一个非常长的TTL而引发一个拒绝服务攻击,如果没有否定缓存,这样的攻击可能比较困难。

通过响应一个错误的A记录也可以实现无法访问服务,这对于终端用户来说是相同的效果。不同的是如果是一个错误的A记录,可能会觉得:网络有出故障了,明天再试一下。而对于NXDOMAIN则可能觉得竟然查询不到了,他们关闭了服务,可能也就不会再重试了。

一个实际的例子是,SMTP邮件服务器当NXDOMAIN攻击的时候,邮件消息将被返回不会被发送(查询不到域名),而一个错误的A记录攻击则会将邮件排队等待,可能直到攻击暂停后才能通过。

对于这样的攻击如果想要成功,NXDOMAIN必须注入到父级域名服务器,或者一个比较繁忙的缓存服务器(缓存中毒)。一种方式是可以通过使用CNAME的方式导致父级域名服务器去查询一个攻击者的服务器。解析器如果希望阻止这样的攻击可以重新查询QNAME,而忽略任何的NS记录。

实施TTL安全检查可以降低这样的攻击情况的发生,因为攻击需要持续的频繁的注入信息到服务器中

DNSSec安全扩展部分提供了一种机制利用SIG记录去验证否定缓存是否是有效的。本文支持该机制的实施,即便是在一个不安全的服务器上也可以提升相关记录的安全传输。