mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-12 04:07:25 +00:00
fix(部署+插件): VNC 兜底登录**真正可用** —— 放开浏览器栈不需要的加固 + 修 3 个插件缺陷
用户原话:「全部放开,插件不让用这些还叫插件吗」。方向是对的:插件架构的边界 应当是**内核暴露给插件的 API 权限**,而不是一层把插件自己的浏览器栈也关在门外的 进程级 syscall 过滤器。 ## ★ 白屏的根因(本次花了大量时间,记录完整线索链) 同一页面、同一 chromium 参数,只换 seccomp: | SystemCallFilter | 结果 | |---|---| | `@system-service` | 页面**永远空白**(截图非白像素 7.9%,连 `typeof WebAssembly` 都不执行) | | 放开 | 完整渲染 TRAE 登录页(非白像素 **90.6%**,标题 `登录 \| TRAE - The Real AI Engineer`) | TRAE 授权页是 React SPA + 字节风控 SDK(`sdk-glue.js`),依赖 WASM/JIT。 白名单**只减不增**地挡住了它。 **为什么不再逐个补白名单**:那是没有终点的维护 —— 每接一个新平台、每换一次前端 SDK 都可能再撞一个缺口,而症状永远是"页面白屏"这种零线索形态。本次为此**错误 怀疑过** 5 个方向(profile 锁 / /tmp 满 / PrivateTmp / 可写 HOME / AF_NETLINK), 全部被排除后才靠**逐 directive 二分**定位。补白名单只会让下一个人重复这个过程。 `packaging/llmsproxy.service` 因此去掉两项: - `SystemCallFilter=@system-service` + `SystemCallArchitectures=native` - `RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX`(缺 `AF_NETLINK` 会让 chromium 建套接字失败 → SSL `net_error -101` → 导航不了) **保留**的加固(已逐条实测不影响浏览器栈):`NoNewPrivileges` / `ProtectSystem=strict` / `ReadWritePaths=/etc/llmsproxy` / `ProtectHome` / `PrivateTmp` / `CapabilityBoundingSet=`(空)/ `ProtectKernel*` / `RestrictSUIDSGID` / `RestrictRealtime` / `LockPersonality`。可写面仍收窄到 `/etc/llmsproxy`。 ## 修掉的 3 个插件缺陷(都是真 bug,各自有判据) 1. **前向引用(第 12 次)**:`sweepSessions` 调 `reapLoopback`/`browserStack`/ `reapBrowser`,三者定义在**后面** ⇒ 未声明时是全局 nil。加载期不报错,只有走到 「会话过期回收」才炸 ⇒ **第一次登录正常、第二次 500**,且已起的浏览器栈不被回收 (进程与端口泄漏)。→ 加前向声明 + 定义改赋值式。 2. **`spawnBrowser` 里 `url` 是未定义全局**(形参名是 `start_url`)⇒ chromium 打开 `http://nil/`。修复前页面 URL = `http://nil/`,修复后 = 完整 `trae.cn/authorization?...`。 3. **chromium 在加固环境自杀**:`FATAL: GPU process isn't usable. Goodbye.` → `--in-process-gpu`(GPU 逻辑跑浏览器进程内,不 fork)。另给可写 HOME/XDG(`ProtectHome=yes` 下 /root 只读会刷 permission denied 噪声)。 另加**存活探针**:此前只等 CDP 端口就报「已就绪」,即便浏览器已死也报成功 —— **谎报比直接报错更坏**(运维会以为是自己操作的问题)。现在会如实报出 `浏览器 CDP 无应答:...FATAL: GPU process isn't usable`。 ## 测试与生产抢资源的修复 生产 `PrivateTmp=yes` 下 Xvfb `:91` 的锁在**服务自己的 /tmp** 里,宿主测试看不到 ⇒ `xdisplayBusy(91)` 判"空闲"⇒ 测试去抢 91 ⇒ `Xvfb 未就绪`。 → 显示器起始号可被 `LLMSPROXY_XDISPLAY_BASE` 覆盖,测试台统一用 300+; `TestXDisplaySelectionReclaimsStaleLocks` 的号段也跟随(否则它造出的陈旧锁**根本 不会被扫描到**,断言永远失败 —— 那是判据与被测对象脱节)。 (端口不需要覆盖点:端口是全局资源,`portFree` 查 /proc/net/tcp 看得见生产占用。) ## 判据 - `TestSweepSessionsHelpersAreForwardDeclared`:三助手必须前向声明 + 定义必须是 赋值式 + 声明必须**先于**使用。 - `TestBrowserStackVerifiesLivenessNotJustPort`:必须有 CDP 真应答判据 + /proc 存活 + 日志真因 + 失败时回收整组进程。 - `TestBrowserStackUsesInProcessGPU`:`--in-process-gpu` / 可写 HOME/XDG 不得被删。 - `TestChannelsVncStackEndToEnd` / `TestXDisplaySelection*`:真实起栈验证。 ## 端到端验证 `bash deploy.sh` → 生产 `active` / `NRestarts=0`;真实登录请求 → `server_browser=True`, VNC 上**完整渲染 TRAE 登录页**(手机号登录 / 抖音 / 苹果 / 企业账号登录全部可见), 标题 `登录 | TRAE - The Real AI Engineer`。 回归:9 个包全绿。
This commit is contained in:
@ -49,14 +49,38 @@ ProtectControlGroups=yes
|
||||
RestrictSUIDSGID=yes
|
||||
RestrictRealtime=yes
|
||||
LockPersonality=yes
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||
# ⚠️ 地址族**不限制**(2026-10-15)。原来是 AF_INET AF_INET6 AF_UNIX,
|
||||
# 但 chromium 需要 AF_NETLINK 做网络状态跟踪;缺它 → 建套接字失败 →
|
||||
# SSL 握手失败(net_error -101)→ 页面导航不了。
|
||||
# 现在不写这一项(systemd 默认不限制)。
|
||||
# CapabilityBoundingSet 置空:本服务不需要任何 capability(不建 netns、不改
|
||||
# 资源限制、不 chown)。留空即"一个都不给",比列一份允许清单更难写错。
|
||||
CapabilityBoundingSet=
|
||||
# @system-service 已实测通过(含一次真实推理请求),它挡掉的是 mount/pivot_root/
|
||||
# keyctl 这类与网关无关的系统调用。
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallArchitectures=native
|
||||
# ⚠️⚠️ **不限制 syscall**(2026-10-15,实测后决定)。
|
||||
#
|
||||
# 原先是 `SystemCallFilter=@system-service` + `SystemCallArchitectures=native`,
|
||||
# 且在真实推理请求上验证过。但它与 VNC 浏览器栈**不兼容**,实测证据:
|
||||
#
|
||||
# 同一页面、同一 chromium 参数,只换 seccomp:
|
||||
# @system-service → 页面**永远空白**(连 `typeof WebAssembly` 都不执行)
|
||||
# 放开 → 完整渲染(TRAE 登录页,非白像素 7.9% → 90.6%)
|
||||
#
|
||||
# 原因:TRAE 的授权页是 React SPA + 字节风控 SDK(sdk-glue.js),依赖 WASM/JIT。
|
||||
# WASM 需要 syscall 白名单里的若干项(`mprotect`/`mmap`/`memfd` 实测单独可用,
|
||||
# 缺口在 `@privileged` 组内的成员),而白名单**只减不增**地挡住了它们。
|
||||
#
|
||||
# 为什么不再逐个补白名单:那是**没有终点的维护**。每接一个新平台、每换一次
|
||||
# 前端 SDK,都可能再撞一个缺口,而症状永远是"页面白屏"这种零线索的形态
|
||||
# (本次为此耗掉大量时间,先后错误怀疑过 profile 锁 / 磁盘满 / PrivateTmp /
|
||||
# 可写 HOME / AF_NETLINK)。加固的意义是防外部攻击面,不是让插件干不了活 ——
|
||||
# 插件架构的边界应当是**内核暴露给插件的 API 权限**(add_source / secrets /
|
||||
# http 等,那些仍然受控),而不是一层把插件自己的浏览器栈也关在门外的
|
||||
# 进程级 syscall 过滤器。
|
||||
#
|
||||
# 仍然保留的加固(见下):NoNewPrivileges / ProtectSystem=strict /
|
||||
# ReadWritePaths / ProtectHome / PrivateTmp / CapabilityBoundingSet=(空)/
|
||||
# ProtectKernel* / RestrictSUIDSGID / RestrictRealtime / LockPersonality。
|
||||
# 这些**都不影响**浏览器栈(已逐条实测),并把可写面收窄到 /etc/llmsproxy。
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user