大多数坏掉的 WireGuard 配置并不是值写错了,而是把正确的值放在了错误的一侧。私钥属于本机,peer 持有对端的公钥,地址放在 interface 块里,但它描述的是隧道所在的位置。把文件读成两个相遇的方向,然后再看辅助脚本在协议之上补了什么。协议本身的字段比文件少,多出来的那些值得单独了解。

interface 的密钥描述本机

interface 块保存本端自己的身份。PrivateKey 就是这个身份,并且永远不会离开主机;对应的公钥由它推导出来,其他 peer 列出的正是这个公钥。ListenPort 是本端接收握手的地方,所以同一地址后面的两个客户端需要不同的端口。

Address 是本机应答的隧道地址,写成带前缀长度的形式,因为它同时也是将要使用的源地址。这里前缀写错会改变主机认为哪些目标处于直连,从而产生看起来像路由而不是配置的故障。

真实客户端里的 [Interface] 块中,其余内容通常由 wg-quick 添加:DNS、MTU、Table 以及 hook 行。这些不是协议字段,裸 wg 环境会完全忽略它们。

每个 peer 条目描述另一侧

[Peer] 块是远端。 PublicKey 是对端的派生公钥,绝不是他们的私钥,而 PresharedKey 是双方必须完全一致的可选附加秘密。Endpoint 是发送目标,而 PersistentKeepalive 要求本机在间隔之后、没有其他流量时发一个包。

这里的大部分混乱来自两个错误。把自己的私钥复制进 peer 块会让两端共用同一身份,握手失败的方式还很难在日志里看出来。Endpoint 在客户端上省略通常是故意的,因为服务器会从入站握手里学到地址;在必须主动发起的服务器上省略就不是。还要记住 keepalive 只是让 NAT 映射保持打开,既不提升吞吐也不降低延迟,所以流量已经在跑时,多三十秒的包什么也换不来。

AllowedIPs 是路由,不是许可清单

AllowedIPs 是 WireGuard 的密码学密钥路由。出站时,它按目的地址决定哪个 peer 收到包。入站时,它按源地址决定来自该 peer 的包是否被接受。同一个字段同时干这两件事,所以它既不是访问控制列表,也不是允许访问的网站清单。

狭窄的 AllowedIPs 在服务器上意味着服务器只接受声称来自那些源地址的包,也只把那些目标发给该客户端。只放宽一侧而不放宽另一侧,就会得到一条能起来但什么都不通的隧道。

wg-quick 在协议之外加了什么

wg-quick 读取同一个文件,并执行协议留给操作系统的网络步骤。它分配 interface 地址,添加由 AllowedIPs 推导出的路由,写入解析器设置,并按顺序执行 hook 命令。

这一层解释了经典的不对称。0.0.0.0/0 和 DNS 行组成的文件给客户端默认路由和解析器,但对服务器的转发或 NAT 规则毫无作用。那些属于服务器自身的配置,任何客户端文件都提供不了。

让文件只有一个归属。SaveConfig 开启时手工编辑意味着下次 interface 关掉时你的修改就消失了,而 interface 不会告诉你发生过这件事。

检查已应用的状态,而不是文件

文件只是一种意图。已应用的状态才是内核实际持有的,而只要辅助工具、drop-in 或 hook 在激活后改过什么,两者就会分叉。

把实时状态和地址配置一起读,再拿两者与你自己写的对比:

sudo wg show
ip -4 addr show dev wg0
ip -4 route show dev wg0
grep -v "^#" /etc/wireguard/wg0.conf

如果 peer 列表为空,说明 interface 创建时配置并没有被应用。如果有地址和路由但计数器一直为零,说明文件已经加载而握手正在失败,这是密钥或端点问题,不是段落顺序问题。编辑之前先对比这两种视角,因为两者之间的差距通常才是真正的故障。

相关文章