WireGuard 重叠网段与精确访问控制
10 月 11 日,在 CachyOS 上连接 WireGuard 后,远端页面还是打不开。VPN 网关能通,域名也能解析到预期地址,但请求没有到达目标服务。
实际看路由,访问目标的流量却走了当前局域网网关,随后收到不可达响应。现用配置没有包含目标主机,而两处网络又使用了重叠网段。隧道已经建立,目标请求仍然留在本地网络。
这次修复先解决客户端选路,再补齐 Hub 上对应身份的转发权限。到 10 月 14 日新增受限客户端时,又把这个过程扩展成了允许与拒绝两面的验收。
排查时,我把域名解析结果、目标地址的路由选择和实际协议访问分开看。DNS 返回预期地址,只能说明名字找对了;还要确认内核把这个地址交给哪个接口。
下面用虚构地址说明重叠关系,均取自 RFC 5737 的文档地址范围,不对应现场网络:本地和远端都使用 192.0.2.0/24,需要访问的远端服务是 192.0.2.80。本地已经有覆盖整个 /24 的路由,远端目标便可能按本地网络处理。
这次只需要访问明确的服务主机,因此给目标补了 /32 路由,让它比本地 /24 更具体。没有把整个重叠网段送入隧道,也没有改默认出口。
客户端配置里的 AllowedIPs 和系统路由也要一起核对。wg-quick 手册说明,它会根据 peer 的 AllowedIPs 推导并添加路由;而 wg 手册管理的是 WireGuard 接口自身的配置,地址和路由由其他网络工具处理。这次重新加载 peer 配置后,还需要让新增的系统路由实际生效。
持久化入口同样要先认准。现场接口由 wg-quick 的服务管理,NetworkManager 看到的是运行时映射。我修改了实际启动时读取的配置,保留原身份和既有路由,没有把临时映射当成长期配置。
临时测试配置能够访问目标,帮助确认了服务确实存在一条可用通路。但不同客户端的服务端权限可能不同,测试配置成功,不能替当前身份完成验收。
原客户端补齐主机路由后,还需要在 Hub 上补对应的转发和源 NAT。规则只匹配指定的隧道来源、目标主机和目标端口:页面服务使用 TCP 443,文件共享使用 TCP 445,既有管理通路继续保留。
AllowedIPs 在两端承担的角色也要分清。客户端用它选择哪些目标交给远端 peer;Hub 则用 peer 的地址范围校验解密后数据包的来源,并选择回包对应的 peer。它的匹配粒度是 IP 地址与前缀,端口权限仍需由防火墙控制。WireGuard 官方的 Cryptokey Routing 说明对此给出了发送与接收两个方向的解释。
所以客户端只下发目标 /32 是选路上的收敛,Hub 仍要独立执行访问控制。客户端能够修改自己的路由,不能依靠交付配置里的目标列表来保证权限范围。
对经过 Hub 转发到其他主机的流量,需要检查 FORWARD 路径;只看 Hub 自身的 INPUT 规则不够。Netfilter 的包过滤说明区分了这两条路径。现场还为这些精确匹配的服务流量配置了 MASQUERADE,处理目标侧的回程;源 NAT 改写源地址的阶段在 POSTROUTING,可参照 Netfilter 的源 NAT 文档。这只是这次网络的回程处理方式,不能据此要求所有 WireGuard 部署都做 NAT。
上线前,规则的启用和撤销先在隔离的网络 namespace 中验证:重复启用不会重复插入规则,撤销后恢复原基线,其他规则保持不变。比较时剔除自然增长的计数器,避免把流量变化误判成规则变化。
随后把策略接到 WireGuard 和防火墙的持久化入口,再做真实链路验证。测试时断开 Wi-Fi,只保留有线网络,避免请求从另一条出口绕过去。页面返回 HTTP 200,TLS 证书验证通过;SMB 协商成功,原管理连接仍可达,本次新增规则也都观察到了流量。结束后清理临时资源,恢复 Wi-Fi。
当时的 SMB 结果只证明已经连到服务并完成协议协商。账号能否登录、能够打开哪些共享目录、能否读写文件,需要另做应用层验收。
10 月 14 日新增客户端时,目标是限定服务访问。交付配置只包含页面服务、文件共享和 DNS 的主机路由,没有默认路由,也没有管理网段路由。Hub 为该身份设置专属策略,精确限制来源、目标和协议端口,再将策略接入 WireGuard 生命周期与防火墙启动钩子。
实施顺序是先让限制策略生效,再登记新 peer。这样可以避免出现身份已经可用、限制规则尚未安装的空档。
这一步还要核对磁盘配置与运行态的差异。持久化文件可能遗漏只存在于接口中的 peer;拿这样的文件做整份同步,会把遗漏当作应当删除的差异。wg 手册中,setconf 以文件内容设置接口,syncconf 则比较文件与接口后应用差异。减少会话中断的能力,并不意味着工具会自动保留文件里没有的配置。
因此,应用前先确认候选配置完整保留现有 peer 及其 AllowedIPs,临近应用时再检查运行态是否被其他操作改变。本次加载没有重启服务,回读确认原有 peer 保留,服务实例标识前后相同。回滚也按本次新增项撤销,避免用旧快照覆盖后来发生的正常变更;撤销新身份时先停用该身份,再移除它的专属限制策略。
验收从公网临时测试环境进行,使用交付配置的同一身份。除近期握手和双向流量外,还逐项检查了服务:
| 访问内容 | 预期 | 实际结果 |
|---|---|---|
| 指定 DNS 的 UDP、TCP 查询 | 允许 | 均通过 |
| 指定页面服务的 HTTPS | 允许 | HTTP 200,TLS 验证通过 |
| 指定文件服务的 SMB | 允许 | 协议协商通过 |
| 文件设备的管理入口 | 拒绝 | 被阻止 |
| 未授权的 SSH 访问 | 拒绝 | 被阻止 |
| 无关公网目标 | 拒绝 | 被阻止 |
拒绝测试还临时扩大了测试环境中的目标路由,并检查 Hub 的拒绝计数。否则,请求可能只是没有进入隧道,不能把“客户端无路由”当成服务端访问控制已经生效。测试结束后,临时命名空间、脚本和配置副本都已清理。
截至 10 月 14 日,服务端开通、配置生成,以及公网同身份的允许和拒绝验证已经完成。目标设备本身尚未导入验收;应用账号登录、文件账号权限与读写、真实重启后的恢复也仍待验证。持久化入口已写入并回读,不能替代这些最终检查。