通过 ssh 反向隧道搭梯子,经过长期验证
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 用了五年没被封