SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor

WireGuard隧道已连接但DNS无法解析?三种故障修复

隧道已建立但出现“connection timed out”或域名解析失败?本文区分解析器无响应、DNS绕过隧道、客户端配置被覆盖三种情况,并给出对应修复方法。

WireGuard 隧道建立后 DNS 为什么会立即失效

通过 WireGuard 使用 DNS 时,通常会出现3种故障,每种故障都有对应的解决方法。可能完全无法解析;也可能名称可以解析,但查询流量从隧道外离开本机;还可能是客户端自己的解析器管理器在接口启动几秒后覆盖设置。问题几乎从来不在隧道本身,而在于告诉客户端应查询哪个解析器的那一行配置,以及决定数据包如何到达该解析器的路由。

WireGuard 只传输 IP 数据包,不了解 DNS(domain name system,即将 example.com 等名称转换为 IP 地址的服务)。客户端 [Interface] 块中的 DNS = 行不是 WireGuard 设置。启动接口的 shell 包装器 wg-quick 会读取该行,然后由 wg-quick 在隧道运行期间修改客户端的解析器配置,并在接口停止时通过 wg-quick down 恢复配置。因此,下面的每个问题都是路由问题或 wg-quick 问题,而不是加密问题。如果隧道尚未建立,请先阅读在自己的 VPS 上自托管 WireGuard VPN,然后再返回本页。

在处理 DNS 前,先确认隧道运行正常。

sudo wg show
ping -c 3 10.8.0.1
ping -c 3 1.1.1.1

wg show 应列出具有最新 latest handshake 的对等端,并且两个 ping 都应收到响应。如果 ping 1.1.1.1 超时,最可能是转发或 NAT(network address translation,网络地址转换)问题,而不是 DNS 问题。修改多少解析器配置都无法解决该问题。丢弃 ICMP 的防火墙也会导致相同的超时,因此在修改转发规则前,请使用 curl -sI https://1.1.1.1 确认。如果 ping 有响应,但实际流量开始传输后吞吐量骤降,这是另一个独立问题。WireGuard 速度慢通常都与 MTU 有关,而不是本页讨论的其他因素。本文所有示例都使用 10.8.0.0/24 作为隧道子网,使用 10.8.0.1 作为服务器的隧道地址。请替换为您自己的值。

故障一:无法解析任何域名,因为解析器没有响应

现象很明确。ping 1.1.1.1 可以正常工作,而 curl https://example.com 返回以下内容:

curl: (6) Could not resolve host: example.com

直接从客户端向隧道解析器发起查询。Ubuntu 和 Debian 中的 dig 来自 dnsutils 软件包。

dig +short @1.1.1.1 example.com
dig +short @10.8.0.1 example.com

第一条命令返回地址,说明数据包可以通过隧道到达互联网。第二条命令没有返回结果,并显示 ;; communication timed out; no servers could be reached。诊断结果就是:客户端指向 10.8.0.1,而 10.8.0.1 没有在 UDP 端口 53 上响应。

原因有两个。服务器上可能没有运行解析器,也可能是服务器防火墙在查询到达前将其丢弃。请在服务器上检查这两项。

sudo ss -ulnp | grep ':53'
sudo nft list ruleset

正在运行且绑定正确的解析器会显示包含 10.8.0.1:53 或 0.0.0.0:53 的行。在 Ubuntu 上,通常容易忽略的是 127.0.0.53:53:它是 systemd-resolved 的存根监听器,绑定到回环地址,其他机器无法访问。将 VPN 客户端指向一台唯一解析器是该存根监听器的服务器,就会产生完全相同的超时。

解决方法是让解析器监听隧道地址,并添加一条允许对等端访问它的防火墙规则。

sudo apt update && sudo apt install -y unbound
printf 'server:\n  interface: 10.8.0.1\n  access-control: 10.8.0.0/24 allow\n' \
  | sudo tee /etc/unbound/unbound.conf.d/wireguard.conf
sudo systemctl restart unbound
sudo ss -ulnp | grep '10.8.0.1:53'

然后仅允许隧道流量访问该端口。使用 nftables 时,将以下两行添加到 /etc/nftables.conf 中的 input 链,并使用 sudo systemctl reload nftables 重新加载。

udp dport 53 iifname "wg0" accept
tcp dport 53 iifname "wg0" accept

使用 ufw 时,sudo ufw allow in on wg0 to any port 53 可以完成相同的操作。不要将端口 53 对公网开放。扫描器会在数天内发现开放的递归解析器,并将其用于放大拒绝服务攻击;服务提供商通常会先于您发现相关流量。

在客户端重新运行 dig +short @10.8.0.1 example.com。如果输出中出现地址,说明解析器路径正常,客户端现在只需使用该解析器。将该行添加到客户端的 [Interface] 块中,然后使用 sudo wg-quick down wg0 && sudo wg-quick up wg0 重启接口。

[Interface]
PrivateKey = <client private key>
Address = 10.8.0.2/32
DNS = 10.8.0.1

故障二:DNS 泄漏,因为分流隧道不会路由解析器

这个问题更严重,因为表面上一切都能正常工作。域名可以解析,网页可以加载,但查询会以明文形式通过你原本不想信任的本地网络。

有两种配置会导致该问题。第一种是客户端配置了 AllowedIPs = 0.0.0.0/0, ::/0,但没有配置 DNS = 行。wg-quick 会在自己的路由表中安装默认路由,并通过 suppress_prefixlength 0 添加一条规则。该规则会有意保留更具体的本地路由,使计算机仍能访问打印机。客户端通过 DHCP 获取的解析器通常是 192.168.1.1 处的路由器,并且会匹配其中一条本地路由。你的流量经过隧道传输,但本地网络仍会收到你查询的完整域名列表。

第二种是分流隧道:配置 AllowedIPs = 10.8.0.0/24 和 DNS = 9.9.9.9。由于 9.9.9.9 不在 AllowedIPs 内,客户端没有通过隧道访问它的路由,因此查询会像第一种情况一样通过本地链路发出。

确认实际响应查询的解析器。whoami.akamai.net 是一个公共测试域名。它会返回发起查询的递归解析器的 IP 地址,因此你可以将其返回值与服务器的公网地址进行比较。

resolvectl status
dig +short whoami.akamai.net
sudo tcpdump -ni any -c 10 port 53

resolvectl status 会为每条链路输出一个块。如果以太网或无线链路对应的块仍显示 Current DNS Server: 192.168.1.1,而 wg0 块不显示任何内容,这就是泄漏。dig +short whoami.akamai.net 返回你的家庭宽带地址而不是服务器地址,可以从远端进一步确认该问题。tcpdump 行是最终确认依据:正常输出会将计算机发出的所有 53 端口数据包放在 wg0 上;发生泄漏时,这些数据包会出现在 wlan0 或 enp3s0 上。通过 lo 传输、往返于 127.0.0.53 的数据包,是应用程序与本地 systemd-resolved stub 之间的通信,不属于泄漏。

修复需要完成两部分,缺一不可。将 DNS 设置为隧道内的地址,并确保该地址位于 AllowedIPs 内。

[Interface]
Address = 10.8.0.2/32
DNS = 10.8.0.1

[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 10.8.0.0/24, 10.20.0.0/16

10.8.0.1 位于 10.8.0.0/24 内,因此查询会加密后发送到服务器。如果你坚持在分流隧道中使用公共解析器,请为其添加主机路由:AllowedIPs = 10.8.0.0/24, 9.9.9.9/32。这样数据包会通过隧道传输,但本地网络仍可能根据之前的会话看出你选择了该服务提供商。自行运行解析器可以避免这个问题。

解析器分配是手动构建 WireGuard 与协调式网状网络之间的明显差异之一,也是 WireGuard 与 Tailscale 的比较 中讨论的权衡因素。运行 自托管 Headscale 控制服务器 可以实现这种协调,而无需将密钥材料交给第三方。如果你担心的正是最后一点,请注意,Tailscale 从不持有用于加密流量的密钥;更值得关注的问题是,遭入侵的协调服务器或被窃取的身份账户可能向你的网络中添加什么。

Linux 客户端上的故障三:resolvconf 与 systemd-resolved 冲突

macOS、Windows、iOS 和 Android 客户端通过官方应用应用 DNS =,通常不会造成太多问题。Linux 的设置由 shell 脚本应用,而脚本必须猜测系统运行的是多个解析器管理器中的哪一个。

第一种故障会直接报错。sudo wg-quick up wg0 停止并显示:

resolvconf: command not found

wg-quick 会调用 resolvconf,但系统未安装该二进制文件。在 Debian 12 和 Ubuntu 24.04 及更高版本中,与 systemd-resolved 通信的 resolvconf 命令由 systemd-resolved 软件包提供。安装该软件包,然后重新启用接口。Ubuntu 22.04 没有单独的软件包。请在该系统上使用 sudo ln -s /usr/bin/resolvectl /usr/local/bin/resolvconf 手动创建兼容性链接,并跳过安装命令。

sudo apt install -y systemd-resolved
sudo systemctl restart systemd-resolved
sudo wg-quick down wg0 && sudo wg-quick up wg0
resolvectl status wg0

第二种故障不会直接显示错误,也是最容易耗费一晚排查时间的一种。接口已启用,resolvectl status wg0 正确显示 DNS Servers: 10.8.0.1,但查询仍会发送到旧的解析器。systemd-resolved 并没有忽略 wg0。与任何路由域都不匹配的名称,会发送到所有被视为默认路由的链路上的 DNS 服务器。如果某条链路配置了 DNS 服务器,但没有 route-only 域,则默认会被视为默认路由。因此,查询会同时发送到 10.8.0.1 和无线链路上的路由器,先成功返回的结果会被采用。路由器只有一跳之遥,通常会先返回;无论结果如何,查询都已经以明文发出。无线链路自身搜索域下的名称(例如 lan)只会发送到该链路,因为最具体的路由域优先。

当 wg-quick 通过 systemd 自带的 resolvconf 包装器应用 DNS = 时,它已经会请求一个全匹配路由域。因此,这种故障主要出现在其他 resolvconf 实现或网络管理器应用了该设置的情况下。显式设置可以避免依赖猜测。

在同一步中设置解析器并声明全匹配路由域。%i 会展开为接口名称,因此此代码块无需修改即可用于任何接口。

[Interface]
Address = 10.8.0.2/32
PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~.
PostDown = resolvectl revert %i

以这种方式使用 PostUp 时,请删除 DNS = 行。否则,两个机制都会写入解析器状态,之后只有其中一个机制会执行清理。~. 参数是关键部分:它会使 wg0 成为所有名称的 route-only 域。这样,systemd-resolved 就不会再将不匹配任何更具体域的名称发送到其他链路。其他链路自身域下的名称(例如 lan)仍会发送到对应链路,这通常正是本地​​主机所需的行为。请验证设置。

resolvectl status wg0

正常输出应包含 DNS Servers: 10.8.0.1 和 DNS Domain: ~.。如果缺少 ~.,说明 resolvectl domain 部分没有执行,wg0 又会与其他链路竞争。不要使用默认路由标志进行检查:即使没有 ~.,它也会读取 yes(或者在 Protocols 行上读取 +DefaultRoute,具体取决于 systemd 版本),因为没有 route-only 域的链路本身就是默认路由。

还需要注意一种情况。如果 /etc/resolv.conf 是普通文件,而不是指向 /run/systemd/resolve/stub-resolv.conf 的符号链接,则说明该文件由其他程序管理,通常是 NetworkManager 或容器运行时。在继续排查其他问题前,先运行 ls -l /etc/resolv.conf,因为每次网络发生变化时都会重写该文件的工具,可能在最不合适的时候撤销你的修改。

升级方案:通过隧道运行自己的过滤解析器

查询可以稳定通过隧道传输后,远端的解析器就成为一个控制点。在那里运行 AdGuard Home,可以为每台已连接的设备提供阻止列表过滤和查询日志,无需客户端软件,也无需逐台配置设备。官方安装脚本经 2026 年 10 月检查,只有一行。

curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v

首次运行时,设置向导监听端口 3000。通过隧道访问 http://10.8.0.1:3000,不要将该端口公开开放;然后在向导中将 DNS 监听地址和管理界面监听地址都设置为 10.8.0.1。如果故障一中的 unbound 仍占用同一地址,请先使用 sudo systemctl disable --now unbound 停止它,因为两个进程会争用同一地址上的 UDP 端口 53,AdGuard Home 会因而退出并显示 listen udp 10.8.0.1:53: bind: address already in use。

如果客户端配置中已经写的是 DNS = 10.8.0.1,则无需修改。现在,查询日志会显示每个对等端发出的、到达该解析器的所有查询;除非将某个客户端排除在日志记录之外。但这是真实的隐私决策,不是无成本的好处:你不再信任互联网服务提供商,而是将信任转移给自己,并且需要负责及时为该服务器安装补丁。暴露在互联网中的服务器必须先完成基本加固,新 VPS 的前十分钟对此有说明。

FAQ

为什么 WireGuard 隧道已连接,但域名无法解析?

隧道只传输数据包,完全不处理域名。因此,隧道正常但查询失败,说明指定的解析器没有响应。在客户端使用 dig +short @10.8.0.1 example.com 测试。收到 communication timed out 响应表示隧道地址上没有解析器监听,通常是因为 systemd-resolved stub 只绑定 127.0.0.53;也可能是服务器防火墙丢弃了从 wg0 到达的 UDP 53 端口流量。先修复监听配置,然后仅为 wg0 放行该端口。

如何检查 DNS 是否通过 WireGuard 泄漏?

在客户端运行 sudo tcpdump -ni any -c 10 port 53,并在浏览网页时监控接口列。离开计算机的每个数据包都应通过 wg0;通过 lo 与 127.0.0.53 之间传输的数据包属于本地 systemd-resolved stub,属于正常现象。如果这些查询出现在无线或以太网接口上,说明查询正在以明文离开。dig +short whoami.akamai.net 可以提供第二个结果,因为它会返回发起查询的递归解析器的公网地址。因此,如果返回的地址不是服务器地址,就可以确认存在泄漏。

使用分流隧道时,是否仍需要 DNS = 行?

需要,并且解析器地址也必须位于 AllowedIPs 内,否则客户端没有到达该地址的路由。使用 AllowedIPs = 10.8.0.0/24 时,10.8.0.1 中的解析器会被覆盖,查询也会加密。诸如 9.9.9.9 这样的公共解析器不在覆盖范围内,因此即使 DNS 行看起来正确,查询仍会通过本地链路发出。

为什么 resolvectl 显示了正确的服务器,但查询仍发送到其他位置?

systemd-resolved 会同时将不匹配任何路由域名的查询发送到所有默认路由链路,包括 wg0,并返回第一个成功的响应。该响应通常来自本地路由器。在客户端的 [Interface] 块中添加 PostUp = resolvectl dns %i 10.8.0.1; resolvectl domain %i ~.,并删除 DNS = 行。随后,resolvectl status wg0 应报告 DNS Domain: ~.;对于其他链路未使用更具体域名声明的所有域名,查询都会只发送到 wg0。

多个客户端出现故障时,应先修复哪个客户端?

先修复一个 Linux 客户端,因为只有它能显示具体工作机制。resolvectl status 和 tcpdump 会告诉您哪个解析器返回了响应,以及数据包通过哪个接口传输。手机和桌面应用会使用相同的 DNS 和 AllowedIPs 值,但不会显示底层过程。因此,Linux 客户端配置正确后,您复制的就是已经验证过的配置。