协议与内核路径弄清之后,生产事故多半出在两张表不一致:cryptokey
routing(wg)与宿主机路由/防火墙(ip/nft)。本文先给可复现的双
netns 实验,再列配置工作流与故障模式。环境:
- OS /
内核:
6.6.87.2-microsoft-standard-WSL2 - 工具:
wireguard-tools v1.0.20260223 - 权限:创建 netns / WireGuard 接口需要
CAP_NET_ADMIN(示例如下用sudo)
密钥在文中用占位符;握手时间、transfer 字节、ping RTT 来自真实执行。
一、工具链最小集
wg genkey # 私钥 → stdout(Base64,44 字符)
wg pubkey # stdin 私钥 → 公钥
wg genpsk # 可选 PSK
wg show [iface] # 运行时状态
wg showconf iface # 导出可再喂给 setconf 的配置
wg set / setconf / syncconf
wg-quick up|down conf # 封装 ip link/addr/route + wg本机核对:wg genkey 与
wg pubkey 往返一致;Base64 公私钥长度均为
44。wg --version 输出含 tools 版本与项目
URL。
哲学提醒:公钥交换是带外的——genkey
只负责本地密钥,不负责信任。
二、双 netns 实测(可复现)
拓扑:两个 netns,各一张 wg0;外层用 veth
模拟「公网」可达性。
wgdemo-a wgdemo-b
wg0 10.66.0.1/24 wg0 10.66.0.2/24
veth-a 192.0.2.1/30 <-------> veth-b 192.0.2.2/30
listen 51820 listen 51820
关键步骤(简化;需 root):
ip netns add wgdemo-a
ip netns add wgdemo-b
ip link add veth-a type veth peer name veth-b
ip link set veth-a netns wgdemo-a
ip link set veth-b netns wgdemo-b
# 为 veth / lo / wg0 配址并 up,wg set 写入私钥、peer、AllowedIPs、EndpointEndpoint 指向对端 veth
地址:51820,AllowedIPs
只放对端隧道地址 /32。
2.1 握手前
wg show(a 侧,以下输出经删减密钥):
interface: wg0
public key: (local public key)
listening port: 51820
peer: (b public key)
endpoint: 192.0.2.2:51820
allowed ips: 10.66.0.2/32
此时没有 latest handshake——尚无会话。
2.2 ping 触发握手
ip netns exec wgdemo-a ping -c 3 -W 1 10.66.0.2实测结果:
PING 10.66.0.2 (10.66.0.2) 56(84) bytes of data.
64 bytes from 10.66.0.2: icmp_seq=1 ttl=64 time=0.361 ms
64 bytes from 10.66.0.2: icmp_seq=2 ttl=64 time=0.257 ms
64 bytes from 10.66.0.2: icmp_seq=3 ttl=64 time=0.160 ms
--- 10.66.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2042ms
rtt min/avg/max/mdev = 0.160/0.259/0.361/0.082 ms
同机 netns 的 RTT 只说明路径打通,不能外推到跨公网延迟或吞吐。
2.3 握手后
peer: (b public key)
endpoint: 192.0.2.2:51820
allowed ips: 10.66.0.2/32
latest handshake: 2 seconds ago
transfer: 476 B received, 532 B sent
latest handshake 与非零
transfer 是会话活着的直接证据。第一次 ping
会叠握手代价;本实验三发均成功,说明 1.5 RTT
确认在局域网量级可忽略——广域网需另测。
清理:ip netns del wgdemo-a;ip netns del wgdemo-b。
三、配置模型:两张表
| 表 | 谁写 | 管什么 |
|---|---|---|
| cryptokey routing | wg / wg-quick 的
[Peer] |
公钥、AllowedIPs、Endpoint、PSK |
| 主机网络 | ip addr / ip route /
nft |
隧道地址、谁从 wg0 出去、过滤 |
wg-quick 会按配置尝试补路由(例如
AllowedIPs = 0.0.0.0/0 时的默认路由与 DNS
处理),但不能替你理解业务拓扑。常见错误:
- AllowedIPs 太窄:对端内网
10.0.0.0/8要访,却只写了隧道/32→ 加密选不中 peer(-ENOKEY语义)。
- AllowedIPs 太宽且冲突:两 peer
都写重叠前缀 → trie
最长匹配决定胜者,表现为「偶发走错隧道」。
- FIB 与 AllowedIPs
脱节:包从别的接口出去,根本不进
wg0。
- 只配了一端 Endpoint:另一端靠漫游学习;若双方都在 NAT 后且都未主动发起,可能长期学不到。
入站方向记住:解密后源 IP 必须落在该 peer 的
AllowedIPs,否则静默丢弃——防火墙在 wg0
上看到的源地址有密码学约束(见哲学/协议篇)。
四、wg-quick
与裸 wg 怎么选
- 单机 / 笔记本 / 简单
site-to-site:
/etc/wireguard/foo.conf+wg-quick up foo。
- 编排系统、K8s
CNI、自研控制器:
ip link add type wireguard+wg set/ netlink,自己管地址与路由。
syncconf:用新配置与内核状态做差量同步,适合自动轮换 peer,避免先down再up的抖动。
配置文件骨架:
[Interface]
PrivateKey = <local private>
ListenPort = 51820
# Address / DNS / PreUp 等由 wg-quick 解释
[Peer]
PublicKey = <remote public>
AllowedIPs = 10.66.0.2/32, 10.0.0.0/24
Endpoint = 203.0.113.5:51820
PersistentKeepalive = 25PersistentKeepalive:NAT
后需要维持映射时再用(秒);内网双静态 endpoint
通常不必。官方与论文都强调 listen port 与源端口一致,利于
NAT。
五、漫游、NAT 与排障顺序
漫游:对端外层源
IP/端口变化时,只要包通过认证,本端更新 endpoint(NDSS
§II-A)。排障时先 wg show 看 endpoint
是否仍是旧地址。
建议顺序:
wg show—— 有无 peer、有无 handshake、transfer 是否增长
ip route get <dst>—— 是否出wg0
- 对端 AllowedIPs 是否包含你的内层源 IP
- UDP 51820(或自定端口)是否被中间策略丢弃
- MTU:WireGuard 默认常设 1420 量级;外层开销约数十字节,TCP 建议配合 MSS clamp,避免巨型内层包分片黑洞
静默丢弃是特性:不要期待「认证失败 ICMP」。抓包只能看到
UDP;要用 wg show 与对端日志对照。
六、明确不适合的场景
| 场景 | 更合适的方向 |
|---|---|
| L2 延伸、广播域 | VXLAN/GRE 等;或 L2VPN + 另议加密 |
| 强制 X.509 合规隧道 | IKEv2 / 企业 SSL VPN |
| 纯 TCP 出境、UDP 被 ban | 需混淆/封装(额外组件,非 WireGuard 本体) |
| 应用层身份与设备姿态 | WireGuard 只做数据面;控制面见零信任 / IAM |
全隧道 AllowedIPs = 0.0.0.0/0
在笔记本远程接入很常见,但要把
kill-switch(防泄漏)、DNS、分流规则当成同一次变更,不要只改
WireGuard 段。
七、小结
- 先让
wg show出现latest handshake,再查应用层。
- AllowedIPs 与
ip route必须一起设计。
- 本机 netns 实验证明:最小配置下三次 ping 全过,RTT 亚毫秒;生产数字需在真实链路上重测。
下一篇把「简单」放进形式化证明、密码敏捷性与后量子争论里检验。
参考资料
实验 / 工具
- 本文 netns 实验:WSL2 Linux
6.6.87.2,wireguard-tools v1.0.20260223(2026-07-19)。 wg(8)、wg-quick(8)man page。
规范 / 论文
- Donenfeld NDSS 2017 §II–IV(cryptokey routing、基本用法、漫游)。
- https://www.wireguard.com/quickstart/
站内
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【WireGuard】内核代码路径:从 wg_xmit 到加解密 worker
基于 Linux 6.6 drivers/net/wireguard:wg_xmit 与 AllowedIPs trie、noise 握手入口、encrypt/decrypt worker、messages.h 常量与 netlink 配置面。与 network/89 全景文互补。
【网络工程】WireGuard 内部实现:Cryptokey Routing、Noise IK 握手与内核数据路径
WireGuard 用不到 4000 行内核代码替代了 IPSec/IKE 数十万行的协议栈——这不是少写功能,而是用固定密码学原语、固定握手模式和 cryptokey routing 这三个设计决定,换掉了证书体系、协商机和 SPD/SAD 分离。本文钻进源码和协议握手过程,逐层拆解 cryptokey routing 的双向执行、Noise IKpsk2 的 1.5 RTT 握手(含四次 ECDH 的安全属性贡献)、内核多核队列架构、timer 状态机、cookie DoS 防御和三密钥对轮换。
【WireGuard】设计哲学:故意的分层破坏与密码学观点
WireGuard 不是把 IPSec 写短一点。NDSS 2017 论文把「正确分层」当成复杂度的来源,用 cryptokey routing、固定原语与带外密钥分发换可审计实现。本文钉住这些设计决定及其边界。
【WireGuard】协议原理:Noise IKpsk2、四次 DH 与 Cookie
对齐 wireguard.com/protocol 与 NDSS 2017:IKpsk2 消息布局、es/ss/ee/se 四次 DH 的安全贡献、1.5 RTT key confirmation、MAC1/MAC2 cookie,以及 messages.h 中的 rekey 常量。