Files
dsh_shenxian/dsh-server-docs/04-调整方案/43-平台root污染用户工作区与属主自愈.md
T
admin 5ad755116e chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
   保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
   工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
   必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
   + ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
   ⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
   验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
2026-09-15 18:47:13 +08:00

47 lines
3.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 43-平台 root 污染用户工作区与属主自愈(2026-09-11 落地)
## 背景与动机
从 guest 最新会话取证时发现:会话里 `pip install --user` 会失败。复核后确认是**平台自身 bug**,不是权限策略问题。
## 现象与根因
- **现象**:guest 的 `ws/.local`、`ws/.local/share/pnpm` **属主是 `root:root`** → 之后用户
`pip install --user` 报 `Permission denied: '.../ws/.local/lib'` —— **在自己的目录里装不了包**。
admin 工作区无此问题(从未被 root 的 pnpm 触碰)。
- **根因**:平台以 **root** 执行 `pnpm`,且 `HOME` 指向用户工作区:
`src/web/routes/business-plugins.ts`(`pnpmEnv`)与 `scripts/ensure-biz-plugins.cjs`。
pnpm 于是在用户家目录建出 `root` 属主的 `.local/share/pnpm`(store)。
- **为什么不能简单改 `HOME`**:代码注释已写明 —— 需与 dsh 实例共用同一个 pnpm store,
否则报 `ERR_PNPM_UNEXPECTED_STORE`(此前已踩过)。所以**不能**把平台侧 pnpm 的 HOME 挪走。
## 实现(只 chown、不删除)
1. **`scripts/ws-cleanup.cjs`** 新增 `reclaimOwnership(root, uid)`:
- 递归 `lstat` + `lchownSync`,**不跟随软链**;
- **只改属主,不删除任何文件**(用户资产与 root 建的 pnpm store 都保留,root 依旧可读写);
- 与清理阈值**无关**,always 执行;新增 **`--reclaim-only`** 模式(只做回收、不跑清理)。
2. **即时**:`scripts/ensure-biz-plugins.cjs` 在 `pnpm add` 成功之后,立刻调
`ws-cleanup --apply --reclaim-only --user-id <id>` → 消除"当天不可用"的窗口。
3. **兜底**:`/etc/cron.d/dsh-maintenance` 新增 **每小时** `25 * * * *` 跑 `--apply --reclaim-only`。
4. **顺带修一个潜在死循环**:`ensure-biz-plugins.cjs` 里 `dest` 的文件名**硬编码**
`business-plugins-0.2.0.tgz` → 改为 `basename(ARTIFACT)`。
(否则一旦走安装分支,依赖名会停在旧版本 → `!upToDate` 恒真 → **每次运行都重装**。)
## 验证记录
| 检查 | 结果 |
|---|---|
| 存量修复(全量扫描) | guest **49 项** root 属主 → `chown -R` 后 **0 项**;admin 本就 0 |
| 人为造污染 → 自愈 | 造 **51 项** root 污染 → `--reclaim-only` 一次回收为 **0** |
| 文件内容/可用性 | 不变(0 字节测试文件回收后属主 `100002:100002`,仍可读写) |
| 真实跑 `ensure-biz-plugins.cjs` | `guest: 已是 business-plugins-0.2.1.tgz → 跳过` —— 无回归 |
## 回滚 / 注意
- 回滚:`cp scripts/ws-cleanup.cjs.bak-<ts> scripts/ws-cleanup.cjs`(并删掉 cron 行)。
- 该 pass **只 chown**,不会让任何体积统计/清理行为改变;`.local` 仍在 T1 清理名单里
(超阈值时会被移入 trash,30 天可恢复)——本改动不改变这一点。
- 迁移到新服务器时:本自愈逻辑在 `scripts/` 内随仓库走;但**新机上首次仍会重新产生**
root 属主的 `.local`(pnpm 行为),由 cron/ensure 兜底收回 → 属正常。