docs: 嵌入 libbox + 系统 VPN API 替换 sudo sing-box 完整设计(方案B)
调研 sing-box 官方客户端架构(libbox via gomobile + NE/VpnService),产出上线标准 的核心引擎嵌入设计:各平台机制、libbox 构建流水线、Flutter 集成改造、签名/权限/ 公证门槛、分阶段迁移、风险。关键结论:中国 VPN 走 App Store 外分发 → macOS 必须用 System Extension(非 App Extension)+ Developer ID + 公证 + 申请 Apple NE 权限。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
# 设计:嵌入 sing-box(libbox)+ 系统 VPN API —— 替换外部 sudo 二进制(方案 B)
|
||||
|
||||
> 状态:设计稿(上线标准)。目标:把当前 PoC 的「`sudo sing-box run` 外部二进制」
|
||||
> 换成**自包含、免 root、可分发**的生产架构,对齐 sing-box 官方客户端做法。
|
||||
|
||||
## 1. Context / 为什么
|
||||
|
||||
当前 `client/lib/bridge/kernel_process.dart` 用 `Process.start(['sudo', sing-box, 'run', '-c', cfg])`
|
||||
拉起**外部** sing-box 二进制建 TUN。问题(上线阻断):
|
||||
- **依赖外部二进制**:换台机器还得另装 sing-box;无法分发自包含安装包。
|
||||
- **需要 root/sudo**:靠 NOPASSWD sudoers 提权,既不安全、又无法上架/分发。
|
||||
- **子进程易孤儿**:sing-box 是 app 子进程,强杀/异常退出残留(已打补丁,但治标)。
|
||||
|
||||
**目标架构**:把 sing-box 编进 app(`libbox` 库),用各平台**系统 VPN 扩展 API**
|
||||
创建 TUN(无需 root),app 与隧道引擎在同进程/扩展进程内通过库调用 + IPC 通信。
|
||||
|
||||
## 2. 参照:sing-box 官方客户端架构
|
||||
|
||||
官方 `sing-box-for-apple`(Swift)、`sing-box-for-android`(Kotlin)就是这套:
|
||||
- **libbox**(`github.com/sagernet/sing-box/experimental/libbox`,Go):核心引擎库。
|
||||
`gomobile bind` 产出 **xcframework**(Apple)/ **aar**(Android)。
|
||||
- **PlatformInterface**:宿主(原生)实现的回调接口,libbox 通过它拿 TUN fd、网络信息、
|
||||
写日志等;宿主无需懂 sing-box 内部。Android 叫 `PlatformInterfaceWrapper`。
|
||||
- **BoxService**:封装 libbox 的启停 + 生命周期;`NewService(config, platformInterface)`。
|
||||
- **CommandServer / CommandClient**(gRPC IPC):取实时状态/速率/连接、下发控制(切节点等),
|
||||
跨「主 app ↔ 扩展进程」通信。→ 我们的连接页速率/延迟可走这个(替换现 clash_api 轮询)。
|
||||
- TUN 由系统 API 提供:**NetworkExtension**(Apple)/ **VpnService**(Android),**无需 root**。
|
||||
|
||||
## 3. 各平台设计
|
||||
|
||||
### 3.1 macOS(当前主力,优先做)
|
||||
|
||||
**关键约束(调研结论)**:Pangolin 走 **App Store 外分发**(中国 VPN 无法上架 App Store),
|
||||
→ 必须用 **System Extension(NEPacketTunnelProvider 作 sysex)**,**不能用 App Extension**:
|
||||
- App Extension(appex):entitlement 无 `-systemextension` 后缀 → **仅 Mac App Store**。
|
||||
- **System Extension(sysex)**:entitlement 带 `-systemextension` 后缀 → **App Store 或 Developer ID 独立分发**。← 我们用这个。
|
||||
- 参考 Apple **TN3134**(NE provider 部署与 entitlement)。
|
||||
|
||||
组成:
|
||||
- **主 app**(Flutter / Runner):UI + 通过 `NETunnelProviderManager` 安装/启停隧道配置。
|
||||
- **System Extension target**(Swift,新增):`NEPacketTunnelProvider` 子类,内部链接
|
||||
**libbox.xcframework**;`startTunnel` 里用下发的 sing-box JSON 调 `libbox.NewService(...)`,
|
||||
TUN fd 由 NE 框架给(`packetFlow` / `NEPacketTunnelNetworkSettings`)。
|
||||
- **App Group**(`group.com.pangolin.pangolin`):主 app 与扩展共享配置/状态/日志文件。
|
||||
- **签名/公证**:Developer ID 签名 + 启用 **Hardened Runtime** + **notarization**;
|
||||
扩展与主 app 都签;申请 **Network Extensions** capability(见 §6)。
|
||||
|
||||
注:`NEProvider` 不能在主 app 进程实例化(Flutter issue #48395)——provider 必须在
|
||||
独立扩展 target;Flutter 侧只做控制(method channel → NETunnelProviderManager)。
|
||||
|
||||
### 3.2 iOS(后续)
|
||||
|
||||
- **Packet Tunnel app extension**(NEPacketTunnelProvider,iOS 用 appex 即可)+ libbox.xcframework。
|
||||
- 分发:TestFlight / 企业签 / 自签(中国无法上架 App Store);仍需 **Network Extensions** entitlement。
|
||||
- 复用 macOS 的 Swift PacketTunnel 代码(NE 在 iOS/macOS 高度共用)。
|
||||
|
||||
### 3.3 Android(后续,工作量相对小)
|
||||
|
||||
- **VpnService**(系统 API,无需 root)+ **libbox.aar**(gomobile bind)。
|
||||
- Kotlin 侧实现 `PlatformInterfaceWrapper`(对齐官方),`BoxService` 启停 libbox;
|
||||
TUN fd 由 `VpnService.Builder.establish()` 提供 → 传给 libbox。
|
||||
- 最接近官方 `sing-box-for-android`,可大量参考。
|
||||
|
||||
### 3.4 Windows(后续)
|
||||
|
||||
- 无 NetworkExtension。方案:**Windows 服务(管理员)运行 libbox + wintun**;
|
||||
app 通过本地 IPC(named pipe / loopback gRPC = libbox CommandServer)控制服务。
|
||||
- libbox 编为 Windows DLL/exe(CGO);wintun.dll 随包。
|
||||
|
||||
### 3.5 Linux(最低优先)
|
||||
|
||||
- 特权 helper / `setcap CAP_NET_ADMIN` + libbox(FFI 或子进程);app 经本地 IPC 控制。
|
||||
|
||||
## 4. libbox 构建流水线(令牌之外的新「核心层」)
|
||||
|
||||
- 固定 sing-box 版本(与节点 sing-box 1.13.x 对齐,避免配置不兼容)。
|
||||
- **Apple**:`gomobile bind -target ios,macos -o Libbox.xcframework ./experimental/libbox`(含
|
||||
device + simulator + macOS arch)。产物纳入 `client/macos|ios/` 的扩展 target。
|
||||
- **Android**:`gomobile bind -target android -o libbox.aar ./experimental/libbox`。
|
||||
- **Desktop(Win/Linux)**:`go build -buildmode=c-shared` 出动态库 + Dart FFI,或服务进程。
|
||||
- CI:每次升 sing-box 版本重编四端产物;校验配置渲染(`clientconfig.go`)与 libbox 兼容。
|
||||
|
||||
## 5. Flutter 集成层(改造点)
|
||||
|
||||
- 现 `VpnBridge` 抽象**保留**(`statusStream` / `statsStream` / `start(config)` / `stop` /
|
||||
`setKillSwitch`),换底层实现:
|
||||
- `DesktopVpnBridge`(现 `Process.start sudo sing-box`)→ 新 **`NativeVpnBridge`**:
|
||||
经 **method channel** 调原生(macOS/iOS:NETunnelProviderManager 启停 + App Group 写配置;
|
||||
Android:VpnService 启停)。`start(configJson)` = 把 /connect 的 sing-box JSON 交给扩展。
|
||||
- `statsStream` / `statusStream` 改接 **libbox CommandClient**(原生侧订阅 → method/event
|
||||
channel 回传 Dart),替换现 clash_api HTTP 轮询。
|
||||
- 上层(connection_provider / 连接页 / 设置 Kill Switch)**几乎不动**——接口不变。
|
||||
- `kernel_process.dart`(sudo 子进程)在桌面端下线(保留作 fallback/调试可选)。
|
||||
|
||||
## 6. 签名 / 权限 / 分发(上线硬门槛)
|
||||
|
||||
- **Apple Network Extensions entitlement**:需向 Apple **申请**(Developer 账号 →
|
||||
Network Extensions capability,packet-tunnel-provider)。**审批是前置阻断项**,先提交。
|
||||
- **付费 Apple Developer 账号**(个人/公司)+ Developer ID 证书。
|
||||
- macOS:sysex 必须 **Developer ID 签名 + Hardened Runtime + 公证**;首次启用 sysex 用户需在
|
||||
「系统设置 → 隐私与安全性」允许。
|
||||
- App Groups capability(主 app + 扩展共享)。
|
||||
- bundle id 规划:app `com.pangolin.pangolin`,扩展 `com.pangolin.pangolin.tunnel`,
|
||||
App Group `group.com.pangolin.pangolin`。
|
||||
|
||||
## 7. 配置 / 状态 数据流
|
||||
|
||||
```
|
||||
控制面 /connect → sing-box JSON
|
||||
│ (Dart) NativeVpnBridge.start(json)
|
||||
▼
|
||||
method channel → 原生:写 App Group 配置 + NETunnelProviderManager.startVPNTunnel
|
||||
▼
|
||||
System Extension(sysex 进程):libbox.NewService(json, platformIface) → 建 TUN(NE 给 fd)
|
||||
▼
|
||||
libbox CommandServer ──(gRPC/IPC)──▶ 原生订阅速率/状态 ──(event channel)──▶ Dart 连接页
|
||||
```
|
||||
|
||||
## 8. 分阶段迁移(PoC 全程保持可用,逐端切换)
|
||||
|
||||
1. **P0 · libbox Apple 产物 + 版本对齐**:编出 `Libbox.xcframework`,固定 sing-box 版本,
|
||||
本地能 `NewService` 起一个最小配置(冒烟)。
|
||||
2. **P1 · macOS System Extension(主力)**:新增 sysex target + NEPacketTunnelProvider +
|
||||
App Group + 签名公证;`NativeVpnBridge` 经 method channel 启停;连接页接 libbox 状态/速率。
|
||||
**达标即 macOS 不再需要外部 sing-box / sudo。**
|
||||
3. **P2 · Android**:VpnService + libbox.aar + PlatformInterfaceWrapper(参考官方)。
|
||||
4. **P3 · iOS**:复用 macOS PacketTunnel 代码 + iOS appex。
|
||||
5. **P4 · Windows/Linux**:libbox 服务/helper + wintun / setcap。
|
||||
- 每阶段:旧 PoC 路径保留为 fallback,直到新路径验收(连真节点、出口 IP、速率、Kill Switch、
|
||||
退出拆隧道、明暗/中英全过)。
|
||||
|
||||
## 9. 风险 / 阻断项
|
||||
|
||||
- **Apple Network Extensions entitlement 审批**(周期不定)——**最先提交**。
|
||||
- 付费开发者账号 + 证书/公证流水线(CI 自动签名公证)。
|
||||
- libbox 与节点 sing-box **版本/配置兼容**:`clientconfig.go` 下发的 JSON 必须被嵌入 libbox 接受
|
||||
(已踩过 1.12+ DNS/urltest 坑,需对齐版本)。
|
||||
- 桌面 Win/Linux 无统一系统 VPN API,需各自特权服务/helper(工作量分散)。
|
||||
- Flutter 与原生扩展的 method/event channel 桥接 + 后台进程生命周期(扩展崩溃恢复)。
|
||||
|
||||
## 10. 工作量 / 取舍
|
||||
|
||||
- P0+P1(macOS 自包含免 root)是**上线最小集**,但涉及 Go 交叉编译 + Xcode 扩展 target +
|
||||
签名公证 + Apple 权限申请,属**重型**(以「周」计,且被 Apple 审批甘特图卡)。
|
||||
- 建议:**先提交 Apple Network Extensions 申请**(并行等待),同时做 P0 libbox 编译冒烟;
|
||||
审批/账号就绪后做 P1。其余端按 P2→P4 排期。
|
||||
- 当前 PoC 足够继续打磨产品功能;本迁移作为**发布前必须完成**的独立 epic 推进。
|
||||
|
||||
## 参考
|
||||
- sing-box for Apple(官方客户端):https://github.com/SagerNet/sing-box-for-apple
|
||||
- sing-box for Android(官方客户端):https://deepwiki.com/SagerNet/sing-box-for-android
|
||||
- libbox command system:https://deepwiki.com/SagerNet/sing-box/6.3-libbox-command-system
|
||||
- sing-box Apple 客户端文档:https://sing-box.sagernet.org/clients/apple/
|
||||
- Apple TN3134(Network Extension provider 部署/entitlement)
|
||||
Reference in New Issue
Block a user