Files
pangolin/docs/vpn-core-embedding.md
T
wangjia b25c8bbc2c
ci-pangolin / Lint — shellcheck (push) Has been cancelled
ci-pangolin / OpenAPI Sync Check (push) Has been cancelled
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Has been cancelled
ci-pangolin / Flutter — analyze + test (push) Has been cancelled
feat(client/macos): P1 方案B 骨架 — System Extension + NETunnelProviderManager 接线
把 PoC 的 sudo sing-box 外部二进制换成自包含、免 root 的 NEPacketTunnelProvider
(System Extension)+ 嵌入 libbox 的生产架构铺好骨架(不破坏现有 PoC 构建)。

- PacketTunnel/:扩展 target 源 — PacketTunnelProvider(LibboxSetup→NewService→start,
  openTun 建 NEPacketTunnelNetworkSettings)、Info.plist(NEProviderClasses)、
  entitlements(packet-tunnel-provider-systemextension + App Group)
- Runner/VpnChannel.swift:主 app 经 NETunnelProviderManager 启停 + 状态/速率回传,
  对齐 Dart 侧 VpnNativeBridge 的 pangolin/vpn channel 契约
- vpn_bridge_provider.dart:kUseNativeVpnMacOS 开关(默认 false,联调通过后置 true)
- docs/p1-macos-system-extension.md:文件清单 + Xcode/签名步骤 + 待办
  (Team BYL4KQHMTN;Network Extensions 已确认自助开通、无需 Apple 审批)

非破坏:新源文件未入 build target、注册行/app-group entitlements 均注释、gate 默认 false。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 11:07:36 +08:00

10 KiB

设计:嵌入 sing-box(libbox)+ 系统 VPN API —— 替换外部 sudo 二进制(方案 B)

状态:设计稿(上线标准)。目标:把当前 PoC 的「sudo sing-box run 外部二进制」 换成自包含、免 root、可分发的生产架构,对齐 sing-box 官方客户端做法。

1. Context / 为什么

当前 client/lib/bridge/kernel_process.dartProcess.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 审批——在 Identifiers → App ID 的 Capabilities 里直接勾 Network Extensions 即得 Packet Tunnel Provider(含 -systemextension 形态)。早年需申请的是 NEHotspotHelper(热点), 与我们无关。故此项不再是阻断项。
  • 付费 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 版本。 已完成:scripts/build-libbox.sh apple macos 复现;用 sagernet/gomobile fork v0.1.12 + sing-box v1.13.13 + 官方 build_libbox 编出双架构(arm64+x86_64) Libbox.xcframework(~204MB),Headers 暴露 LibboxNewService / LibboxCommandClient (状态·速率·连接)等 API。产物不入 git(体积大),按需用脚本重编 / 后续走 CI。
  2. P1 · macOS System Extension(主力):新增 sysex target + NEPacketTunnelProvider + App Group + 签名公证;NativeVpnBridge 经 method channel 启停;连接页接 libbox 状态/速率。 达标即 macOS 不再需要外部 sing-box / sudo。 🚧 骨架已落:App/扩展 entitlements(App Group)、VpnChannel(NETunnelProviderManager 启停+状态/速率回传)、PacketTunnel/(Provider+Info.plist+entitlements)、Dart 分派开关 kUseNativeVpnMacOS(默认 false)。接线指南:p1-macos-system-extension.md。 Network Extensions 权限已确认自助开通、无需 Apple 审批;Team ID BYL4KQHMTN。 待人工:Xcode 建 target + 加 Libbox.xcframework + 移植 libbox 平台接口 + 签名公证。
  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 审批已排除:自助开通,无审批(见 §6)。
  • 付费开发者账号 + 证书/公证流水线(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 推进。

参考