WireGuard 与现代 VPN

系统讲解 WireGuard 的设计原理与工程落地:从 Noise_IK 握手、Curve25519 与 ChaCha20-Poly1305 密码学套件,到 Cryptokey Routing 的 AllowedIPs 语义、漫游与密钥管理,再到 wg-quick 配置、性能对比与多点对多点组网实践。

IPsec 的配置需要理解 IKE、SPD、SAD、策略与提案协商的层层嵌套,OpenVPN 则把 TLS 控制通道和一堆可调参数塞进同一个进程——两者都能用,但都「难配、难审、难排错」。WireGuard 反其道而行:整个实现只有约四千行内核代码,密码学套件固定不可协商,配置就是几段 [Peer],却能做到更快的握手与更高的吞吐。

本文从 WireGuard 的设计哲学讲起,拆解 Noise_IK 握手与固定密码学套件的选择,然后重点解释 Cryptokey Routing 里 AllowedIPs 的双重语义,最后落到 wg-quick 实战配置、性能对比与多点对多点组网。

一、从 IPsec/OpenVPN 到 WireGuard

1.1 设计哲学:少即是多

WireGuard 的核心判断是:绝大多数 VPN 部署只用得到一套「现代且安全」的密码学,与其提供几十种可协商组合让人配错,不如把组合固定死。这带来三个直接结果:代码量小到可审计、没有降级攻击面、握手极快。

WireGuard 与前辈的对比:
  IPsec/IKEv2 : 协议栈庞大,策略与提案组合爆炸,互操作性差
  OpenVPN     : 用户态,TLS 控制通道,配置项繁多,吞吐受限于实现
  WireGuard   : 内核态,固定密码学,约 4000 行,无算法协商

1.2 固定的密码学套件

WireGuard 不允许协商算法,密码学原语在编译期确定。这些原语都是现代、抗侧信道、且在常见 CPU 上有加速实现的。

用途算法说明
密钥交换Curve25519ECDH,256 位,抗侧信道
对称加密ChaCha20-Poly1305AEAD,无 AES-NI 也快
哈希BLAKE2s比 SHA 更快,用于握手哈希
握手 MACHMAC-BLAKE2s / keyed-BLAKE2s认证与密钥派生
密钥派生HKDF从握手结果派生会话密钥

二、Noise 协议框架与握手

2.1 Noise_IK 握手模式

WireGuard 的握手基于 Noise Protocol Framework 的 IK 模式:发起方(I)的静态公钥对响应方(R)已知,响应方的静态公钥对发起方已知。一次握手只有两个报文,即可完成双向认证并派生出会话密钥。

Noise_IK 握手(两报文):
  发起方 Initiator                        响应方 Responder
  ------------------                      -----------------
  Handshake Initiation
    + 加密的静态公钥 (I 的身份)
    + 时间戳(抗重放)
    + 临时公钥 ephemeral
                       -------------->

                                            Handshake Response
                                              + 响应方临时公钥
                                              + 加密的空载荷
                       <--------------

  握手完成 → 双方派生:
    - 一对传输密钥(发送/接收各一)
    - 用于 Cookie 抗 DoS 的密钥

WireGuard 用单调递增的时间戳抗重放:握手报文里带 TAI64N 时间戳,响应方拒绝「不新于上次」的握手。面对 DoS 攻击(伪造源 IP 发起大量握手),WireGuard 可选启用 Cookie:响应方先要求对方回一个基于其 IP 计算的 Cookie,验证通过才做昂贵的 Curve25519 运算。

Cookie 抗 DoS 流程:
  1. 攻击者伪造源 IP 发送握手请求
  2. 响应方不回真实响应,而是回 Cookie Reply
     (MAC = keyed-BLAKE2s(源 IP, 服务端密钥))
  3. 攻击者必须用正确源 IP 收到 Cookie 才能继续
  4. 伪造源 IP 收不到 → 攻击失效

2.3 密钥的静默轮换

会话密钥不是固定的:WireGuard 每约 120 秒(REKEY_AFTER_TIME)主动发起新握手轮换密钥,并在约 180 秒(REJECT_AFTER_TIME)后拒绝使用旧密钥。轮换是静默的,应用层无感知,实现了前向保密与密钥新鲜度。

参数默认值含义
REKEY_AFTER_TIME120s主动重新握手
REKEY_ATTEMPT_TIME90s重试握手的最长时间
REJECT_AFTER_TIME180s拒绝旧密钥的截止
REKEY_AFTER_MESSAGES2^60按报文数触发轮换

三、Cryptokey Routing 与密钥管理

3.1 AllowedIPs 的双重语义

AllowedIPs 是 WireGuard 最容易被误解的配置项,它同时承担两个职责:出方向的路由选择(哪些目标 IP 走这个 Peer)和入方向的访问控制(只接受源 IP 落在列表内的包)。一个 Peer 的 AllowedIPs 写错,往往表现为「能发不能收」或「能收不能发」。

AllowedIPs 双重语义:
  出方向(路由):本机发出的包,目标 IP 匹配某 Peer 的 AllowedIPs
                 → 加密后发给该 Peer
  入方向(过滤):从某 Peer 收到的包,源 IP 必须落在其 AllowedIPs
                 → 否则丢弃(防源地址伪造)

  例:AllowedIPs = 10.0.0.2/32, fd00::2/128
     - 发往 10.0.0.2 的包走这个 Peer
     - 只接受来自 10.0.0.2 的包

3.2 密钥对与公钥交换

每个节点持有一对 Curve25519 密钥:私钥本地保密,公钥可自由分发。WireGuard 本身没有 PKI、没有证书、没有 CA——它靠「配置里写上对方的公钥」建立信任,这让密钥分发退化为简单的配置管理问题。

# 生成密钥对
wg genkey | tee privatekey | wg pubkey > publickey
chmod 600 privatekey

# 从已有私钥推导公钥
wg pubkey < privatekey

# 生成预共享密钥(可选,叠加后量子对称加固)
wg genpsk > preshared.key

3.3 预共享密钥的用途

除公钥外,WireGuard 支持可选的预共享密钥(PSK)。它不替代公钥握手,而是叠加在握手结果之上——即使未来 Curve25519 被量子计算机攻破,PSK 仍提供一层对称加固。这是「现在先布防、以后好升级」的低成本做法。

四、性能与对比

4.1 吞吐与延迟

WireGuard 在内核态处理数据包,且密码学原语都有高效实现,因此吞吐远高于用户态的 OpenVPN。在常见 x86 服务器上,单条 WireGuard 隧道可跑满多 Gbps,接近线速;OpenVPN 往往卡在数百 Mbps。

维度WireGuardOpenVPNIPsec/IKEv2
运行位置内核态用户态内核态
代码规模~4000 行十万行级庞大
密码学协商无,固定可协商可协商
握手耗时毫秒级数十毫秒毫秒级
典型吞吐多 Gbps数百 Mbps多 Gbps
移动漫游原生支持需重连支持
NAT 穿透UDP + 保持UDP/TCPUDP

4.2 漫游与 NAT 保持

WireGuard 的 Peer 是「无连接」的:只要收到来自某个 IP:端口 的合法(能解密)握手,就更新该 Peer 的端点。手机从 Wi-Fi 切到 4G、公网 IP 变化,都无需重新配置——这被称为「被动漫游」。为保证 NAT 映射不过期,需要 PersistentKeepalive。

漫游机制:
  - Peer 端点不是固定的,而是「最近一次成功解密报文的来源」
  - IP 变化后,只要对端能再次发来合法报文,端点自动更新
  - NAT 后的节点需 PersistentKeepalive = 25 秒保持映射

注意:只有一端主动发包,NAT 映射才会刷新
      → 位于 NAT 后的 Peer 设 keepalive,公网侧可不设

五、实战配置

5.1 最简单的点对点

用 wg-quick 配合配置文件,可以在几行内建起一条点对点隧道。配置分 [Interface] 与 [Peer] 两段。

# /etc/wireguard/wg0.conf  —— 节点 A(10.0.0.1)
[Interface]
PrivateKey = <A 的私钥>
Address = 10.0.0.1/24
ListenPort = 51820

[Peer]
# 节点 B
PublicKey = <B 的公钥>
AllowedIPs = 10.0.0.2/32
Endpoint = 203.0.113.2:51820
PersistentKeepalive = 25
# 启停隧道
wg-quick up wg0
wg-quick down wg0

# 查看当前状态(握手时间、收发字节)
wg show

# 动态添加 Peer(无需重启)
wg set wg0 peer <公钥> allowed-ips 10.0.0.3/32 endpoint 203.0.113.3:51820

5.2 全流量 VPN(网关模式)

要让客户端所有流量都走 VPN,把 AllowedIPs 设为 0.0.0.0/0, ::/0,并在服务端开启转发与 NAT。注意此时客户端配置里要写 DNS,否则仍用本地解析。

# 客户端:全流量走 VPN
[Peer]
PublicKey = <服务端公钥>
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = vpn.example.com:51820
PersistentKeepalive = 25
DNS = 10.0.0.1
# 服务端开启转发与 NAT
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -A FORWARD -i wg0 -j ACCEPT
iptables -A FORWARD -o wg0 -j ACCEPT

5.3 多点对多点(Hub-and-Spoke 与 Full Mesh)

中心辐射模式下,所有节点只与 Hub 建隧道,Hub 负责转发;AllowedIPs 在 Hub 上列出所有 Spoke 的地址。Full Mesh 则每个节点互相配置 Peer,无中心节点,延迟最低但配置量为 O(N²)。

Hub-and-Spoke:
  Hub AllowedIPs 含所有 Spoke 网段 → 负责路由转发
  优点:配置集中、易管控;缺点:Hub 是瓶颈与单点

Full Mesh:
  每个节点为其他所有节点配 [Peer]
  优点:无中心、延迟最低;缺点:配置 O(N²)、密钥分发复杂

5.4 常见故障对照

现象原因排查
能握手无流量AllowedIPs 不含对端源地址wg show 看 latest handshake
单向不通一端未设 keepalive,NAT 映射过期在 NAT 后节点设 PersistentKeepalive
大包超时MTU 未下调设 MTU = 1420 或更小
握手不更新时钟不同步校时,检查时间戳
全流量不通未开转发或 NATsysctl net.ipv4.ip_forward、iptables 规则
# 探测隧道 MTU(外层 UDP 隧道需预留开销)
ping -M do -s 1400 -c 1 10.0.0.2

# 建议:WireGuard 默认 MTU 1420,IPv6 或额外封装时下调

5.5 密钥轮换与配置自动化

WireGuard 没有 CA,密钥分发就是「把公钥填进配置」。节点一多,手工维护 [Peer] 会迅速失控,必须自动化:把密钥与 Peer 列表放进配置管理或 KV 存储,用模板渲染出 wg0.conf。

# 用 wg 动态增删 Peer,无需重启接口
wg set wg0 peer <公钥> allowed-ips 10.0.0.9/32
wg set wg0 peer <公钥> remove

# 导出当前运行配置(含运行时端点),用于备份或迁移
wg showconf wg0

# 用 systemd 管理持久化
systemctl enable wg-quick@wg0
密钥轮换策略:
  - 每个节点独立密钥对,绝不共享私钥
  - 私钥权限 600,仅 root 可读
  - 定期轮换(如 90 天),先加新公钥再删旧公钥
  - PSK 可选,逐 Peer 配置,作为后量子加固

5.6 与其他方案的组合

WireGuard 只解决「点对点加密隧道」,它不做路由分发、不做服务发现。生产组网通常把 WireGuard 与路由协议或编排系统组合:用 BGP 在隧道之上分发网段,或用编排工具统一下发 Peer 配置。

组合作用场景
WireGuard + BGP隧道上跑动态路由多站点互联、自动收敛
WireGuard + systemd-networkd声明式配置服务器自动化
WireGuard + 配置管理模板化 Peer 分发大规模节点
WireGuard + 监控采集 wg show 指标隧道健康与流量审计
# 采集隧道健康指标(握手时间、收发字节、丢包)
wg show all dump | awk '{print $1, $5, $6, $7, $8}'

# 若 latest-handshake 长期不更新,说明隧道已断

5.7 MTU 与分片排错

WireGuard 走 UDP,封装开销不小:外层 IP(20/40)+ UDP(8)+ WireGuard 头(32)= 60~80 字节。默认 MTU 1420 是为 IPv4 场景预留的保守值,IPv6 或叠加其他隧道时需要再下调,否则会出现「握手正常、传输卡死」。

MTU 计算(IPv4 外层):
  1500 - 20(IP) - 8(UDP) - 32(WG) = 1440
  wg-quick 默认取 1420(留安全余量)

叠加隧道(如 WG over WG):
  1420 - 60 = 1360,需逐层下调
# 逐字节探测隧道真实 MTU(-M do 禁止分片)
for s in 1400 1380 1360 1340; do
  ping -M do -s $s -c 1 -W 1 10.0.0.2 >/dev/null 2>&1 \
    && echo "MTU ok: $((s+28))" && break
done

六、小结

主题核心要点落地建议
设计固定密码学、内核态、无协商生产直接上,无需调算法
握手Noise_IK 两报文,抗重放保证时钟同步
密钥Curve25519 密钥对 + 可选 PSKPSK 作为后量子加固
路由AllowedIPs 兼路由与 ACL出入双向都要覆盖对端地址
漫游端点随合法报文自动更新NAT 后节点设 keepalive
性能内核态,多 Gbps注意 MTU 与转发规则

WireGuard 的价值在于把 VPN 从「一门需要考证的技术」变成「一段可以看懂配置」。它用固定的现代密码学和极小的代码量换来可审计性与性能,用 AllowedIPs 一个字段同时表达路由与访问控制。想理解它在更大网络里的位置,可以对照 网络安全与加密通信 里的密钥与认证体系、零信任架构 中「网络即边界」的思路,以及 IP 路由与转发 里 AllowedIPs 背后的路由表语义。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「network」更多文章

  1. MPTCP 与多路径传输
  2. 流量整形与 TC/qdisc
  3. 网络命名空间与容器网络底层