WireGuard 节点端口的每日更新
Wireguard 是一款开源的个人局域网工具,其特点是速度快、安全性高、易于使用。相比较 OpenVPN,Wireguard 的速度可以提升 10 倍以上,而且由于 Wireguard 使用了 Noise 协议替代了传统的 TLS 协议,因此它也更加安全,此外 Wireguard 的协议设计简单且高效,因此使用起来非常方便。
只需要在两台需要通信的设备上安装 Wireguard 即可,然后分别配置好对端的公钥和 IP 地址,即可建立起一个安全的隧道。一般而言,特殊用途总是需要一台具有公网 IP 的中转服务器,但由于 Wireguard 的端口是固定的,因此如果对端的 IP 地址被封锁,那么 Wireguard 就无法使用了。
本文介绍了一种对 WireGurad Client 魔改的方法,使其能够每次建立隧道时使用变化的端口以应对封锁。目前在 Windows Client 得到了实现,正在解决 iOS 和 macOS Wireguard Client 的版本编译问题,稍后将提供对应的下载链接。
简单来说,Wireguard 客户端都是基于 wireguard-go 这个模块通过 IPC 通信实现通道操作的,为了实现动态变更端口,只需要在平台代码调用 wireguard-go 模块中间替换和修改配置文件中的端口号即可。由于 Windows 和 macOS 下 Wireguard 的隧道配置文件都是加密的,所以通过第三方工具修改并拉起隧道并不方便。
Windows 的魔改
对于 Windows 客户端而言,GUI 界面是 go 调用 Win32 API 实现的,原本代码封装的很好,因此只需要在读取到加密配置文件后,根据隧道名来动态生成对端 IP,然后将消息发送给 wireguard-go 模块以创建隧道,下面实现了一种从配置文件名中解析 BASE PORT,然后加上当天是今年的第几天来生成对端端口的方法,配合服务端 cron 执行的脚本实现隧道地址每天变化。
// parser.go
//如果 name 以 -auto+number 结尾,那么将 endpoint 更改为 number + time.Now().YearDay()
if strings.Contains(name, "-auto+") {
//从 name 中提取 -auto+number 中的 number,为纯数字
number := strings.Split(name, "-auto+")[1]
port, err := parsePort(number)
if err != nil {
return nil, err
}
newPort := uint16(time.Now().YearDay()) + port
e.Port = newPort
peer.Endpoint = *e
} else {
peer.Endpoint = *e
}
服务端脚本(Ubuntu):
base_port=10086
echo "Wireguard port updater start at $(date +%Y-%m-%d\ %H:%M:%S)"
port_now=$(cat /etc/wireguard/wg0.conf | awk -F ' *= *' '/ListenPort/ {print $2}')
echo "Now Port is $port_now"
new_port=$(expr `date '+%j'` + $base_port)
echo "The New Port will be $new_port"
/bin/systemctl stop wg-quick@wg0.service
sleep 1
sed -i '/^ListenPort =/s/^ListenPort =.*/ListenPort = '"$new_port"'/' /etc/wireguard/wg0.conf
sleep 1
echo "Deny $port_now on ufw, Allow $new_port on ufw"
ufw deny $port_now/udp
ufw allow $new_port/udp
echo "Start Wireguard service"
/bin/systemctl start wg-quick@wg0.service
wg
echo "Done update port"
macOS 的自动化启停脚本和任务栏快捷方式
一方面,魔改 macOS 和 iOS 的代码可行,比如可以为 iOS 增加 Home Widget 和 Siri 快捷指令,URLScheme 等,另一方面,也不是很必要。macOS 下有直接基于 CLI 的 wireguard-tools 可用,其背后同样是基于跨端的 wireguard-go 实现的。只需要简单的 brew install wireguard-tools
即可。和 Linux 类似,直接在 /opt/homebrew/etc/wireguard/wg0.conf
下配置好 wg-quick 配置,然后直接 wg-quick up/down wg0 即可。
因此,简单的 Bash 脚本就能像服务端一样每天自动更新端口:
base_port=10086
echo "Wireguard port updater start at $(date +%Y-%m-%d\ %H:%M:%S)"
endpoint=$(awk '/Endpoint/{print}' /opt/homebrew/etc/wireguard/wg0.conf)
ip=$(echo $endpoint | awk -F'[: ]+' '{print $3}')
port=$(echo $endpoint | awk -F'[: ]+' '{print $4}')
day=$(date +%j)
new_port=$(($day + $base_port))
sed -i '' "s/\(:[0-9]\{1,5\}\)/:$new_port/g" /opt/homebrew/etc/wireguard/wg0.conf
sudo wg-quick up wg0
echo "Done update port and start vpn"
使用这种方法会直接创建一个静态路由,将基本所有流量导向 utunX:netstat -rn
可以看到,在关闭 wg-quick down wg0 后会删除这条路由。注意,使用此方法不需要安装 GUI 版本,同时也没有网络扩展,因此无法在网络偏好设置控制 Wireguard。
CyberMe 提供了一个 macOS 下的状态栏图标,点击以触发 netstat -rn
显示路由,可通过判断是否存在网关为 utunX 的路由来知晓现在 VPN 状态,点击菜单可执行上述脚本以自动启停 wg-quick。