64c427cf11
- docs/android-client-design.html / killswitch-design.html / index.html - docs/superpowers/plans/2026-06-22-android-client.md - todo/ KillSwitch 三端待办(#1 mac/#2 Android/#3 Windows) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
152 lines
12 KiB
HTML
152 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>KillSwitch 设计与跨平台能力矩阵(知识库)</title>
|
||
<style>
|
||
:root{
|
||
--bg:#0f1117; --panel:#171a22; --panel2:#1d2129; --fg:#e6e8ee; --fg2:#a8afbd;
|
||
--accent:#e0884f; --accent2:#5fb0c9; --ok:#5ec27a; --bad:#e06a6a; --warn:#e0b84f;
|
||
--border:#272c36; --mono:"SF Mono",ui-monospace,Menlo,Consolas,monospace;
|
||
--sans:-apple-system,"PingFang SC","Helvetica Neue",Arial,sans-serif;
|
||
}
|
||
*{box-sizing:border-box}
|
||
body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);line-height:1.7;font-size:15px}
|
||
.wrap{max-width:920px;margin:0 auto;padding:48px 24px 96px}
|
||
h1{font-size:30px;line-height:1.3;margin:0 0 8px;letter-spacing:-.01em}
|
||
.sub{color:var(--fg2);font-size:15px;margin:0 0 32px}
|
||
h2{font-size:21px;margin:48px 0 14px;padding-bottom:8px;border-bottom:1px solid var(--border)}
|
||
h3{font-size:16px;margin:28px 0 8px;color:var(--accent2)}
|
||
p{margin:10px 0}
|
||
code{font-family:var(--mono);font-size:.88em;background:var(--panel2);padding:1px 6px;border-radius:5px;color:#f0d9c4}
|
||
pre{background:#0a0c11;border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow-x:auto;font-family:var(--mono);font-size:13px;line-height:1.55;color:#cdd3df}
|
||
.tag{display:inline-block;font-size:12px;font-weight:600;padding:2px 9px;border-radius:999px;vertical-align:middle}
|
||
.tag.bad{background:rgba(224,106,106,.16);color:var(--bad)}
|
||
.tag.ok{background:rgba(94,194,122,.16);color:var(--ok)}
|
||
.tag.warn{background:rgba(224,184,79,.16);color:var(--warn)}
|
||
.tag.info{background:rgba(95,176,201,.16);color:var(--accent2)}
|
||
.card{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin:16px 0}
|
||
.card.root{border-left:3px solid var(--accent)}
|
||
.card h3{margin-top:0}
|
||
table{width:100%;border-collapse:collapse;margin:16px 0;font-size:14px}
|
||
th,td{text-align:left;padding:9px 12px;border-bottom:1px solid var(--border);vertical-align:top}
|
||
th{color:var(--fg2);font-weight:600;font-size:13px}
|
||
td code{font-size:.85em}
|
||
.ok-c{color:var(--ok)} .bad-c{color:var(--bad)} .warn-c{color:var(--warn)}
|
||
ul,ol{padding-left:22px;margin:10px 0}
|
||
li{margin:5px 0}
|
||
.lead{background:linear-gradient(180deg,rgba(224,136,79,.10),transparent);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin:0 0 8px}
|
||
.small{color:var(--fg2);font-size:13px}
|
||
hr{border:none;border-top:1px solid var(--border);margin:40px 0}
|
||
a{color:var(--accent2)}
|
||
.back{display:inline-block;margin-bottom:24px;font-size:13px}
|
||
.lvl{font-weight:700}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="wrap">
|
||
|
||
<a class="back" href="index.html">← 文档索引</a>
|
||
|
||
<h1>KillSwitch 设计与跨平台能力矩阵<span class="small">(知识库)</span></h1>
|
||
<p class="sub">Pangolin 客户端 · 断网保护设计依据与现状 · 2026-06-22</p>
|
||
|
||
<div class="lead">
|
||
KillSwitch 不是「一个开关」,而是<b>分层能力</b>——理想态、平台天花板、当前实现是三件不同的事。本文沉淀其设计依据与现状。
|
||
</div>
|
||
|
||
<h2>1. 本质:fail-closed</h2>
|
||
<p>KillSwitch 的本质是 <b>fail-closed</b>:</p>
|
||
<div class="card"><b>隧道不处于活动状态时,禁止任何流量走明文/默认路径出去。</b></div>
|
||
<p>目标是堵住 VPN 掉线瞬间的 <b>IP / DNS 泄漏</b>。没有 KillSwitch 时,隧道一掉,流量静默回落到运营商默认路由,用户真实 IP 与 DNS 查询直接暴露——对科学上网场景是致命泄漏。</p>
|
||
|
||
<h2>2. 理想设计的 5 条属性</h2>
|
||
<ol>
|
||
<li><b>默认关闭(fail-closed)</b>:隧道一旦不可用,流量是「被丢弃」而非「放行」。</li>
|
||
<li><b>扛得住进程死亡</b> ← 最难。app/内核进程崩溃或被系统杀掉后,封锁<b>依然生效</b>。这要求封锁由 <b>OS 内核/框架</b>强制,而不是靠 app 进程内的路由表。</li>
|
||
<li><b>覆盖所有「缺口窗口」</b>:开机后还没连上的窗口、掉线重连的窗口、进程崩溃的窗口——三个都要堵。</li>
|
||
<li><b>DNS 防泄漏</b>:DNS 查询也强制走隧道或被一并阻断。</li>
|
||
<li><b>可控范围 + 诚实 UI</b>:可选放行 LAN(局域网打印机等)、可选分应用;并<b>如实</b>告诉用户当前到底保护到了哪一层,不夸大。</li>
|
||
</ol>
|
||
|
||
<h2>3. 能力分级(统一标尺)</h2>
|
||
<table>
|
||
<thead><tr><th>Level</th><th>机制</th><th>能堵什么</th><th>堵不住什么</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><span class="lvl bad-c">L0 无</span></td><td>掉线即走明文</td><td>—</td><td>全泄漏</td></tr>
|
||
<tr><td><span class="lvl warn-c">L1 内核路由绑定(软)</span></td><td>路由绑死隧道接口(如 sing-box <code>strict_route</code>)</td><td>进程活着、隧道未建/重连时不泄漏</td><td><b>进程一死、接口被拆 → 路由恢复 → 泄漏</b></td></tr>
|
||
<tr><td><span class="lvl ok-c">L2 OS 防火墙强制(硬)</span></td><td>OS 级包过滤规则(NE 框架 / WFP / nftables)阻断非隧道流量</td><td><b>进程死了也照堵</b>(规则在 OS 内核,不在 app)</td><td>开机到规则生效前的窗口</td></tr>
|
||
<tr><td><span class="lvl ok-c">L3 常开 + 开机持久</span></td><td>L2 + OS 从开机起自动拉起 VPN 并强制阻断</td><td>开机窗口也堵,全程 fail-closed</td><td>(基本无死角)</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p><b>理想态 = L3。</b></p>
|
||
<div class="card root">
|
||
<b>关键认知</b>:L1 与 L2 之间有一条<b>质变线</b>——L1 是「app 进程内的约束」,L2/L3 是「OS 框架的约束」。<b>只有跨过这条线才算「真 KillSwitch」</b>。目前 Pangolin 各平台用的 <code>strict_route</code> 都还停在 L1。
|
||
</div>
|
||
|
||
<h2>4. 各平台<b>能力天花板</b></h2>
|
||
<table>
|
||
<thead><tr><th>平台</th><th>机制</th><th>天花板</th><th>说明</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><b>macOS</b></td><td>NetworkExtension System Extension</td><td><span class="ok-c">L3</span></td><td><code>includeAllNetworks=true</code> 全量入隧道 + <code>NEOnDemandRule</code> 常开 + <code>enforceRoutes</code>。NE 守护进程(neagent)在系统级强制,<b>扛 app 崩溃</b>。Apple 平台原生支持最完整。</td></tr>
|
||
<tr><td><b>iOS</b></td><td>NetworkExtension</td><td><span class="ok-c">L3</span></td><td>同 NE 原语,甚至更干净;on-demand「按需常开」。⚠️ 目前<b>无 iOS 客户端</b>。</td></tr>
|
||
<tr><td><b>Android</b></td><td>VpnService</td><td><span class="ok-c">L3</span><span class="small">,但有条件</span></td><td>app 内只能到 <b>L1</b>(<code>strict_route</code>);真正的 L2/L3 = 系统设置「<b>始终开启 VPN + 无 VPN 时阻止连接</b>」,OS 级强制、扛崩溃、开机生效。<b>但 app 不能编程开启</b>,只能深链引导用户手动开(或 Device Owner/MDM 下发)。</td></tr>
|
||
<tr><td><b>Windows</b></td><td>子进程 sing-box + wintun</td><td><span class="warn-c">L2(需开发)</span></td><td>现仅 <code>strict_route</code>(L1)。要到 L2 须让 app 装 <b>WFP(Windows Filtering Platform)过滤器</b>阻断非隧道流量;做成系统服务持久化才能扛崩溃。</td></tr>
|
||
<tr><td><b>Linux</b><span class="small">(非主目标)</span></td><td>子进程 + tun</td><td><span class="warn-c">L2(需开发)</span></td><td>L1 现成;L2 靠 nftables/iptables killswitch 链。</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p><b>一句话</b>:Apple 两端原生能直达 L3;Android 能到 L3 但要用户手动配合;Windows/Linux 要自己写 OS 防火墙规则才能上 L2。</p>
|
||
|
||
<h2>5. 各平台<b>当前实际实现</b>(代码事实)</h2>
|
||
<table>
|
||
<thead><tr><th>平台</th><th>当前 Level</th><th>真实状态</th><th>代码位置</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><b>Windows</b></td><td><span class="warn-c">L1</span></td><td>✅ 改 <code>strict_route</code> + 子进程重载 + 退避重连。进程被硬杀仍泄漏。</td><td><code>desktop_vpn_bridge.dart:305</code>(<code>applyKillSwitchToConfig</code>)、<code>:178</code></td></tr>
|
||
<tr><td><b>Linux</b></td><td><span class="warn-c">L1</span></td><td>同 Windows(共用 <code>DesktopVpnBridge</code>)。</td><td>同上</td></tr>
|
||
<tr><td><b>macOS</b></td><td><span class="bad-c">L0</span></td><td>❌ stub:<code>setKillSwitch</code> 直接 <code>result(nil)</code>,<code>includeAllNetworks()→false</code>。天花板 L3,实际啥都没做。</td><td><code>VpnChannel.swift:69</code>、<code>PacketTunnelProvider.swift:224</code></td></tr>
|
||
<tr><td><b>Android</b></td><td><span class="bad-c">L0</span></td><td>❌ stub:<code>setKillSwitch</code> 只打日志(TODO 11G)。</td><td><code>MainActivity.kt</code>(<code>setKillSwitch</code> 分支)</td></tr>
|
||
<tr><td><b>iOS</b></td><td>—</td><td>无客户端。</td><td>—</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<p class="small">注:macOS 自 <code>kUseNativeVpnMacOS=true</code>(<code>vpn_bridge_provider.dart:19</code>)起走原生 System Extension,不再走 <code>DesktopVpnBridge</code>。所以 macOS 的 <code>strict_route</code>(Windows 路线)对它<b>不生效</b>,必须走 NE 原生路线。</p>
|
||
|
||
<h2>6. Pangolin 现实判断与决策</h2>
|
||
<ul>
|
||
<li><b>能力与实现严重不匹配</b>:macOS 明明能 L3,现在却是 L0(最差);Windows 反而是唯一做了的(L1)。</li>
|
||
<li><b>Android 客户端方案的 KillSwitch 定位</b>(MVP+ 阶段):app 内做到 <b>L1(<code>strict_route</code>,与 Windows 一致)</b>,再<b>引导用户开系统 Always-on 拿到 L3</b>,UI 上诚实标注「彻底防泄漏需在系统设置开启」。这是「app 能力上限 + OS 兜底」的合理组合,不夸大。</li>
|
||
<li><b>跨端一致性缺口</b>(按性价比排序):
|
||
<ol>
|
||
<li><b>macOS 原生 11G</b>(<code>includeAllNetworks</code> + on-demand)—— 天花板 L3、改动集中在 Swift,<b>性价比最高</b>。</li>
|
||
<li><b>Windows L1→L2</b>(WFP 防火墙强制)—— 工作量较大(需原生过滤器 + 持久化服务)。</li>
|
||
<li><b>Android</b> —— 受 OS 限制,编程上限就是 L1,剩下靠引导,无更高可做空间。</li>
|
||
</ol>
|
||
</li>
|
||
</ul>
|
||
|
||
<h2>7. 关联待办</h2>
|
||
<p>KillSwitch 相关待办见项目 <code>todo/</code>(<code>/todo list</code>),主要三条:</p>
|
||
<ul>
|
||
<li><span class="tag info">#1 mac</span> macOS 原生 KillSwitch 补齐 L0→L3(<code>includeAllNetworks</code> + on-demand)</li>
|
||
<li><span class="tag info">#2 Android</span> Android KillSwitch L1(<code>strict_route</code>)+ 引导系统 Always-on(属 Android 客户端 11G)</li>
|
||
<li><span class="tag info">#3 Windows</span> Windows KillSwitch L1→L2(WFP 防火墙强制,backlog)</li>
|
||
</ul>
|
||
|
||
<h2>8. 参考实现位置索引</h2>
|
||
<table>
|
||
<thead><tr><th>主题</th><th>文件</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>桥接契约(<code>setKillSwitch</code> 方法签名)</td><td><code>client/lib/bridge/vpn_bridge.dart</code></td></tr>
|
||
<tr><td>平台分派(哪个平台走哪个 bridge)</td><td><code>client/lib/bridge/vpn_bridge_provider.dart</code></td></tr>
|
||
<tr><td>Windows/Linux 实现(strict_route)</td><td><code>client/lib/bridge/desktop_vpn_bridge.dart</code></td></tr>
|
||
<tr><td>macOS 原生通道(stub)</td><td><code>client/macos/Runner/VpnChannel.swift</code></td></tr>
|
||
<tr><td>macOS 隧道 Provider</td><td><code>client/macos/PacketTunnel/PacketTunnelProvider.swift</code></td></tr>
|
||
<tr><td>Android 通道(stub)</td><td><code>client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/MainActivity.kt</code></td></tr>
|
||
<tr><td>Android VPN 服务</td><td><code>client/android/app/src/main/kotlin/com/pangolin/pangolin_vpn/PangolinVpnService.kt</code></td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|