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 一律写「远程服务器」。
78 lines
6.1 KiB
Markdown
78 lines
6.1 KiB
Markdown
# 32 · mcntimo 会话取证与「dsh 沙箱不可用」P0 核查
|
||
|
||
- 日期:2026-09-11
|
||
- 触发:用户要求「看 admin 在 mcntimo 工作空间下的对话记录,能发现哪些问题」
|
||
- 状态:**🔍 核查完成,P0 待决策(未修复)**
|
||
|
||
> **TL;DR**|**结论**:admin 在 mcntimo 工作区的会话取证 → 查出 **「dsh 沙箱不可用 → 拒绝执行任何 shell」** 这一 P0。
|
||
> **关键**:取证方法可复用(会话事件流 → 错误归类 → 根因);该 P0 由档案 23/33 处置。
|
||
> **状态**:🔍 核查完成(P0 当时待决策)
|
||
|
||
## 一、取证方法(可复用)
|
||
|
||
- 会话文件:`/var/lib/dshs/users/<UID>/home/sessions/--<cwd 路径把斜杠换成横杠>--/session-<uuid>/session.jsonl.zstd`
|
||
- 工作区注册表:`$DSH_HOME/storages/workspace.json`(id → path/title/sessionIds)
|
||
- **坑**:文件是**追加式多帧 zstd**。Node 内置 `zstdDecompressSync` / `createZstdDecompress` 只能解**第一帧**(得 240 字节 session 头,随后报 `Unknown frame descriptor`)→ **必须用 zstd CLI**(服务器原先未装,已 `yum install -y zstd` 装上 `zstd-1.5.1-2.0.2.al8`)。
|
||
- mcntimo 共 2 个会话:主会话 **2268 行 / 1.61 MB**;另一会话 4 行(空会话)。
|
||
|
||
## 二、P0:dsh 的 `workspace-write` 沙箱在平台环境下不可用 → 拒绝执行任何 shell
|
||
|
||
**现象**:实例里执行 shell 报
|
||
|
||
```
|
||
sandbox mode "workspace-write" is requested but no sandbox backend is usable on this host;
|
||
refusing to run the command unconfined. ... otherwise switch the consumer to danger-full-access.
|
||
```
|
||
|
||
出自 `@deepseek-ai/dsh-sandbox/lib/index.js:185`。**fail-closed:没有可用后端就拒绝执行**,不是降级。
|
||
|
||
**独立核实**(非仅凭日志):
|
||
|
||
| 项 | 实测值 | 结论 |
|
||
|---|---|---|
|
||
| 内核 | `5.10.134-19.1.al8.x86_64` | **Landlock 需 5.13+ → 不可用** |
|
||
| LSM | `/sys/kernel/security/lsm` = `capability,selinux` | **landlock 未启用** |
|
||
| `/sys/kernel/security/landlock` | 不存在 | 同上 |
|
||
| bubblewrap | 0.4.0 已装 | 但 dsh 的后端探测仍判为不可用 |
|
||
| 平台合成根 /bin /sbin /lib 符号链接 | **在位**(档案 23 的修复) | 该修复未解决本问题 |
|
||
| 实例内直接跑命令 | `nsenter -t <pid> -m -p ... /bin/sh -c "id"` → **成功**(uid=114801) | 平台层没坏,坏的是 **dsh 内部沙箱探测** |
|
||
| 实例命名空间内跑嵌套 bwrap | 失败(execvp 阶段) | 与「后端不可用」一致;**未复现**日志里的 `Can't mount proc: Operation not permitted` |
|
||
|
||
**会话证据(行号分布)**:沙箱报错出现在第 **44 / 113-121 / 156 / 174 / 180 / 614-619 / 658 / 805-809 / 908 / 1060-1065** 行 —— **分布全篇**;而**末 400 行报错数 = 0**,因为用户中途手动把权限切到 `danger-full-access`。
|
||
→ 定性:**默认模式坏、切了就好**的模式性问题,不是一次性故障。
|
||
|
||
**影响面(判断)**:
|
||
|
||
1. **新会话 / 新用户开箱即用不了 shell**(默认 `workspace-write`)—— 最严重:普通用户第一件想做的事就撞墙。
|
||
2. 报错文案是写给开发者的(「Install bubblewrap or run a Landlock-enforcing kernel…」),**普通用户看不懂**;且把「切 danger-full-access」这条降级路径直接写在报错里 —— **等于引导用户主动关掉沙箱**。
|
||
3. 用户为此**连点三次要求「突破限制」**(见下),产品侧无阻断、无上报。
|
||
|
||
**未做(待决策)**:仍需在**新会话(默认 workspace-write)**里实测一次 shell,确认 P0 是否仍存活。若存活,候选方向:
|
||
|
||
- **A(推荐)**:平台层已提供 bwrap + uid + cgroup 隔离,dsh 自己的沙箱属**冗余**且有副作用 → 把 dsh 沙箱默认设为 `danger-full-access`,并在实例 UI/文档说明「隔离由平台层负责」。需同步评估语义(用户会看到「完全权限」字样)。
|
||
- **B**:让 dsh 的 bwrap 后端在嵌套环境下可用(需 platform bwrap 放开 `--unshare-user` 等,风险与工作量都更高)。
|
||
- **C**:保持现状 + 只做报错文案的产品化提示(治标)。
|
||
|
||
## 三、其他发现(P1/P2)
|
||
|
||
| 级别 | 问题 | 证据 |
|
||
|---|---|---|
|
||
| P1 | 权限预设「每会话」、UI 不可持久化,新会话回落 `workspace-write`;报错却让用户去找 consumer 切换 | 会话内助手原话「默认仍是 workspace-write,预设是每会话的」 |
|
||
| P1 | **摘护栏只需一次点击**:`ask → never` + `danger-full-access`,**无二次确认、无风险提示** | 会话出现 `approval policy changed from "ask" to "never"` |
|
||
| P1 | **工作区是空目录**(`ws/mcntimo` 只有 `.`/`..`),建会话无校验、无提示 | `ls .` 输出 `total 8` |
|
||
| P2 | 用户**3 次**要求「突破沙箱限制」,产品侧无阻断/无审计上报 | 会话原文「可以试试看能否突破限制」「现在已开启完全权限,是否可以突破限制」 |
|
||
| P2 | 工具报错噪音大、常指向不存在路径,浪费轮次 | `rg: /opt/dshs: No such file`、`rg: /var/log: No such file` |
|
||
|
||
**凭据扫描**:**0 命中**(无 sk- / bearer / JWT / AKIA / 私钥)。
|
||
**异常中断**:未发现(6 轮 turn/end 全 completed;53 次工具调用,其中 7 次 bash 有 2 次被沙箱拒)。
|
||
|
||
## 四、附:同一批并行的另一条只读通道(dsh-market 安全审计)
|
||
|
||
结论摘要(尚未单独成档):`dsh-market` **不能装任意来源,但能自助安装官方目录全部 3408 款插件、零审批**,且**「配置备份恢复」路径可绕过目录校验直接装任意 GitHub 源** → **对「admin 投放 → 用户启用」第三层管控构成实质绕过**(风险等级:中;实例隔离兜住爆炸半径,越不到宿主/其他租户/平台 DB)。
|
||
|
||
证据:`routes.ts:4193` 安装前只校验「是否在社区目录里」;`registry.ts:169 loadRegistry()` 每次实时抓官方 `plugins.json`,无本地白名单;`backup.ts:139 validatedBackup` 只校验格式、不校验依赖 spec。
|
||
|
||
## 五、本次附带的环境变更
|
||
|
||
- 服务器安装 `zstd-1.5.1-2.0.2.al8`(yum 官方源,经用户授权)—— 用于解压会话记录。
|