Unbound

来自 ArchWiki

Unbound 是一个验证性、递归和缓存 DNS 解析器。根据维基百科

Unbound 已经取代了 Berkeley Internet Name Domain (BIND) 成为多个开源项目中的默认基础系统名称服务器,因为它被认为对于大多数应用程序来说更小、更现代且更安全。

安装

安装 unbound 软件包。

此外,expat 软件包是 #DNSSEC 验证所必需的。

配置

默认配置已包含在 /etc/unbound/unbound.conf 中。以下章节重点介绍配置文件的不同设置。有关其他设置和更多详细信息,请参阅 unbound.conf(5)

除非另有说明,否则本节中列出的任何选项都应放在配置文件的 server 部分下,如下所示

/etc/unbound/unbound.conf
server:
  ...
  setting: value
  ...

本地 DNS 服务器

如果您想使用 unbound 作为本地 DNS 服务器,请在 /etc/resolv.conf 中将您的名称服务器设置为环回地址 ::1127.0.0.1

/etc/resolv.conf
nameserver ::1
nameserver 127.0.0.1
options edns0 trust-ad

请务必按照域名解析#覆盖_/etc/resolv.conf中所述保护 /etc/resolv.conf 免受修改。

提示: 一种简单的方法是安装 openresolv 并配置 /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 后,请专门检查正在使用的服务器是否为 ::1127.0.0.1

您现在可以设置 unbound,使其可以#转发查询,也许是将所有查询都转发到您选择的 DNS 服务器。

根提示

为了递归查询未缓存为地址的主机,解析器需要从服务器树的顶部开始并查询根服务器,以了解要查询的地址的顶级域的位置。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 计时器完成。有关示例,请参阅 #Roothints systemd 计时器

DNSSEC 验证

要使用 DNSSEC 验证,服务器信任锚的以下设置应位于 server:

/etc/unbound/unbound.conf
  trust-anchor-file: trusted-key.key

此设置是默认完成的[1]/etc/unbound/trusted-key.key/etc/trusted-key.key 复制而来,后者由 dnssec-anchors 依赖项提供,其 PKGBUILD 使用 unbound-anchor(8) 生成文件。

只有在被查询的 DNS 服务器支持 DNSSEC 时,才会执行 DNSSEC 验证。如果已将常规#转发查询设置为不支持 DNSSEC 的 DNS 服务器,则它们的应答,无论是什么,都应被视为不安全的,因为无法执行 DNSSEC 验证。

注意: 包含 DNSSEC 检查会显著增加初始查找的 DNS 查找时间,直到地址被缓存。

测试验证

要测试 DNSSEC 是否正常工作,在启动 unbound.service 后,执行

$ unbound-host -vDr go.dnscheck.tools

响应应为 IP 地址,旁边带有单词 (secure)

$ unbound-host -vDr badsig.go.dnscheck.tools

这里的响应应包含 (BOGUS (security failure))

此外,您可以使用 drill 来测试解析器,如下所示

$ drill badsig.go.dnscheck.tools
$ drill go.dnscheck.tools

第一个命令应给出 rcodeSERVFAIL。第二个命令应给出 rcodeNOERROR

转发查询

如果您只想将查询转发到外部 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 地址范围进行回复[2]

/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
注意: 阻止子网 127.0.0.0/8 会影响某些应用程序,例如垃圾邮件或广告阻止列表。添加 ::ffff:0:0/96 可阻止 IPv4 映射的 IPv6 地址绕过过滤器。

请注意,如果 Unbound 中的地址属于 private-domain 中的域或由 local-data 指定,则它们可能来自排除的子网,因此您需要按照 #使用 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
注意: 正向区域和存根区域之间存在差异 - 存根区域仅在直接连接到权威 DNS 服务器时才有效。如果 BIND DNS 服务器提供权威 DNS,这将适用于来自 BIND DNS 服务器的查找 - 但如果您将查询引用到 unbound 服务器,并且内部查找被转发到另一个 DNS 服务器,则在此处的计算机中将引用定义为存根区域将不起作用。 在这种情况下,有必要定义一个正向区域,如上所述,因为正向区域可以将查询链接到其他 DNS 服务器。即,正向区域可以将查询引用到递归 DNS 服务器。 这种区别很重要,因为如果您不恰当地使用存根区域,则不会收到任何错误消息来指示问题所在。

您可以使用以下行设置 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 生成的文件[3]

include: "/etc/unbound/resolvconf.conf"
手动指定 DNS 服务器

要为本地计算机和本地网络之外的默认正向区域使用特定服务器,请向配置文件添加名称为 . 的正向区域。在此示例中,所有请求都将转发到 Google 的 DNS 服务器

forward-zone:
  name: "."
  forward-addr: 8.8.8.8
  forward-addr: 8.8.4.4

使用 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

要控制哪些系统可以通过 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 服务器。它类似于 pdnsdpdnsd-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
  • 将缓存转储到 stdout
 # 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 服务器并不比运行一个提供所有功能的服务器本身更安全。(在 Talk:Unbound#两个 DNS 服务器并不比一个本身更安全 中讨论)

对于希望在单台计算机上同时运行验证性、递归、缓存 DNS 服务器以及权威 DNS 服务器的用户,可以参考 wiki 页面 NSD,其中给出了此类系统的配置示例。与单个 DNS 服务器提供所有这些功能相比,使用一个服务器用于权威 DNS 查询,而使用单独的 DNS 服务器用于验证性、递归、缓存 DNS 功能,可以提高安全性。许多用户都使用 Bind 作为单个 DNS 服务器,并且 NSD wiki 页面中提供了一些关于从 Bind 迁移到运行 NSD 和 Bind 的组合的帮助。

面向 WAN 的 DNS

还可以更改服务器正在监听的配置文件和接口,以便本地网络外部的计算机的 DNS 查询可以访问 LAN 内的特定计算机。这对于可以从任何地方访问的 Web 和邮件服务器非常有用,并且可以采用与多年来使用 bind 实现的相同的技术,结合防火墙计算机上合适的端口转发,将传入的请求转发到正确的计算机。

Roothints 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 支持预取,其中缓存的 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,每个 CPU 有 2 个核心,请使用 8。

outgoing-range 设置为尽可能大的值,请参阅上面引用的网页中的章节,了解如何克服总共 1024 的限制。这可以同时为更多客户端提供服务。对于 1 个核心,尝试 950。对于 2 个核心,尝试 450。对于 4 个核心,尝试 200num-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

参见