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 一律写「远程服务器」。
6.1 KiB
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。
→ 定性:默认模式坏、切了就好的模式性问题,不是一次性故障。
影响面(判断):
- 新会话 / 新用户开箱即用不了 shell(默认
workspace-write)—— 最严重:普通用户第一件想做的事就撞墙。 - 报错文案是写给开发者的(「Install bubblewrap or run a Landlock-enforcing kernel…」),普通用户看不懂;且把「切 danger-full-access」这条降级路径直接写在报错里 —— 等于引导用户主动关掉沙箱。
- 用户为此连点三次要求「突破限制」(见下),产品侧无阻断、无上报。
未做(待决策):仍需在**新会话(默认 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 官方源,经用户授权)—— 用于解压会话记录。