所有文章 → 文章详情

从零开始的 WireGuard 双栈 VPN 搭建与多客户端配置

只需要有公网 IPv6 地址就可以哦,无须公网 v4 或内网穿透;简单上手,轻松部署~

留学的朋友们一定都经历过的痛苦:国外访问一些国内的服务经常出现“该地区不可用”不说,有的甚至连网站都上不去(说的就是你,夸克)。因而我需要一个稳定可靠连回家里局域网的隧道。
TP-LINK 路由器倒是自带了 L2TP/IPsec,可惜安卓 15 已经去除了对该协议的内置支持;且 TP-LINK 的 DDNS 服务也将于2025年6月30日停止;路由器另一个自带的花生壳 DDNS 风评也不好。而 WireGuard 的良好名声我又早有耳闻,遂决定另放一台闲置的树莓派作为 wg1 网关。拜拜了您嘞,TP-LINK。

1. wg 即 WireGuard 的缩写,本文将会延续使用此缩写,并不再说明

开始之前

你需要有

  1. 一台空闲的电脑 或者 树莓派 或者 软路由 或者 任何能跑Linux的嵌入式设备(甚至手机)
  2. 一个公网 IP 地址(v4 v6均可,本文将一般家庭宽带的动态公网 v6 地址作为示例)
  3. 一个域名(可有可无,用于 DDNS)
  4. 一点点耐心

我使用运行 Ubuntu server 24.04.2 的树莓派 4B 作为网关,实测系统 + wg 服务一共占用不到 500MB 内存,对配置需求不高。亦可以使用 OpenWrt 等轻量级发行版进一步减少资源占用。

DDNS

由于我家里的宽带只有公网 v6 地址,又是动态的。我需要通过 DDNS 动态解析设备 IP 到一个域名上去,才能持续从外访问家里的设备。如果你是动态公网 v4 地址也建议执行这一步,除非你有静态公网 v4。

如果没有自己的域名,网上有一些 DDNS 服务提供免费的二级域名,请自行搜寻配置。

有自己域名的,直接在本地部署 ddns-go 即可:

ddns-go Release 中下载对应系统的预编译包并解压。

使用以下命令安装到系统服务:

sudo ./ddns-go -s install

ddns-go 的配置网页默认运行在本地 9876 端口上,浏览器访问 [运行ddns-go服务的主机名或IP]:9876 即可进入管理页面。

首先根据不同 DNS 服务商的教程配置好你的访问信息,以授权 ddns-go 修改域名的 DNS 解析。

在下方 IPv4 和 IPv6 的选项中勾选需要解析的 IP 类别,并在 Domains 中填写需要解析到的目标域名。

完成配置后保存即可,检查是否能 ping 通域名,可以就成功了。

警告

将设备的公网 IP 解析到域名上会增加其被扫描或攻击的概率,请确认防火墙已正确配置以阻止面向内网的服务被从外部恶意访问。

对于 Cloudflare CDN 用户:
由于 Cloudflare CDN 仅能代理 HTTP/HTTPS 流量,因此开启代理后 Wireguard/SSH 流量会被丢弃(除非使用企业版 Cloudflare Spectrum)。请确认解析设置的代理状态已关闭(仅 DNS),如图所示:

不过话说回来,Cloudflare 支持代理 WebSocket。所以理论上可以将 Wireguard 流量封装进 WebSocket 内,经由 Cloudflare 代理后还能实现从外部双栈连接,顺带解决家宽只有公网 v6 的痛点。我真是天才( 理论存在,等我暑假里有工夫实践一下。

此理论已得到验证,详见此文:

这么想想 Cloudflare 能在国内活到现在也挺离谱的,这玩意能整出来的各种骚操作数不胜数。

密钥对生成

安装 WireGuard 包:

sudo apt update
sudo apt install wireguard

生成服务器密钥对:

wg genkey | tee server_private.key | wg pubkey > server_public.key

生成客户端密钥对:

wg genkey | tee client_private.key | wg pubkey > client_public.key

这样会在当前工作目录下生成两对密钥对,分别用于服务器的公钥和私钥以及客户端的公钥和私钥。

服务端配置

WireGuard 的配置文件存储在/etc/wireguard/下,进入目录并创建一个wg0.conf

注:由于 wg 配置文件会包含服务器私钥信息,因此该目录权限为600,会拒绝非 root 用户的访问,使用sudo -ssu获取 root 权限后再操作。

wg0.conf内容如下:

[Interface]
# 把 server_private.key 的内容粘贴到 <SERVER_PRIVATE_KEY> 中
PrivateKey = <SERVER_PRIVATE_KEY>
# wg 虚拟网卡上的虚拟地址,此处给服务器分配了 10.8.0.1/24 和 fd00:dead:beef::1/64。
Address = 10.8.0.1/24, fd00:dead:beef::1/64

# wg 监听端口,随意指定一个不被占用的都行
ListenPort = 51820

# 防火墙/转发规则
# 把 -o eth0 改为你实际的外网接口
PostUp = iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE; ip6tables -t nat -A POSTROUTING -s fd00:dead:beef::/64 -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE; ip6tables -t nat -D POSTROUTING -s fd00:dead:beef::/64 -o eth0 -j MASQUERADE

# 客户端
[Peer]
# 把 client_public.key 的内容粘贴到 <CLIENT_PUBLIC_KEY> 中
PublicKey = <CLIENT_PUBLIC_KEY>
# 给该客户端分配的静态地址
AllowedIPs = 10.8.0.2/32, fd00:dead:beef::2/128

记得按照注释把要改的地方改成你的配置。

开启内核转发

为了使 wg 网关同时转发我们的所有流量到公网,需要开启系统内核转发。

/etc/sysctl.conf中取消注释或添加以下两行:

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

sudo sysctl -p使其生效,修改后的配置会被输出。

启动服务

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0

启动后输入sudo wg可以看到 wg 的当前状态

客户端配置

客户端也有类似服务器的配置文件,大差不差:

[Interface]
# 客户端私钥,把 client_private.key 的内容粘贴进来
PrivateKey = <CLIENT_PRIVATE_KEY>
# 客户端 IP,要与服务端 Peers 中设置的相同
Address = 10.8.0.2/24, fd00:dead:beef::2/64

# 通过隧道访问外网时的 DNS 设置,我这里用的是 Cloudflare DNS 能满足绝大多数需求
DNS = 1.1.1.1

[Peer]
# 服务端公钥,把 server_public.key 的内容粘贴进来
PublicKey = <SERVER_PUBLIC_KEY>

# 服务端地址:端口,v4地址和域名可以直接写,v6地址要加[],像这样:[1234:5678:1248::9]:51820
Endpoint = example.com:51820

# 需要走隧道的IP段,我这里代理全部流量所以写0.0.0.0/0, ::/0
AllowedIPs = 0.0.0.0/0, ::/0

PersistentKeepalive = 25

然后将配置文件保存并导入进 wg 客户端,至此就可以正常连接隧道了。

关于 MTU 值

重要

MTU 配置不正确会导致无法正常上网

wg 接口默认 MTU 为 1420,但倘若出现能 ping 通却无法上网的情况,大概率是 MTU 太大导致的。此时需要手动设置一个更小的 MTU。
在客户端的[Interface]中添加MTU = 1280,确认连接正常后逐渐向上调节,找到最大可用的 MTU 即可。

多客户端配置

使用上述方法为客户端生成多个密钥对,在服务端配置文件中加入新 Peer,填写公钥并分配 IP:
[Peer]
PublicKey = 
AllowedIPs = 10.8.0.2/32, fd00:dead:beef::2/128

[Peer]
PublicKey = 
AllowedIPs = 10.8.0.3/32, fd00:dead:beef::3/128

[Peer]
PublicKey = 
AllowedIPs = 10.8.0.4/32, fd00:dead:beef::4/128
# ...