所有文章 → 文章详情

使用 wstunnel 通过 Cloudflare 代理 WireGuard

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

重要

这不是异地组网的最佳实践,只是为了实验我前一篇文章的想法,演示其技术可行性。像本文中这样的协议套娃会导致性能开销变大,还可能导致 TCP meltdown 等网络性能问题。另外,本方案仅能用于异地组网,不要妄想用这个做违法的事情,即使经过 Websocket 包装,流量行为依然和标准网页浏览时的 ws 有很大差异。请始终遵守当地的相关法律及规定。

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 流量被一并代理。