为什么个人站长也该有一条自己的加密隧道
多数个人站长的服务器架构都长一个样:一台 VPS 跑网站,偶尔再从另一家买一台做备份或测试。前几年这套结构很省事,因为「管理」本质上就是 SSH。但只要你同时拥有两台以上的机器,就会撞上三个很具体的麻烦:
- 内网互通全靠公网。备份机要拉数据库,就得把 MySQL 端口开放到公网并配 IP 白名单;测试机要连生产 Redis,就得给 Redis 设一个密码然后监听
0.0.0.0。每一个跨机端口开放,都是一次攻击面扩大。 - 管理入口就是 SSH 端口本身。暴露 22 端口的机器,日志里每天都有几十上百条爆破记录,fail2ban 只能事后封 IP,封不完。
- 异地办公没有内网可回。人在外面,想让笔记本直连服务器私网地址、像在公司一样访问内部服务,纯公网架构做不到。
这三件事的公共解法是同一样东西:一条点对点的加密隧道,把散落各处的机器组成一张虚拟内网。传统方案是 OpenVPN,功能全面但配置冗长、性能一般、代码库庞大。这篇文章讲 WireGuard——它用不到 4000 行内核代码做到了同一件事,配置文件通常不到 15 行,握手速度是毫秒级,而且已经被并入 Linux 5.6 之后的内核主线。
需要提前说清楚边界:WireGuard 不是「翻墙工具」,也不是传统意义的「VPN 服务商软件」。它的定位是网络层(L3)的点对点隧道,你要自己持有两端。理解这一点,后面的配置逻辑才顺。
一、WireGuard 的设计哲学:为什么它这么短
理解 WireGuard 的配置为什么这么简单,关键在于它做了几个非常激进的取舍。
第一,它只认密钥,不认身份协商。OpenVPN 有用户名密码、有证书链、有 TLS 握手、有各种 cipher 协商参数。WireGuard 把这一切砍掉,每台机器只持有一个密钥对(Curve25519 椭圆曲线),公钥当身份,私钥留在本地。没有 CA、没有证书过期、没有吊销列表——要「吊销」一台机器,只需要把所有对端配置里它的公钥删掉。
第二,它把加密算法写死,不协商。WireGuard 固定使用 ChaCha20 做对称加密、Poly1305 做认证、Curve25519 做密钥交换、BLAKE2s 做哈希、HKDF 做密钥派生。这不是设计缺陷而是刻意为之:不协商意味着不存在降级攻击,也不存在「双方支持的 cipher 不匹配」这类经典故障。代价是你无法为合规要求换成国密算法。
第三,它绑定 UDP 和无状态设计。WireGuard 跑在 UDP 上,没有 TCP-over-TCP 的经典问题(外层 TCP 重传与内层 TCP 重传互相放大导致雪崩)。它还引入了 crypto key routing:只有经过验证的包才会被内核接受,未通过验证的包直接静默丢弃,不回任何响应。这正是它天生抗端口扫描的原因——扫描器发探测包得不到任何回应,无法判断这个 UDP 端口后面是否有人。
这三条加起来解释了一个现象:WireGuard 的配置文件短,不是因为它「简化了」,而是因为它把协商空间全部取消,把复杂度转移到了密钥分发环节。所以真正的操作难点从来不是写配置,而是管理这些公钥。
二、五分钟搭起两端隧道:完整的实操流程
下面是一个最小可用例子:一台公网 VPS(假设公网 IP 是 203.0.113.10,作为「枢纽节点」)连一台内网备份机。
第一步,两端各生成密钥对。WireGuard 自带密钥生成命令,不需要 OpenSSL:
# 在每一台机器上分别执行
umask 077
wg genkey | tee /etc/wireguard/private.key | wg pubkey > /etc/wireguard/public.key
chmod 600 /etc/wireguard/private.key
# 查看生成的公钥(私钥务必不要外传)
cat /etc/wireguard/public.key这里有个新手最容易踩的坑:umask 077 不能省。WireGuard 的私钥是一个 32 字节的 base64 字符串,权限一旦是 644,同机其他用户就能读到,而内核不会给你任何提示。wg-quick 在启动时会检查私钥权限,权限过松会直接拒绝启动。
第二步,在枢纽节点上写配置。文件放在 /etc/wireguard/wg0.conf:
[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <枢纽节点的私钥内容>
# 开启转发,让备份机能借道访问其他网段
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
[Peer]
# 备份机的公钥
PublicKey = <备份机的公钥内容>
AllowedIPs = 10.10.0.2/32第三步,在备份机上写配置。
[Interface]
Address = 10.10.0.2/24
PrivateKey = <备份机的私钥内容>
[Peer]
# 枢纽节点的公钥
PublicKey = <枢纽节点的公钥内容>
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.10.0.0/24
# 每 25 秒发一次保活包,让 NAT 映射不过期
PersistentKeepalive = 25第四步,启动并验证。
# 两台机器都执行
systemctl enable --now wg-quick@wg0
# 查看隧道状态,重点看 latest handshake
wg show
# 从备份机 ping 枢纽节点的内网地址
ping -c 3 10.10.0.1正常输出里 wg show 会显示 latest handshake: 12 seconds ago 以及 transfer: 1.2 KiB received, 1.5 KiB sent。如果 latest handshake 一直不出现,说明握手根本没成功——这时候不要怀疑配置语法,直接进入下一节的排查流程。
三、AllowedIPs 是 WireGuard 最容易被误解的一个字段
几乎所有 WireGuard 配置问题都绕不开 AllowedIPs。它的名字极具误导性,因为它同时承担了两个完全不同的职责。
职责一(出方向):它是一张路由表。当本机要发一个包给某个 IP 时,内核会遍历所有 Peer 的 AllowedIPs,找出哪个 Peer 声明了这个 IP 段,然后把包交给它。所以 AllowedIPs = 0.0.0.0/0 的含义是「所有流量都走这个 Peer」,也就是把 WireGuard 当默认网关用(全局代理模式)。
职责二(入方向):它是一张访问控制表。当某个包从隧道进来,WireGuard 会用「本机已配置的某个 Peer 的公钥」验证这个包,然后检查包的源 IP是否落在该 Peer 的 AllowedIPs 里。不在,包就被丢弃。
这两条职责合在一起产生了一个非常经典的故障:两台机器配置都对、密钥都对、握手也成功了,ping 对方内网地址却不通。典型原因是枢纽节点的 AllowedIPs 只写了单个 /32,而备份机要从整个 /24 网段回包。修复方式是把 Peer 的 AllowedIPs 写成对方实际会使用的网段:
# 备份机侧:访问枢纽节点所在的整个虚拟网段,而不只是枢纽节点本身
[Peer]
PublicKey = <枢纽节点公钥>
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.10.0.0/24 # 注意是 /24,不是 10.10.0.1/32
PersistentKeepalive = 25另一类误解是把 AllowedIPs 当防火墙。它确实限制入方向源地址,但它不做端口级过滤。也就是说,隧道打通以后,备份机能访问枢纽节点上任意监听的端口,包括 MySQL 和 Redis。所以正确的安全模型是两层:WireGuard 负责「谁能进这张网」,主机防火墙(iptables 或 ufw)负责「进了网能碰哪些端口」。
四、把数据库访问从公网收回来:这是隧道最实在的收益
对个人站长来说,WireGuard 最有价值的一个用法不是「远程办公」,而是把本来必须暴露在公网的内部服务全部收回内网。
以 MySQL 跨机备份为例。改造前的状态是:bind-address = 0.0.0.0,3306 对全网开放,靠防火墙 IP 白名单挡着。改造后:
# /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
bind-address = 10.10.0.1 # 只监听 WireGuard 虚拟网卡
然后直接把 3306 从公网防火墙里删掉:
# 删除公网规则,3306 不再对任何公网 IP 开放
iptables -D INPUT -p tcp --dport 3306 -j ACCEPT
# 只允许来自 wg0 网卡的数据库连接
iptables -A INPUT -i wg0 -p tcp --dport 3306 -j ACCEPT
iptables -A INPUT -i wg0 -p tcp --dport 6379 -j ACCEPT这一步做完,收益是立刻可量化的:MySQL 的 aborted_connects 计数会停止增长(公网扫描器再也连不上),Redis 可以安心关掉 requirepass 只靠网络层隔离,审计日志里只有真实的备份机 IP(10.10.0.2)而不是一团来源不明的地址。
这里有个必须注意的顺序问题:先确认隧道通了,再删公网规则。反过来做会导致备份机直接失联,只能去控制台救火。稳妥的做法是分两次改:第一次保留公网规则、加上 wg0 规则,跑一周备份确认稳定;第二次才删除公网规则。
五、故障排查:握手不成功的五类成因
WireGuard 的故障排查比 OpenVPN 简单得多,因为状态全部集中在 wg show 一条命令里。按下面的顺序排查,能覆盖绝大多数情况。
成因一:UDP 端口根本没通。症状是 wg show 里完全没有 latest handshake,且 transfer 只有发出的字节没有收到的字节。验证方法:
# 在枢纽节点确认真的在监听 UDP
ss -lunp | grep 51820
# 从外部测试端口可达性(需要另一台机器)
nc -vzu 203.0.113.10 51820注意 nc -z 对 UDP 的结论不可靠。因为 WireGuard 对未验证的包静默丢弃,即使端口是通的,UDP 探测也可能表现为「无响应」。所以更靠谱的判断依据是主机防火墙和云厂商安全组:去云控制台确认 51820/UDP 入方向是放行的。这是最高频的成因,尤其在有安全组的云主机上。
成因二:公钥配错或复制时带了换行。症状是双方都有发包记录,但握手始终失败。检查方法:
# 确认配置里的公钥与对方机器输出的完全一致(44 字符,以 = 结尾)
wg show wg0 peers
# 对比:如果这里显示的公钥与对方 cat public.key 的结果不同,就是配错了复制公钥时最常见的错误是把私钥当公钥填。两者都是 44 字符的 base64 字符串,肉眼极难区分。养成习惯:每次复制后专门 cat 一次确认来源文件是 public.key。
成因三:PersistentKeepalive 缺失导致 NAT 映射过期。症状很典型——刚启动能通,隔几分钟或几十分钟后自己断了,重启就恢复。这几乎百分之百是 NAT 超时问题。只要有一端在 NAT 后面(家用宽带、云主机的 NAT 网关),就必须在NAT 那一侧的配置里加:
[Peer]
PersistentKeepalive = 2525 秒是官方推荐值,不是随手取的:它能覆盖绝大多数 NAT 设备 30 秒到 2 分钟的空闲超时窗口,同时不至于产生过多心跳流量。
成因四:AllowedIPs 网段冲突。如果你家里或办公网的网段也是 10.10.0.0/24,那么本地路由表里已经有这个网段指向真实网卡,WireGuard 添加的路由会失败或被忽略。排查命令:
# 查看实际生效的路由,确认 wg0 是否真的接管了该网段
ip route show | grep wg0
# 查看本地已有网段,找一个不冲突的
ip addr show解决办法是换一个冷门网段,比如 10.77.0.0/24、172.31.9.0/24,避开 10.0.0.0/8、192.168.0.0/16 这两种家用设备最爱用的段。
成因五:内核模块没加载。部分精简发行版或自定义内核不带 WireGuard。症状是 wg-quick up 报 Unknown device type。验证与安装:
modprobe wireguard && echo "module OK" || echo "module missing"
modinfo wireguard | head -5
# Debian/Ubuntu 上安装用户态与工具
apt-get install -y wireguard wireguard-tools如果是内网无法联网的机器,需要单独下载 wireguard-dkms 并编译,这要求 linux-headers-$(uname -r) 已安装。
六、多机互联:从「点对点」到「星型拓扑」
两台机器的配置理解了,扩展到多台只是重复动作。推荐采用星型拓扑(所有节点都连枢纽节点)而不是全互联(每两台之间都配一个 Peer),原因很实际:
- 配置量。全互联时每加一台机器都要改所有已有机器;星型只需要在枢纽节点加一段 Peer 配置,新机器只配一个 Peer。
- 公钥分发。星型下每台机器只需要知道枢纽节点的公钥,泄露面小。
- 故障定位。任意两点不通时,先判断「是否都能连上枢纽」即可二分定位。
枢纽节点加一台新机器的完整操作:
# 1. 新机器生成密钥对并输出公钥
umask 077 && wg genkey | tee private.key | wg pubkey
# 2. 在枢纽节点上分配 IP 并追加 Peer(无需重启服务)
wg set wg0 peer <新机器公钥> allowed-ips 10.10.0.3/32
# 3. 持久化到配置文件,避免重启后丢失(wg set 是运行时修改,不落盘!)
wg-quick save wg0wg set 是运行时生效、内存中的修改,重启就会丢。很多人配置完发现「重启后新加的机器连不上了」,就是因为漏了 wg-quick save 这一步。这个命令会把当前运行时状态反向写回 /etc/wireguard/wg0.conf。
七、和 OpenVPN 的取舍:什么时候不该选 WireGuard
说完优点也要说局限。WireGuard 在这几种场景下不是最优解:
- 需要走 TCP 443 伪装成 HTTPS 流量。WireGuard 只跑 UDP。在严格封锁 UDP 的网络环境里,OpenVPN 或基于 TLS 的方案(如
sing-box、xray)才有生存空间。 - 需要用户名密码 + 动态 IP 的多人接入。WireGuard 是静态密钥模型,接入者一多,公钥管理会变成负担。此时可以考虑在 WireGuard 之上套一层管理面板。
- 需要合规审计与详细日志。WireGuard 默认不记录任何连接日志(这是隐私优点,但也是审计缺点)。要日志得靠外部工具从
wg show定期采样。
反过来,只要是「少量固定机器、追求性能和简单、机器都在自己控制下」的场景,WireGuard 几乎是当前最优选择。
八、一份可以直接抄的生产配置清单
把前面的要点收敛成一份可直接使用的枢纽节点配置模板:
[Interface]
Address = 10.77.0.1/24
ListenPort = 51820
PrivateKey = <枢纽节点私钥>
MTU = 1420
PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = iptables -A FORWARD -i %i -j ACCEPT
PostUp = iptables -A FORWARD -o %i -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT
PostDown = iptables -D FORWARD -o %i -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# 内网备份机
PublicKey = <备份机公钥>
AllowedIPs = 10.77.0.2/32
[Peer]
# 家用笔记本
PublicKey = <笔记本公钥>
AllowedIPs = 10.77.0.3/32两个细节值得单独说明:
MTU = 1420 解决的问题。默认情况下 wg0 的 MTU 是 1420,但这个值在部分运营商线路上仍然偏大,表现为「小包能通、大包卡死」——ping 正常但 scp 大文件或 HTTPS 页面加载时断时续。如果你的隧道出现这种症状,把 MTU 依次下调到 1380、1360 测试:
# 测试实际可用的最大不分片包长(1472 + 28 字节 IP/UDP 头 = 1500)
ping -M do -s 1472 -c 3 10.77.0.2
ping -M do -s 1400 -c 3 10.77.0.2%i 变量。在 PostUp/PostDown 里写 %i 会被替换成接口名(这里是 wg0)。比硬编码 wg0 更稳,因为将来你把接口改名成 wg1 时规则不用跟着改。
九、常见问题
Q:WireGuard 会影响网站正常访问吗?不会,如果 AllowedIPs 只写虚拟内网网段。只有写成 0.0.0.0/0(全局模式)时才会接管所有流量,那时候所有出站请求(包括网站对外发起的 API 调用)都会走隧道,需要额外配置和验证。
Q:重启服务器后隧道不通了,怎么办?先确认 systemctl is-enabled wg-quick@wg0 返回 enabled。如果没启用,随手加的 Peer 或整个隧道都不会在重启后恢复。
Q:能不能把 WireGuard 用在 Docker 容器里?可以,但要注意容器共享宿主机内核,wg-quick 需要 NET_ADMIN 权限。更简单的做法是容器直接使用宿主机已经建好的 wg0 网卡,通过 --network host 或路由配置访问内网地址。
Q:私钥泄露了怎么办?立刻在所有对端配置里删除该公钥,然后重新生成密钥对。WireGuard 没有吊销列表,删除 Peer 是唯一的吊销手段,所以要保证你能访问到所有对端。
Q:公共 WiFi 下连不上自己的隧道?大多数情况是对方封锁了 UDP 出站。可以用 udp2raw 之类的工具把 UDP 伪装成 TCP/ICMP,代价是多了用户态转发开销。
总结
WireGuard 真正改变的不是「能不能连上」,而是个人站长的网络架构思路。在它出现之前,跨机通信的默认答案是把端口开放到公网,然后祈祷 IP 白名单和 fail2ban 够用;在它之后,默认答案变成了「先建一张虚拟内网,让所有内部服务只在这张网上可见」。
回到开头那三个麻烦,逐条对照收益:
- 内网互通——MySQL、Redis 全部改绑
10.77.0.1,公网端口彻底关闭,攻击面从「全网」缩小到「四个已知 IP」。 - 管理入口——SSH 可以只监听 wg0,22 端口从公网消失,爆破日志直接归零。
- 异地办公——笔记本连上隧道后,直接访问服务器内网地址,体验和在同机房没区别。
配置本身确实只有十几行,但真正需要建立的是三件认知:AllowedIPs 双职责(出方向是路由、入方向是 ACL)、PersistentKeepalive 必须放在 NAT 侧、运行时修改必须 wg-quick save 才落盘。这三条吃透,剩下的都是复制粘贴。
最后提醒一句操作顺序:先通隧道、再关公网端口,中间留一段观察期。安全加固翻车的绝大多数案例,都不是配置写错,而是关得太快、没有留下回退路径。