留学的朋友们一定都经历过的痛苦:国外访问一些国内的服务经常出现“该地区不可用”不说,有的甚至连网站都上不去(说的就是你,夸克)。因而我需要一个稳定可靠连回家里局域网的隧道。
TP-LINK 路由器倒是自带了
L2TP/IPsec,可惜安卓 15 已经去除了对该协议的内置支持;且 TP-LINK 的 DDNS 服务也将于2025年6月30日停止;路由器另一个自带的花生壳 DDNS
风评也不好。而 WireGuard 的良好名声我又早有耳闻,遂决定另放一台闲置的树莓派作为 wg1 网关。拜拜了您嘞,TP-LINK。
1. wg 即 WireGuard 的缩写,本文将会延续使用此缩写,并不再说明
开始之前
你需要有
- 一台空闲的电脑 或者 树莓派 或者 软路由 或者 任何能跑Linux的嵌入式设备(甚至手机)
- 一个公网 IP 地址(v4 v6均可,本文将一般家庭宽带的动态公网 v6 地址作为示例)
- 一个域名(可有可无,用于 DDNS)
一点点耐心
我使用运行 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 -s或su获取 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
# ...