Linux运维17 min read

WireGuard VPN 搭建与运维实战

为什么选 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 ServerClient 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

⚠️ 避坑 #1wg-quick up 需要配置文件路径格式为 /etc/wireguard/<name>.conf,接口名就是 <name>wg0.conf → 接口 wg0。接口名最长 15 字符,不能用 wg(保留)。

AllowedIPs 路由原理

WireGuard 的 AllowedIPs 同时承担两个功能:

  1. 入站过滤:只接受源 IP 在此列表的数据包
  2. 出站路由:目标 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 的特定路由走原网关(防止路由环路)

⚠️ 避坑 #2AllowedIPs = 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

⚠️ 避坑 #4PersistentKeepalive 设太小(如 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"

⚠️ 避坑 #5wg syncconfwg-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 一劳永逸。

十条避坑清单

  1. 私钥权限 600:私钥可读 = VPN 被冒充,chmod 600 *_private.key
  2. AllowedIPs 不重叠:多个 Peer 的 AllowedIPs 重叠 = 路由歧义,后配置的覆盖前者
  3. 服务器别配 0.0.0.0/0:只在客户端配全流量 VPN,服务器配了 = 流量黑洞
  4. PersistentKeepalive = 25:NAT 后的客户端必配,否则连接超时断开
  5. ip_forward 必须开:服务器要转发 VPN 流量到内网,sysctl net.ipv4.ip_forward=1
  6. wg syncconf 优于重启:加 Peer 用 wg syncconf,不中断现有连接
  7. MTU 1280 保平安:遇到大包丢失先降 MTU,1280 是 IPv6 最小值一定不会错
  8. 密钥定期轮换:90 天换一次密钥对,降低泄漏风险
  9. 删除离职 Peer:及时清理不再使用的 Peer,减少攻击面
  10. 别用 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 就能稳定运行。

分享:

相关文章

评论区