沙箱化代码执行
adk-sandbox crate 为 ADK agent 提供隔离的代码执行,具有两个级别的隔离:
- 进程隔离 — 具有环境隔离和超时强制执行的子进程
- 操作系统级沙箱配置文件 — 对文件系统、网络和进程创建的内核级限制
后端
| 后端 | 隔离级别 | 语言 | 功能标志 |
|---|---|---|---|
ProcessBackend | 环境 + 超时 | Rust, Python, JS, TS, 命令 | process(默认) |
ProcessBackend + sandbox | 内核级 | 同上 | process + sandbox-native |
WasmBackend | 完整(内存、文件系统、网络) | 仅 WASM | wasm |
你得到的是哪种隔离?
ProcessBackend::isolation() 报告了它,因此这不是可以从 crate 名称推断出来的:
| 结果 | 含义 |
|---|---|
IsolationClass::SubprocessOnly | 一个带有已清空环境、超时限制以及自身进程组的子进程。操作系统不再施加进一步限制:代码可以读取宿主文件系统并访问网络。这就是 ProcessBackend::default() 提供给你的能力。 |
IsolationClass::OsEnforced | 已附加一个强制执行器 和 一个策略,因此操作系统会限制该子进程。 |
关于进程后端,有两点值得了解:
- 程序会在清空环境之前解析。 一个裸的
python3、node或rustc会在调用者的PATH上查找,并作为绝对路径传递给子进程。这意味着子进程启动时不需要自己的PATH——此前调用者必须把PATH放在ExecRequest::env中,这也让已执行的代码能够在其上启动其他任何东西。 - 编译通过与执行相同的边界运行。 过去,Rust 源代码是通过在共享路径之外构建的命令来编译的,因此编译过程没有 enforcer 包装器、没有超时、也没有进程组。这很重要,因为编译并不是静态不变的:
include_str!会读取文件,而过程宏会在生成的二进制存在之前运行任意代码。编译阶段会接收一个平台特定的 toolchain allow-list;在 Windows 上,这包括LIB,并使用 Rust toolchain 的rust-lldlinker,因此不会选择link.exe之前在PATH上的无关项。OS enforcer 正是约束该阶段的机制。
环境优先级
SandboxPolicy::env 为每次执行提供默认值,而 ExecRequest::env
会按调用逐一覆盖它们。此前 policy 的变量完全被忽略了。
OS 沙箱配置文件
OS 级沙箱执行会在内核级别限制子进程。这超出了环境隔离的范围——操作系统本身会阻止未经授权的文件系统访问、网络连接和进程创建。
平台支持
| 平台 | 执行器 | 工作方式 |
|---|---|---|
| macOS | Seatbelt (sandbox-exec) | 系统调用级规则:“默认允许,危险拒绝”——拒绝写入、网络和 fork;读取不受限制 |
| Linux | bubblewrap (bwrap) | 文件系统命名空间隔离(白名单挂载) |
| Windows | AppContainer | 未实现 — 强制器报告自身不可用 |
快速开始
use adk_sandbox::{
ProcessBackend, ProcessConfig, SandboxBackend,
SandboxPolicyBuilder, get_enforcer,
};
// 1. Define what the sandboxed process can do
let policy = SandboxPolicyBuilder::new()
.allow_read("/usr") // Read system libraries
.allow_read_write("/tmp/work") // Write to work directory
.allow_process_spawn() // Python needs to exec
// Network is denied by default
.env("PATH", "/usr/bin:/usr/local/bin")
.build();
// 2. Get the platform-appropriate enforcer
let enforcer = get_enforcer()?;
// 3. Create a sandboxed backend
let backend = ProcessBackend::with_sandbox(
ProcessConfig::default(),
enforcer,
policy,
);
// 4. Execute code — network is blocked, writes restricted
let result = backend.execute(request).await?;
功能标志
[dependencies]
# Auto-detect platform enforcer
adk-sandbox = { version = "2.0.0", features = ["process", "sandbox-native"] }
# Or pick a specific platform
adk-sandbox = { version = "2.0.0", features = ["process", "sandbox-macos"] }
adk-sandbox = { version = "2.0.0", features = ["process", "sandbox-linux"] }
SandboxPolicy
策略定义了沙箱化进程被允许执行的操作:
| 字段 | 默认值 | 描述 |
|---|---|---|
allowed_paths | [] (拒绝所有) | 具有只读或读写访问权限的文件系统路径 |
allow_network | false | 是否允许网络访问 |
allow_process_spawn | false | 是否允许生成子进程 |
env | {} | 沙箱化进程的环境变量 |
平台差异
macOS(Seatbelt): 使用“默认允许,危险项拒绝”——默认拥有完全访问权限,然后阻止网络、文件写入和进程创建。纯白名单方式行不通,因为 Python 在启动时需要几十个 macOS 特定的 syscall 类别。
Linux(bubblewrap): 使用基于命名空间的白名单——默认什么都不存在,只挂载所需内容。使用 apt install bubblewrap 或 dnf install bubblewrap 安装。
Windows(AppContainer): 未实现。设计上是基于令牌的 ACL——带有默认无访问权限的受限 SID,然后在特定路径上授予 ACL——但容器创建、ACL、capabilities 和 job-object 清理都缺失,因此 probe() 返回 EnforcerUnavailable。在 Windows 上不带 enforcer 运行,或者使用 macOS 或 Linux,因为只有这些平台上的强制执行是真实生效的。
示例
参见 examples/sandbox_agent/,了解一个完整的由 LLM-agent 驱动的示例,它在受沙箱保护的环境中执行 Python 代码,并由操作系统内核阻止网络访问。