Debian 服务器 sing-box 代理踩坑实录:VLESS 被精准封杀之后

前言

手上有一台海外 VPS,上面跑了 sing-box 服务端,配了双协议:VLESS + Reality 走 TCP 443,Hysteria2 走 UDP 8443。目标很简单 —— 给内网一台 Debian 13 VM 装上 sing-box 客户端,连到这台 VPS 做透明代理,解决 GitHub、Docker Hub 之类海外资源拉不动的问题。

结果比想象中折腾得多。VLESS Reality 在 GFW 面前完全没扛住,TCP 握手能过,ClientHello 一发出去就被 RST。最后还是靠 Hysteria2 的 UDP 通道才跑通。

这篇文章把整个过程和踩过的坑都记下来,省得下次再踩一遍。

安装 sing-box

VM 本身访问不了 GitHub,所以官方的一键脚本直接报废。绕路方案是用 GitHub 镜像站拼 deb 包的直链下载。

Terminal window
# 逐个版本试,镜像不稳定,哪个能下用哪个
for ver in 1.11.7 1.11.6 1.11.5; do
curl -fsSL -o /tmp/sing-box.deb \
"https://ghfast.top/https://github.com/SagerNet/sing-box/releases/download/v${ver}/sing-box_${ver}_linux_amd64.deb"
[ $? -eq 0 ] && break
done
dpkg -i /tmp/sing-box.deb

这里有两个细节值得说一下。镜像站本身就不稳定,版本号是逐个试出来的,不能直接写死最新版。如果装 1.12+,配置文件的字段名有 breaking changes(比如 inet4_address 改成了 address 数组),直接用服务端的配置模板会报错。

客户端配置

从服务端私钥推导 Reality 公钥

服务端的 Reality 配置里只有 private_key,但客户端需要 public_key 来完成 TLS 握手。X25519 的私钥和公钥是一一对应的,用 Python 几行就能推出来。

from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
import base64
private_key_b64 = "<YOUR_PRIVATE_KEY>"
private_bytes = base64.b64decode(private_key_b64 + "==")
private_key = X25519PrivateKey.from_private_bytes(private_bytes)
public_key = private_key.public_key()
pub_b64 = base64.urlsafe_b64encode(
public_key.public_bytes_raw()
).decode().rstrip("=")

这里踩了一个编码坑。sing-box 用的是 URL-safe base64-_ 分别替代 +/,而且不带 = 补齐。用标准 base64 编码带 / 字符的话,sing-box 会报 illegal base64 data at input byte 24。正确的公钥是类似 mIXry4FE5HzXqBhBxpp9uBDc_RdCSbmDmJk8O6Uzh1Y 这种格式。

VLESS Reality:翻车实录

VLESS Reality 的客户端配置看起来没什么问题:

{
"outbounds": [{
"type": "vless",
"tag": "proxy",
"server": "<YOUR_VPS_IP>",
"server_port": 443,
"uuid": "<YOUR_UUID>",
"flow": "xtls-rprx-vision",
"tls": {
"enabled": true,
"server_name": "www.microsoft.com",
"utls": {"enabled": true, "fingerprint": "chrome"},
"reality": {
"enabled": true,
"public_key": "<YOUR_PUBLIC_KEY>",
5 collapsed lines
"short_id": "<YOUR_SHORT_ID>"
}
}
}]
}

TCP 三次握手能完成 ——nc -zv 测 443 端口显示 open。但 sing-box 在 TLS ClientHello 发出去之后 1-2 毫秒就收到了 RST:

ERROR connection: open outbound connection:
read tcp 192.168.3.216:33642-><VPS_IP>:443:
read: connection reset by peer

openssl s_client 裸测也一样 ——ClientHello 发出后服务端 0 字节回应,连接直接被 reset。

换 Hysteria2:通了

Hysteria2 基于 QUIC / UDP,GFW 对 UDP 的检测能力远弱于 TCP,而且 Hysteria2 本身对协议指纹的隐藏做得更好。

{
"outbounds": [{
"type": "hysteria2",
"tag": "proxy",
"server": "<YOUR_VPS_IP>",
"server_port": 8443,
"password": "<YOUR_PASSWORD>",
"tls": {
"enabled": true,
"server_name": "localhost",
"insecure": true
}
}]
}

测试结果:

=== Google ===
HTTP 200 (1.325s)
=== GitHub ===
HTTP 200 (0.855s)
=== IP check ===
{"origin": "<YOUR_VPS_IP>"}

出口 IP 确认是 VPS 的 IP,代理正常工作。

最终配置

sing-box 客户端配置

{
"log": {"level": "info", "timestamp": true},
"inbounds": [
{
"type": "mixed",
"tag": "mixed-in",
"listen": "127.0.0.1",
"listen_port": 2080
}
],
"outbounds": [
{
"type": "hysteria2",
"tag": "proxy",
"server": "<YOUR_VPS_IP>",
16 collapsed lines
"server_port": 8443,
"password": "<YOUR_PASSWORD>",
"tls": {
"enabled": true,
"server_name": "localhost",
"insecure": true
}
},
{"type": "direct", "tag": "direct"}
],
"route": {
"auto_detect_interface": true,
"rules": [{"ip_is_private": true, "outbound": "direct"}],
"final": "proxy"
}
}

几个关键设计决策:

  • 只用了 mixed inbound(HTTP + SOCKS5),没有开 TUN 模式。在服务器环境(尤其有 Docker 网桥的时候),TUN 的 auto_route + strict_route 会劫持全部路由,而代理链路本身也走默认路由,形成死循环导致整机断网。这个坑在 PVE 虚拟化环境里尤其容易触发。
  • 内网 IP 走直连(ip_is_private),其余全部走代理出口。
  • 域名类规则(geosite:cn 之类的)因为没下载 geoip.db 就没加,如果后续需要可以手动补上。

系统环境变量

写入 /etc/environment,让系统级别的工具(apt、curl 等)自动走代理:

Terminal window
http_proxy=http://127.0.0.1:2080
https_proxy=http://127.0.0.1:2080
no_proxy=localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,.cn,.local

no_proxy 里把内网网段和 .cn 域名都排除了,避免国内访问绕路。

Docker 守护进程代理

Docker 拉镜像不走系统环境变量,得单独给 Docker daemon 配。创建 /etc/systemd/system/docker.service.d/http-proxy.conf

[Service]
Environment="HTTP_PROXY=http://127.0.0.1:2080"
Environment="HTTPS_PROXY=http://127.0.0.1:2080"
Environment="NO_PROXY=localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,.cn,.local"

然后 systemctl daemon-reload && systemctl restart docker 生效。

后续:Hysteria2 突然断连的排查

代理跑了两天之后突然挂了。Hermes Gateway 疯狂报 API 超时,curl 走代理也全部 timeout。下面是把整个排查过程拆开讲。

现象

curl -x http://127.0.0.1:2080 http://ip.sb 直接卡死,sing-box 日志反复报:

ERROR: open outbound connection: http3: parsing frame failed:
timeout: no recent network activity

看起来像是 VPS 没响应,第一反应是 GFW 又开始搞 UDP 了。

排查过程

第一步:确认 VPS 是不是挂了。 SSH 到 VPS,systemctl status sing-box 显示 running,ss -ulnp | grep 8443 端口在监听,证书也没过期。而且 VPS 日志里能看到它成功接收了来自 VM 公网 IP 的 Hysteria2 连接,说明 UDP 包确实能到达 VPS。

第二步:怀疑 ISP 丢 UDP。conntrack 看了 VM 的连接跟踪表,UDP NAT 映射状态是 ESTAB + ASSURED,看起来一切正常。但错误持续出现,不是偶发丢包 —— 是完全不通。

第三步:发现版本差了两代。 VPS 装的 1.13.11,VM 装的是 1.11.7。Go 版本也不一样(1.25 vs 1.24),QUIC 库实现有差异。1.11 和 1.13 之间 QUIC 协议参数可能已经不兼容了 —— 握手能过,但 HTTP / 3 帧解析阶段客户端和服务端对不上。

升级客户端

VM 没有互联网,不能直接从 GitHub 下。绕路方案:Mac 下载 → scp 到 VM → dpkg 安装。

Terminal window
# 在能上网的机器上下载(Mac / VPS 都行)
curl -fsSL -o /tmp/sing-box.deb \
"https://github.com/SagerNet/sing-box/releases/download/v1.13.11/sing-box_1.13.11_linux_amd64.deb"
# scp 到 VM
scp /tmp/sing-box.deb root@192.168.3.216:/tmp/
# 在 VM 上执行
DEBIAN_FRONTEND=noninteractive dpkg -i --force-confold /tmp/sing-box.deb
systemctl daemon-reload
systemctl restart sing-box

--force-confold 保留现有配置文件,避免 dpkg 问是否覆盖。

升级后版本对齐,curl -x http://127.0.0.1:2080 http://ip.sb 立刻返回 VPS IP。Hermes Gateway 的 Telegram 和飞书也瞬间连上了。

踩坑总结

现象解决
GitHub 被墙官方 deb 下不了用镜像站拼直链逐个版本试
GeoIP 自动下载sing-box 启动时尝试下载 geoip.db,被阻断后 FATAL不用 geoip 规则,或设 ENABLE_DEPRECATED_GEOIP=true
Reality 公钥编码illegal base64 data at input byte 24sing-box 用 URL-safe base64,无 padding
VLESS TCP 被 RSTClientHello 后 1-2ms connection resetGFW 深度包检测精准识别,放弃 TCP 走 UDP
TUN 模式断网整台服务器失联服务器环境不要用 TUN,用 HTTP / SOCKS5 proxy
sing-box 版本差异inet4_address deprecated1.12+ 改用 address 数组格式
客户端版本不匹配Hysteria2 突然断连,http3: parsing frame failed: timeout升级到与服务端一致(1.13.11),排查时先看版本再看 GFW

给 PVE 宿主机的备忘

如果以后要给 PVE 宿主机(也是 Debian)装代理:

  1. 同一套流程:装 sing-box → 写 Hysteria2 客户端配置 → 启动
  2. PVE 宿主机同样不要开 TUN,用 mixed inbound
  3. 更简单的做法:直接让 PVE 走这台 VM 的代理,export https_proxy=http://192.168.3.216:2080,不用额外装 sing-box

最后说一句,GFW 的升级速度比很多人想象的快。VLESS Reality 在 2024 年还能用,到 2025 年 TCP 层面的检测已经能做到毫秒级 RST 注入了。UDP 协议(Hysteria2、TUIC 等)目前还算可用,但谁也说不准能撑多久。有条件的还是备几个不同协议的方案,别把鸡蛋放一个篮子里。