目前,域名系统(DNS)备受关注,这项已有36年历史的协议正面临重大变革提议。互联网名称服务起源于ARPANET,自诞生以来从未出现过破坏向后兼容性的变更。但旨在改变DNS传输机制的新提议可能会打破这一局面。
本文将介绍两种用于保护DNS的新兴技术:基于TLS的DNS(DoT)和基于HTTPS的DNS(DoH),并说明如何使用NGINX Open Source和NGINX Plus实现这两种技术。
[编辑:本文是探讨NGINX JavaScript模块用例的系列文章之一。有关完整列表,请参阅《NGINX JavaScript模块用例》。
本文中的代码已更新为使用 js_import 指令,该指令取代了已弃用的 js_include指令(适用于NGINX Plus R23<.htmla>及更高版本)。有关详细信息,请参阅NGINX JavaScript模块的参考文档,其中的配置示例部分展示了NGINX配置文件和JavaScript文件的正确语法。]
DNS 的发展历程
DNS是为美国国防部高级研究计划局(ARPA)的早期互联网创建命名服务的第二次尝试。第一次尝试是John Postel于1979年发布的互联网名称服务器协议,即IEN-116。DNS采用分层设计,使主机名能够以分区形式进行分散管理,并由多个独立机构分别负责。首批DNS RFC于1983年发布(RFC 882和883)。尽管这些标准多年来经过多次扩展,但按照当时所定义标准编写的客户端至今仍可正常运行。
那么,为什么现在要更改这一协议?它显然一直按预期运行,足以证明其设计者当初的信心。他们确信自己的设计正确无误,甚至没有在DNS数据包中加入版本号;我想不出还有多少协议能做到这一点。DNS诞生于一个更为单纯的时代,当时大多数协议都使用明文,并且通常采用7位ASCII编码。但与20世纪80年代的ARPANET相比,如今的互联网要危险得多。现在,大多数协议都已采用传输层安全协议(TLS)进行加密和验证。DNS的批评者认为,早就应该为其增加一些安全保护措施。
因此,正如DNS是为互联网提供名称服务协议的第二次尝试一样,基于TLS的DNS(DoT)和基于HTTPS的DNS(DoH)正逐渐成为保护DNS协议安全的第二次尝试。第一次尝试是一种名为DNSSEC的扩展。虽然大多数顶级域(TLD)都使用DNSSEC,但其设计目的并非加密DNS数据包中承载的数据,而只是验证数据是否遭到篡改。DoT和DoH是将DNS封装在TLS隧道中的协议扩展;如果得到采用,它们将终结长达36年的向后兼容性。
深入了解DoT和DoH
我认为,DoT在很大程度上被视为一种合理的扩展。互联网号码分配机构(IANA)已为其分配了专用端口号(TCP/853),它只是将TCP DNS数据包封装在TLS加密隧道中。许多协议此前都采用过这种方式:HTTPS是封装在TLS隧道中的HTTP,而SMTPS、IMAPS和LDAPS则分别是相应协议的安全版本。DNS一直使用UDP(在某些情况下使用TCP)作为传输协议,因此增加TLS封装并不算重大变化。
相比之下,DoH更具争议。DoH将DNS数据包封装在HTTP GET或POST请求中,然后通过HTTPS连接使用HTTP/2或更高版本发送。实际上,这看起来与其他HTTPS连接并无区别,企业或服务提供商无法查看发出的具体请求。Mozilla及其他支持者表示,这种做法可防止他人窥探用户访问的网站,从而增强用户隐私。
但事实并非完全如此。DoH的批评者指出,它并不是DNS领域的Tor。这是因为,当浏览器最终连接到通过DoH查询到的主机时,请求几乎肯定会使用TLS服务器名称指示(SNI)扩展,其中包含主机名,并以明文形式发送。此外,如果浏览器尝试使用在线证书状态协议(OSCP)验证服务器证书,该过程很可能也以明文形式进行。因此,任何能够监控DNS查询的人,也能读取连接中的SNI或OCSP验证中的证书名称。
对许多人来说,DoH最大的问题在于,浏览器厂商会选择默认接收用户DoH请求的DNS服务器(例如,对于美国的Firefox用户,这些DNS服务器属于Cloudflare)。DNS服务器运营商可以看到用户的IP地址,以及用户请求访问的网站域名。这看似无关紧要,但伊利诺伊大学的研究人员发现,仅凭网页元素请求的目标地址,也就是所谓的页面加载指纹,就可以推断出某人访问过哪些网站。随后,这些信息可用于“分析用户特征,并向其投放定向广告”。
NGINX如何提供帮助?
DoT和DoH本身并非有害,在某些使用场景中还能增强用户隐私。不过,越来越多的人认为公共的集中式DoH服务不利于保护用户隐私,因此强烈建议避免使用此类服务。
无论如何,您可能会自行管理网站和应用的DNS区域,其中一些是公有区域,一些是私有区域,还有一些采用分割视界。您或许会在某个时候决定运行自己的DoT或DoH服务。此时,NGINX可以提供帮助。
DoT提供的隐私增强功能为DNS安全带来了诸多优势,但如果当前的DNS服务器不支持DoT,该怎么办?NGINX可以在DoT与标准DNS之间提供网关,从而解决这一问题。
或者,您可能看重DoH在DoT端口可能被阻止时穿越防火墙的能力。NGINX同样可以提供帮助,充当DoH到DoT/DNS的网关。
部署简单的DoT-DNS网关
NGINX Stream(TCP/UDP)模块支持SSL终止,因此设置DoT服务非常简单。只需编写几行NGINX配置,即可创建一个简单的DoT网关。
需要为DNS服务器配置一个“upstream”块,并配置一个“server”块来终止TLS连接:
正在加载代码示例…
当然,也可以反向处理,将传入的DNS请求转发到上游DoT服务器。不过,这种方式的实用性较低,因为大多数DNS流量使用UDP,而NGINX只能在DoT与其他TCP服务(例如基于TCP的DNS)之间进行转换。
正在加载代码示例…
简单的DoH-DNS网关
与DoT网关相比,简单DoH网关的配置略为复杂。我们需要同时使用HTTPS服务和Stream服务,并通过JavaScript代码和NGINX JavaScript module<.htmla> (njs)在这两种协议之间进行转换。最简单的配置如下:
正在加载代码示例…
此配置仅执行将数据包发送至DNS服务所需的最低限度处理。此用例假定其他所有过滤、日志记录或安全功能均由上游DNS服务器执行。
此配置中使用的JavaScript脚本(nginx_stream.js)包含多个DNS库模块文件。在dns.js模块中,变量dns_decode_level用于设置DNS数据包的处理程度。处理DNS数据包显然会影响性能。如果使用与上述类似的配置,请将dns_decode_level设置为0。
更高级的DoH网关
NGINX非常擅长处理HTTP,因此,我们认为仅将NGINX用作简单的DoH网关,未能充分发挥其价值。
此处使用的JavaScript代码可配置为对DNS数据包进行完整或部分解码。这样便可为DoH查询构建HTTP内容缓存,并根据DNS响应中的最小TTL设置Expires和Cache-Control标头。
下面是一个更完整的示例,其中进一步优化了连接,并支持内容缓存和日志记录:
正在加载代码示例…
使用NGINX Plus功能的高级DNS过滤器
如果您订阅了NGINX Plus,可以将上述示例与NGINX Plus的一些高级功能结合使用,例如主动健康检查和高可用性;还可以使用缓存清除API来管理缓存的DoH响应。
还可以使用NGINX Plus键值存储构建DNS过滤系统。该系统通过返回可有效阻止访问恶意域名的DNS响应,保护用户免受恶意域名侵害。您可以使用RESTful NGINX Plus API动态管理键值存储的内容。
我们将恶意域名分为“blocked”和“blackhole”两类。DNS过滤系统会根据域名所属类别,以不同方式处理针对该域名的DNS查询:
- 对于blocked域,系统会返回
NXDOMAIN响应(表示该域不存在) - 对于黑洞域,收到
A记录请求时,它会返回一条值为0.0.0.0的DNSA记录;收到AAAA(IPv6)记录请求时,则返回一条值为::的AAAA记录;对于其他记录类型,它会返回不含任何应答记录的响应
当DNS过滤器收到请求时,会先检查是否存在与所查询的FQDN完全匹配的键。如果找到,则按照关联值的指定方式“清理”(阻止或黑洞处理)该请求。如果没有完全匹配项,则会在两个列表中查找该域名,一个是阻止域名列表,另一个是黑洞域名列表。如果找到匹配项,则以相应方式清理该请求。
如果查询的域名不是恶意域名,系统会将请求转发到Google DNS服务器进行常规处理。
设置键值存储
我们在NGINX Plus键值存储中使用两类条目来定义被视为恶意的域名:
- 完全限定域名(FQDN)的单独条目,每个条目的值为
blocked或blackhole - 两个域名列表,对应的键分别为
blocked_domains和blackholed_domains,每个键均映射到一个采用逗号分隔值(CSV)格式的域名列表
以下键值存储配置应放在stream上下文中。keyval_zone指令为键值存储分配一个名为dns_config的内存块。第一个keyval指令会加载所有已设置且匹配的FQDN键值对,第二个和第三个指令则定义两个域名列表:
正在加载代码示例…
然后,我们可以使用NGINX Plus API,将值“blocked”或“blackhole”分配给要明确过滤的任意FQDN键,也可以修改与“blocked_domains”或“blackhole_domains”键关联的CSV格式列表。我们可以随时修改或删除确切的FQDN,也可以修改任一列表中的域名集合,DNS过滤器会立即同步这些更改。
选择处理请求的上游服务器
以下配置会加载$dns_response变量。该变量由下文“js_preread”中的server块内定义的配置侦听DNS查询的服务器指令填充。当调用的JavaScript代码确定需要清理请求时,会根据具体情况将该变量设置为blocked或blackhole。
我们使用map指令将$dns_response变量的值赋给$upstream_pool变量,从而控制由下文“定义上游服务器”中的哪个上游组处理请求。对非恶意域名的查询会转发到默认的Google DNS服务器,而对已阻止或黑洞域名的查询则由相应类别的上游服务器处理。
正在加载代码示例…
定义上游服务器
此配置定义了上游服务器组blocked、blackhole和google,分别处理对已阻止域、黑洞域和非恶意域的请求。
正在加载代码示例…
配置侦听DNS查询的服务器
在此,我们定义在stream上下文中侦听传入DNS请求的服务器。js_preread指令会调用Javascript代码,对DNS数据包进行解码,从数据包的NAME字段中检索域名,并在键值存储中查找该域名。如果该域名与某个FQDN键匹配,或者位于blocked_domains或blackhole_domains列表中任一域名的区域内,则会被清除。否则,该域名将发送到Google DNS服务器进行解析。
正在加载代码示例…
基本就完成了,只需添加最后一个server块,用黑洞响应或阻止响应来应答查询。
正在加载代码示例…
请注意,此块中的服务器与其上方的实际DNS服务器几乎完全相同。主要区别在于,这些服务器会向客户端发送响应数据包,而不是将请求转发到上游DNS服务器组。JavaScript代码会检测其是否从端口9953或9853运行;如果是,则不会设置标志来指示应阻止该数据包,而是使用实际的响应数据包填充$dns_response。整个过程就是这样。
当然,我们也可以将此过滤机制应用于DoT和DoH服务,但为了简化操作,这里使用了标准DNS。如何将DNS过滤与DoH网关相结合,留给读者自行实践。
测试过滤器
我们之前已使用NGINX Plus API添加了两个FQDN条目,并在两个域名列表中添加了一些域名:
$ curl -s http://localhost:8080/api/5/stream/keyvals/dns_config | jq{
"www.some.bad.host": "blocked",
"www.some.other.host": "blackhole",
"blocked_domains": "bar.com,baz.com",
"blackhole_domains": "foo.com,nginx.com"
}因此,当我们请求解析www.foo.com(被黑洞处理的域名foo.com中的主机)时,会得到一条IP地址为0.0.0.0的A记录:
$ dig @localhost www.foo.com
; <<>> DiG 9.11.3-1ubuntu1.9-Ubuntu <<>> @localhost www.foo.com
; (1 server found)
;; global options: +mcd
;; Got answer:
;; ->>HEADER,,- opcode: QUERY, status: NOERROR, id: 58558
;; flags: qr aa rd ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
; www.foo.com. IN A
;; ANSWER SECTION:
www.foo.com. 300 IN A 0.0.0.0
;; Query time: 0 msec
;; SERVER: 172.0.0.1#53(126.0.0.1)
;; WHEN: Mon Dec 2 14:31:35 UTC 2019
;; MSG SIZEW rcvd: 45访问代码
DOH和DOT文件可在我的“GitHub代码仓库”中获取:
- 位于njs.d/dns文件夹中的JavaScript代码
- 位于examples文件夹中的NGINX配置
如需试用NGINX Plus,立即开始30天免费试用,或联系我们,讨论您的使用场景。
相关博客文章
NGINX Plus R26 正式发布
NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。
使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。
Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。
NGINX Plus R19 正式发布
NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。
NGINX现已正式加入F5
NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。
