WireGuard 无法访问家庭局域网的排查指南
WireGuard 握手成功但无法访问 LAN 内设备?本文详解四个关键配置点,包括 AllowedIPs 设置、IP 转发开启及路由表配置,助您快速定位并解决数据包无法到达目标主机的问题。
为什么家庭 LAN 无法通过 WireGuard 响应
要通过 WireGuard 路由到家庭 LAN,必须同时正确配置4项设置。即使其中3项正确,隧道看起来仍然完全正常,这正是问题难以排查的原因。wg show显示最近完成过握手,ping 10.8.0.1在几毫秒内返回响应,而 ping 192.168.20.10 完全没有返回。
以下是数据包依次经过的完整检查项。LAN 表示局域网,即家庭路由器后面的私有网络。
- 客户端上的
AllowedIPs必须覆盖远程子网,否则数据包根本不会进入隧道。 - 服务器上的
AllowedIPs必须覆盖客户端的隧道地址,否则数据包解密后会立即被丢弃。 - 服务器上的
net.ipv4.ip_forward必须设为1,因为 Linux 会丢弃所有目标地址不是本机的数据包。 - LAN 必须知道返回
10.8.0.0/24的路由。可在服务器上配置 masquerade 规则,或在家庭路由器上配置静态路由。
这些设置中的任何一项出错时,都不会显示错误消息。不会记录日志,也不会发出警告,握手会始终正常工作。按顺序检查这些设置,通常约1分钟即可找出出错项。
本指南使用的网络
下面的所有地址都只是示例。请替换为您自己的地址,并保持替换结果一致,因为配置只更新了一部分是此问题的第二常见原因。
- 家庭 LAN 为
192.168.20.0/24。家庭路由器为192.168.20.1。 - WireGuard 服务器是该 LAN 中的一台 Linux 主机。其 LAN 接口
enp1s0使用192.168.20.5,隧道接口wg0使用10.8.0.1。 - 要访问的主机是位于
192.168.20.10的 NAS(网络附加存储)。 - 客户端是一台位于其他网络中的笔记本电脑,在隧道内使用
10.8.0.2。
服务器是 LAN 中的一台主机,不是路由器本身。通常是 Raspberry Pi 或旧的 mini PC。这一点与规则 4 有关:除非您告知路由器,否则路由器不知道隧道的存在;LAN 中的每台主机都会将发往子网外的流量发送到该路由器。
如果您的家庭网络连接没有公网 IP 地址,这些配置无法单独工作,因为互联网中的任何主机都无法向您的家庭网络发起握手。通过 VPS 中转一节介绍这种情况。相同的四条规则仍然适用,但还需要额外维护一个对等端。
AllowedIPs 表示两种不同的含义
一个设置承担两项功能。在连接两端以相同方式理解它,是大多数此类工单的根本原因。WireGuard 将此机制称为 cryptokey routing,详见 WireGuard 如何将公钥绑定到 IP 地址范围。
从出站方向看,AllowedIPs 是路由表。 wg-quick 会将每个条目转换为指向 wg0 的路由。只有当某个对等端声明的范围包含 192.168.20.10 时,发往该地址的数据包才会被加密并发送给该对等端。只列出 10.8.0.0/24 时,笔记本会将 LAN 流量从本地 Wi-Fi 发出,结果要么丢失,要么到达完全不同的 192.168.20.10。
从入站方向看,AllowedIPs 是访问控制列表。 WireGuard 解密来自对等端的数据包后,会将内部源地址与该对等端的 AllowedIPs 进行匹配;如果不匹配,就丢弃该数据包。除非启用内核调试消息,否则不会记录任何日志。唯一的线索是 wg0 上的接收错误计数,该计数由 ip -s link show wg0 输出,而数据包会直接消失。
因此,两个配置文件绝不是彼此的镜像。客户端列出希望通过服务器访问的目标。服务器列出允许该客户端用作源地址的地址范围。
匹配的配置文件对
客户端,/etc/wireguard/wg0.conf:
[Interface]
PrivateKey = <laptop private key>
Address = 10.8.0.2/32
[Peer]
PublicKey = <server public key>
Endpoint = home.example.com:51820
AllowedIPs = 10.8.0.0/24, 192.168.20.0/24
PersistentKeepalive = 25服务器,/etc/wireguard/wg0.conf:
[Interface]
PrivateKey = <server private key>
Address = 10.8.0.1/24
ListenPort = 51820
[Peer]
PublicKey = <laptop public key>
AllowedIPs = 10.8.0.2/32家庭路由器还需要配置端口转发,将 UDP 51820 转发到 192.168.20.5;否则握手不会开始,客户端日志中会出现 Handshake for peer 1 did not complete after 5 seconds, retrying。本指南假定您已经完成这一步。
这里有 4 行配置不同于普通的全隧道设置,而且每一处差异都有明确目的。
- 客户端使用
AllowedIPs = 10.8.0.0/24, 192.168.20.0/24,而不是0.0.0.0/0, ::/0。这是分流隧道:隧道子网和家庭 LAN 通过wg0,其他流量继续使用本地路由。网页浏览不会经过家庭网络连接;如果您只需要访问 NAS,通常应采用这种设置。 - 服务器使用
AllowedIPs = 10.8.0.2/32,而不是一个地址范围。请在此处填写10.8.0.0/24,这样该单个客户端可以使用隧道内的任意地址。之后如果添加地址范围重叠的第二个 peer,流量会转到最后配置的 peer,且任何位置都不会打印错误。 - 仅在客户端使用
PersistentKeepalive = 25。客户端位于 NAT(网络地址转换)之后,其路由器在静默 1 到 2 分钟后会忘记 UDP 映射,因此服务器将无法再访问客户端。服务器有公网地址,不需要 keepalive。 - 目前还不要添加
DNS =行。添加后会更改整个客户端计算机的名称解析。下面的 DNS 部分会在您启用此配置前解释其作用。
全隧道同样可以访问 LAN,因为 0.0.0.0/0 匹配所有地址。但它会承载您的全部流量,并导致客户端无法解决的子网冲突。
两端使用同一子网会导致连接失败
选择一个几乎不会与其他网络重复的家庭子网,例如 192.168.20.0/24 或 10.44.7.0/24。192.168.1.0/24 和 192.168.0.0/24 是大多数消费级路由器的出厂默认设置,因此你的笔记本迟早会连接到某个咖啡馆或酒店网络,而该网络恰好使用相同的地址范围。
地址冲突会直接阻止连接,但在两种模式下的表现不同。在拆分隧道中,wg-quick 尝试为一个已存在于 Wi-Fi 接口上的前缀添加路由。如果现有路由的 metric 相同,ip route add 会拒绝该操作,导致接口无法启动:
RTNETLINK answers: File exists在由 NetworkManager 管理的笔记本上,Wi-Fi 路由通常带有 600 之类的 metric,因此添加操作会成功,而 metric 为 0 的隧道路由会获得优先级。NAS 可以响应,但咖啡馆网络中该地址范围内的所有主机(通常包括其 DNS 服务器)现在都会位于隧道后方。
在全隧道模式下,wg-quick 会安装策略路由规则,明确让更具体的路由绕过主路由表。本地的 192.168.1.0/24 路由会优先于隧道路由,因此所有发往远程 LAN 的数据包都会通过本地链路发送。隧道已启动,握手也正常,但 NAS 无法访问。唯一真正的解决方法是为家庭 LAN 重新编号。
将服务器配置为路由器
ip route show default
printf 'net.ipv4.ip_forward = 1\n' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forward第一条命令会显示 LAN 接口的实际名称。当前镜像通常使用 enp1s0 或 ens3 这样的名称,很少使用 eth0。如果 masquerade 规则指定了错误的接口,就不会匹配任何流量。最后一条命令应输出 net.ipv4.ip_forward = 1。直接执行 sudo sysctl -w 也会设置相同的值,但该设置会在下一次重启后丢失,这正是“运行到星期二就失效”这一经典问题的原因。
转发还必须通过防火墙。在启用 ufw 的 Ubuntu 上,除非在 /etc/default/ufw 中设置 DEFAULT_FORWARD_POLICY="ACCEPT",否则转发的数据包会被丢弃。使用默认 iptables 后端的 Docker 在启动时需要启用转发时,会自行设置相同的策略,除非在 /etc/docker/daemon.json 中设置 ip-forward-no-drop。因此,在一台从未手动配置防火墙的服务器上,如果 sudo iptables -S FORWARD | head -1 输出 -P FORWARD DROP,通常表示这是 Docker 设置的;但配置脚本或其他防火墙管理器也可能设置该值。无论由谁设置,都必须为隧道流量配置明确的允许规则。
为什么回复数据包始终无法返回
正确配置规则 1 到 3 后,ping 确实可以到达 NAS。但您仍然看不到响应,因为回复没有返回路径。NAS 回复 10.8.0.2,这是其自身子网之外的地址,因此会将数据包交给默认网关,即位于 192.168.20.1 的家庭路由器。该路由器从未学习过 10.8.0.0/24 的路由,因此会将回复转发到自己的默认网关,也就是互联网连接,随后数据包被丢弃。请求可以到达,但回复会被丢弃。
选项 A:在 WireGuard 服务器上配置 masquerade。 服务器会将其从 enp1s0 转发出去的每个隧道数据包的源地址改写为 192.168.20.5,即该接口自身的 LAN 地址。此时,NAS 看到的请求来自同一子网中的邻居,因此会直接向服务器回复;服务器再还原地址改写,并将回复通过隧道发送回去。LAN 上的其他设备无需修改任何配置。
将规则写入服务器的 [Interface] 块中,使规则随接口一同创建和删除:
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o enp1s0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o enp1s0 -j MASQUERADE如果设备已经由 nftables 管理,则改为写入 /etc/nftables.conf:
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
ip saddr 10.8.0.0/24 oifname "enp1s0" counter masquerade
}
}保留 counter 关键字。没有它,sudo nft list ruleset 会打印不包含数据包计数的规则,而该计数正是判断规则是否生效的依据。
Masquerade 还有一个容易忽略的好处。许多主机运行的防火墙只接受来自自身子网的连接。Windows 文件共享默认采用这种行为,多个 NAS 管理面板也是如此。对于这类主机,即使路由完全正确,来自 10.8.0.2 的数据包仍会被丢弃。使用 masquerade 后,源地址变为 LAN 地址,因此这些规则可以匹配。代价是,所有隧道客户端在每台 LAN 主机的日志中都会显示为 192.168.20.5,因此您无法区分客户端,LAN 设备上的按客户端规则也无法生效。
选项 B:在家庭路由器上配置静态路由。 告诉路由器 10.8.0.0/24 位于 192.168.20.5 后面。在 Linux 路由器上只需执行一条命令:
sudo ip route add 10.8.0.0/24 via 192.168.20.5消费级路由器通常会在高级设置中提供名为 Static Routes 或 Routing 的页面:目标地址 10.8.0.0,掩码 255.255.255.0,网关 192.168.20.5。将其保存到路由器的持久配置中,因为在 Linux 主机上手动执行的 ip route add 会在下次重启后丢失。
这种方式会保留真实的客户端地址,因此 LAN 上的日志和按客户端规则仍然有效。它要求路由器支持静态路由,并且只对将该路由器用作默认网关的主机有效。任何配置了按子网限制的本地防火墙的主机,仍需为 10.8.0.0/24 单独添加规则。建议先使用 masquerade,因为它不需要修改您已控制的设备之外的任何配置;当您需要保留真实客户端地址时,再改用静态路由。
如何判断四项中的哪一项有问题
从客户端开始向外排查。每一步都能说明数据包是否已经到达该位置。
数据包是否进入隧道?在客户端运行:
ip route get 192.168.20.10输出应显示 dev wg0。如果显示的是 Wi-Fi 接口,则说明规则 1 有误:客户端的 AllowedIPs 未覆盖 LAN 子网;或者相同网段的本地路由优先级更高,这就是上文所述的子网冲突。出现 ping: connect: Network is unreachable 也指向同一行配置。
数据包是否到达服务器?在服务器上运行以下命令,然后从客户端 ping NAS:
sudo tcpdump -ni wg0 icmp正常路径会显示 IP 10.8.0.2 > 192.168.20.10: ICMP echo request。其中 ICMP 是 internet control message protocol(互联网控制消息协议),也是 ping 使用的协议。在握手正常的情况下,如果这里没有任何输出,几乎总是规则 2 有误:该对等端的服务器 AllowedIPs 未包含 10.8.0.2,因此数据包在解密阶段就被丢弃,还未到达 wg0。如果服务器上的 ip -s link show wg0 中 RX 错误计数随每次 ping 增加,即可确认这一点。如果计数保持不变,请检查客户端上的防火墙是否阻止了发往隧道的出站流量。
数据包是否离开服务器并进入 LAN?在服务器上监视 LAN 侧:
sudo tcpdump -ni enp1s0 icmp如果在 wg0 上能看到请求,而这里没有任何输出,说明规则 3 有误:转发功能未启用,或 FORWARD 规则丢弃了数据包。较少见的情况是,严格反向路径过滤(rp_filter)丢弃了数据包。这通常发生在服务器返回源地址的路由经过其他接口时。如果这里能看到源地址为 10.8.0.2 的请求,但没有回复,通常说明规则 4 有误,回复没有返回路由。不过,忽略 ping 的主机,或只响应来自自身子网的 ping,也会表现出相同现象。如果这里能看到源地址为 192.168.20.5 的请求,但没有回复,说明 masquerade 规则正常工作,问题在于目标主机本身没有响应。此时应检查 NAS 上的防火墙;如果目标是 Linux 主机,还应检查 net.ipv4.icmp_echo_ignore_all 是否设置为忽略 ping。将 icmp 替换为 port 445 或需要检查的其他端口后,同样的排查步骤也适用于其他服务。
可以访问 IP,但无法解析名称
ssh 192.168.20.10 正常工作,而 ssh nas.home.arpa 失败:
ssh: Could not resolve hostname nas.home.arpa: Name or service not known隧道本身没有问题。名称解析走的是独立路径,您的笔记本仍在向从本地 Wi-Fi 获取的解析器发起请求。该解析器不知道家庭网络中的名称。
要让家庭名称正常解析,必须同时满足两个条件。解析器地址必须位于客户端的 AllowedIPs 中,否则 DNS(域名系统)查询不会进入隧道。解析器还必须接受源地址为 10.8.0.2 的查询。许多家庭解析器默认拒绝此类请求:使用 local-service 运行的 dnsmasq 只响应来自直接连接子网的查询,Pi-hole 也默认仅允许本地请求。伪装规则可以隐藏此问题,因为重写后,查询会以 192.168.20.5 作为源地址到达。
客户端只需添加一行配置:
DNS = 192.168.20.1在需要 openresolv 或等效配置的 Linux 客户端上,否则 wg-quick 会以 resolvconf: command not found 停止。设置前,先确认它在使用 systemd-resolved 的系统上的行为:wg-quick 会独占注册这些服务器,因此隧道建立后,笔记本上的查询都会发送到家庭解析器,而不仅是家庭名称。只有其他接口声明了更具体域名的名称才会继续发送到其他位置。使用 resolvectl status wg0 检查结果。如果您希望家庭名称在家庭网络中解析,其他名称在本地解析,这就是分流 DNS;修复 WireGuard 隧道上的 DNS 详细介绍了完整配置。
家中没有公网 IP?让 VPS 充当中转
如果路由器状态页显示 WAN 地址属于 100.64.0.0/10,说明你位于 CGNAT(运营商级网络地址转换)之后,来自互联网的握手无法到达家中。这里的私有 192.168.x.x 地址通常表示你的路由器前面还有第二台路由器,也就是双重 NAT:将 ISP 提供的调制解调器路由器切换为桥接模式,或者也在该设备上转发 UDP 51820。如果无法修改该设备,或者它自己的 WAN 地址也不是公网地址,则按 CGNAT 处理。出站握手仍可正常工作,因此需要增加一个具有公网地址的第三个节点。使用一台小型 VPS 运行中心节点,家中的设备主动连接到它。
这 4 条规则不变。现在它们跨越两跳,因此需要维护的配置增加一倍。
- 在 VPS 上,家中设备的对等端条目获取
AllowedIPs = 10.8.0.3/32, 192.168.20.0/24:填写其自身的隧道地址,以及允许它代为传输的子网。 - 在 VPS 上,笔记本电脑的对等端条目仍使用
AllowedIPs = 10.8.0.2/32。 - 在笔记本电脑上,VPS 对等端获取
AllowedIPs = 10.8.0.0/24, 192.168.20.0/24,因为现在所有流量都发送到中心节点。 - 在家中设备上,VPS 对等端获取
AllowedIPs = 10.8.0.0/24,并且家中设备承载PersistentKeepalive = 25,因为它现在位于 NAT 后面。 - VPS 还需要
net.ipv4.ip_forward = 1,其转发链必须允许wg0到wg0,因为笔记本电脑的流量从同一接口进入并离开。为普通全隧道 VPS 编写的防火墙规则恰好会阻止这种流量。
如果还没有配置 VPS 端,在 VPS 上设置 WireGuard介绍了密钥生成、防火墙和 systemd 单元的配置方法。有关访问无法接受入站连接的设备的通用方案,请参阅从 CGNAT 后建立反向隧道。如果不想再手动维护对等端地址,运行 Tailscale 子网路由器可以自动完成地址维护,并实现相同的路由功能。
使其在重启后仍能运行
sudo systemctl enable --now wg-quick@wg0
sudo systemctl enable --now nftables
sudo wg showenable --now 是最容易被忽略的部分。手动运行的 wg-quick up wg0 在下一次内核升级并重启后就会消失。wg show 应列出该对端,并显示最近的 latest handshake 记录,同时两个方向的传输计数器都应为非零值。
之后添加第二个客户端不需要重启,因为重启会断开当前已连接的所有客户端:
sudo bash -c 'wg syncconf wg0 <(wg-quick strip wg0)'wg-quick strip 会输出配置,但不会包含只有 wg-quick 能识别的密钥;syncconf 会在保持活动会话运行的情况下应用差异。它只更新对端。修改 Address 或新增 PostUp 行,仍需要完整执行 down 和 up。
FAQ
为什么可以 ping 通 WireGuard 服务器,却无法访问家庭 LAN 中的其他设备?
能 ping 通 10.8.0.1 只能证明隧道已建立。LAN 中的其他设备无法访问,原因通常是路由问题。客户端的 AllowedIPs 必须包含 192.168.20.0/24,否则数据包不会进入隧道。服务器需要将 net.ipv4.ip_forward 设置为 1,否则会丢弃目标地址不是自身的数据包。LAN 还需要一条返回 10.8.0.0/24 的路由。在服务器上运行 sudo tcpdump -ni enp1s0 icmp,同时执行 ping:如果请求以源地址 10.8.0.2 发出,却没有任何响应返回,通常说明缺少返回路径。
家庭路由器上是否需要静态路由?
只有在不使用 masquerade 规则时才需要。WireGuard 服务器上的 masquerade 规则会将隧道流量的源地址改写为服务器自身的 LAN 地址。这样,LAN 主机会向已知的邻居返回数据,路由器也无需参与。另一种方法是添加一条经由服务器 LAN 地址访问 10.8.0.0/24 的静态路由。当您希望在 LAN 主机的日志中保留真实的客户端地址,或希望在这些主机上配置按客户端区分的防火墙规则时,静态路由值得采用。
为什么两端使用相同子网会导致隧道中断?
笔记本无法同时为同一前缀使用两条路由。如果本地网络分配 192.168.1.0/24,而家庭 LAN 也是 192.168.1.0/24,则分流隧道 wg-quick up 可能在 ip route add 因 RTNETLINK answers: File exists 拒绝请求时失败;或者在 Wi-Fi 路由的 metric 更高时成功建立,但会将该范围内的本地主机隐藏在隧道后面。全隧道可以建立,但 wg-quick 会安装策略规则,使主路由表中更具体的路由失效,因此本地网络优先,远程 LAN 仍然无法访问。将家庭 LAN 重新编号为较少使用的网段,例如 192.168.20.0/24。客户端没有其他解决方法。
可以通过 IP 访问 NAS,但无法通过名称访问。缺少什么?
名称解析不会自动通过隧道传输。将 DNS = 192.168.20.1(家庭解析器)添加到客户端的 [Interface] 块中,并确保该地址包含在对等端的 AllowedIPs 内,否则查询不会进入隧道。然后确认解析器会响应来自其自身子网之外的查询,因为 dnsmasq 配合 local-service,以及 Pi-hole 的仅本地监听模式,都会拒绝此类查询。WireGuard 服务器上的 masquerade 规则可以改写查询的源地址,从而绕过这一限制。
家庭网络没有公网 IP。仍然可以访问 LAN 吗?
可以,但需要第三个节点。在 CGNAT 后面时,路由器的 WAN 地址不是公网地址,因此互联网中的对等端无法主动向其发起握手;但出站握手通常不受影响。在具有公网地址的 VPS 上运行 WireGuard,让家庭设备通过 PersistentKeepalive = 25 主动连接 VPS,并在 VPS 上为家庭设备配置对等端条目,其中 AllowedIPs 包含其隧道地址以及 192.168.20.0/24。随后还需要在 VPS 上启用转发,并添加允许 wg0 到 wg0 的转发规则。