本页内容
- 准备工作
- 安装 Unbound
- 配置 Unbound:Unbound 基础配置、启用 DNS 缓存、加固 Unbound、定义私有地址与访问控制列表、设置本地域名与子域、Unbound 优化与微调、将 Unbound 配置为支持 DNS-over-TLS (DoT) 的 DNS 解析器
- 配置 Firewalld
- 通过 Rsyslog 与 Logrotate 设置 Unbound 日志
- 为客户端配置 DNS 解析器:通过 NetworkManager、通过 systemd-resolved 与 NetworkManager
- 验证 Unbound DNS 服务器
- 总结
- Unbound 基础配置
- 启用 DNS 缓存
- 加固 Unbound
- 定义私有地址与访问控制列表
- 设置本地域名与子域
- Unbound 优化与微调
- 将 Unbound 配置为支持 DNS-over-TLS (DoT) 的 DNS 解析器
- 通过 NetworkManager
- 通过 systemd-resolved 与 NetworkManager
Unbound 是一款免费且开源的 DNS 服务器软件,可用作验证型、递归型和缓存型 DNS 解析器。它是一款功能丰富的 DNS 服务器,支持 DNS-over-TLS (DoT)、DNS-over-HTTPS (DoH)、查询名称最小化(Query Name Minimisation)、积极使用 DNSSEC 验证缓存(Aggressive Use of DNSSEC-Validated Cache),以及权威区域(authority zones)支持。Unbound 专注于 DNS 的隐私与安全,同时又不牺牲速度与性能。
Unbound 主要由 NLnet Labs 开发,并以 BSD 许可证发布,支持基于 DNS 服务器开放标准的现代特性。Unbound 经过了严格的审计,可在 Linux、BSD 和 macOS 上运行。Unbound 适用于上述大多数操作系统,并可通过系统包管理器安装。
在本教程中,我将在 Rocky Linux 9 服务器上使用 Unbound 搭建一台本地 DNS 服务器。你将把 Unbound 配置为权威型、验证型与递归缓存型 DNS。此外,你还将把 Unbound 配置为本地网络的 DNS 解析器,并在其上启用 DNS-over-TLS (DoT)。
在本教程结束时,你还将通过 Rsyslog 与 Logrotate 配置 Unbound 日志,并搭建一台 Linux 客户端机器,将 Unbound 用作 DNS 解析器,然后从该客户端验证整个安装。
准备工作
在开始本教程之前,请确保你已满足以下要求:
- 一台已安装 Rocky Linux 9 的服务器——本示例使用主机名为 'unbound-rocky'、IP 地址为 '192.168.5.25' 的 Rocky Linux。
- 一个具有 root/sudo 管理员权限的非 root 用户。
- SELinux 运行在 permissive(宽松)模式。
这样就准备好了。如果所有条件都已满足,就可以继续在服务器上安装 Unbound。
安装 Unbound
Unbound 是一款支持大多数操作系统的 DNS 服务器软件,包括 Linux、BDS 和 macOS。在 Rocky Linux 上,Unbound 软件包默认可在官方的 Rocky Linux AppStream 软件仓库中获取。
在第一步中,你将把 Unbound 软件包安装到 Rocky Linux 系统上。
现在运行以下 dnf 命令,确认 AppStream 仓库中可用的 'unbound' 软件包。截至本文撰写时,Rocky Linux AppStream 仓库提供的是 Unbound 1.16。
sudo dnf info unbound
输出:

通过下面的 dnf 命令安装 Unbound。出现提示时,输入 y 确认并按 ENTER 继续。
sudo dnf install unbound
输出:

Unbound 安装完成后,通过下面的 systemctl 命令启动并启用 'unbound' 服务。这将在你的 Rocky Linux 服务器上启动 Unbound,并使其在系统启动时自动运行。
sudo systemctl start unbound
sudo systemctl enable unbound
使用下面的 systemctl 命令验证 Unbound 服务。输出 'active (running)' 表示 Unbound 服务正在运行;输出 'loaded ....; enabled;...' 表示 Unbound 服务已启用。
sudo systemctl status unbound
输出:

你的 Unbound 现已安装完成,并以默认配置文件 '/etc/unbound/unbound.conf' 运行。接下来,你将修改 Unbound 配置文件 '/etc/unbound/unbound.conf',将 Unbound 设置为权威型、验证型与递归缓存型 DNS,并启用 DoT,使 Unbound 作为 DNS 解析器工作。
配置 Unbound
默认情况下,Unbound 配置文件位于 '/etc/unbound/unbound.conf'。在这一步中,你将修改配置文件 '/etc/unbound/unbound.conf',然后完成 Unbound 的安装配置与优化。
你将把 Unbound 配置为权威型、验证型与递归缓存型 DNS。此外,还将针对性能、隐私与安全对 Unbound 安装进行优化。最后,你会将 Unbound 配置为本地网络的 DNS 解析器。
首先,运行下面的 wget 命令,将根 DNS 文件下载到 '/etc/unbound/root.hints'。然后,将该文件 '/etc/unbound/root.hints' 的属主与属组修改为 'unbound'。
wget https://www.internic.net/domain/named.root -O /etc/unbound/root.hints
sudo chown unbound:unbound /etc/unbound/root.hints
接下来,将默认的 Unbound 配置文件备份为 '/etc/unbound/unbound.conf.orig',并使用下面的 nano 编辑器命令修改原文件 '/etc/unbound/unbound.conf'。
sudo cp -v /etc/unbound/unbound{.conf,.conf.orig}
sudo nano /etc/unbound/unbound.conf
现在让我们开始配置 Unbound。
Unbound 基础配置
首先,你将设置 Unbound 服务应监听的 IP 地址与端口。然后,你还将设置用于验证上游连接的证书包,并通过 'root-hints' 参数添加根 DNS 服务器。
在 'server:' 段落中添加以下行。通过这些设置,你将使 Unbound 在本地 IP 地址 '192.168.5.25' 上使用默认 UDP 端口 '53' 运行。
server: ... ... # interface-automatic: no do-ip6: no interface: 192.168.5.25 port: 53 prefetch: yes tls-cert-bundle: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem root-hints: /etc/unbound/root.hints
参数详解:
- do-ip6:设为 'yes' 以在 IPv6 下运行 Unbound,或设为 'no' 以禁用 IPv6。
- interface:Unbound 将监听的网络接口或 IP 地址。你可以使用 IP 地址或接口名(如 'eth0')。此外,还可通过 'IP-ADDRESS@PORT' 这样的格式在指定端口上运行。
- port:指定 Unbound 运行的端口,客户端的连接将由该端口处理。DNS 默认端口为 53。
- prefetch:设为 'yes' 以启用对即将过期的消息缓存条目的预取。
- tls-cert-bundle:用于验证上游连接的数字证书。在 RHEL 系发行版上,证书文件位于 '/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem'。
- root-hints:包含根 DNS 服务器详细信息的文件。你已将该文件下载到 '/etc/unbound/root.hints'。
启用 DNS 缓存
现在添加以下行,通过 Unbound 启用递归缓存 DNS。这将在指定时间内缓存客户端在 Unbound 服务器上发起的 DNS 查询。
cache-max-ttl: 14400 cache-min-ttl: 1200
参数详解:
- cache-max-ttl:DNS 缓存中 RRSets 与消息的 TTL(生存时间)。单位为秒。
- cache-min-ttl:缓存的最小生存时间。默认值为 0,但你可以按需调整,例如设为 '1200' 秒。不要将其设置为超过 1 小时,否则会因数据陈旧而出现问题。
加固 Unbound
在 RHEL 系发行版上,Unbound 的部分隐私与安全参数默认已启用。不过,你还可以添加更多类似的参数。
hide-identity: yes hide-version: yes use-caps-for-id: yes
参数详解:
- hide-identity:设为 yes 以禁用对 id.server 或 hostname.bind 查询的应答。
- hide-version:设为 yes 以禁用 version.server 与 version.bind 查询。
- use-caps-for-id:设为 yes 以在查询中使用 '0x20 编码' 来防范伪造攻击。
定义私有地址与访问控制列表
接下来,你需要为本地网络定义 private-address 与 ACL(访问控制列表)。请务必将下方各行中的本地子网替换为你当前的网络环境。
private-address: 192.168.0.0/16 private-address: 169.254.0.0/16 private-address: 172.16.0.0/12 private-address: 10.0.0.0/8 private-address: fd00::/8 private-address: fe80::/10 #control which clients are allowed to make (recursive) queries access-control: 127.0.0.1/32 allow_snoop access-control: ::1 allow_snoop access-control: 127.0.0.0/8 allow access-control: 192.168.5.0/24 allow
参数详解:
- private-address:定义你基础设施中的私有网络子网。只有 'private-domain' 与 'local-data' 名称才允许使用这些私有地址。
- access-control:定义访问控制,决定哪些客户端可以向 Unbound 服务器发起(递归)查询。参数 'allow' 将启用递归查询,而 'allow_snoop' 会同时启用递归与非递归查询。
设置本地域名与子域
在配置完 private-address 与访问控制列表后,你现在可以通过 Unbound 的 'local-zone' 参数创建本地域名。这在你的本地网络中运行多个自托管应用时尤为有用。你可以轻松定义自己的域名或子域,并将其指向特定的目标 IP 地址。
在本示例中,你将设置一个 'static'(静态)本地域名 'garden.lan',并通过 'local-data' 参数创建多个子域。每个子域都将指向一个特定的 IP 地址,同时你还将通过 'local-data-ptr' 参数创建 PTR 记录。
# local zone local-zone: "garden.lan." static local-data: "firewall.garden.lan. IN A 10.0.0.1" local-data: "vault.garden.lan. IN A 10.0.0.2" local-data: "media.garden.lan. IN A 10.0.0.3" local-data: "docs.garden.lan. IN A 10.0.0.4" local-data: "wiki.garden.lan. IN A 10.0.0.5" local-data-ptr: "10.0.0.1 firewall.garden.lan" local-data-ptr: "10.0.0.2 vault.garden.lan" local-data-ptr: "10.0.0.3 media.garden.lan" local-data-ptr: "10.0.0.4 docs.garden.lan" local-data-ptr: "10.0.0.5 wiki.garden.lan"
参数详解:
- local-zone:在此定义本地域名。
- local-data:为子域定义 A 记录以及将要解析的本地 IP 地址。
- local-data-ptr:为子域定义 PTR 记录。
Unbound 优化与微调
接下来,添加以下行以优化你的 Unbound 安装。你可以根据当前环境对下方参数进行调整与微调。
msg-cache-slabs: 8 rrset-cache-slabs: 8 infra-cache-slabs: 8 key-cache-slabs: 8 rrset-cache-size: 256m msg-cache-size: 128m so-rcvbuf: 8m
参数详解:
- msg-cache-slabs:消息缓存使用的 slab 数量。设为 8 可优化 Unbound,使其使用更多内存进行缓存。
- rrset-cache-slabs:RRset 缓存使用的 slab 数量。设为 8 可优化 Unbound,使其为 RRSet 缓存使用更多内存。
- infra-cache-slabs:基础设施缓存使用的 slab 数量。设为 8 可优化 Unbound,使其为基础设施缓存使用更多内存。
- key-cache-slabs:密钥缓存使用的 slab 数量。设为 8 可优化 Unbound,使其为密钥缓存使用更多内存。
- rrset-cache-size:指定 RRSet 缓存使用的内存大小。本示例使用 256MB,而默认值仅为 4MB。
- msg-cache-size:指定消息缓存使用的内存大小。本示例使用 128MB,而默认值仅为 4MB。
- so-rcvbuf:将 DNS 端口 53/udp 的缓冲区大小设置为 8MB。
将 Unbound 配置为支持 DNS-over-TLS (DoT) 的 DNS 解析器
最后,添加一个新的 'forward-zone' 段落,将 Unbound 配置为本地网络的 DNS 解析器。本示例使用启用了 DoT(DNS-over-TLS)的 Quad9 DNS 服务器作为上游 DNS 解析器。
forward-zone: name: "." forward-ssl-upstream: yes ## Also add IBM IPv6 Quad9 over TLS forward-addr: 9.9.9.9@853#dns.quad9.net forward-addr: 149.112.112.112@853#dns.quad9.net
参数详解:
- forward-zone:定义 Unbound 的转发区域。
- name:设为 "." 以转发所有 DNS 查询。
- forward-addr:使用指定的转发器来转发所有 DNS 查询。本示例使用启用了 DNS-over-TLS (DoT) 的 Quad9 DNS。
全部完成后,保存并退出文件 '/etc/unbound/unbound.conf'。
接下来,运行以下命令验证 Unbound 配置,确保配置正确无误。成功后,你将看到输出 'unbound-checkconf: no errors in /etc/unbound/unbound.conf'。
unbound-checkconf

现在通过下面的 systemctl 命令重启 Unbound 服务,使更改生效。
sudo systemctl restart unbound
至此,你已完成 Unbound 的配置,它现在正以 IP 地址 '192.168.5.25' 和默认 UDP 端口 '53' 运行。
在接下来的步骤中,你将配置 firewalld 以开放 DNS 端口,并通过 Rsyslog 与 Logrotate 设置 Unbound 日志。
配置 Firewalld
至此,Unbound 已在默认 UDP 端口 53 上运行。现在你必须在 firewalld 上开放 DNS 端口 53/UDP,并允许客户端访问你的 Unbound DNS 服务器。
运行下面的 firewall-cmd 命令,添加新服务 'dns'。然后重新加载 firewalld 使更改生效。成功后,你将在终端看到类似 'success' 的输出。
sudo firewall-cmd --add-service=dns --permanent
sudo firewall-cmd --reload
接下来,运行以下命令,验证 firewalld 上已启用的服务列表。你应该会看到 'dns' 服务已添加到 firewalld。
sudo firewall-cmd --list-all
输出:

通过 Rsyslog 与 Logrotate 设置 Unbound 日志
在配置完 firewalld 之后,你现在将通过 rsyslog 与 logrotate 为 Unbound 设置日志文件。rsyslog 服务将为 Unbound 创建专用的日志文件,而 logrotate 将在特定时间轮转 Unbound 日志文件。
使用下面的 nano 编辑器命令,创建新的配置文件 '/etc/rsyslog.d/unbound.conf'。
sudo nano /etc/rsyslog.d/unbound.conf
在该文件中添加以下行。这样,rsyslog 将为 '$programname' == 'unbound' 创建新的日志文件 '/var/log/unbound.log'。
# Log messages generated by unbound application if $programname == 'unbound' then /var/log/unbound.log # stop processing it further & stop
完成后保存并退出文件 '/etc/rsyslog.d/unbound.conf'。
接下来,使用下面的 nano 编辑器命令,创建新的 logrotate 配置文件 '/etc/logrotate.d/unbound'。
sudo nano /etc/logrotate.d/unbound
在该文件中添加以下行。这将为 Unbound 日志文件 '/var/log/unbound.log' 创建按天进行的日志轮转。
/var/log/unbound.log {
daily
rotate 7
missingok
create 0640 root adm
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}
完成后保存文件并退出编辑器。
最后,运行以下 systemctl 命令,重启 rsyslog 与 logrotate 服务,使更改在你的系统上生效。
sudo systemctl restart rsyslog
sudo systemctl restart logrotate
至此,你已完成 Unbound 的安装。在下一步中,你将学习如何搭建本地客户端机器,使用两种方法将 Unbound 用作 DNS 解析器,然后从该客户端验证 Unbound DNS 服务器。

为客户端配置 DNS 解析器
要在客户端机器上设置 DNS 解析器,你可以使用多种方法。在这一步中,你将学习如何通过 NetworkManager,以及如何通过 systemd-resolved 与 NetworkManager 结合的方式来设置 DNS 解析器。
通过 NetworkManager
如果你想直接通过 NetworkManager 设置 DNS 解析器,那么你必须编辑网络接口配置文件,这些文件存储在 '/etc/NetworkManager/system-connections/' 目录下。
在本示例中,客户端机器的主要连接为 'eth0',因此 NetworkManager 上的配置应为 '/etc/NetworkManager/system-connections/eth0.nmconnection'。你的接口名可能有所不同,例如 eth1 等。
使用下面的 nano 编辑器命令,打开 NetworkManager 接口配置文件 '/etc/NetworkManager/system-connections/eth0.nmconnection'。
sudo nano /etc/NetworkManager/system-connections/eth0.nmconnection
在 '[ipv4]' 段落中添加以下行。同时,请务必将 'dns' 参数中的 IP 地址替换为你自己的 Unbound DNS 服务器。
[ipv4] ... dns=192.168.5.25 ignore-auto-dns=true
完成后保存并退出文件。

接下来,运行下面的 systemctl 命令重启 NetworkManager 服务,使更改生效。然后,通过 cat 命令查看 DNS 解析器配置文件 '/etc/resolv.conf'。
sudo systemctl restart NetworkManager
cat /etc/resolv.conf
你应该会看到 IP 地址 '192.168.5.25' 已被配置为客户端机器的默认 DNS 解析器。

通过 systemd-resolved 与 NetworkManager
另一种设置 DNS 解析器的方式是通过 systemd-resolved 与 NetworkManager。通过这种方式,你可以轻松地在整个系统范围内更改 DNS 解析器,而不依赖于系统中的网络接口。
在 RHEL 系发行版上,systemd-resolved 默认尚未安装。你可以通过下面的 dnf 命令轻松安装它。出现提示时,输入 y 确认并按 ENTER 继续。
sudo dnf install systemd-resolved
输出:

systemd-resolved 安装完成后,使用下面的 nano 编辑器命令打开配置文件 '/etc/systemd/resolved.conf'。
sudo nano /etc/systemd/resolved.conf
在 '[Resolver]' 段落中,取消 'DNS' 参数的注释,并将你的 Unbound DNS 服务器 IP 地址填入其中。
[Resolver] DNS=192.168.5.25
完成后保存并退出文件。
接下来,运行下面的 systemctl 命令启动并启用 'systemd-resolved' 服务。
sudo systemctl start systemd-resolved
sudo systemctl enable systemd-resolved

现在验证 'systemd-resolved' 服务,确保该服务正在运行。你应该收到类似 'active (running)' 的输出,这表示服务正在运行;而输出 'Loaded ../../systemd-resolved.service; enabled;..' 则表明该服务已启用,并将在系统启动时自动启动。
sudo systemctl status systemd-resolved
输出:

在 systemd-resolved 运行之后,接下来你将修改 NetworkManager 服务的 DNS 后端。
使用下面的 nano 编辑器命令,打开 NetworkManager 配置文件 '/etc/NetworkManager/NetworkManager.conf'。
sudo nano /etc/NetworkManager/NetworkManager.conf
在 '[main]' 段落下,添加 'dns' 参数,其值设为 'systemd-resolved'。这将覆盖 NetworkManager 上的 DNS 服务器设置,使其使用 'systemd-resolved' 服务。
[main] dns=systemd-resolved
完成后保存并退出文件。
现在运行下面的 systemctl 命令重启 NetworkManager 服务,使更改生效。NetworkManager 的新 DNS 解析器配置写入在 '/run/NetworkManager/resolv.conf' 文件中。
sudo systemctl restart NetworkManager
运行以下命令删除默认文件 '/etc/resolve.conf'。然后,创建从 '/run/NetworkManager/resolv.conf' 到 '/etc/resolv.conf' 的新符号链接文件。
rm -f /etc/resolv.conf
ln -s /run/NetworkManager/resolv.conf /etc/resolv.conf
至此,你的客户端机器现在正使用 systemd-resolved 作为 DNS 解析器。在 'systemd-resolved' 背后,你实际使用的是 Unbound DNS 服务器。
以下是使用 systemd-resolved 与 NetworkManager 后 '/etc/resolv.conf' 文件的内容详情。
cat /etc/resolv.conf
输出:

验证 Unbound DNS 服务器
运行下面的 dig 命令,确认 Unbound DNS 正作为 DNS 解析器工作。参数 '@192.168.5.25' 确保你使用的是运行在 IP 地址 '192.168.5.25' 上的 Unbound DNS 服务器。
dig @192.168.5.25
成功后,你将收到来自根 DNS 服务器的应答,如下所示。此外,你会注意到头部输出中的 'ad'(authentic data,真实数据)标志,这表示 DNSSEC 已启用。

接下来,运行以下命令,确认客户端可以访问互联网上的域名。
dig google.com
dig fb.com
成功后,你应该会收到域名 'google.com' 与 'fb.com' 的 DNS 记录详情。你可以看到响应查询的 DNS 解析器是 '127.0.0.53#53',也就是将 Unbound 作为默认解析器的 systemd-resolved。此外,你还可以看到每次查询的 'Query time':对 'google.com' 的查询时间为 '74ms',对 'fb.com' 的查询时间为 '154ms'。
google.com 的输出:

fb.com 的输出:

如果你重新运行上面的 dig 命令,'Query time' 应该会缩短。这证明你的查询已被缓存,DNS 缓存正在生效。
dig google.com
dig fb.com
输出:


接下来,通过下面的 dig 命令验证本地域名或子域。如果成功,每个子域都将指向在 Unbound 配置文件 '/etc/unbound/unbound.conf' 中配置的正确 IP 地址。
dig firewall.garden.lan +short
dig vault.garden.lan +short
dig media.garden.lan +short
输出:

现在运行下面的 dig 命令,确认 PTR 记录指向正确的域名。
dig -x 10.0.0.1 +short
dig -x 10.0.0.2 +short
dig -x 10.0.0.3 +short
输出:

最后,你还可以通过 tcpdump 验证 DoT(DNS over TLS)。通过下面的 dnf 命令,将 'tcpdump' 软件包安装到你的 Unbound 服务器上。
sudo dnf install tcpdump
出现提示时输入 y 并按 ENTER 继续。

现在运行下面的 tcpdump 命令,监控接口 'eth0' 上 DoT 端口 853 的流量。在本示例中,Unbound DNS 正以 IP 地址 '192.168.5.25' 和接口 'eth0' 运行。
tcpdump -vv -x -X -s 1500 -i eth0 'port 853'
切换到客户端机器,运行以下命令,通过下面的 dig 命令访问外部/互联网域名。
dig twitter.com
输出:

之后,切回 Unbound 服务器,你现在应该会在 tcpdump 输出中看到类似如下的内容。

至此,你已通过 Unbound 安装并配置了本地 DNS 服务器。同时,你还通过 systemd-resolved 与 NetworkManager 在 RHEL 客户端上配置了 DNS 解析器。
总结
在本指南中,你已在 Rocky Linux 9 服务器上安装了 Unbound 本地 DNS 服务器。你启用了 DNS 缓存、DNSSEC(默认已启用),配置了 private-address 与 ACL,通过 local-zone 添加了本地域,随后将 Unbound 配置为支持 DoT(DNS-over-TLS)的 DNS 解析器。
此外,你还配置了基础的 DNS 隐私与安全,对 Unbound 进行了优化,并通过 rsyslog 与 logrotate 配置了 Unbound 日志。
在本指南的最后,你还学习了如何在 RHEL 系发行版上通过 NetworkManager 与 systemd-resolved 设置 DNS 解析器,以及使用 dig 命令检查 DNS 服务器的基本方法。