我在mini主机上安装了 OpenClaw 对接了飞书和微信,有时候他自动升级后就挂了,飞书问问题卡且不回复,但是如果是在外面没法远程家里的机器,回家一查升级配置又变了,整个飞书对接网关挂了,但是又无法SSH连接。
这一刻我才意识到:过度依赖局域网管理本地设备,风险极高。升级任何基础服务前,必须先留好一条「后路」。
为什么传统的远程方案救不了我
事情发生后,我复盘了一下。平时我远程管理家里设备主要依赖以下几条路:
- DDNS + 端口映射:因为家里的网络没有固定公网 IP,所以用的是 Cloudflare Tunnel。但是也慢。
- Tailscale:组网工具,平时很方便。但是服务在国外,慢且我觉得不安全。
- IPv6:运营商给的 IPv6 地址经常变动,而且很多移动网络环境下 IPv6 访问质量不稳定。
一句话:慢,体验差。
方案对比:Cloudflare Tunnel vs Tailscale vs FRP
为了不再重蹈覆辙,我决定在另一台独立设备上部署一个「不依赖网关」的远程通道。接下来几天我逐一试了市面上主流的方案:
| 方案 | 优点 | 缺点 |
|---|---|---|
| Cloudflare Tunnel | 零配置、HTTPS 自动证书、免费 | 依赖 cloudflared 守护进程,网关挂了即失效;国内偶发连接不稳定 |
| Tailscale | P2P 直连、组网灵活、免费版够用 | 需要目标设备在线且运行 Tailscale 客户端;网关离线后内网路由中断 |
| FRP (Fast Reverse Proxy) | 自建服务端、完全可控、不依赖网关转发 | 需要自己维护一台公网服务器;需要配置服务端和客户端 |
试过一轮之后,我的结论是:
- Cloudflare Tunnel 和 Tailscale 都是好产品,但它们的共同点是「依赖目标网络本身的健康状态」。一旦网关/core 设备挂了,这些方案就无能为力。
- FRP 的优势在于「独立性」。只要被控设备能通电联网(哪怕走手机热点),它就能主动向公网服务器建立反向隧道,完全不需要本地网关参与。
于是我决定:在独立服务器上部署 frps,在所有需要远程救砖的设备上部署 frpc。
新问题:FRP 在手机上没法直接用
方案确定后,我很快在家里的 NAS 和软路由上都跑起了 frpc。但很快遇到了一个棘手的问题:
FRP 官方没有提供 Android 客户端。
而我最迫切的场景,恰恰是用手机远程 SSH 到内网设备。现有的 Android SSH 客户端(如 JuiceSSH、Termius、ConnectBot)都不支持 FRP 隧道。
我尝试过一些曲线救国的办法:
- 在手机上跑 Termux,然后在 Termux 里手动启动 frpc,再用 SSH 客户端连接
127.0.0.1:6000。能跑,但 Termux 后台很容易被系统杀掉,体验极差。 - 用 VPN 类工具做全局转发,但配置复杂而且不够稳定。
最终我意识到:最优雅的方案,是把 frpc 直接集成到 SSH 客户端里。点击连接主机时,App 自动在后台启动 frpc visitor 隧道,隧道就绪后再建立 SSH 连接。全程对用户透明。
改造 ConnectBot:给老牌 SSH 客户端加上 FRP 翅膀
ConnectBot 是一款老牌的开源 Android SSH 客户端,代码质量高、功能完整、协议支持全面。最重要的是:它是开源的,可以用 Kotlin 直接改。
我的改造思路很简单:
|
核心实现
嵌入式 frpc 二进制
我使用 Go 交叉编译了 Android 版的
frpc,以libfrpc.so的形式打包进 APK。这样 Android 安装时会自动按 ABI 解压到nativeLibraryDir,避免了 Android 10+ 对动态提取二进制文件的执行限制。GOOS=android GOARCH=arm64 CGO_ENABLED=0 \
go build -tags noweb -ldflags="-checklinkname=0 -s -w" \
-o libfrpc.so ./cmd/frpcFrpManager—— frpc 生命周期管理新建了一个
FrpManager类,负责:- 从 APK 中提取/定位 frpc 二进制文件
- 根据主机配置动态生成
frpc.toml - 启动 frpc 进程并轮询等待本地端口就绪
- 连接断开后自动停止进程、删除临时配置文件
数据库模型扩展
在
Host实体中新增了 FRP 相关字段:frpEnabled—— 是否启用 FRP 隧道frpServerAddr/frpServerPort—— FRP 服务端地址frpAuthToken—— 认证令牌frpVisitorName/frpServerName—— Visitor 与服务名称frpSecretKey—— STCP 加密密钥frpBindAddr/frpBindPort—— 本地绑定地址和端口
SSH 传输层集成
在
SSH.kt的connect()方法中,如果检测到frpEnabled,先调用FrpManager.startVisitor()建立隧道,然后将 SSH 连接目标从hostname:port切换为frpBindAddr:frpBindPort。断开时自动调用stopVisitor()。配置导入导出
支持通过 JSON 文件批量导入主机配置,一次性把所有内网设备的 FRP 参数配好:
{
"hosts": [
{
"nickname": "软路由",
"protocol": "ssh",
"hostname": "127.0.0.1",
"port": 22,
"username": "root",
"frpEnabled": true,
"frpServerAddr": "your-server.com",
"frpServerPort": 7000,
"frpAuthToken": "your-token",
"frpVisitorName": "router-visitor",
"frpServerName": "ssh-router",
"frpBindAddr": "127.0.0.1",
"frpBindPort": 6001
}
]
}
使用体验
改造完成后,整体体验非常流畅:
- 打开 App,点击主机列表中的「软路由」
- 终端里会先打印
Starting FRP visitor tunnel... - 约 1~2 秒后显示
FRP tunnel ready on 127.0.0.1:6001 - 接着自动建立 SSH 连接,输入密码后即可正常操作
- 退出会话时,frpc 进程自动停止,不占用后台资源
效果截图:


服务端配置参考
整个方案需要三端配合:
1. FRP 服务端(frps)
部署在一台有公网 IP 的服务器上:
|
2. 被控端 frpc(软路由/NAS)
|
3. 手机端 ConnectBot + FRP
如上面的 JSON 配置所示,每个主机使用不同的 frpBindPort(6000、6001…)避免冲突。
为什么这个方案更可靠
现在再回头看最初的故障场景:
- OpenClaw 升级导致网关挂了 → 没关系,frpc 跑在 NAS 上,NAS 直连光猫拨号,不依赖网关。
- 如果 NAS 也挂了 → 软路由上的 frpc 还在,可以 SSH 上去修。
- 手机热点给设备供网 → frpc 只要能连公网,就能建立隧道。
FRP 的核心优势是「反向连接」:被控设备主动向服务端发起连接,不需要公网 IP,也不需要本地网关做端口映射。只要有一条能出网的链路,就能被远程访问。
项目开源
这个定制版 ConnectBot 已经开源在 GitHub:
⚠️ 未在 Google Play 上架,仅在 GitHub Releases 提供 APK 下载。
如果你也有类似的需求 —— 需要在手机上通过 FRP 隧道 SSH 管理内网设备,可以直接下载使用,或基于代码自行定制。
总结
这次「救砖」经历让我深刻体会到:
- 任何核心基础设施升级前,先确保有独立的远程通道。不要把所有远程 eggs 放在同一个篮子里。
- 自建 FRP 比 Cloudflare Tunnel / Tailscale 更适合「救砖」场景,因为它的反向连接机制不依赖本地网络拓扑的健康状态。
- 手机端缺少 FRP 客户端是痛点,但通过改造 ConnectBot 可以优雅解决,体验接近原生。
现在我的架构是这样的:
|
下次再升级 OpenClaw,我可以淡定地坐在咖啡馆里,掏出手机,打开 ConnectBot,连上软路由,一步步排错 —— 而不是在寒风中焦急地赶回家。
评论
0 条评论