跳转至内容

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 服务器。

根提示 (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 检查会显著增加初始 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

第一个命令应返回 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 地址范围的响应[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 地址绕过此过滤器。

请注意,如果 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
注意 转发区 (forward zones) 和存根区 (stub zones) 之间存在区别 —— 存根区仅在直接连接到权威 DNS 服务器时才有效。如果 BIND DNS 服务器提供权威 DNS,这对于来自 BIND 的查找是有效的;但如果您将查询转发到另一个执行递归查找的 unbound 服务器,则在此处定义存根区是不起作用的。在这种情况下,必须定义如上所述的转发区,因为转发区可以将查找链式转发到其他 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
  • 将缓存转储到标准输出
    # 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 服务器的用户,参考 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 个核心,尝试 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

参见

© . This site is unofficial and not affiliated with Arch Linux.

Content is available under GNU Free Documentation License 1.3 or later unless otherwise noted.