feat(client/macos): P1 方案B 骨架 — System Extension + NETunnelProviderManager 接线
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

把 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>
This commit is contained in:
wangjia
2026-06-19 11:07:36 +08:00
parent 64a1a64c3f
commit b25c8bbc2c
10 changed files with 480 additions and 6 deletions
+98
View File
@@ -0,0 +1,98 @@
# P1 · macOS System Extension 接线指南(方案B)
> 状态:**骨架已落**(本仓库)。把 PoC 的 `sudo sing-box run` 外部二进制换成自包含、免 root
> 的 NEPacketTunnelProvider(System Extension)+ 嵌入式 libbox。本文档列清单 + 待人工在
> Xcode/开发者后台完成的步骤。总览见 [vpn-core-embedding.md](./vpn-core-embedding.md)。
## 0. 关键标识(已确定)
| 项 | 值 |
|---|---|
| Team ID | **BYL4KQHMTN** |
| 主 app bundle id | `com.pangolin.pangolin`(App ID 已注册,勾 Network Extensions + App Groups)|
| 隧道扩展 bundle id | `com.pangolin.pangolin.tunnel`(**待注册**,同样勾 Network Extensions + App Groups)|
| App Group | `group.com.pangolin.pangolin`(**待在 Identifiers → App Groups 注册**)|
| 分发方式 | 站外 Developer ID + 公证(非 App Store)→ entitlement 用 `-systemextension` 后缀 |
| 权限审批 | **无需**——Network Extensions 在 Identifiers 自助勾选即得(Packet Tunnel Provider 已 GA)|
## 1. 仓库里已就位的文件(骨架)
```
client/macos/
├─ Runner/
│ ├─ DebugProfile.entitlements / Release.entitlements ← application-groups 已备(注释,待 §3.6 解开)
│ ├─ MainFlutterWindow.swift ← VpnChannel 注册行已备(注释,待 §3.6 解开)
│ └─ VpnChannel.swift ← 主app:NETunnelProviderManager 启停+状态/速率回传(待加入 target)
└─ PacketTunnel/ ← 新 System Extension target 的源
├─ PacketTunnelProvider.swift ← 扩展:LibboxSetup→NewService→start;openTun 建 TUN
├─ PacketTunnel.entitlements ← packet-tunnel-provider-systemextension + app group
└─ Info.plist ← NEProviderClasses → PacketTunnelProvider
client/lib/bridge/
├─ vpn_bridge.dart ← VpnNativeBridge(method/event channel,已存在)
└─ vpn_bridge_provider.dart ← kUseNativeVpnMacOS 开关(默认 false;原生就绪后置 true)
```
**Channel 契约**(`VpnNativeBridge``VpnChannel`):
- MethodChannel `pangolin/vpn`:`start(configJson)` / `stop` / `getStatus` / `selectOutbound(tag)` / `getActiveOutbound` / `setKillSwitch(bool)`
- EventChannel `pangolin/vpn/status`:状态字符串(`on`/`connecting`/`off`/…)
- EventChannel `pangolin/vpn/stats`:每秒一帧 `{up, down, uplinkTotal, downlinkTotal}`
## 2. 开发者后台(你来做,几分钟)
1. **App Group**:Identifiers → App Groups → `group.com.pangolin.pangolin`
2. 回到 App ID `com.pangolin.pangolin`**App Groups** capability → Edit → 勾上该 group。
3. 新建 App ID `com.pangolin.pangolin.tunnel`(扩展)→ 勾 **Network Extensions** + **App Groups**(关联同一 group)。
4. **Developer ID Application** 证书(Certificates → ,若还没有)——签名 + 公证用。
## 3. Xcode:新增 System Extension target + 接 libbox(核心人工步骤)
> Flutter 工程开 `client/macos/Runner.xcworkspace`。
1. **编 libbox**:`bash scripts/build-libbox.sh apple macos` → 得 `Libbox.xcframework`(~204MB,不入 git)。
2. **新 target**:File → New → Target → **Network Extension**(macOS)→ Packet Tunnel
- Product Name:`PacketTunnel`,bundle id `com.pangolin.pangolin.tunnel`,语言 Swift。
- 删掉模板生成的 `PacketTunnelProvider.swift`,把 `client/macos/PacketTunnel/` 下的三个文件
加入该 target(Provider/Info.plist/entitlements);Info.plist 与 entitlements 设为本 target 用。
3. **链接 libbox**:把 `Libbox.xcframework` 拖进工程,在 **PacketTunnel** target 的
*Frameworks and Libraries* 加入(Embed & Sign);解开 `PacketTunnelProvider.swift`
`import Libbox` 与被注释的真实接线段(`LibboxSetup`/`LibboxNewService`/`service.start()`)。
4. **补平台接口**:`PangolinPlatformInterface``LibboxPlatformInterfaceProtocol` 的完整实现
(openTun / writeLog / useProcFS / findConnectionOwner / defaultInterfaceMonitor /
getInterfaces / underNetworkExtension / systemCertificates …)——**对照官方
[sing-box-for-apple `ExtensionPlatformInterface`](https://github.com/SagerNet/sing-box-for-apple)
逐方法移植**(NE 部分两端高度一致,可大段复用)。这是 P1 唯一"重"的代码块。
5. **sysex 激活**:`VpnChannel.activateSystemExtensionIfNeeded()`
`OSSystemExtensionRequest.activationRequest(forExtensionWithIdentifier: tunnelBundleId, queue:)`
+ delegate 等结果;首启用户在「系统设置 → 隐私与安全性」点允许。
6. **接主 app(Runner)**:
-`client/macos/Runner/VpnChannel.swift` 加入 **Runner** target 的 *Compile Sources*
- 解开 `MainFlutterWindow.swift``VpnChannel.register(...)` 那行注释。
- 解开 `Runner/DebugProfile.entitlements``Release.entitlements``application-groups` 注释块
(注册 App Group 后)。
> 这三处在仓库里**有意保持注释/未接**,以免在 target/签名就绪前破坏现有 PoC 构建。
7. **签名**:两个 target(Runner + PacketTunnel)都用 Team **BYL4KQHMTN** + Developer ID;
开 Hardened Runtime;Runner 的 *System Extensions* capability 自动带出。
## 4. 速率/状态联调(libbox CommandServer)
- 扩展内 `LibboxNewCommandServer`(loopback)暴露状态/速率/连接;主 app 用 `LibboxCommandClient`
订阅 → 回填 `pangolin/vpn/stats``…/status`(替换 `VpnChannel` 里的占位 timer)。
- 连接页(`connect_page.dart`)与统计页已消费这两路流,**无需改动**。
## 5. 切换上线
- 全部联调通过(连真节点、出口 IP=节点、速率/延迟、Kill Switch、退出拆隧道、明暗/中英)后,
`vpn_bridge_provider.dart``kUseNativeVpnMacOS`**true**
- PoC 的 `DesktopVpnBridge`(sudo 子进程)保留作 fallback/调试,不删。
## 6. 待办清单(TODO,代码内已标 `TODO(P1)`)
- [ ] 开发者后台:App Group + 扩展 App ID + Developer ID 证书(§2)
- [ ] Xcode:新 PacketTunnel target + 加入 `Libbox.xcframework`(§3.13.3)
- [ ] 解开 `PacketTunnelProvider.swift` 真实接线 + 移植 `PangolinPlatformInterface`(§3.4)
- [ ] `OSSystemExtensionRequest` 激活流程(§3.5)
- [ ] `LibboxCommandClient` 接速率/状态(§4)
- [ ] `setKillSwitch` / `selectOutbound` 真实实现
- [ ] 签名 + 公证流水线(Team BYL4KQHMTN)
- [ ] 联调通过 → `kUseNativeVpnMacOS = true`(§5)
+10 -3
View File
@@ -94,8 +94,10 @@
## 6. 签名 / 权限 / 分发(上线硬门槛)
- **Apple Network Extensions entitlement**:需向 Apple **申请**(Developer 账号 →
Network Extensions capability,packet-tunnel-provider)。**审批是前置阻断项**,先提交。
- **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 用户需在
「系统设置 → 隐私与安全性」允许。
@@ -126,6 +128,11 @@ libbox CommandServer ──(gRPC/IPC)──▶ 原生订阅速率/状态 ──(
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](./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。
@@ -134,7 +141,7 @@ libbox CommandServer ──(gRPC/IPC)──▶ 原生订阅速率/状态 ──(
## 9. 风险 / 阻断项
- **Apple Network Extensions entitlement 审批**(周期不定)——**最先提交**
- ~~Apple Network Extensions entitlement 审批~~ → **已排除**:自助开通,无审批(见 §6)
- 付费开发者账号 + 证书/公证流水线(CI 自动签名公证)。
- libbox 与节点 sing-box **版本/配置兼容**:`clientconfig.go` 下发的 JSON 必须被嵌入 libbox 接受
(已踩过 1.12+ DNS/urltest 坑,需对齐版本)。