46 lines
3.0 KiB
Markdown
46 lines
3.0 KiB
Markdown
# 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 兜底收回 → 属正常。
|