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 一律写「远程服务器」。
74 lines
5.6 KiB
Markdown
74 lines
5.6 KiB
Markdown
# 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,本次未采用。
|