Paranoid Penguin - DNS缓存投毒,第二部分:DNSSEC验证
上个月,我详细描述了 DNS 缓存投毒的问题,以及它为何从根本上改变了我们对 DNS 安全性的理解。 以前,似乎保持 DNS 服务器打补丁,并限制其执行递归查询和区域传送的主机就足够了,但现在我们别无选择,只能关注本地解析器和递归 DNS 服务器从其他服务器接收的 DNS 数据的真实性。
这是因为 DNS 递归的工作方式使得攻击者很容易触发事件,从而直接将伪造的 DNS 数据注入到递归服务器的缓存中,导致依赖该服务器的所有用户被重定向到特定电子商务和在线银行站点的冒名顶替的“恶意双胞胎”站点,或恶意的、传播恶意软件的网站等等。
上个月,我通过解释说,尽管针对卡明斯基缓存投毒攻击的短期修复方案是修补 DNS 软件,以便递归服务器为其 DNS 查询随机化源 UDP 端口,但这只会使攻击耗时更长(尽管时间会更长得多);它并没有消除这种威胁。 相反,最好的保护是权威 DNS 服务器的管理员对其所有区域数据进行密码学签名,以及递归或缓存 DNS 服务器的管理员配置其服务器以检查它们遇到的所有签名区域的签名。
所有这些签名/验证功能都是通过 DNSSEC(DNS 协议的一组扩展)实现的。 大多数现代 DNS 服务器软件包现在都支持 DNSSEC(最值得注意的例外是 djbdns)。
本月,我将解释如何配置您的递归/缓存 DNS 服务器以检查 DNS 区域数据签名。 由于 Internet 软件协会的 BIND 软件包是迄今为止 UNIX 和类 UNIX 系统最流行的 DNS 服务器应用程序,因此我的示例都涉及 BIND。
如果您管理自己的 DNS 区域,您也应该对自己的区域进行签名并发布您的证书和签名,但这超出了本文的范围。 (有关其他 DNSSEC 信息和教程的链接,请参阅“资源”。 我也可能会在未来的专栏中介绍区域签名和 DNSSEC 密钥管理。)
请注意,我现在首选的 Linux 服务器发行版是 Ubuntu Server 10.10,因此我的示例都直接适用于 Ubuntu 和其他 Debian 衍生版本。 如果您运行其他发行版,我的示例仍然应该有用,因为 Ubuntu 和 Debian 在打包 BIND 方面唯一特殊之处是将它的配置文件 (named.conf) 分解为几个部分(named.conf.options、named.conf.local 和 named.conf.default-zones),这些部分通过“include”语句读入 named.conf。
我主要希望您从本文中了解的是如何在基于 BIND 的域名服务器上启用 DNSSEC 验证。 我本可以用大部分篇幅来概述 DNSSEC 是什么、它是如何工作的等等,但为了简洁起见,我给出了低注意力跨度版本。
DNSSEC 是 DNS 区域数据的公钥基础设施 (PKI)。 当区域管理员对给定区域中所有不同类型的资源记录 (RR) 进行数字签名,并发布这些签名以及区域签名密钥的公钥证书时,任何针对该区域进行查询的递归域名服务器都能够验证这些签名,从而获得密码学证明,证明给定 DNS 查询的答案没有被伪造或篡改。
一开始这听起来可能并不简单,但在实践中,它甚至比这还要复杂得多。 这是因为 DNS 既是分层的又是分布式的,顶部有一个“根”区域,底部有各个主机名和其他资源记录。 中间是区域和子区域的层级。
以顶级域名 (TLD) .us 为例。 它由 50 多个子区域组成,每个子区域代表美利坚合众国的一个不同的州或保护地——例如,mn.us 代表明尼苏达州,wi.us 代表威斯康星州等等。 在每个“州”子区域内,可能有数百个子子区域,代表城市、县、州或市政政府机构等等。 例如,lib.mn.us 由明尼苏达州的公共图书馆使用,而 stpaul.lib.mn.us 由圣保罗公共图书馆系统使用。
假设我是 mycowtown.lib.mn.us 的 DNS 管理员,我对该区域中的所有记录进行签名,并发布相应的 RRSIG、DNSKEY 和其他相关记录。 我多么值得称赞!
但是,如果有人尝试解析我的域中的名称,例如 interwebs.mycowtown.lib.mn.us,他们最多会与四个其他域名服务器对话,然后才能一直到达我的美丽的、签名的区域——即“.”(根区域)、.us、.mn.us 和 .lib.mn.us 的各自权威域名服务器。 有什么可以阻止有人篡改对那些先前的递归查询的答案? (谁是 .us 的权威? 谁是 .mn.us 的权威? 谁是 .lib.mn.us 的权威?)
显然,必须有一个“验证链”,从我真正想要验证的区域 (mycowtown.lib.mn.us) 一直向上到根域。 碰巧的是,“.”根已签名,我稍后将向您展示如何下载和验证初始根密钥。 .us 和 .mn.us 也是如此。 但是,.lib.mn.us 尚未签名(在撰写本文时)。 这是否意味着在它下面的区域签名是毫无意义的?
完全不是。 Internet 软件协会(BIND 的创建者和维护者)维护着一个 DNS 旁路验证 (DLV) 数据库,其中包含具有此类验证链中存在差距的区域的密钥。 如果我对 mycowtown.lib.mn.us 进行签名,并将我的密钥签名密钥注册到 dlv.isc.org,则配置为使用 DLV 的解析域名服务器仍然能够构建足够完整的验证链,通过查看 isc.org 证明我的密钥签名密钥的有效性,而该密钥实际上用于对密钥(区域签名密钥)进行签名,而我实际上使用这些密钥对区域数据进行签名。
在最新版本的 BIND 中,DNS 旁路验证不仅默认启用,而且也已预先配置。
在深入了解域名服务器配置之前,我应该描述的最后一个 DNSSEC 机制是自动密钥管理。 我暗示存在两种 DNS 密钥:密钥签名密钥 (KSK) 和区域签名密钥 (ZSK)。 两种类型的密钥都必须定期重新生成——ZSK 每隔几个月重新生成一次,您实际上使用 ZSK 对区域数据进行签名(尽管在我看来,这样做的必要性并不能很好地说明 DNSSEC 中 PKI 的安全性如何)。 自然,每次您更改 KSK 或 ZSK 时,都必须重新签名您的整个区域。
说这使区域签名管理有点令人头疼是轻描淡写; 但是,有多种方法可以自动化此过程。 幸运的是,现在您和我只关心验证密钥,而不是维护密钥,并且最新版本的 BIND 9 具有一种机制,可以自动检查和更新缓存域名服务器的 DNS 密钥缓存。
这就是 named.conf 中的 managed-keys{} 语句,它可以代替静态 trusted-keys{} 定义使用。 当您使用根区域的签名密钥设置 BIND 时,您将使用 managed-keys{} 语句执行此操作,该语句指定一个“初始”密钥,该密钥本身不是 KSK 或 ZSK,而是用于透明的密码学事务,您的域名服务器在该事务中向根区域权威机构查询其当前公钥 ZSK 的副本,并缓存它收到的答案。
但我有点超前了。 让我们设置一个缓存域名服务器,然后在它上面启用 DNSSEC 验证。
如果您已经有一个仅缓存域名服务器(或也缓存的通用域名服务器),并且您只需要知道如何设置 DNSSEC 验证,您可以跳到下面的“设置 DNSSEC 验证”部分。 但是,为了完整起见,并为了指出一些最好更改的默认设置,这里有一个快速设置过程。
首先,安装您的发行版支持的 BIND9 的任何(子)版本。 在撰写本文时,Ubuntu 10.10 包括 BIND 版本 9.7.1; 要安装它,请使用以下命令
sudo apt-get install bind9 bind9utils
bind9 软件包以守护进程 named 的形式提供 BIND 9.7.1 本身,以及其配置文件、手册页和库。 bind9utils 软件包提供方便的命令,例如 rndc 和 named-checkconf,以及 DNSSEC 命令 dnssec-keygen 和 dnssec-signzone。 后两个命令仅用于创建和维护实际的 DNSSEC 区域密钥和签名。 如果您使用 DNSSEC 所做的只是验证来自其他区域的签名,则实际上不需要这些命令。
在 Debian 和 Ubuntu 系统上,bind9 软件包将其配置文件放在 /etc/bind 中。 我们在这里关注的文件是 /etc/bind/named.conf、/etc/bind/named.conf.options 和 /etc/bind/bind.keys。
实际上,在这三个文件中,我们只会编辑一个文件,named.conf.options。 我提到其他两个文件是为了指出 named.conf 使用“include”语句从 /etc/bind/named.conf.options、/etc/bind/named.conf.local(其中包含您的本地区域文件)和 /etc/bind/named.conf.default-zones(其中包含本地环回接口的默认区域信息)中提取内容。
在这一点上,我有一个好消息要告诉您:您的 Debian 或 Ubuntu 系统的 named.conf.options 文件在技术上已经设置为将 named 作为仅缓存域名服务器运行。 坏消息是,在您可以将其视为安全的缓存域名服务器之前,需要对其进行一些加强。
列表 1 显示了默认的 Debian/Ubuntu named.conf.options 文件(省略了注释行)。
列表 1. 默认的 Ubuntu/Debian named.conf.options 文件
options { directory "/var/cache/bind"; auth-nxdomain no; # conform to RFC1035 listen-on-v6 { any; }; };
相比之下,列表 2 更安全。
列表 2. named.conf.local
acl mynetworks { 192.168.100.0/24; 10.10.0.0/16; }; options { directory "/var/cache/bind"; allow-query { mynetworks; }; auth-nxdomain no; # conform to RFC1035 listen-on-v6 { none; }; };
让我们讨论一下为什么列表 2 更好。 首先,我定义了一个访问控制列表 (ACL),其中指定了两个 IP 网络,采用“CIDR 表示法”。 从技术上讲,这不是一个选项,但它需要在任何 option 语句之前加载,因此它需要放在此处或 named.conf 中,在此行之前
include "/etc/bind/named.conf.options";
就其本身而言,此 acl 不执行任何操作。 但是一旦定义了它,我就可以创建一个引用它的“allow-query”选项,正如您在列表 2 中看到的那样,这正是我所做的。 显然,在为自己使用调整此文件时,您应该将我的 acl 语句中的列表(“192.168.100.0/24; 10.10.0.0/16;”)替换为您的组织的本地 IP 子网列表。
我所做的另一个安全调整是将“listen-on-v6”选项的值从“any”更改为“none”。 因为我的本地子网都不使用 IPv6,所以没有理由监听任何本地 IPv6 接口。 从技术上讲,如果我甚至没有连接到我的服务器的任何 IPv6 接口,并且如果我已经设置了 acl 并在 allow-query 语句中指定了它,这应该无关紧要。 所以,也许我只是在这里完全关闭 IPv6 有点偏执,但关闭未使用的功能几乎总是一件好事。
编辑并保存您的 /etc/bind/named.conf.options 文件后,您可以通过运行以下命令来检查您的工作named-checkconf命令,不带任何参数,如下所示
bash-$ sudo named-checkconf
假设这没有返回任何配置错误(我自己有放错位置或省略分号的倾向),那么您可以使用 rndc 命令使正在运行的 named 进程重新加载其配置和区域文件,如下所示
bash-$ sudo rndc reload
现在,您可以通过登录到网络上的其他主机并运行dig针对它的一个或两个查询来测试您的服务器。 例如,如果我的缓存域名服务器的 IP 地址是 192.168.100.253,我可以让它查找 www.linuxjournal.com 的 DNS 信息,如下所示
mick@someotherhost:/home/mick$ dig @192.168.100.253 ↪www.linuxjournal.com
当然,您可以简单地将您的客户端系统配置为使用您的缓存域名服务器作为其默认域名服务器,在这种情况下,您可以省略@192.168.100.253在上面的命令中。 但是,在您确定它有效之前,您可能不想这样做。
如果它不起作用,请确保您的客户端系统的 IP 地址属于您在 /etc/bind/named.conf.options 中设置的任何 acls 中指定的 IP 网络之一,如我之前所述。
至此,您的仅缓存域名服务器已启动并正常工作。 现在您可以配置 DNSSEC 验证。
回到您的缓存域名服务器,您只需在 /etc/bind/named.conf.options 文件的 options{} 部分中添加三行,外加一个新的 managed-keys{} 部分,如列表 3 所示。
列表 3. 启用 DNSSEC 的 named.conf.local
acl mynetworks { 192.168.100.0/24; 10.10.0.0/16; }; options { directory "/var/cache/bind"; allow-query { mynetworks; }; auth-nxdomain no; # conform to RFC1035 listen-on-v6 { none; }; dnssec-enable yes; dnssec-validation yes; dnssec-lookaside auto; }; managed-keys { "." initial-key 257 3 8 " AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQ bSEW0O8gcCjFFVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh /RStIoO8g0NfnfL2MTJRkxoXbfDaUeVPQuYEhg37NZWA JQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaDX6RS6CXp oY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3 LQpzW5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGO Yl7OyQdXfZ57relSQageu+ipAdTTJ25AsRTAoub8ONGc LmqrAmRLKBP1dfwhYB4N7knNnulqQxA+Uk1ihz0= "; };
列表 3 中的前两行新行,dnssec-enable yes;和dnssec-validation yes;,在您的缓存域名服务器上启用 DNSSEC。 这实际上是一个冗余设置,因为“yes”是 BIND 9.5 及更高版本中这两个设置的默认值,但指定它们也无妨。
第三行新行,dnssec-lookaside auto;,告诉 BIND/named 在任何时候都无法验证从给定的资源记录一直到根 (.) 的完整信任链时,自动使用 DLV。 如果您忘记了 DLV 的工作原理,请参阅本文前面的“DNSSEC 概述”部分。
正如我在该部分中提到的,最新版本的 BIND 预先配置为查找 isc.org 的 DLV 仓库。 您要做的就是将“dnssec-lookaside”设置为“auto”,BIND 将完成剩下的工作。 随着越来越多的 TLD 被签名,此功能将变得不那么重要。
这引出了 named.conf.options 文件中的最后一个新元素:managed-keys{} 部分。 这指定了 DNS “根”域的密钥,它是任何 DNSSEC 信任链的顶部。
您不一定需要在 DNS 层次结构中指定任何“低于”根的密钥; 如果您从知道根密钥开始,您可以信任来自根域名服务器的签名回复。 这种信任向下流向来自 TLD(.gov、.us、.net 等)的签名数据,依此类推。 验证向下链中的“差距”有望通过 DLV 处理。
看在上帝的份上,不要只是逐字复制列表 3 中“.”的密钥条目! Tony Finch 撰写了一个快速简便的检查和验证(初始)根证书的程序(请参阅“资源”)。 总结来说,此程序包括以下步骤。
1) 使用以下dig命令获取当前的根证书并将其保存到文件 root-dnskey
bash-$ dig +multi +noall +answer DNSKEY . >root-dnskey
2) 创建此证书的哈希值并使用此命令将其保存到文件 root-ds
bash-$ $ dnssec-dsfromkey -f root-dnskey . >root-ds
3) 从 https://data.iana.org/root-anchors/root-anchors.xml 中提取官方根证书的哈希值,并将其与您刚刚创建的 root-ds 文件进行比较。 为了更加谨慎,您可以使用 PGP 检查 root-anchors.xml 的签名(请参阅 Tony Finch 的文章)。
4) 如果哈希值匹配,请将密钥(长密钥,编号 257)从 root-dnskey 复制到您的 managed-keys 语句中,如列表 3 所示。 此块的第一行(在managed-keys {行之后)应与列表 3 中的相同。
与您之前的更改一样,在您保存 named.conf.options 后,您应该使用named-checkconf检查它,然后使用rndc reload.
加载它。 最后,要测试 DNSSEC 验证,请使用 dig 测试一些已知的签名记录,例如 www.isc.org。 务必使用+dnssec标志,如下所示
mick@someotherhost:/home/mick$ dig @192.168.100.253 ↪www.isc.org +dnssec
如果一切正常,dig 的输出应指示已设置“ad”(验证数据)标志。 列表 4 显示了成功回复我们的示例 dig 命令的第一部分内容。 请注意以;; flags: qr rd ra ad;.
列表 4. 成功 DNSSEC 验证的 dig 输出
; <<>> DiG 9.6.0-APPLE-P2 <<>> @192.168.100.253 www.isc.org +dnssec ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62704 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 5, ADDITIONAL: 13
至此,您的域名服务器已成功验证签名区域数据! 现在,我要感谢您并道别。 似乎每隔几年我都会这样做,我将暂停几个月。 但是,我计划在那之后恢复 Paranoid Penguin,以焕然一新的面貌为您带来阅读乐趣。
在那之前,照顾好自己,尤其是您的 Linux 系统!
资源
DNSSEC—DNS 安全扩展—协议主页:www.dnssec.net
Alan Clegg 的“DNSSEC—卡明斯基事件后的生活和热爱生活; 或者:我是如何克服恐惧并签署我的区域的。” 2008 年 10 月 30 日在 REN-ISAC 上的演示文稿:www.ren-isac.net/techburst/hardcopy/ren-isac_techburst_20081030_clegg_dnssec.pdf
Geoff Huston 的“DNSSEC、部署和 DNS 安全扩展的基本观察”:www.circleid.com/posts/dnssec_deployment_and_dns_security_extensions
Ubuntu 10.10 服务器指南:“第 7 章。域名系统 (DNS)”: https://help.ubuntu.com/10.10/serverguide/C/dns.html
BIND 9.7 管理员参考手册 (ARM): ftp.isc.org/isc/bind9/cur/9.7/doc/arm/Bv9ARM.pdf
Tony Finch 的“如何使用 BIND-9.7 设置 DNSSEC 验证”:fanf.livejournal.com/107310.html
Mick Bauer (darth.elmo@wiremonkeys.org) 是美国最大的银行之一的网络安全架构师。 他是 O'Reilly 图书Linux 服务器安全第二版(以前称为使用 Linux 构建安全服务器)的作者,偶尔在信息安全会议上发表演讲,并且是“网络工程波尔卡舞曲”的作曲家。