为什么选 WireGuard
传统 VPN 三选一(IPSec/OpenVPN/WireGuard),WireGuard 在运维体验上碾压前两者:
| 维度 | IPSec | OpenVPN | WireGuard |
|---|---|---|---|
| 代码行数 | ~40 万行 | ~10 万行 | ~4000 行 |
| 内核态 | 是 | 否(用户态 tun) | 是(Linux 5.6+ 原生) |
| 配置复杂度 | 极高(Phase1/2/SPI) | 中(证书/TLS) | 极低(公钥 + IP) |
| 性能 | 高 | 中(用户态开销) | 最高(内核态 + 现代加密) |
| 漫游 | 不支持 | 部分支持 | 原生支持(手机切 WiFi/4G 不断连) |
| 审计 | 难(代码量大) | 中 | 易(4000 行可人工审计) |
WireGuard 的设计哲学:密码学极简——只用 ChaCha20 + Poly1305 + Curve25519 + BLAKE2s,没有算法协商、没有降级攻击面。配置即密钥对 + 对端公钥 + IP,没有证书链的复杂度。
安装
Linux(内核模块)
# Ubuntu 20.04+ / Debian 11+(内核 5.6+ 已内置)
apt install -y wireguard wireguard-tools
# CentOS 8 / RHEL 8+(需 EPEL + 内核模块)
dnf install -y epel-release
dnf install -y wireguard-tools
# CentOS 7 需手动编译内核模块或升级内核
# 验证内核模块
modprobe wireguard
lsmod | grep wireguard
# wireguard 110592 0
其他平台
| 平台 | 安装方式 |
|---|---|
| Windows | 官方下载 .msi(wireguard.com/install) |
| macOS | brew install wireguard-tools 或 App Store |
| Android | Google Play / F-Droid |
| iOS | App Store |
所有客户端使用相同的配置格式,跨平台统一。
密钥体系
WireGuard 的认证完全基于公钥加密——没有用户名密码、没有证书。
# 生成密钥对(每台机器一组)
wg genkey | tee server_private.key | wg pubkey > server_public.key
wg genkey | tee client_private.key | wg pubkey > client_public.key
# 可选:预共享密钥(PSK,额外一层对称加密,防量子计算)
wg genpsk > preshared.key
# 权限保护(私钥必须 600)
chmod 600 *_private.key preshared.key
密钥管理原则:
- 私钥永不离开本机(不传不复制不邮件)
- 公钥可任意分发(用于配置对端)
- PSK 可选但推荐(增强安全性)
- 密钥泄漏 = 该节点被冒充,需重新生成并更新所有对端配置
单服务器 + 多客户端拓扑
最常见的企业远程办公场景:一台公网服务器做 VPN 网关,N 个客户端通过它访问内网。
┌──────────────────┐
Client A ───────→│ VPN Server │
Client B ───────→│ (公网 IP) │──────→ 内网 10.0.0.0/24
Client C ───────→│ 10.10.0.1 │
└──────────────────┘
服务器配置
# /etc/wireguard/wg0.conf
[Interface]
PrivateKey = <server_private.key 内容>
Address = 10.10.0.1/24
ListenPort = 51820
# 数据包转发 + NAT(让客户端能访问内网)
PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# Client A
[Peer]
PublicKey = <client_A_public.key 内容>
PresharedKey = <preshared.key 内容>
AllowedIPs = 10.10.0.2/32 # 只允许该客户端使用此 IP
# Client B
[Peer]
PublicKey = <client_B_public.key 内容>
PresharedKey = <preshared.key 内容>
AllowedIPs = 10.10.0.3/32
# Client C
[Peer]
PublicKey = <client_C_public.key 内容>
PresharedKey = <preshared.key 内容>
AllowedIPs = 10.10.0.4/32
客户端配置
# /etc/wireguard/wg0.conf(Client A)
[Interface]
PrivateKey = <client_A_private.key 内容>
Address = 10.10.0.2/24
DNS = 10.10.0.1, 8.8.8.8 # VPN 内网 DNS + 公网 DNS
[Peer]
PublicKey = <server_public.key 内容>
PresharedKey = <preshared.key 内容>
Endpoint = vpn.example.com:51820 # 服务器公网地址
AllowedIPs = 10.0.0.0/8, 10.10.0.0/24 # 走 VPN 的网段
PersistentKeepalive = 25 # NAT 后保持连接(每 25 秒心跳)
启动与管理
# 启动(创建 wg0 接口)
wg-quick up wg0
# 停止
wg-quick down wg0
# 开机自启
systemctl enable wg-quick@wg0
# 查看状态
wg show
# interface: wg0
# public key: <server_public_key>
# private key: (hidden)
# listening port: 51820
#
# peer: <client_A_public_key>
# preshared key: (hidden)
# endpoint: 203.0.113.5:51820
# allowed ips: 10.10.0.2/32
# latest handshake: 1 minute, 23 seconds ago
# transfer: 1.45 MiB received, 15.32 MiB sent
⚠️ 避坑 #1:
wg-quick up需要配置文件路径格式为/etc/wireguard/<name>.conf,接口名就是<name>。wg0.conf→ 接口wg0。接口名最长 15 字符,不能用wg(保留)。
AllowedIPs 路由原理
WireGuard 的 AllowedIPs 同时承担两个功能:
- 入站过滤:只接受源 IP 在此列表的数据包
- 出站路由:目标 IP 在此列表的数据包走此 peer 的隧道
# 客户端配置
[Peer]
Endpoint = vpn.example.com:51820
AllowedIPs = 10.0.0.0/8, 192.168.0.0/16
# 效果:
# - 访问 10.x.x.x → 走 VPN 隧道
# - 访问 192.168.x.x → 走 VPN 隧道
# - 访问公网 → 走本地默认路由(不经过 VPN)
全流量 VPN(类似 OpenVPN 的 redirect-gateway):
# 客户端配置
AllowedIPs = 0.0.0.0/0, ::/0
# wg-quick 会自动处理:
# 1. 删除默认路由
# 2. 添加 wg0 为新默认路由
# 3. 添加服务器 Endpoint 的特定路由走原网关(防止路由环路)
⚠️ 避坑 #2:
AllowedIPs = 0.0.0.0/0在服务器端配置 = 灾难。服务器会对所有客户端声明"我能路由所有 IP",导致客户端流量黑洞。全流量 VPN 只在客户端侧配置。
站点到站点互联(Site-to-Site)
两个办公网络通过 WireGuard 互联:
办公室 A (10.1.0.0/24) 办公室 B (10.2.0.0/24)
┌──────────────┐ ┌──────────────┐
│ Gateway A │═════════════════════│ Gateway B │
│ 10.1.0.1 │ WireGuard │ 10.2.0.1 │
│ wg:10.99.0.1 │ Tunnel │ wg:10.99.0.2 │
└──────────────┘ └──────────────┘
Gateway A 配置:
[Interface]
PrivateKey = <A_private>
Address = 10.99.0.1/30
ListenPort = 51820
# 允许转发到办公室 B 的网段
PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
[Peer]
PublicKey = <B_public>
Endpoint = office-b.example.com:51820
AllowedIPs = 10.2.0.0/24, 10.99.0.2/32
PersistentKeepalive = 25
Gateway B 配置:
[Interface]
PrivateKey = <B_private>
Address = 10.99.0.2/30
ListenPort = 51820
PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
[Peer]
PublicKey = <A_public>
Endpoint = office-a.example.com:51820
AllowedIPs = 10.1.0.0/24, 10.99.0.1/32
PersistentKeepalive = 25
办公室内网机器路由配置(在办公室 A 的内网机器上):
# 添加到办公室 B 的路由,走 Gateway A
ip route add 10.2.0.0/24 via 10.1.0.1
或在办公室 A 的 DHCP 服务器下发静态路由。
⚠️ 避坑 #3:站点到站点场景两端的
AllowedIPs不能重叠。如果办公室 A 内网是10.1.0.0/24,VPN 隧道 IP 用10.99.0.0/30,不能复用10.1.0.0网段,否则路由冲突。
NAT 穿透
WireGuard 的 UDP 协议天然支持 NAT 穿透,但有以下细节:
客户端在 NAT 后
# 客户端配置加 PersistentKeepalive
[Peer]
Endpoint = vpn.example.com:51820
PersistentKeepalive = 25 # 关键:每 25 秒发心跳,保持 NAT 映射
NAT 路由器的 UDP 映射表通常 30-60 秒超时。没有心跳的话,映射过期后服务器回包到不了客户端,连接"断"了但 wg show 仍显示在线。
双方都在 NAT 后(无公网 IP)
两端都没公网 IP,需要 STUN 中继或公网跳板:
# 方案一:公网跳板做中继(最简单)
# 跳板服务器配置
[Interface]
Address = 10.99.0.1/24
ListenPort = 51820
# 两个 NAT 后的客户端都连跳板
# 客户端间通过跳板转发(需加 PostUp iptables FORWARD)
# 方案二:使用 STUN 打洞(复杂,不保证成功)
# 需要第三方 STUN 服务器发现公网映射地址
# WireGuard 本身不支持自动 STUN,需手动配置 Endpoint
⚠️ 避坑 #4:
PersistentKeepalive设太小(如 5 秒)浪费流量,设太大(如 120 秒)可能超不过 NAT 超时。25 秒是社区验证的黄金值。
密钥与配置自动化管理
手动管理多客户端密钥容易出错,推荐工具:
wg-genweb(Web 管理界面)
# Docker 部署
docker run -d \
--name wg-genweb \
-p 8080:8080 \
-v /etc/wireguard:/data \
wg-genweb/wg-genweb
# 访问 http://localhost:8080
# 功能:Web 界面添加/删除客户端,自动生成配置 + 二维码
脚本批量管理
#!/bin/bash
# /opt/wireguard/add-client.sh
# 用法: ./add-client.sh <client_name>
CLIENT_NAME=$1
VPN_IP_BASE="10.10.0"
LAST_OCTET=$(grep -oP '10\.10\.0\.\K\d+' /etc/wireguard/wg0.conf | sort -n | tail -1)
NEW_OCTET=$((LAST_OCTET + 1))
CLIENT_IP="${VPN_IP_BASE}.${NEW_OCTET}"
# 生成密钥对
CLIENT_DIR="/etc/wireguard/clients/${CLIENT_NAME}"
mkdir -p $CLIENT_DIR
wg genkey | tee $CLIENT_DIR/private.key | wg pubkey > $CLIENT_DIR/public.key
chmod 600 $CLIENT_DIR/private.key
CLIENT_PRIV=$(cat $CLIENT_DIR/private.key)
CLIENT_PUB=$(cat $CLIENT_DIR/public.key)
SERVER_PUB=$(grep PrivateKey /etc/wireguard/wg0.conf | awk '{print $3}' | wg pubkey)
PSK=$(cat /etc/wireguard/preshared.key)
# 追加 Peer 到服务器配置
cat >> /etc/wireguard/wg0.conf << EOF
# $CLIENT_NAME
[Peer]
PublicKey = $CLIENT_PUB
PresharedKey = $PSK
AllowedIPs = $CLIENT_IP/32
EOF
# 生成客户端配置
cat > $CLIENT_DIR/wg0.conf << EOF
[Interface]
PrivateKey = $CLIENT_PRIV
Address = $CLIENT_IP/24
DNS = 10.10.0.1
[Peer]
PublicKey = $SERVER_PUB
PresharedKey = $PSK
Endpoint = vpn.example.com:51820
AllowedIPs = 10.0.0.0/8
PersistentKeepalive = 25
EOF
# 生成二维码(手机扫码导入)
qrencode -t png -o $CLIENT_DIR/qrcode.png < $CLIENT_DIR/wg0.conf
# 重载配置(不中断现有连接)
wg syncconf wg0 <(wg-quick strip wg0)
echo "Client $CLIENT_NAME added: $CLIENT_IP"
echo "Config: $CLIENT_DIR/wg0.conf"
echo "QR Code: $CLIENT_DIR/qrcode.png"
⚠️ 避坑 #5:
wg syncconf比wg-quick down && wg-quick up更优雅——不中断现有连接,只应用配置变更。
高可用方案
单 VPN 服务器是单点故障。两种 HA 方案:
方案一:双服务器 + DNS 轮询
# DNS 配置
vpn.example.com. 300 IN A 203.0.113.10 # VPN Server 1
vpn.example.com. 300 IN A 203.0.113.11 # VPN Server 2
# 客户端配置(Endpoint 用域名,自动 DNS 轮询)
[Peer]
Endpoint = vpn.example.com:51820
缺点:DNS 轮询不健康检查,一个服务器挂了部分客户端仍连它。
方案二:Anycast + BGP(大型部署)
# 两台 VPN 服务器宣告相同 Anycast IP
# Bird BGP 配置
protocol bgp upstream {
local as 65001;
neighbor 203.0.113.1 as 65000;
import all;
export filter {
if net = 198.51.100.1/32 then accept;
reject;
};
}
# 客户端连 Anycast IP,BGP 自动路由到最近的可用节点
[Peer]
Endpoint = 198.51.100.1:51820
方案三:Keepalived + VRRP(同机房)
# 两台 VPN 服务器共享 VIP
# Keepalived 配置
vrrp_instance VI_1 {
state MASTER/BACKUP
interface eth0
virtual_router_id 51
priority 100/90
advert_int 1
virtual_ipaddress {
203.0.113.100 # VIP
}
notify_master "/usr/local/bin/wg-failover.sh primary"
notify_backup "/usr/local/bin/wg-failover.sh secondary"
}
# 客户端连 VIP
[Peer]
Endpoint = 203.0.113.100:51820
监控与运维
# 实时状态
wg show
wg show all dump # 机器可读格式(适合监控脚本)
# 手shake 检查(长时间无 handshake = 连接断了)
wg show | grep "latest handshake"
# 无 handshake 行 = 从未连上
# "latest handshake: 2 hours ago" = 连接已断
# 流量统计
wg show wg0 transfer
# peer: <pubkey>
# transfer: 1.45 MiB received, 15.32 MiB sent
# 日志(WireGuard 内核模块日志很少)
journalctl -k | grep wireguard
dmesg | grep wireguard
# 连接追踪
conntrack -L | grep 51820
Prometheus 监控(wireguard_exporter):
# 关键告警规则
- alert: WireGuardPeerDown
expr: wireguard_peer_last_handshake_seconds > 180
for: 2m
labels: { severity: warning }
annotations:
summary: "WireGuard peer {{ $labels.peer }} down (no handshake > 3min)"
- alert: WireGuardInterfaceDown
expr: wireguard_interface_up == 0
for: 1m
labels: { severity: critical }
安全加固
# 1. 防火墙只放行 WireGuard 端口
iptables -A INPUT -p udp --dport 51820 -j ACCEPT
iptables -A INPUT -j DROP
# 2. 更改默认端口(减少扫描噪音)
ListenPort = 51820 → ListenPort = 随机高端口
# 3. 禁用 root SSH(VPN 连上后用普通用户 + 密钥)
PermitRootLogin no
# 4. 限制 VPN 网段访问范围(最小权限)
# 服务器 iptables 只允许 VPN 网段访问特定服务
iptables -A INPUT -i wg0 -p tcp --dport 22 -s 10.10.0.0/24 -j ACCEPT
iptables -A INPUT -i wg0 -j DROP # 其余 VPN 流量拒绝
# 5. 定期轮换密钥(如每 90 天)
# 6. 审计对端列表(及时删除离职员工的 peer)
常见故障排查
| 故障 | 诊断 | 解决 |
|---|---|---|
| 无法连接 | wg show 无 handshake |
检查 Endpoint 地址/端口/防火墙;确认公钥配对正确 |
| 能连但访问不了内网 | ping 10.10.0.1 通但 ping 10.0.0.1 不通 |
服务器未开 ip_forward / NAT(PostUp iptables) |
| 连上后断网 | 客户端所有流量不通 | AllowedIPs = 0.0.0.0/0 导致默认路由被覆盖;检查 wg-quick 路由表 |
| 频繁断连 | wg show handshake 间隔 > 3min |
客户端加 PersistentKeepalive = 25 |
| 手机切网络断连 | 4G→WiFi 后不通 | 同上,PersistentKeepalive 解决 |
| MTU 问题 | 大包丢但小包通 | 降低 MTU:MTU = 1280(IPv6 最小 MTU) |
| 速度慢 | iperf3 测速低 |
检查 CPU(加密开销);确认内核态模块加载 |
| DNS 不解析 | ping IP 通但域名不通 | 客户端配置 DNS = 10.10.0.1 |
⚠️ 避坑 #6:MTU 问题非常隐蔽。WireGuard 默认 MTU 1420,但叠了一层 PPPoE/6in4 隧道后实际可用 MTU 可能只有 1400。大包(如 HTTPS 握手)丢失但小包(ping)正常。设
MTU = 1280一劳永逸。
十条避坑清单
- 私钥权限 600:私钥可读 = VPN 被冒充,
chmod 600 *_private.key - AllowedIPs 不重叠:多个 Peer 的 AllowedIPs 重叠 = 路由歧义,后配置的覆盖前者
- 服务器别配 0.0.0.0/0:只在客户端配全流量 VPN,服务器配了 = 流量黑洞
- PersistentKeepalive = 25:NAT 后的客户端必配,否则连接超时断开
- ip_forward 必须开:服务器要转发 VPN 流量到内网,
sysctl net.ipv4.ip_forward=1 - wg syncconf 优于重启:加 Peer 用
wg syncconf,不中断现有连接 - MTU 1280 保平安:遇到大包丢失先降 MTU,1280 是 IPv6 最小值一定不会错
- 密钥定期轮换:90 天换一次密钥对,降低泄漏风险
- 删除离职 Peer:及时清理不再使用的 Peer,减少攻击面
- 别用 UDP 53 端口:有人用 53 端口伪装 DNS 绕过防火墙,但会被 DNS 放大攻击误伤
总结
WireGuard VPN 运维核心要点:
- 密钥对 = 身份——私钥不离机,公钥配对端,无密码无证书
- AllowedIPs = 路由——同时做入站过滤和出站路由,重叠 = 灾难
- PostUp iptables = 网关——服务器开 ip_forward + NAT 才能转发到内网
- PersistentKeepalive = NAT 穿透——25 秒心跳是 NAT 后客户端的生命线
- wg syncconf = 无中断变更——加删 Peer 不断现有连接
相比 OpenVPN/IPSec,WireGuard 用 4000 行代码做到了更安全、更快、更简单。掌握密钥管理、路由配置、NAT 穿透这三块,企业 VPN 就能稳定运行。
