通过 ssh 反向隧道搭梯子,经过长期验证

Shiliew - 专注提供中国大陆稳定的 VPN

0@FabricPath

需要国内有公网 IP ,跑了约一年的时间,非常的稳定,各种开大会都很稳定。


通过日志观察,ssh 连接不会被异常中断,最长一次 8 个 ssh 连接存活了 30 天,直到重新拨号。因为国内有公网 IP 的家宽不多,所以猜测这个方向的审查力度较弱。同时长连接也避免了 per connection 都要往远端创建一个 tcp 长连接的问题(之前试过 mihomo 自带的 mux ,体感对性能影响较大)。


从性能上来看,似乎是直接跳过了流量的明细审查(也有可能是 ssh 协议豁免,没仔细测试),表现在,如果正向建联( trojan-go ),speedtest 只能到 400Mbps ,通过 ssh 反向隧道,能打到 1400Mbps ,等同于走 cloudfront cdn 接入的性能。

下面是配方,仅供参考,没尝试非 ssh 的其他协议(比如 frp ),也可以自行尝试其他反向连接的协议。

```
整体架构

家庭路由器具有公网可访问地址,Mihomo 内置一个 SSH Server 。VPS 上的 ssh-reverse-agent 作为 SSH Client ,主动建立 8 条 SSH 长连接到路由器。

业务流量方向如下:
局域网设备
    │ TCP
    ▼
家庭路由器上的 Mihomo
    │ 规则匹配 ssh-reverse 出站
    │ 从 8 条 SSH 长连接中轮询选择一条
    │ SSH Server 主动打开业务 Channel
    ▼
VPS 上的 ssh-reverse-agent
    │ 建立到目标的 TCP 连接
    ▼
目标服务器
注意:SSH 长连接由 VPS 主动连回家庭路由器,但业务 Channel 由 家庭路由器上的 SSH Server 主动向 VPS 的 SSH Client 打开。使用 Go 的 golang.org/x/crypto/ssh 实现,SSH Server 必须集成在 Mihomo 中,不依赖路由器上的 OpenSSH 或 Dropbear 。

Mihomo 侧实现
1. 新增顶层配置 ssh-reverse-servers ,用于配置监听地址、SSH 主机私钥和允许接入的 agent 公钥。
2. 新增出站类型 ssh-reverse ,通过 server-ref 引用本地 SSH Server ,通过 agent-id 选择对应 VPS 。
3. 维护已认证的 SSH 会话池,按 agent 和连接槽位管理;连接断开后移除,重连注册后替换旧会话。
4. 收到出站 TCP 请求时,使用原子计数器进行轮询,选择一条可用 SSH 会话。
5. 调用该会话的 OpenChannel ,使用自定义 Channel 类型,例如 [email protected] 。通过 Channel 的附加数据传递协议版本、目标主机名、端口和拨号超时。
6. 将打开成功的 Channel 包装成 Mihomo 出站接口需要的 net.Conn ,接入现有流量转发逻辑。
示例配置:
ssh-reverse-servers:
  - name: home-entry
    listen: 0.0.0.0:122
    host-key: /path/to/home_ed25519
    authorized-agents:
      - id: vps-a
        public-key: "ssh-ed25519 <AGENT_PUBLIC_KEY>"

proxies:
  - name: VPS-REVERSE
    type: ssh-reverse
    server-ref: home-entry
    agent-id: vps-a
VPS agent 侧实现
1. 使用独立的 Ed25519 私钥向 Mihomo 认证,并固定校验 Mihomo 的 SSH 主机公钥。
2. 启动 8 个连接 worker ,各自维护一条 SSH 长连接,具有独立槽位编号。
3. 实现心跳、连接超时及带随机抖动的指数退避重连。
4. 接收 Mihomo 主动打开的自定义 Channel ,解析目标地址;域名由 VPS 解析。
5. 在 VPS 上拨号连接目标 TCP 服务。拨号成功后接受 Channel ,失败则拒绝 Channel ,并返回明确错误。
6. 在 SSH Channel 与目标 TCP 连接之间进行双向数据拷贝,正确处理 EOF 、半关闭、完整关闭和异常退出,及时释放连接、goroutine 及并发配额。
并发与性能设计

每条 SSH 长连接都支持多个业务 Channel ,每个 Channel 对应一条被代理的 TCP 连接。因此,8 条 SSH 长连接可以承载远超 8 条的业务并发连接。agent 提供可配置的全局通道上限,默认 4096 ,由全部 SSH 连接共享,并计入正在拨号的请求。

多条 SSH 长连接用于分散加解密负载,并减少单条底层 TCP 发生丢包时的影响范围。单条业务连接固定使用一条 SSH 长连接,不进行跨连接拆包或聚合;同一 SSH 连接中的 Channel 仍然共享底层 TCP 的队头阻塞。

加密优先采用 [email protected] ,适合具有 AES 硬件加速的设备。SSH 使用标准协议握手,但不应宣称其协议头能保证绕过网络审查。

实现时要特别注意:单个业务 Channel 的取消或超时,不应直接关闭共享 SSH 长连接,也不能通过修改底层 TCP deadline 来影响其他 Channel 。 日志应明确区分 SSH 会话建立与断开、目标拨号失败,以及单个业务 Channel 的正常关闭或传输错误
```

1@hanfengzxh

有了 ai 可以自己实现封装协议,又稳又安全。

2@FabricPath

@hanfengzxh 我没试过其他非标协议。如果我去搞 gfw 的话,看到非标准流量特征,且长期 SrcIP 单一、连接 cps 搞、带宽大,会考虑直接掐掉。
其实正向连接大家都玩不出啥花儿了,gfw 也不需要太复杂的策略,如果一个目标 IP 常年有入站请求,并且 src ip 很单一,那很容易就可以判定为是梯子了。
现在反向连接审查得少,我估计纯粹是因为国内家宽有公网的不多。

3@zizon

可能你直接 ssh tunnel 也一样.
tcp 层面你还是点对点 tcp 流量特征.

可能只是因为有公网 IP,更接近出口层,GFW 信道/侧信道反而更少.

4@yeyeye

ssh -d 算是最早的梯子了,甚至无需服务器端有单独的软件,本身就是 ssh 服务器标准功能( ssh 自带的功能其实挺多的,proxy 、端口转发[类似 frp]……)。

稳定了一段时间,后来通通被检测特征,限速,断开连接。然后大家都放弃了这种方式。。

从特征来说,ssh 发送的命令通常都很小数据包(低频,上行包很小,下行数据量很少,小概率使用 scp 互传文件才会有大一点的数据),对比之下,梯子的特征太明显(包大小、时序、频率等等)。

豁免?当年人家第一个干掉的就是 ssh 隧道啊。。

而且反向由国外连国内,也是在检测内的。


唯一有可能性的就是大量开发 ssh 协议的应用,使得 ssh 协议失去“专用协议”的特征,慢慢的无法准确检测,才可能放松。

当然我不是说完全不能用,你都使用了,效果在这里,肯定暂时是可用的,只是真不适合大范围使用,特征太过于明显了。。。现在的梯子,普遍是把自己伪装成正常流量。

而且 ssh 协议最多用于控制服务器,直接给你来一个限速,正常使用 ssh 管理服务器其实也能用,对所有人的影响很小。。这也是为什么大家早期放弃这个方式的原因,太过于局限了。

5@nekoyaki

ssh 隧道我估计是看接入链路中的某个环节, 我在 2014 年左右就开始用它,印象里大约到 2015 或者 16 年,就发现已经没法用了,会被定向封端口,或者会被严重干扰速度。
当时还有一个 obfuscated ssh 项目,是 fork 了一个很老版本的 ssh ,增加了混淆手段,用这个会稳定很多, 但因为 ssh 隧道自身一个 tcp 连接,有时候还容易“假死”,比如进程还在但端口监听没了, 或者 ssh 断了会导致整个链路断掉,之类的很多情况,所以我后来只把它当备用隧道方案了。 再后来这个工具也不维护了,我就没有继续用它了。

6@nomansky

古早的梯子技术。ssh 特征这么明显,没被封只是你运气好而已。

7@est

稳定?稳定是别人没拿你当 KPI 刷。。。。

8@FabricPath

@est 你自己实现协议不就是为了特征少吗,因为国内没多少人有公网 IP ,所以只是检测重点不在这里;如果要检测梯子流量,那太容易了,只要二元组流量过大、连接数过多,直接掐了就行了。
@nomansky 就是因为特征明显才猜测临时进了豁免通道,并且 ssh 建连接的频率极低,也比非 mux 情况下的新建连接数量少得多;非公开协议,那太明显了。
@yeyeye 就是因为国内有公网的少。相比伪装成正常的流量,之前最开始灰度接反向 ssh 的时候,和走 cloudfront 的正向连接做负载均衡,开会的时候会直接基于二元组封禁到 cloudfront 接入点 IP 的连接(不清楚非公网 IP 是否也会使用二元组封禁),但是 ssh 隧道还活着。

此外基于 gfwreport 过去几年的报告来看,gfw 审查模型多检测连接前 N 个报文,对于超长时间的长连接来说,没明显感知到会中途检测的情况。包括之前使用 mux 的时候,也观察到新建连接超时,存量连接能正常通信的情况,不过没定量评估正向长连接的稳定性,纯感觉。

@zizon 正向的 ssh tunnel 和其他协议一样,只能跑到 400Mbps ,速度明显会低一截。

9@est

@FabricPath 自己实现协议特征少是一方面,特征杂是另一方面。

比如以一千个人有一千种协议。这样 KPI 刷起来就没那么痛快

一千个机场一种协议,一次性可以刷个大的 KPI 

站在对方的视角思考问题。

10@FabricPath

@est 我关心对方 kpi 干嘛,被封了就继续用 cdn 接入就行了,本来家宽有公网就少,提供一个思路和测试结果而已,不知道你要杠啥。

11@crc8

我是从 SSH 转到 SS 来的。

12@v2gba

我记得反向是严打目标

good luck

13@lymanbernadette6

文艺复兴..

14@xeonforce

这给我干哪年来了,虽然这个方式还在内网开发上还常用,但拿来翻属实过于文艺复兴了..临时用用还行,以前会封端口的

15@leaflxh

快进到某天 OP 发帖:《服务器登不上去了》

16@FabricPath

@leaflxh 过去 5 、6 年一直用同一个 lightsail ,就今年流量大升了规格才换了 IP ,只有开大会的时候会短时间掐二元组。

@xeonforce 放到过去靠二元组流量特征去无脑掐的年代,这种模式容易被识别,放到现在,从泄露的 gfw 代码来看,中心都放到去扫描前 N 个报文的特征,而且 gfw 用的 dpdk ,dpdk 本身要做包缓存就需要消耗巨量的 hugepage ,不做包缓存又解决不了 SNI 分片。

从 gfw repo 的论文也能看出来,gfw 是对连接做了缓存的,并且在高峰期还会降级

17@vicalloy

早期用 ssh 隧道,被封的速度都是以分钟算的。
被封后等一段时间可以解封,没敢头铁继续用。

18@FabricPath

顺便补充一下为啥我会用反向的连接,因为去年粗略扫了一遍 tsg 的源码,发现他主要审查的方向是 tls 、quic ,并且从文档和已有的规则集来看,具备明显的境内->境外和境外->境内方向性,审查重点就是境外往境外主动发起的 tls 和 quic 。
但是并没有发现“长连接”,“二元组”等明显的规则,从过去几年只有特殊时期才封二元组来看,这应该是一个 adhoc 的规则集,平时不会启用。

至于为什么是 ssh ,纯粹是因为懒得做实验,ssh 比 frp 更像是正常流量,并且启用之后一直很稳定,也没动力去探索其他协议。
虽然从代码中也没找到明确豁免 ssh 的逻辑,但是从性能测试来看,使用反向的 ssh 会明显比正向的 ssh 隧道更快,且可以稳定复现,说明至少现在部署的 gfw 版本,大概率对两个方向的处理有差异。

19@FabricPath

https://geedge.report/assets/geedge-tsg-usenix26.pdf 附上今年的研究报告。

20@cr3bit

身边统计学:裸 ssh 配置改着改着 client_loop: send disconnect: Connection reset ,不套一层什么 overlay 根本没规律的断

21@MarkLeeyun

这是在干啥。。我经常用 reverse 连接局域网和另一个局域网。不过这个延迟够么? tcp 上面的是不是延迟偏高啊

22@ronman

@hanfengzxh #1 大隐隐于市才是真的,非标一抓一个准

23@le4tim

我其实不太想看到这样的帖子,因为我也知道。用的人多了就必然不好用。

24@iTYC

可爱程度不亚于发了个贴子说自己 ss 用了五年没被封

Shiliew - 专注提供中国稳定的 VPN