591 lines
17 KiB
Markdown
591 lines
17 KiB
Markdown
# QimingClaw Agent 沙箱架构设计
|
||
|
||
> **版本**: 1.1.1
|
||
> **更新日期**: 2026-04-10
|
||
> **状态**: 已实现(持续迭代)
|
||
|
||
---
|
||
|
||
## 1. 概述
|
||
|
||
### 1.1 目标
|
||
|
||
为 QimingClaw Agent Electron 客户端提供一个**安全、可配置、开箱即用**的沙箱执行环境。
|
||
|
||
### 1.2 设计原则
|
||
|
||
| 原则 | 说明 | 实现方式 |
|
||
|------|------|---------|
|
||
| **多平台支持** | macOS / Linux / Windows | `PlatformAdapter` 统一抽象 + 各平台实现 |
|
||
| **可配置** | 用户可控制是否启用 | 四种模式配置 |
|
||
| **开箱即用** | 无需手动安装依赖 | 预编译 + 系统内置 |
|
||
| **渐进增强** | 自动选择最优方案 | 平台检测 + 降级 |
|
||
| **安全隔离** | 保护用户系统安全 | 系统级沙箱 |
|
||
|
||
---
|
||
|
||
## 2. 架构设计
|
||
|
||
### 2.1 整体架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ QimingClaw 沙箱架构 │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ Layer 1: 配置管理 (SandboxConfig) │
|
||
│ ├─ 模式: off / on-demand / non-main / all │
|
||
│ ├─ 存储: electron-store │
|
||
│ └─ UI: 设置界面 + 首次引导 │
|
||
│ │
|
||
│ Layer 2: 平台抽象 (PlatformAdapter) │
|
||
│ ├─ 统一平台能力(isWindows/isMacOS/isLinux) │
|
||
│ ├─ 统一命令探测与路径分隔符 │
|
||
│ ├─ 统一 helper/bwrap 资源解析 │
|
||
│ └─ 统一推荐 backend/type │
|
||
│ │
|
||
│ Layer 3: 沙箱实现 (SandboxInterface) │
|
||
│ ├─ macOS: sandbox-exec (系统内置) │
|
||
│ ├─ Linux: bubblewrap (系统可用) │
|
||
│ ├─ Windows: Codex Sandbox (预编译二进制) │
|
||
│ └─ None: 无沙箱 (关闭模式) │
|
||
│ │
|
||
│ Layer 4: 权限管理 (PermissionManager) │
|
||
│ ├─ 权限检查 │
|
||
│ ├─ 用户审批 │
|
||
│ └─ 审计日志 │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.2 目录结构
|
||
|
||
```
|
||
qiming-agent/
|
||
├── crates/
|
||
│ ├── qiming-sandbox/ # 沙箱源码(独立维护)
|
||
│ │ ├── Cargo.toml
|
||
│ │ ├── README.md
|
||
│ │ ├── LICENSE (Apache-2.0)
|
||
│ │ │
|
||
│ │ ├── crates/
|
||
│ │ │ ├── windows-sandbox/ # Windows 实现(基于 Codex)
|
||
│ │ │ │ ├── Cargo.toml
|
||
│ │ │ │ ├── src/
|
||
│ │ │ │ │ ├── lib.rs
|
||
│ │ │ │ │ ├── sandbox.rs
|
||
│ │ │ │ │ ├── process.rs
|
||
│ │ │ │ │ └── network.rs
|
||
│ │ │ │ └── tests/
|
||
│ │ │ │
|
||
│ │ │ ├── macos-sandbox/ # macOS 封装
|
||
│ │ │ └── linux-sandbox/ # Linux 封装
|
||
│ │ │
|
||
│ │ ├── bindings/ # Node.js 绑定
|
||
│ │ │ ├── package.json
|
||
│ │ │ ├── native/ # .gitignore
|
||
│ │ │ └── index.ts # TypeScript 封装
|
||
│ │ │
|
||
│ │ └── prebuilt/ # 预编译产物(入库)
|
||
│ │ ├── darwin-x64/
|
||
│ │ ├── linux-x64/
|
||
│ │ └── win32-x64/
|
||
│ │ ├── index.node
|
||
│ │ └── qiming-sandbox.exe
|
||
│ │
|
||
│ └── agent-electron-client/
|
||
│ ├── docs/sandbox/ # 沙箱文档
|
||
│ │ ├── ARCHITECTURE.md # 本文档
|
||
│ │ ├── IMPLEMENTATION.md # 实施指南
|
||
│ │ └── API.md # API 文档
|
||
│ │
|
||
│ ├── scripts/
|
||
│ │ └── prepare-sandbox.ts # 复制预编译产物
|
||
│ │
|
||
│ └── resources/
|
||
│ └── sandbox/ # 运行时产物(不入库)
|
||
│ ├── darwin-x64/
|
||
│ ├── linux-x64/
|
||
│ └── win32-x64/
|
||
│ └── qiming-sandbox.exe
|
||
```
|
||
|
||
### 2.3 统一平台抽象(v1.1.1)
|
||
|
||
`src/main/services/system/platformAdapter.ts` 提供统一平台能力抽象:
|
||
|
||
- `createPlatformAdapter()`:统一 `darwin/linux/win32` 分支入口
|
||
- `getCommandProbe()`:统一 `which/where` 命令探测
|
||
- `getRecommendedSandboxBackend()/Type()`:统一 backend/type 推荐
|
||
- `resolveBundledLinuxBwrapPath()`:统一 Linux bwrap 内置路径解析
|
||
- `resolveBundledSandboxHelperPath()`:统一 helper 内置路径解析
|
||
|
||
当前已接入 sandbox 关键链路:
|
||
|
||
- `system/shellEnv.ts`
|
||
- `sandbox/policy.ts`
|
||
- `sandbox/SandboxManager.ts`
|
||
- `sandbox/serviceBootstrap.ts`
|
||
- `sandbox/SandboxInvoker.ts`
|
||
- `engines/acp/acpTerminalManager.ts`
|
||
- `engines/acp/acpClient.ts`
|
||
- `engines/acp/strictPermissionGuard.ts`
|
||
- `utils/processTree.ts`
|
||
|
||
---
|
||
|
||
## 3. 平台实现方案
|
||
|
||
### 3.1 macOS: sandbox-exec
|
||
|
||
**技术**: Seatbelt (sandbox-exec)
|
||
|
||
**优势**:
|
||
- ✅ 系统内置(macOS 10.5+)
|
||
- ✅ 零依赖
|
||
- ✅ 性能最优(~5% 开销)
|
||
|
||
**配置示例**:
|
||
```scheme
|
||
(version 1)
|
||
(allow default)
|
||
|
||
; 允许网络访问(白名单)
|
||
(allow network-outbound
|
||
(remote tcp "github.com" 443)
|
||
(remote tcp "npmjs.org" 443)
|
||
)
|
||
|
||
; 工作区读写
|
||
(allow file-read* (subpath "/Users/.../workspace"))
|
||
(allow file-write* (subpath "/Users/.../workspace"))
|
||
|
||
; 禁止敏感目录
|
||
(deny file-read* (subpath "~/.ssh"))
|
||
(deny file-read* (subpath "~/.aws"))
|
||
```
|
||
|
||
**开箱即用**: ⭐⭐⭐⭐⭐
|
||
|
||
---
|
||
|
||
### 3.2 Linux: bubblewrap
|
||
|
||
**技术**: bubblewrap (bwrap)
|
||
|
||
**优势**:
|
||
- ✅ 主流发行版可用
|
||
- ✅ 轻量级(~5% 开销)
|
||
- ✅ 命名空间隔离
|
||
|
||
**依赖安装**:
|
||
```bash
|
||
# Debian/Ubuntu
|
||
sudo apt install bubblewrap socat ripgrep
|
||
|
||
# Fedora
|
||
sudo dnf install bubblewrap socat ripgrep
|
||
|
||
# Arch Linux
|
||
sudo pacman -S bubblewrap socat ripgrep
|
||
```
|
||
|
||
**执行示例**:
|
||
```bash
|
||
bwrap \
|
||
--ro-bind /usr /usr \
|
||
--bind /workspace /workspace \
|
||
--unshare-all \
|
||
--die-with-parent \
|
||
bash -c "command"
|
||
```
|
||
|
||
**开箱即用**: ⭐⭐⭐⭐
|
||
|
||
---
|
||
|
||
### 3.3 Windows: Codex Sandbox
|
||
|
||
**技术**: Rust + Windows API
|
||
|
||
**来源**: OpenAI Codex (Apache-2.0)
|
||
|
||
**优势**:
|
||
- ✅ 完整复用 Codex 实现
|
||
- ✅ 预编译二进制
|
||
- ✅ Windows 原生 API
|
||
|
||
**实现特性**:
|
||
- Job Objects (进程管理)
|
||
- Restricted Tokens (权限限制)
|
||
- Network Proxy (网络过滤)
|
||
- Seccomp-like (系统调用过滤)
|
||
|
||
**开箱即用**: ⭐⭐⭐⭐⭐ (预编译提供)
|
||
|
||
---
|
||
|
||
## 4. 配置系统
|
||
|
||
### 4.1 沙箱模式
|
||
|
||
#### 启用/禁用(SandboxPolicy)
|
||
|
||
| 模式 | 主会话 | 其他会话 | 安全性 | 便利性 | 适用场景 |
|
||
|------|--------|---------|--------|--------|---------|
|
||
| **off** | 无沙箱 | 无沙箱 | ⭐ | ⭐⭐⭐⭐⭐ | 完全信任环境 |
|
||
| **on-demand** | 询问 | 询问 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 混合场景 |
|
||
| **non-main** ⭐ | 无沙箱 | 有沙箱 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | **推荐默认** |
|
||
| **all** | 有沙箱 | 有沙箱 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | 高安全需求 |
|
||
|
||
#### 严格度模式(SandboxMode)
|
||
|
||
> v0.10+ 新增,跨平台统一接口,各平台实现语义有差异。
|
||
|
||
| Mode | 说明 | 默认 |
|
||
|------|------|------|
|
||
| **compat** | 兼容优先,平衡安全与可用性 | ✅ 默认 |
|
||
| **strict** | 最小权限,最大限制 | |
|
||
| **permissive** | 宽松模式,仅排障用途 | |
|
||
|
||
**各平台实现差异**:
|
||
|
||
| 平台 | strict | compat | permissive |
|
||
|------|--------|--------|-----------|
|
||
| Linux (bwrap) | 最小 ro-bind:仅 `/usr` `/bin` `/sbin` `/lib` `/lib64` `/etc` `/opt` `/usr/local` | 全局 ro-bind `--ro-bind / /` | 完整 rw bind,无 namespace 隔离 |
|
||
| macOS (seatbelt) | exec allowlist 仅命令本身(不含 `startupExecAllowlist`) | exec allowlist 含启动链 | 全局 file-write + unrestricted process-exec |
|
||
| Windows (helper `run`) | `writable_roots` 仅首个路径(workspace-first)+ WRITE_RESTRICTED,APPDATA 不在 ALLOW ACEs | `writable_roots` 全部 + WRITE_RESTRICTED,APPDATA 在 ALLOW ACEs | `writable_roots` 全部 + `--no-write-restricted` |
|
||
| Windows (helper `serve`) | `--write-restricted`,仅 workspace + TEMP/TMP | `--write-restricted`,workspace + TEMP/TMP + APPDATA | 无 WRITE_RESTRICTED(进程级不限制写入) |
|
||
|
||
> **v1.1.0 变更**: Windows `serve` 子命令新增 `--write-restricted` flag。
|
||
> Policy JSON 新增 `sandbox_mode` 字段,Rust helper 的 `compute_allow_paths()` 根据此字段
|
||
> 决定是否将 APPDATA/LOCALAPPDATA 加入 ALLOW ACEs。
|
||
> strict 模式下 APPDATA 不可写(进程级真正限制),compat 模式下可写。
|
||
|
||
> **v1.1.1 同步**:
|
||
> 1) macOS strict 的 seatbelt exec allowlist 不再纳入 startup chain(仅命令本身);
|
||
> 2) Windows helper `run` strict 下 `writable_roots` 仅保留首个路径(workspace-first);
|
||
> 3) 平台分支入口统一收敛到 `PlatformAdapter`。
|
||
|
||
> `qimingcode` 在 strict 模式下还包含 ACP 权限层的二次写入门控(`strictPermissionGuard`):
|
||
> 写入路径必须位于 `workspace/temp/isolatedHome`(strict 下不含 `appData`),
|
||
> 路径缺失时 fail-closed,写入权限仅 `allow_once`。
|
||
>
|
||
> `qimingcode` warmup 复用同样受 sandbox policy 约束:warmup 会记录 policy 指纹,
|
||
> 当用户修改 sandbox mode/policy 后,若指纹不一致则立即放弃复用并冷启动新引擎。
|
||
|
||
### 4.2 配置结构
|
||
|
||
```typescript
|
||
interface SandboxConfig {
|
||
// 沙箱类型
|
||
type: SandboxType;
|
||
// 运行平台
|
||
platform: Platform;
|
||
// 是否启用
|
||
enabled: boolean;
|
||
// 工作区根目录
|
||
workspaceRoot: string;
|
||
// 沙箱严格度模式(v0.10+)
|
||
mode?: "strict" | "compat" | "permissive";
|
||
|
||
// 网络策略
|
||
networkEnabled?: boolean;
|
||
|
||
// 资源限制
|
||
memoryLimit?: string;
|
||
cpuLimit?: number;
|
||
diskQuota?: string;
|
||
}
|
||
```
|
||
|
||
### 4.3 默认配置
|
||
|
||
```typescript
|
||
const DEFAULT_SANDBOX_CONFIG: SandboxConfig = {
|
||
mode: "non-main",
|
||
|
||
platform: {
|
||
darwin: { enabled: true, type: "seatbelt" },
|
||
linux: { enabled: true, type: "bubblewrap" },
|
||
win32: { enabled: true, type: "codex" },
|
||
},
|
||
|
||
network: {
|
||
enabled: true,
|
||
allowedDomains: [
|
||
"github.com",
|
||
"*.github.com",
|
||
"npmjs.org",
|
||
"registry.npmjs.org",
|
||
"pypi.org",
|
||
],
|
||
deniedDomains: [],
|
||
},
|
||
|
||
filesystem: {
|
||
allowRead: ["."],
|
||
denyRead: ["~/.ssh", "~/.aws", "~/.gnupg"],
|
||
allowWrite: [".", "/tmp"],
|
||
denyWrite: [".env", "*.pem", "*.key"],
|
||
},
|
||
|
||
resources: {
|
||
memory: "2g",
|
||
cpu: 2,
|
||
timeout: 300,
|
||
},
|
||
|
||
preferences: {
|
||
showNotifications: true,
|
||
askForDangerousOps: true,
|
||
auditLogging: true,
|
||
},
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 统一 API
|
||
|
||
### 5.1 SandboxInterface
|
||
|
||
```typescript
|
||
interface SandboxInterface {
|
||
// 初始化沙箱
|
||
initialize(config: SandboxConfig): Promise<void>;
|
||
|
||
// 执行命令
|
||
execute(
|
||
command: string,
|
||
cwd: string,
|
||
options?: ExecuteOptions
|
||
): Promise<ExecuteResult>;
|
||
|
||
// 文件操作
|
||
readFile(sessionId: string, path: string): Promise<string>;
|
||
writeFile(sessionId: string, path: string, content: string): Promise<void>;
|
||
|
||
// 状态查询
|
||
isAvailable(): Promise<boolean>;
|
||
getStatus(): SandboxStatus;
|
||
|
||
// 生命周期
|
||
cleanup(): Promise<void>;
|
||
}
|
||
|
||
interface ExecuteOptions {
|
||
timeout?: number;
|
||
signal?: AbortSignal;
|
||
onOutput?: (data: string) => void;
|
||
}
|
||
|
||
interface ExecuteResult {
|
||
exitCode: number;
|
||
stdout: string;
|
||
stderr: string;
|
||
}
|
||
|
||
interface SandboxStatus {
|
||
available: boolean;
|
||
type: "seatbelt" | "bubblewrap" | "codex" | "none";
|
||
platform: string;
|
||
version?: string;
|
||
}
|
||
```
|
||
|
||
### 5.2 自动选择实现
|
||
|
||
```typescript
|
||
class AutoSandbox implements SandboxInterface {
|
||
private sandbox: SandboxInterface;
|
||
|
||
async initialize(config: SandboxConfig): Promise<void> {
|
||
const platform = os.platform();
|
||
|
||
switch (platform) {
|
||
case "darwin":
|
||
this.sandbox = new MacSandbox();
|
||
break;
|
||
case "linux":
|
||
this.sandbox = new LinuxSandbox();
|
||
break;
|
||
case "win32":
|
||
this.sandbox = new WindowsSandbox();
|
||
break;
|
||
default:
|
||
this.sandbox = new NoneSandbox();
|
||
}
|
||
|
||
await this.sandbox.initialize(config);
|
||
}
|
||
|
||
// 代理所有接口方法...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 构建和发布
|
||
|
||
### 6.1 构建流程
|
||
|
||
```bash
|
||
# 1. 构建 Rust 沙箱(所有平台)
|
||
cd crates/qiming-sandbox
|
||
make build
|
||
|
||
# 或构建特定平台
|
||
make build-darwin
|
||
make build-linux
|
||
make build-windows
|
||
|
||
# 2. 复制到预编译目录
|
||
make prebuilt
|
||
|
||
# 3. 准备 Electron 资源
|
||
cd ../agent-electron-client
|
||
npm run prepare:sandbox
|
||
```
|
||
|
||
### 6.2 Makefile
|
||
|
||
```makefile
|
||
# crates/qiming-sandbox/Makefile
|
||
|
||
.PHONY: build clean prebuilt
|
||
|
||
build:
|
||
cargo build --release
|
||
cd bindings && npm run build
|
||
|
||
build-darwin:
|
||
cargo build --release --target x86_64-apple-darwin
|
||
cd bindings && npm run build -- --target x86_64-apple-darwin
|
||
|
||
build-linux:
|
||
cargo build --release --target x86_64-unknown-linux-gnu
|
||
cd bindings && npm run build -- --target x86_64-unknown-linux-gnu
|
||
|
||
build-windows:
|
||
cargo build --release --target x86_64-pc-windows-msvc
|
||
cd bindings && npm run build -- --target x86_64-pc-windows-msvc
|
||
|
||
prebuilt: build
|
||
mkdir -p prebuilt/darwin-x64
|
||
mkdir -p prebuilt/linux-x64
|
||
mkdir -p prebuilt/win32-x64
|
||
|
||
cp target/release/qiming-sandbox prebuilt/darwin-x64/ || true
|
||
cp target/release/qiming-sandbox prebuilt/linux-x64/ || true
|
||
cp target/x86_64-pc-windows-msvc/release/qiming-sandbox.exe prebuilt/win32-x64/ || true
|
||
|
||
cp bindings/native/*.node prebuilt/darwin-x64/ || true
|
||
cp bindings/native/*.node prebuilt/linux-x64/ || true
|
||
cp bindings/native/*.node prebuilt/win32-x64/ || true
|
||
|
||
clean:
|
||
cargo clean
|
||
rm -rf bindings/native/*.node
|
||
rm -rf prebuilt/*
|
||
```
|
||
|
||
### 6.3 prepare-sandbox.ts
|
||
|
||
```typescript
|
||
// scripts/prepare-sandbox.ts
|
||
|
||
import * as fs from "fs";
|
||
import * as path from "path";
|
||
|
||
const SANDBOX_SRC = path.join(__dirname, "..", "..", "qiming-sandbox", "prebuilt");
|
||
const SANDBOX_DEST = path.join(__dirname, "..", "resources", "sandbox");
|
||
|
||
const PLATFORM = process.platform;
|
||
const ARCH = process.arch;
|
||
|
||
function prepareSandbox(): void {
|
||
const sourceDir = path.join(SANDBOX_SRC, `${PLATFORM}-${ARCH}`);
|
||
const targetDir = path.join(SANDBOX_DEST, `${PLATFORM}-${ARCH}`);
|
||
|
||
fs.mkdirSync(targetDir, { recursive: true });
|
||
|
||
if (fs.existsSync(sourceDir)) {
|
||
const files = fs.readdirSync(sourceDir);
|
||
files.forEach(file => {
|
||
fs.copyFileSync(
|
||
path.join(sourceDir, file),
|
||
path.join(targetDir, file)
|
||
);
|
||
});
|
||
console.log(`✅ Sandbox binaries copied for ${PLATFORM}-${ARCH}`);
|
||
} else {
|
||
console.log(`ℹ️ No sandbox binaries needed for ${PLATFORM}-${ARCH}`);
|
||
fs.writeFileSync(path.join(targetDir, ".gitkeep"), "");
|
||
}
|
||
}
|
||
|
||
prepareSandbox();
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 许可证
|
||
|
||
### 7.1 许可证兼容性
|
||
|
||
| 项目 | 许可证 | 兼容性 |
|
||
|------|--------|--------|
|
||
| **Codex** | Apache-2.0 | ✅ |
|
||
| **QimingClaw** | Apache-2.0 / MIT | ✅ |
|
||
| **qiming-sandbox** | Apache-2.0 | ✅ |
|
||
|
||
### 7.2 版权声明
|
||
|
||
```
|
||
Copyright 2024-2026 QimingClaw Team
|
||
|
||
Licensed under the Apache License, Version 2.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
You may obtain a copy of the License at
|
||
|
||
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
||
---
|
||
|
||
This project includes code from OpenAI Codex:
|
||
https://github.com/openai/codex
|
||
|
||
Original copyright notice:
|
||
Copyright 2024 OpenAI
|
||
|
||
Licensed under the Apache License, Version 2.0
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 致谢
|
||
|
||
本沙箱方案基于以下开源项目:
|
||
|
||
- **OpenAI Codex** - Windows Sandbox 实现
|
||
- **Anthropic** - @anthropic-ai/sandbox-runtime
|
||
- **LobsterAI** - QEMU 虚拟机沙箱参考
|
||
- **pi-mono** - 配置系统参考
|
||
|
||
---
|
||
|
||
## 9. 参考
|
||
|
||
- [OpenAI Codex](https://github.com/openai/codex)
|
||
- [Anthropic Sandbox Runtime](https://www.npmjs.com/package/@anthropic-ai/sandbox-runtime)
|
||
- [LobsterAI 本地源码](/Users/apple/workspace/LobsterAI)
|
||
- [pi-mono 本地源码](/Users/apple/workspace/pi-mono)
|
||
- [macOS Seatbelt](https://developer.apple.com/documentation/security/app_sandbox)
|
||
- [bubblewrap](https://github.com/containers/bubblewrap)
|
||
|
||
---
|
||
|
||
**文档版本**: 1.0.0
|
||
**最后更新**: 2026-03-27
|