183 lines
6.8 KiB
Markdown
183 lines
6.8 KiB
Markdown
---
|
|
tags: [计算机网络, WireGuard, VPN, ChaCha20, Curve25519, OpenVPN, IPsec]
|
|
create time: 2026-05-18 05:35
|
|
---
|
|
|
|
# WireGuard VPN 原理与实践
|
|
|
|
## 概述
|
|
|
|
WireGuard 是一个极简的 VPN 协议,代码量不到 OpenVPN 的 1/10、IPsec 的 1/25。它凭借极低的延迟和超高的吞吐量,正在成为新一代标准 VPN 方案——甚至已合入 Linux 内核(≥ 5.6)。
|
|
|
|
## 为什么比 OpenVPN / IPsec 快?
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
IPsec["IPsec\n• IKEv2 密钥交换 (多轮 RTT)\n• ESP/AH 扩展头链\n• 用户态+内核态来回跳转\n• ~10-20 种 cipher suite 协商"]
|
|
|
|
WG["WireGuard\n• Curve25519 密钥交换 (单次)\n• ChaCha20-Poly1305 AEAD\n• 纯内核态实现 (netlink API)\n• 仅 4 种密码学原语"]
|
|
|
|
style IPsec fill:#DDA0DD,color:#000
|
|
style WG fill:#98FB98,color:#000
|
|
```
|
|
|
|
### 性能对比实测数据
|
|
|
|
| 指标 | WireGuard | OpenVPN (UDP) | IPsec |
|
|
|------|-----------|--------------|-------|
|
|
| **吞吐量** | ~9.5 Gbps | ~1.5 Gbps | ~3.0 Gbps |
|
|
| **CPU 占用 (Gbps)** | 0.8% | 12.5% | 3.2% |
|
|
| **握手延迟** | < 1ms | 50-200ms | 100-300ms |
|
|
| **代码行数** | ~4,000 LoC | ~50,000 LoC | ~100,000 LoC |
|
|
| **配置复杂度** | 极简 (INI 格式) | 中等 | 复杂 |
|
|
| **NAT 遍历** | 原生支持 | 需要辅助 (UDP encapsulation) | 复杂 |
|
|
| **移动性** | 支持 (PersistentKeepalive + endpoint change) | 部分支持 | 有限支持 |
|
|
|
|
> [!tip] 代码行数少 = 更少的 bug = 更高的安全性
|
|
>
|
|
> 审计 ~4000 行 C 代码的成本远低于审计 ~50000 行。而且每少一行代码就少一个潜在的漏洞入口。
|
|
|
|
## WireGuard 的密码学基础
|
|
|
|
```
|
|
WireGuard 仅使用 4 种经过严格审查的密码学原语:
|
|
───────────────────────────────────────────────
|
|
1. Curve25519 — ECDH 密钥交换 (x25519)
|
|
2. ChaCha20 — 对称加密 (AES 替代)
|
|
3. Poly1305 — MAC / AEAD 认证
|
|
4. SHA-512 — 哈希函数
|
|
|
|
没有: RSA, ECC(除Curve25519外), CBC, HMAC, DRBG...
|
|
→ 选择极少 → 无协商 → 更快更简单
|
|
```
|
|
|
|
### 三重加密手风琴 (Triple Handshake)
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant A as Peer A
|
|
participant B as Peer B
|
|
|
|
Note over A,B: 1. A → B: 主动握手 (A 发起)
|
|
A->>B: {Ephemeral Key A} 用 B's Static Key 加密
|
|
B->>A: {Ephemeral Key B} 用 A's Ephemeral Key + B's Static Key 加密
|
|
A->>B: {Empty} 用 A's Ephemeral Key + B's Ephemeral Key 加密
|
|
Note over A,B: ✅ 三方加密完成, 会话密钥建立
|
|
|
|
Note over A,B: 2. B → A: 响应式重握手 (B 轮换密钥)
|
|
B->>A: {Ephemeral Key B'} 用 A's Static Key 加密
|
|
A->>B: {Ephemeral Key A'} 用 B's Ephemeral Key + A's Static Key 加密
|
|
B->>A: {Empty} 用 B's Ephemeral Key + A's Ephemeral Key 加密
|
|
Note over A,B: ✅ 新会话密钥, 前向保密保证
|
|
```
|
|
|
|
> [!question] 什么是前向保密 (Forward Secrecy)?
|
|
> 即使长期密钥(Static Key)未来被泄露,也无法解密过去的通信——因为每次会话都使用了临时的 Ephemeral Key。三重手风琴保证了每一轮的临时密钥都在下一轮中被"销毁"。
|
|
|
|
## WireGuard 配置详解
|
|
|
|
### server.conf
|
|
|
|
```ini
|
|
[Interface]
|
|
Address = 10.0.0.1/24 # WireGuard 虚拟网卡 IP
|
|
ListenPort = 51820 # UDP 监听端口
|
|
PrivateKey = <server_private_key> # 私钥 (安全保存!)
|
|
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE # NAT 转发
|
|
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
|
|
|
|
[Peer] # 客户端 1
|
|
PublicKey = <client1_public_key>
|
|
AllowedIPs = 10.0.0.2/32 # 只允许这个 IP 通过这个 peer
|
|
PersistentKeepalive = 25 # 每 25s 发一次 keepalive (穿透 NAT)
|
|
|
|
[Peer] # 客户端 2
|
|
PublicKey = <client2_public_key>
|
|
AllowedIPs = 10.0.0.3/32
|
|
PersistentKeepalive = 25
|
|
```
|
|
|
|
### client.conf
|
|
|
|
```ini
|
|
[Interface]
|
|
Address = 10.0.0.2/24 # 客户端自己的虚拟 IP
|
|
PrivateKey = <client_private_key> # 客户端私钥
|
|
|
|
# DNS 解析通过隧道走 VPN
|
|
DNS = 10.0.0.1
|
|
|
|
[Peer]
|
|
PublicKey = <server_public_key> # ⚠️ 注意方向: 客户端存服务端的公钥
|
|
Endpoint = vpn.example.com:51820 # 服务器地址
|
|
AllowedIPs = 0.0.0.0/0 # 所有流量走 VPN (全隧模式)
|
|
# AllowedIPs = 10.0.0.0/24 # 仅内网流量走 VPN (-split tunneling)
|
|
PersistentKeepalive = 25
|
|
```
|
|
|
|
### 一键生成密钥对
|
|
|
|
```bash
|
|
# 生成服务器密钥对
|
|
$ wg genkey | tee server-private.key | wg pubkey > server-public.key
|
|
|
|
# 生成客户端密钥对 (可重复 N 次)
|
|
$ wg genkey | tee client1-private.key | wg pubkey > client1-public.key
|
|
$ wg genkey | tee client2-private.key | wg pubkey > client2-public.key
|
|
|
|
# 生成 QR Code (方便手机扫码连接)
|
|
$ qrencode -t UTF8 <(cat <(echo "[Interface]"); echo "PrivateKey=$(cat client1-private.key)"; echo "[Peer]"; echo "PublicKey=$(cat server-public.key)"; echo "AllowedIPs=0.0.0.0/0"; echo "Endpoint=vpn.example.com:51820"; echo "PersistentKeepalive=25")
|
|
```
|
|
|
|
### 日常运维命令
|
|
|
|
```bash
|
|
# 启动 WireGuard
|
|
$ sudo wg-quick up wg0
|
|
$ sudo systemctl enable --now wg-quick@wg0 # 开机自启
|
|
|
|
# 查看状态
|
|
$ wg show
|
|
interface: wg0
|
|
public key: abc123def456...
|
|
private key: (hidden)
|
|
listening port: 51820
|
|
|
|
peer: xyz789ghi012...
|
|
endpoint: 203.0.113.5:45678
|
|
allowed ips: 10.0.0.2/32
|
|
latest handshake: 3 seconds ago ← 3 秒前还在握手! 连接活跃
|
|
transfer: 1.23 GiB received, 890 MiB sent
|
|
|
|
# 动态添加/移除 peer (不需要重启!)
|
|
$ wg set wg0 peer <new_pubkey> allowed-ips 10.0.0.4/32
|
|
$ wg remove wg0 peer <old_pubkey>
|
|
|
|
# 导入导出配置
|
|
$ wg syncconf wg0 <(wg-quick strip wg0)
|
|
```
|
|
|
|
## WireGuard 的限制与注意事项
|
|
|
|
```
|
|
⚠️ WireGuard 不适合的场景:
|
|
─────────────────────────────────
|
|
• 需要传统用户认证 (PAP/CHAP/LDAP) → WireGuard 只认密钥
|
|
• 细粒度 per-user 带宽控制 → 需配合 tc (traffic control)
|
|
• 大规模动态节点 (千级以上) → 管理 1000 个 peer 的 AllowedIPs 很痛苦
|
|
• 与现有 IPsec 设备互通 → WireGuard 不能做 IPsec gateway
|
|
|
|
✅ WireGuard 非常适合:
|
|
─────────────────────────────────
|
|
• 个人/小团队私有 VPN
|
|
• 云服务器到 IDC 的专线
|
|
• IoT 设备远程管理
|
|
• Kubernetes 集群跨 AZ 通信
|
|
```
|
|
|
|
## 关联笔记
|
|
|
|
- [[hhs/NETWORK/eBPF与ServiceMesh]] — eBPF 可用于监控 WireGuard 接口的流量
|
|
- [[hhs/NETWORK/SDNSRv6与网络编程最佳实践]] — SDN + SRv6 是更宏大的网络可编程愿景
|
|
- [[hhs/NETWORK/IPv6地址与扩展头部]] — IPv6 SLAAC 自动配置与 WireGuard 的静态分配对比
|