所有文章 → 文章详情

使用 wstunnel 通过 Cloudflare 代理 WireGuard

将 IPv6 单栈服务器通过 Cloudflare 代理后实现双栈 WireGuard 连接

如果你像我一样不幸有一个 v6 单栈的服务器运行在家宽下,满心欢喜的配置好 wg1 准备连接,结果发现:在外的大多公共网络都不支持IPv6、英国运营商大多没有移动网络的 IPv6 支持、英国某市场占有率第二的宽带运营商也不支持 IPv6、公共网络封锁 UDP 和高端口,某些甚至仅开放 80,443 这俩基础端口、国内家宽又阻止 80,443 等端口的外部访问、wg 流量易被识别和拦截等种种种种问题。。。

wstunnel 正是你的救星!它通过将 wg 流量封装进 WebSocket 中,使流量看起来像正常的网页连接,并能够通过 Cloudflare 代理实现对单栈服务器的双栈访问。

本方法已在我校校园网中实践成功:我校校园网封锁除 80,443 以外的所有端口,阻止所有 UDP 连接,阻止所有常见 VPN 协议:市面上的 VPN 只要不是私有协议的均无法使用。我并不想费时费力自己写一个 VPN 协议,本文是我能想到有效方法中最简单快速的做法。

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

开始之前

阅读本文之前,您应基本理解并熟悉 WireGuard 的配置方法。如果您还不知道如何在服务器上配置基本的 wg 服务,请先阅读以下文章:

本文将不再赘述 wg 的基本配置问题。

另外,你需要有

  1. 一台安装并配置了 WireGuard 服务的服务器,且有单栈公网 IP
  2. 一个使用 Cloudflare DNS 的域名
  3. 一点点基础的 Linux 操作常识

配置 wstunnel 服务器

安装 wstunnel

wstunnel 官方仓库中下载最新的发行版。

解压后赋予执行权限并将其放置到系统路径下:

chmod +x wstunnel
mv wstunnel /usr/local/bin

安装证书

这步理论上是可选的,但是我强烈建议您使用证书来加密 Cloudflare 与源服务器之间的通信以达到最大安全性。毕竟你用 wstunnel 一大原因就是为了规避防火墙,对吧?所以尽量加密所有通信环节当然是理所应当的。

进入 Cloudflare 域名控制面板 → SSL/TLS → 源服务器,在源证书选项中点击创建证书。

选择使用 Cloudflare 生成私钥和 CSR,私钥类型为 RSA (2048),覆盖的主机名我选择直接默认用通配符覆盖所有二级域(*.example.com),有效期最长15年。

点击创建后将证书和私钥分别复制并保存到文本文件中,在本例中证书为cert.pem,私钥为key.pem

在服务器中新建一个目录存放证书和私钥:

sudo mkdir -p /etc/wstunnel
sudo cp origin.pem /etc/wstunnel/cert.pem
sudo cp origin.key /etc/wstunnel/key.pem

为了私钥的安全性,我们需要创建一个新用户使其作为证书目录的所有者。

创建名为wstunnel的新用户:

sudo adduser --system --no-create-home --group --shell /usr/sbin/nologin wstunnel

更新所有者和权限:

sudo chown -R wstunnel:wstunnel /etc/wstunnel
sudo chmod 400 /etc/wstunnel/key.pem
sudo chmod 444 /etc/wstunnel/cert.pem

配置 systemd 服务

新建一个 systemd 服务:

sudo vi /etc/systemd/system/wstunnel.service

写入如下配置:

[Unit]
Description=wg over wstunnel
After=network-online.target
Wants=network-online.target

[Service]
# 使用刚创建的wstunnel用户来执行,使其有权限访问私钥
User=wstunnel
Group=wstunnel
# 保留低端口绑定能力
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes

ExecStart=/usr/local/bin/wstunnel server \
          --log-lvl INFO \
          --restrict-to localhost:51820 \
          --tls-certificate /etc/wstunnel/cert.pem \
          --tls-private-key /etc/wstunnel/key.pem \
          --restrict-http-upgrade-path-prefix "loremipsum" \
          wss://[::]:2096

Restart=on-failure
RestartSec=2

[Install]
WantedBy=multi-user.target

此处--restrict-to localhost:51820指仅将流量转发给本地的51820端口,请根据你的服务器 wg 监听端口自行修改

--tls-certificate填入证书路径,--tls-private-key填入私钥路径

--restrict-http-upgrade-path-prefix是可选的密码项,客户端连接时需要使用相同字符串进行验证。不想要认证的话请删除此参数。

最后wss://[::]:2096代表监听本地 2096 端口传入的 IPv6 Websocket 连接,如果要监听来自 IPv4 的连接则需改成wss://0.0.0.0:2096

完成配置后启动服务:

sudo systemctl daemon-reload
sudo systemctl enable --now wstunnel

查看运行日志:

sudo journalctl -u wstunnel -f

成功启动输出:

INFO wstunnel::tunnel::server::server: Starting wstunnel server listening on [::]:2096

Cloudflare 配置

DNS 代理

请检查并确认 Cloudflare 域名控制面板 → DNS → 记录 中服务器的解析记录处于已代理状态(小橙云)。

WebSocket

请检查并确认 Cloudflare 域名控制面板 → 网络 → WebSockets 开关处于启用状态。

SSL/TLS

若您完成了前述证书安装步骤,推荐启用 SSL/TLS → 源服务器 → 经过身份验证的源服务器拉取开关,并在 SSL/TLS → 概述 → SSL/TLS 加密中将加密模式改为完全(严格),以获得更好的安全性。

Origin Rules 更改端口

为了使外部通过 443 端口连接,同时避开家宽的 80/443 端口限制,需要使用 Cloudflare Origin Rules 来转换端口。

在 Cloudflare 域名控制面板 → 规则 → 概述中创建一个新的 Origin Rules 规则。

使用自定义筛选表达式:当主机名等于你服务器所使用的主机名时,目标端口重写到2096。(2096没有被国内运营商封锁,你也可以使用其他没有被封锁的端口)

例如:服务器主机名为 server.example.com 上,那么筛选表达式则为:

(http.host eq "server.example.com")

所有对 server.example.com 的请求都会被重定向到主机的2096端口。
也就是客户端连接wss://server.example.com:443,实际请求会转发到你主机的2096端口上去。

客户端连接

连接 wstunnel

在客户端上同样安装 wstunnel,使用以下命令连接:

wstunnel client \
    --log-lvl INFO \
    --http-upgrade-path-prefix "loremipsum" \
    -L 'udp://51820:127.0.0.1:51820?timeout_sec=0' \
    wss://server.example.com

--http-upgrade-path-prefix填写之前服务器设置的认证字符串,若未设置则需删除此参数。

-L的用法和 SSH 隧道类似。前面的51820是本机本地的监听端口,本机的 wg 服务将会连接到 localhost:51820,后面的51820则是服务器上的本地51820(也就是 wg 的监听端口)。

连接 WireGuard

将已有 wg 配置中节点(Peer)的对端(Endpoint)改为localhost:51820

确保路由的IP 地址(段)(AllowedIPs)中仅包含 wg 接口地址,比如10.0.0.0/8。这是因为如果 AllowedIPs 为0.0.0.0/0,wg 启动时会同时代理 wstunnel 的流量,而此时 wstunnel 却尝试在 wg 的代理中连接远程服务器,这样会造成死循环。

然而只能代理局域网意义不大,也有一些同时代理外网的解决方案:在 Linux 上可以将 wstunnel 的流量打标并从 wg 从排除。然而在我使用的 macOS 下,并不支持这么操作。有一个折中的解决方案:通过这个计算工具,设置 wg 的 AllowedIPs 以将所有 Cloudflare IP 以及你在使用的 DNS 地址排除在外。这样的缺点是但凡使用 Cloudflare 的站点一律都不会被代理,但是好在国内大多站点都不用 Cloudflare,所以单纯用作回国 VPN 问题不大。

补充说明

使用上述方法时,尽可能将 wg 配置中的 DNS 设置为和本地 DNS 不同的服务器,以确保 DNS 流量被一并代理。