Unbound
Unbound 是一个支持验证、递归和缓存的 DNS 解析器。根据 Wikipedia
- Unbound 已在多个开源项目中取代了 Berkeley Internet Name Domain (BIND) 成为默认的基础系统域名服务器,在大多数应用场景中,它被认为更小巧、更现代且更安全。
安装
此外,#DNSSEC 验证还需要 expat 软件包。
配置
默认配置文件已包含在 /etc/unbound/unbound.conf 中。以下部分重点介绍了配置文件的不同设置。有关其他设置和更多详细信息,请参阅 unbound.conf(5)。
除非另有说明,本节中列出的任何选项均应放置在配置文件的 server 部分中,如下所示:
/etc/unbound/unbound.conf
server: ... setting: value ...
本地 DNS 服务器
如果您想使用 unbound 作为本地 DNS 服务器,请在 /etc/resolv.conf 中将您的域名服务器设置为环回地址 ::1 和 127.0.0.1。
/etc/resolv.conf
nameserver ::1 nameserver 127.0.0.1 options edns0 trust-ad
请确保保护 /etc/resolv.conf 免受修改,具体方法如 域名解析#覆盖 /etc/resolv.conf 所述。
/etc/resolvconf.conf。/etc/resolvconf.conf
name_servers="::1 127.0.0.1" resolv_conf_options="edns0 trust-ad"
然后运行 resolvconf -u 来生成 /etc/resolv.conf。
有关如何测试您的设置,请参阅 域名解析#查找工具。
在对 resolv.conf 进行永久更改后,请特别检查所使用的服务器是否为 ::1 或 127.0.0.1。
现在,您可以设置 unbound 以便将查询 #转发查询(也许是所有查询)到您选择的 DNS 服务器。
根提示 (Root hints)
对于并非缓存地址的递归查询,解析器需要从服务器树的顶部开始查询根服务器,以了解所查询地址的顶级域名位于何处。Unbound 附带了内置的默认根提示。因此,如果软件包定期更新,则无需人工干预。否则,最好使用 root-hints 文件,因为内置提示可能会过时。
首先将 unbound 指向 root.hints 文件:
root-hints: root.hints
然后,将一个 root hints 文件放入 unbound 配置目录。最简单的方法是运行以下命令:
# curl --output /etc/unbound/root.hints https://www.internic.net/domain/named.cache
在实际使用此文件(而非内置提示)时,建议每六个月左右更新一次 root.hints,以确保根服务器列表是最新的。这可以手动完成,也可以使用 systemd 定时器完成。请参阅 #根提示 systemd 定时器获取示例。
DNSSEC 验证
要使用 DNSSEC 验证,服务器信任锚 (trust anchor) 的以下设置应放在 server: 下:
/etc/unbound/unbound.conf
trust-anchor-file: "/etc/unbound/trusted-key.key"
此设置已在默认配置文件中启用。
/etc/unbound/trusted-key.key 是从 /etc/trusted-key.key 复制而来的,后者由依赖项 dnssec-anchors 提供。该软件包的 PKGBUILD 使用 unbound-anchor(8) 生成此文件。
仅当所查询的 DNS 服务器支持 DNSSEC 时,才会进行 DNSSEC 验证。如果常规的 #转发查询 已被设置为不支持 DNSSEC 的 DNS 服务器,那么它们的响应(无论是什么)都应被视为不安全的,因为无法执行 DNSSEC 验证。
测试验证
要测试 DNSSEC 是否工作,请在 启动 unbound.service 后执行:
$ unbound-host -vDr test.dnscheck.tools
响应应为 IP 地址,且其后带有 (secure) 字样。
$ unbound-host -vDr badsig.test.dnscheck.tools
此处响应应包含 (BOGUS (security failure))。
-r 以获取正确的响应。否则所有 rcodes 都会响应为 SERVFAIL。此外,您可以使用 drill 测试解析器,如下所示:
$ drill badsig.test.dnscheck.tools $ drill test.dnscheck.tools
第一个命令应返回 rcode 为 SERVFAIL。第二个应返回 rcode 为 NOERROR。
转发查询
如果您只想将查询转发到外部 DNS 服务器,请跳至 #转发所有剩余请求。
允许本地网络使用 DNS
使用 openresolv
如果您的网络管理器支持 openresolv,您可以 配置它,为 Unbound 提供本地 DNS 服务器和搜索域。
/etc/resolvconf.conf
... private_interfaces="*" # Write out unbound configuration file unbound_conf=/etc/unbound/resolvconf.conf
运行 resolvconf -u 生成该文件。
配置 Unbound 以读取 openresolv 生成的文件,并允许对私有 IP 地址范围的响应[1]。
/etc/unbound/unbound.conf
include: "/etc/unbound/resolvconf.conf" ... server: ... private-domain: "intranet" private-domain: "internal" private-domain: "private" private-domain: "corp" private-domain: "home" private-domain: "lan" unblock-lan-zones: yes insecure-lan-zones: yes ...
此外,您可能需要禁用私有 DNS 命名空间的 DNSSEC 验证(参见 RFC 6762 附录 G)。
/etc/unbound/unbound.conf
... server: ... domain-insecure: "intranet" domain-insecure: "internal" domain-insecure: "private" domain-insecure: "corp" domain-insecure: "home" domain-insecure: "lan" ...
从响应中排除本地子网
从 DNS 响应中排除本地网络是有用的,因为它可以防止 DNS 重绑定攻击。默认情况下此功能未激活,但您可以在配置文件中添加任何您想要的子网。
private-address: local_subnet/subnet_mask
您可以通过以下字符串添加所有私有和链路本地子网:
private-address: 10.0.0.0/8 private-address: 172.16.0.0/12 private-address: 192.168.0.0/16 private-address: 169.254.0.0/16 private-address: fd00::/8 private-address: fe80::/10
::ffff:0:0/96 可阻止 IPv4 映射的 IPv6 地址绕过此过滤器。请注意,如果 excluded 子网中的地址属于 private-domain 定义或由 local-data 指定的域名,Unbound 可能会在其响应中包含这些地址。因此,您需要像 #使用 openresolv 中描述的那样定义 private-domain,以便能够查询本地域名地址。
包含本地 DNS 服务器
为了包含一个用于转发和反向本地地址的本地 DNS 服务器,需要设置类似于下面这些行,并包含正向和反向查找(通过更改下面行中的 10.0.0.1,相应地选择为本地网络提供 DNS 的服务器的 IP 地址)。
local-zone: "10.in-addr.arpa." transparent
上面的这一行对于使反向查找正常工作非常重要。
forward-zone: name: "mynetwork.com." forward-addr: 10.0.0.1
forward-zone: name: "10.in-addr.arpa." forward-addr: 10.0.0.1
您可以使用以下行设置 localhost 的正向和反向查找:
local-zone: "localhost." static local-data: "localhost. 10800 IN NS localhost." local-data: "localhost. 10800 IN SOA localhost. nobody.invalid. 1 3600 1200 604800 10800" local-data: "localhost. 10800 IN A 127.0.0.1" local-zone: "127.in-addr.arpa." static local-data: "127.in-addr.arpa. 10800 IN NS localhost." local-data: "127.in-addr.arpa. 10800 IN SOA localhost. nobody.invalid. 2 3600 1200 604800 10800" local-data: "1.0.0.127.in-addr.arpa. 10800 IN PTR localhost."
转发所有剩余请求
使用 openresolv
如果您的网络管理器支持 openresolv,您可以 配置它,为 Unbound 提供上游 DNS 服务器。
/etc/resolvconf.conf
... # Write out unbound configuration file unbound_conf=/etc/unbound/resolvconf.conf
运行 resolvconf -u 生成该文件。
最后,配置 Unbound 以读取 openresolv 生成的文件[2]。
include: "/etc/unbound/resolvconf.conf"
手动指定 DNS 服务器
要为不在本地机器和本地网络内的默认转发区使用特定服务器,请在配置文件中添加一个名为 . 的转发区。在此示例中,所有请求都被转发到 Google 的 DNS 服务器:
forward-zone: name: "." forward-addr: 8.8.8.8 forward-addr: 8.8.4.4
- 此示例使用 Google Public DNS。请将其替换为您信任的 DNS 解析器。请参阅 域名解析#第三方 DNS 服务。
- 强烈建议在连接到第三方 DNS 服务时使用 加密协议。请参阅 #使用 DNS over TLS 进行转发。
使用 DNS over TLS 进行转发
要使用 DNS over TLS,您需要启用 tls-system-cert 选项,允许 unbound 转发 TLS 请求,并指定任意数量支持 DNS over TLS 的服务器。
对于每个服务器,您需要使用 @ 指定连接端口,并使用 # 指定其域名。域名对于 TLS 身份验证是必需的,并且还允许设置存根区以及使用 unbound-control forward control 命令时使用域名。forward-addr 指定中不应包含任何空格。
/etc/unbound/unbound.conf
...
server:
...
tls-system-cert: yes
...
forward-zone:
name: "."
forward-tls-upstream: yes
forward-addr: 1.1.1.1@853#cloudflare-dns.com
访问控制
您可以通过 IP 地址指定要从哪些接口响应查询。默认情况下,它监听 localhost。
要监听所有接口,请使用以下设置:
interface: 0.0.0.0 interface: ::0
0.0.0.0 和 ::)。要控制哪些系统可以通过 IP 地址访问服务器,请使用 access-control 选项:
access-control: subnet action
例如:
access-control: 192.168.1.0/24 allow
action 可以是 deny(丢弃消息)、refuse(友好错误回复)、allow(允许递归)或 allow_snoop(允许递归和非递归)。默认情况下,除 localhost 外的所有访问均被拒绝。
用法
启动 Unbound
启动/启用 unbound.service systemd 服务。
远程控制 Unbound
unbound 附带了 unbound-control 实用程序,使我们能够远程管理 unbound 服务器。它类似于 pdnsd 的 pdnsd-ctl 命令。
设置 unbound-control
在开始使用它之前,需要执行以下步骤:
1) 首先,您需要运行以下命令:
# unbound-control-setup
这将为服务器和客户端生成自签名证书和私钥。这些文件将创建在 /etc/unbound 目录中。
2) 之后,编辑 /etc/unbound/unbound.conf 并填入以下内容。control-enable: yes 选项是必需的,其余选项可根据需要调整。
remote-control:
# Enable remote control with unbound-control(8) here.
# set up the keys and certificates with unbound-control-setup.
control-enable: yes
# what interfaces are listened to for remote control.
# give 0.0.0.0 and ::0 to listen to all interfaces.
control-interface: 127.0.0.1
# port number for remote control operations.
control-port: 8953
# unbound server key file.
server-key-file: "/etc/unbound/unbound_server.key"
# unbound server certificate file.
server-cert-file: "/etc/unbound/unbound_server.pem"
# unbound-control key file.
control-key-file: "/etc/unbound/unbound_control.key"
# unbound-control certificate file.
control-cert-file: "/etc/unbound/unbound_control.pem"
使用 unbound-control
可以与 unbound-control 一起使用的一些命令包括:
- 打印统计信息而不重置它们
# unbound-control stats_noreset
- 将缓存转储到标准输出
# unbound-control dump_cache
- 刷新缓存并重新加载配置
# unbound-control reload
请参阅 unbound-control(8) 以获取其支持操作的详细列表。
技巧与提示
域名黑名单
要屏蔽某个域名,请使用 local-zone: "domainname" always_refuse。
将黑名单保存为单独的文件(例如 /etc/unbound/blacklist.conf)以方便管理,并从 /etc/unbound/unbound.conf 中包含它。例如:
/etc/unbound/blacklist.conf
local-zone: "blacklisted.example" always_refuse local-zone: "anotherblacklisted.example" always_refuse
/etc/unbound/unbound.conf
server: ... include: /etc/unbound/blacklist.conf
- 为了使这些主机返回某些 OK 状态,您可以将 127.0.0.1 重定向更改为您控制的服务器,并让该服务器以空的 204 回复进行响应,请参阅 此页面。
- 要将其他来源的 hosts 文件转换为 unbound 格式,请执行:
$ grep '^0\.0\.0\.0' hostsfile | awk '{print "local-zone: \""$2"\" always_refuse"}' > /etc/unbound/blacklist.conf - 可以在 OpenWrt 的 adblock 软件包的 README 中找到潜在黑名单来源的列表。
添加权威 DNS 服务器
对于希望在单台机器上同时运行验证、递归、缓存 DNS 服务器以及权威 DNS 服务器的用户,参考 NSD wiki 页面可能会有帮助,该页面给出了此类系统的配置示例。使用一个服务器处理权威 DNS 查询,而另一个独立的 DNS 服务器处理验证、递归、缓存功能,比单一 DNS 服务器提供所有这些功能具有更高的安全性。许多用户使用 Bind 作为单一 DNS 服务器,NSD wiki 页面提供了一些关于从 Bind 迁移到 NSD 和 Bind 组合的帮助。
面向 WAN 的 DNS
也可以更改配置文件和服务器监听的接口,以便来自本地网络以外机器的 DNS 查询可以访问 LAN 内的特定机器。这对于从任何地方都可以访问的 Web 和邮件服务器非常有用。可以采用多年来使用 bind 所实现的技术,结合防火墙机器上的适当端口转发,将传入请求转发到正确的机器。
根提示 systemd 定时器
这是一个示例 systemd 服务和定时器,使用 #根提示 中的方法每月更新 root.hints:
/etc/systemd/system/roothints.service
[Unit] Description=Update root hints for unbound After=network.target [Service] ExecStart=/usr/bin/curl -o /etc/unbound/root.hints https://www.internic.net/domain/named.cache
/etc/systemd/system/roothints.timer
[Unit] Description=Run root.hints monthly [Timer] OnCalendar=monthly Persistent=true [Install] WantedBy=timers.target
启动/启用 roothints.timer systemd 定时器。
保持 DNS 缓存始终更新
unbound 支持预取 (prefetching),即在缓存的 DNS 条目过期之前自动更新它们,以保持缓存始终处于最新状态。引用 unbound.conf(5) 手册页,开启此功能会增加约 10% 的流量和机器负载,但热门条目不会从缓存中过期。这在具有高 RTT 的移动链路中特别有用。
要启用预取,请在 server 部分下添加:
prefetch: yes
提供过期记录
2020 年 3 月,RFC 8767 发布,它规定了解析器何时以及如何从其缓存中提供陈旧数据。如果数据在 TTL 过期时无法进行权威刷新,则该记录可以像未过期一样使用。从 1.6.0 版本开始,Unbound 具备了响应过期记录的能力。
要启用提供过期记录,请在 server 部分下添加:
serve-expired: yes serve-expired-ttl: 172800 # between 86400 (1 day) and 259200 (3 days) serve-expired-client-timeout: 1800 # RFC 8767 recommended value
故障排除
关于 num-threads 的问题
unbound.conf(5) § outgoing~2 提到:
outgoing-range: <number>
Number of ports to open. This number of file descriptors can be opened per thread.
一些来源建议将 num-threads 参数设置为 CPU 核心数。示例 unbound.conf.example 文件仅有:
# number of threads to create. 1 disables threading.
# num-threads: 1
然而,如果不引起 unbound 在日志中输出关于超过文件描述符限制的警告,是无法随意将 num-threads 增加到 1 以上的。实际上,对于大多数在小型网络或单台机器上运行的用户来说,没有必要为了性能提升而将 num-threads 增加到 1 以上。如果您确实希望这样做,请参阅 官方文档,以下经验法则应该有效:
- 将
num-threads设置为系统上的 CPU 核心数。例如,对于 4 个 CPU 每个 2 个核心,使用 8。
将 outgoing-range 设置为尽可能大的值,请参阅上述参考网页中关于如何克服总数 1024 限制的部分。这可以一次服务更多的客户端。对于 1 个核心,尝试 950。对于 2 个核心,尝试 450。对于 4 个核心,尝试 200。num-queries-per-thread 最好设置为 outgoing-range 大小的一半。
由于对 outgoing-range 的限制也限制了 num-queries-per-thread,最好使用 libevent 进行编译,这样 outgoing-range 就没有 1024 的限制。如果您需要以这种方式为重型 DNS 服务器进行编译,您将需要从源代码编译程序,而不是使用 unbound 软件包。
启动后首次查询失败
如果没有配置存储后端,unbound 在每次启动和每次服务(重)启动时都会以完全空的缓存开始。在缓存为空的情况下,第一个请求会向远程/上游 DNS 服务器触发大量查询。该请求数量会迅速触及 unbound 的配额。默认的 unbound 1.22.0 配置将上游查询次数限制为 128 次。即使是未来默认值为 200 的版本也无法完全解决此问题。
结果是您的第一次 DNS 查询将失败,而第二次查询成功。启用错误日志记录后,您会看到类似的消息:
error: SERVFAIL <1.1.1.1.in-addr.arpa. PTR IN>: all servers for this domain failed, at zone 1.1.1.in-addr.arpa. no server to query no addresses for nameservers}}
在调试模式下,日志将显示类似:
debug: request 1.1.1.1.in-addr.arpa. has exceeded the maximum global quota on number of upstream queries 131}}
更改您的 unbound.conf 并重启 unbound,将默认配额增加到其他遇到此问题的用户所推荐的更宽松的值,例如:
max-global-quota: 300
(递归)查询超时
即使拥有 1 Gbit/s FTTH 连接,默认的 unbound 配置也可能导致递归查询超时。在调试模式下,日志将显示:
debug: drop reply, it is older than discard-timeout
如果您想等待比默认的 1.9 秒更长的响应时间,请更改您的 unbound.conf:
discard-timeout: 3800 # in milliseconds