跳转至内容

Unbound

来自 ArchWiki

Unbound 是一个进行验证、递归和缓存的 DNS 解析器。根据 Wikipedia

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

安装

安装 unbound 包。

此外,#DNSSEC 验证 需要 expat 包。

配置

本文章或章节需要扩充。

原因: 添加有关使用 Redis 配置持久化缓存的说明。(在 Talk:Unbound 中讨论)

默认配置文件已包含在 /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 自带默认的内置提示。因此,如果软件包定期更新,则无需手动干预。否则,使用根提示文件是一个好习惯,因为内置提示可能会过时。

首先将 unbound 指向 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: "/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 检查会显著增加 DNS 查询时间,尤其是在地址被缓存之前的初始查找。

测试验证

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

$ unbound-host -vDr test.dnscheck.tools

应答应包含 IP 地址,并且旁边有 (secure) 字样。

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

此处应答应包含 (BOGUS (security failure))

注意 如果您使用不执行 DNSSEC 的独立 DNS 转发器,请从选项中删除 -r 以获得正确的应答。否则,所有 rcodes 都将响应 SERVFAIL

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

$ drill badsig.test.dnscheck.tools
$ drill test.dnscheck.tools

第一个命令应返回 SERVFAILrcode。第二个命令应返回 NOERRORrcode

转发查询

如果您只想将查询转发到外部 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
注意 阻止子网 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 生成的文件[2]

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
注意 此示例使用了 Cloudflare 的公共 DNS 解析器。请将其替换为您信任的 DNS 解析器。请参阅 Domain name resolution#Third-party DNS services

访问控制

您可以按 IP 地址指定要响应查询的接口。默认是监听 localhost

要监听所有接口,请使用以下命令:

interface: 0.0.0.0
interface: ::0
警告 除非您特别想运行开放 DNS 解析器,否则请不要将解析器配置为监听公共(面向 Internet)IP 地址或所有 IP 地址(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 响应,请参阅 此页面
  • 要将来自其他来源的主机文件转换为 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 服务器的用户来说,参考 NSD Wiki 页面可能是有益的,该页面提供了此类系统的配置示例。拥有一个用于权威 DNS 查询的服务器和一个用于验证、递归、缓存 DNS 功能的独立 DNS 服务器,比一个提供所有这些功能的单一 DNS 服务器具有更高的安全性。许多用户已将 Bind 用作单一 DNS 服务器,并且在从 Bind 迁移到结合使用 NSD 和 Bind 的过程中,NSD Wiki 页面提供了一些帮助。

面向 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 arbitrarily 增加到 1 以上。实际上,对于大多数运行在小型网络或单台机器上的用户来说,没有必要通过将 num-threads 增加到 1 以上来寻求性能提升。如果您确实想这样做,请参阅 官方文档,并且以下经验法则应该有效:

num-threads 设置为系统 CPU 核心数。例如,对于 4 个 CPU 和每个 CPU 2 个核心,请使用 8。

outgoing-range 设置为尽可能大的值,请参阅上面网页中的相关部分,了解如何克服总共 1024 的限制。这可以同时服务更多客户端。单核尝试 950。双核尝试 450。四核尝试 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

参见