feat(relay): B7 — close browser DPoP-subprotocol loop + harden staging mint

- Read DPoP proof from term.dpop.<b64u> WS subprotocol (browsers can't set WS headers);
  header wins, else subprotocol. Fail-closed decoder. Unblocks real browser connect.
- F1: rate-limit /auth/mint per-IP via Redis token bucket (salted-hash key, 429 on burst,
  before password compare).
- F2: wire real per-tenant active WS count into activeSessionCount (was hardcoded 0).
- F5: scrub error logs to e.message/.code (no DSN leak, INV9).
relay-run: tsc clean, 92 tests pass (+18). F3/F4 -> Phase 2 backlog.
This commit is contained in:
Yaojia Wang
2026-07-06 16:26:05 +02:00
parent aa1912b962
commit bfe1be1dfe
8 changed files with 378 additions and 17 deletions

View File

@@ -28,7 +28,8 @@
- **目标**: DEPLOY_RELAY §4 的 **Phase 1「1b 全量持久化 staging」**。VPS 上 Docker 自建 Postgres+Redis;已备案域名走 443 + Let's Encrypt;agent 跑在操作者本机向外拨号。完整文件级方案见 [PLAN_RELAY_PHASE1.md](./PLAN_RELAY_PHASE1.md)。
- **进度**: **Wave A/B/C/D/E 代码全部完成并通过 Verify** —— 一个后台 Workflow(12 agents,0 error)跑完 A2、B1B5、B6、C、D1、E。Verify 门槛:**四个包 tsc 全干净、可构建包全 build 成功、314/314 测试通过**(control-plane 103 / relay-run 74 / agent 137 + relay-web 118)。对抗式安全评审:**所有硬不变量 PASS**(INV2 opaque splice、Origin/CSWSH 精确匹配、mTLS INV14 registry 门控、token-mint 密码学、撤销 INV12、jti 单次、参数化 SQL)。
- **各任务**: A2 P3 server 入口+Redis 总线 `[x]`;B1 共享 store EnforceDeps `[x]`;B2 registry-backed MtlsVerifier `[x]`(异步阻抗由 B5 的 `bridgeAsyncMtls` per-DER 预取桥无锁解决);B3 store RouteResolver `[x]`;B4 撤销订阅 `[x]`;B5 `main-phase1.ts` 组合入口 + staging `/auth/mint` 签发 `[x]`;C agent `dist/cli.js` 构建+`runTunnel` 运行入口 `[x]`;D1 同源托管 relay-web `[x]`;E systemd+脚本+RUNBOOK `[x]`;**B6 relay-web 登录/DPoP `[~] PARTIAL`**。
- **两个待收尾(B7)**: ① **功能阻塞** — 浏览器 WS 无法设请求头,B6 把 DPoP proof 放进 `term.dpop.<b64u>` 子协议,**relay 侧(browser-server.ts)必须从子协议读取并喂给 `UpgradeRequest.dpop`**,否则真实浏览器一律被 DPoP 拒(401)。② **F1 安全** — 公网 :443 上的 `/auth/mint` 无限速/锁定(评审最大攻击面);顺带 F2(`activeSessionCount` 硬编码 0,并发上限失效)、F5(裸 error 日志或泄露 DSN)。F3/F4(撤销后 agent 空转重连、slowloris)为 LOW,记入 Phase 2 backlog。
- **B7 收尾 `[x]` DONE**: ① 功能阻塞已闭环 — `browser-server.ts` 现从 `term.dpop.<b64u>` 子协议读 DPoP proof 喂 `UpgradeRequest.dpop`(头优先、否则回退子协议),真实浏览器可通过 DPoP 门。② F1 `/auth/mint` 加 Redis 按 IP 限流(HMAC 盐哈希 key,burst5/refill0.2,早于口令比对,耗尽 429);F2 `activeSessionCount` 接真实同租户在线 WS 计数;F5 错误日志只记 `e.message(+.code)` 防 DSN 泄漏。relay-run `npx vitest run`**10 files / 92 passed**、tsc 干净。**F3/F4(撤销后 agent 空转重连、slowloris)为 LOW Phase 2 backlog。**
- **⚠️ 端到端仅可在 VPS 验证**:浏览器登录→token→DPoP→upgrade→agent 拼接,本地只到类型/单元层。E2E5 远端执行见 `deploy/RUNBOOK.md`(需 VPS 访问权)。
- **远端(需 VPS 执行,见 `deploy/RUNBOOK.md`)**: DNS、Let's Encrypt 签证、私有 enrollment CA + capability keypair 生成、阿里云安全组放行 443/AGENT_PORT、systemd 起两个服务、端到端 enroll→dial→浏览器点进 shell。
- **未做/推迟到 Phase 2**: 真 KMS、F6 replay、WebAuthn step-up(staging 用 `NO_STEPUP_POLICY`)、通配多租户、F3/F4。
- **未做/推迟到 Phase 2**: 真 KMS(dev signer 暂用)、F6 replay(`loadReplay` fail-closed)、WebAuthn step-up(staging 用 `NO_STEPUP_POLICY`)、通配多租户。