feat(client/macos): OSSystemExtensionRequest 激活 sysext
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

VpnChannel.activateSystemExtensionIfNeeded 实装:start 前请求系统加载/更新
PacketTunnel sysext;首启弹「隐私与安全性」待用户允许(await 阻塞到批准,
扩展没加载隧道起不来)。SysExtActivationDelegate 处理 finish/fail/replace。

至此 macOS 原生 VPN 栈构建完整:Runner 嵌入 sysext 到
Contents/Library/SystemExtensions,编译全过。运行待:sysext 签名放行 +
systemextensionsctl developer on(或 Developer ID 公证)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-19 15:54:14 +08:00
parent f2a4290602
commit fb1ef9ba01
+41 -4
View File
@@ -12,6 +12,7 @@
import FlutterMacOS
import NetworkExtension
import SystemExtensions
// NSLog (Runner < macOS 11,os.Logger )
private func vpnLog(_ message: String) { NSLog("[pangolin/vpn] %@", message) }
@@ -24,6 +25,7 @@ final class VpnChannel: NSObject {
private var statusObserver: NSObjectProtocol?
private var statsTimer: Timer?
private var manager: NETunnelProviderManager?
private var sysextDelegate: SysExtActivationDelegate?
static func register(with registrar: FlutterPluginRegistrar) {
let instance = VpnChannel()
@@ -100,11 +102,17 @@ final class VpnChannel: NSObject {
return mgr
}
// sysex
// / PacketTunnel System Extension
// ; didFinishWithResult
private func activateSystemExtensionIfNeeded() async throws {
// TODO(P1): OSSystemExtensionRequest.activationRequest(
// forExtensionWithIdentifier: tunnelBundleId, queue: .main) + delegate
// 4
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
let req = OSSystemExtensionRequest.activationRequest(
forExtensionWithIdentifier: Self.tunnelBundleId, queue: .main)
let delegate = SysExtActivationDelegate(continuation: cont)
self.sysextDelegate = delegate //
req.delegate = delegate
OSSystemExtensionManager.shared.submitRequest(req)
}
}
// /
@@ -161,3 +169,32 @@ private final class StatsStreamHandler: NSObject, FlutterStreamHandler {
}
func onCancel(withArguments _: Any?) -> FlutterError? { owner?.onStatsCancel(); return nil }
}
// System Extension ,
// didFinishWithResult( await ,
// )
private final class SysExtActivationDelegate: NSObject, OSSystemExtensionRequestDelegate {
private let continuation: CheckedContinuation<Void, Error>
private var resumed = false
init(continuation: CheckedContinuation<Void, Error>) { self.continuation = continuation }
func request(_ request: OSSystemExtensionRequest,
didFinishWithResult result: OSSystemExtensionRequest.Result) {
guard !resumed else { return }
resumed = true
continuation.resume()
}
func request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) {
guard !resumed else { return }
resumed = true
continuation.resume(throwing: error)
}
func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
vpnLog("system extension 待用户允许(系统设置 → 隐私与安全性)")
}
func request(_ request: OSSystemExtensionRequest,
actionForReplacingExtension existing: OSSystemExtensionProperties,
withExtension ext: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction {
.replace
}
}