Files
dsh_ai1net_server/dsh-server-docs/04-调整方案/33-实例权限默认档位改为完全权限.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

74 lines
5.6 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.
# 33 · 实例权限默认档位改为「完全权限」(复用官方 DSH_PERMISSION_MODE)
- 日期:2026-09-11
- 触发:档案 32 的 P0(dsh `workspace-write` 沙箱在本机不可用 → 拒绝执行任何 shell)+用户决策「方案 A」+追问「假如用户就是想授权 agent 执行、不想反复点确认,最佳方案是什么」+「可以复用官方的权限设置逻辑」
- 状态:**✅ 已实施(commit 见 git),配置链路已核验;末段 UI 点击验收待人工**
> **TL;DR**|**结论**:实例权限默认档位改为**「完全权限」**(复用官方 `DSH_PERMISSION_MODE`),避免 agent 每步都弹确认。
> **关键**:方案 A:复用官方权限设置链路,不改官方包;配置链路已核验。
> **状态**:✅ 已实施(末段 UI 点击验收待人工)
## 一、方案选型
| 方案 | 做法 | 结论 |
|---|---|---|
| 裸改内置预设 | 直接用 `danger-full-access` 预设 | ❌ 该预设**把 approval 绑成 `never`**(`{sandbox:'danger-full-access', approval:'never'}`),会静默摘掉全部确认 |
| 自造自定义预设 | profile `cordis.patch.yml` 给 `permission-presets` 行加 `config.presets` 新增一档 | 可行(已核实 patch 支持 config 覆盖),但要给**每个用户**铺 profile、且 `config` 是整体替换 |
| **✅ 采用:官方 env 开关** | `DSH_PERMISSION_MODE=danger-full-access` | **一条环境变量**:官方 `dsh-base/cordis.patch.yml:217/233` 直接读它决定 sandbox mode 与 approval policy;**新用户自动生效**(env 随 spawn 注入,无需铺 profile);且**完全复用官方逻辑**(预设表 / 设置 UI 都不动) |
**官方逻辑原文**(`@deepseek-ai/dsh-base/cordis.patch.yml`):
```yaml
mode: !!js process.env.DSH_PERMISSION_MODE ?? 'workspace-write'
policy: !!js "(process.env.DSH_PERMISSION_MODE ?? 'workspace-write') === 'danger-full-access' ? 'never' : 'ask'"
```
→ 设为 `danger-full-access` 时:沙箱模式 = 完全权限,审批策略 = `never`(**一次都不弹确认**,正是「授权一次、别反复点」要的效果)。
## 二、为什么这样是安全的(隔离边界上移)
| 层 | 谁提供 | 状态 |
|---|---|---|
| **内层** | dsh 自带沙箱(`read-only`/`workspace-write`,靠 Landlock/bubblewrap) | **本机不可用**(内核 5.10 无 Landlock、bwrap 后端在平台容器内探测失败)→ 受限模式 fail-closed 拒绝执行 |
| **外层** | 平台 `systemd-run --scope` + `bwrap` + `setpriv` | 正常,**真正的边界**:mount 路径隔离(只暴露用户根)+ uid 隔离 + cgroup(512M/150%/128) |
源码依据:`SANDBOX_UNAVAILABLE` 的定义即「**a requested confined mode** when no backend is usable」——`danger-full-access` **不是受限模式**,不需要后端;其报错文案本身也把 `danger-full-access` 列为官方逃生口径。→ 改档位不会引入新的暴露面,只是**不再叠加一个坏的冗余层**。
## 三、实现(2 处,均在本仓库自研编排器内,不碰官方程序)
| 文件 | 改动 |
|---|---|
| `src/supervisor/spawn.ts` | `ALLOWED_ENV` 增加 `DSH_PERMISSION_MODE`(否则被 `scrubEnv` 丢弃 —— 这是本项目的已知卡点) |
| `src/supervisor/orchestrator.ts` | `baseEnv()` 注入 `DSH_PERMISSION_MODE: process.env.DSH_PERMISSION_MODE ?? 'danger-full-access'`(**运维可用同名 env 覆盖**,改档位不必改码) |
`npm run build` 通过;`systemctl restart dshs` 后实例 respawn。
## 四、验证
| 项 | 结果 |
|---|---|
| 实例进程 env | ✅ `pid=240941/240943/240944` 全部 `DSH_PERMISSION_MODE=danger-full-access` |
| 是否存在用户级覆盖 | ✅ **无**:admin 与 guest 的 `settings.yaml` 都**没有** `permission:` 键;全盘 yaml/yml/json 除会话记录外无命中 → 配置派生的默认即生效 |
| 新用户是否自动生效 | ✅ 是(env 在 spawn 时注入,与 profile 无关) |
| 官方逻辑映射 | ✅ 源码核实(见上 yaml 片段) |
| 受限模式才需后端 | ✅ 源码核实(`SANDBOX_UNAVAILABLE` = confined mode 专用) |
| 末段 UI 实测 | ⚠️ **未完成**:headless Chrome 能进实例、UI 显示访问模式,但「新建会话 / 发送消息」的脚本化点击未成功创建会话(dsh 会话在发首条消息时才落盘)。**留给人工点击一次确认** |
## 五、重要副作用(必须知道)
**权限档位是「会话级播种」的,不跟随后续默认变化。**
会话创建时会写入 3 条种子事件(实测旧会话 `session-00341dea`):
```
permission/preset {preset:"workspace-write"}
sandbox/mode {mode:"workspace-write"}
approval/policy {policy:"ask"}
```
→ **已存在的旧会话仍显示「工作区内修改」**(实例 UI 就是这个表现),只有**新建的会话**才拿到「完全权限」。
因此:给用户的话术应是「**新开一个会话**」,而不是「刷新页面」。
## 六、风险与后续
1. **审批确认被关闭**:`danger-full-access` 在官方语义下等价于 `approval: never`。平台容器仍是硬边界,但失去了「危险操作人工确认」这层交互护栏。
2. **未覆盖的残留风险**(与本次改动无关,但关掉审批后更值得关注):
- 实例**共享宿主网络**(可连 3080/8888/32022 等宿主监听端口)
- **出网无白名单**(档案 14 只封了云元数据 + 观测外联)
3. **建议后续**:① 补出网白名单(nftables);② 若要保留「危险操作确认」又想免去反复点击,可评估在 profile patch 里新增自定义预设(`sandbox: danger-full-access` + `approval: ask`)——但那属于方案 A2,本次未采用。