Files
dsh_shenxian/dsh-server-docs/04-调整方案/32-mcntimo会话取证与沙箱P0核查.md
T

77 lines
6.1 KiB
Markdown
Raw Normal View History

# 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 官方源,经用户授权)—— 用于解压会话记录。