Skip to content
SandboxWasmtimeSecurity

Wasmtime 沙箱配置指南:最小权限原则实践

为什么选择 Wasmtime

Vigils 运行不可信的 MCP Server。传统选择是 Docker 容器,但 MCP Server 通常是单文件 WASM 模块,Docker 的启动开销(秒级)和镜像体积(百 MB 级)是过度杀伤。

Wasmtime 提供:

  • 毫秒级启动:适合 Agent 工具调用的 latency 要求
  • 精细资源控制:CPU、内存、燃料(fuel)均可量化限制
  • WASI 能力模型:通过 capability 显式授权,默认无任何权限
  • 确定性执行:WASM 的 sandbox 边界由 VM 保证,不依赖 OS 命名空间

从 wasmtime 25 升级到 43.0.2 的过程中,我们修复了 15 个 RUSTSEC advisory,涉及内存安全、资源泄漏和依赖链漏洞。

默认拒绝策略

Vigils 沙箱的默认配置是"零权限":

network:       deny
filesystem:    deny
env:           clear (env_clear)
cpu_limit:     1 core
memory_limit:  128 MiB
wall_time:     30s

任何扩权都必须显式声明。例如一个需要读取临时目录的 MCP Server:

vigils-hub add-local-mcp ./server.wasm \
  --allow-dir /tmp/mcp-work \
  --cpu-ms 5000 \
  --mem-mb 64

没有 --allow-dir,Server 对文件系统的访问会被 WASI 层直接拒绝。

资源限制实战

CPU 限制

使用 wasmtime 的 fuel 机制:每条 WASM 指令消耗固定 fuel,耗尽即 trap。

let mut config = Config::new();
config.consume_fuel(true);
let engine = Engine::new(&config)?;
let mut store = Store::new(&engine, ());
store.add_fuel(10_000_000_000)?; // 约 10s 单核等价

内存限制

通过 StoreLimits 设置线性内存和 table 的上限:

store.limiter(|state| &mut state.limits);

超过限制时,WASM 的 memory.grow 返回 -1,由 Guest 决定如何处理(通常 panic)。

时间限制

使用 async + tokio::time::timeout 包装 Store 的执行。对于同步路径,Vigils 使用独立进程 + waitpid timeout,防止任何阻塞调用逃逸。

Linux Landlock LSM

Vigils 在 Linux 上使用 Landlock 为沙箱增加第二道防线。这是整个 workspace 中唯一允许 unsafe_code 的地方(ADR 0007 §I-7.8 明确批准)。

Landlock 在 exec 前通过 pre_exec 钩子设置自限制规则:

// vigils-sandbox-linux/src/landlock.rs
command.pre_exec(|| {
    let ruleset = create_ruleset()?;
    restrict_self(ruleset)?; // unsafe: pre_exec 要求 async-signal-safe
    Ok(())
});

即使 WASI 层被绕过(例如通过原生代码编译的 WASM),Landlock 也会在内核层拒绝未授权的文件/网络访问。

RUSTSEC 修复清单

wasmtime 25 → 43.0.2 的升级修复了以下类别的漏洞:

  1. 内存安全:WASM 线性内存边界检查优化中的 off-by-one
  2. 资源泄漏:fuel 耗尽后未正确释放 Store 资源
  3. 依赖链:wasmparser 中的深度嵌套拒绝服务
  4. 编译器:cranelift 的 speculative execution 信息泄漏(CVE-2024-XXXX 类)

Vigils 的 cargo deny 配置要求所有依赖必须通过 RUSTSEC 扫描,每季度更新一次基线。

监控与应急

沙箱运行时的监控指标:

  • sandbox.exec.duration_ms —— 识别异常慢的执行
  • sandbox.mem.peak_mb —— 内存泄漏检测
  • sandbox.fuel.remaining —— 燃料消耗模式分析
  • sandbox.landlock.violations —— Landlock 拦截事件(应为 0)

当沙箱进程超出任何限制时,Vigils 执行以下动作:

  1. 发送 SIGKILL(不可捕获)
  2. 记录 DecisionRecord,decision = Deny,reason = SandboxLimitExceeded
  3. 触发审计事件 sandbox.exec_killed
  4. 向 Agent 返回结构化错误,不暴露内部限制值

与其他沙箱技术对比

技术启动延迟资源开销安全边界适用场景
Docker1-3s百 MBNamespace + seccomp长期服务
gVisor2-5s百 MB用户态内核不可信多租户
Firecracker100-300ms数十 MBKVMServerless
seccomp-bpf0ms0系统调用过滤辅助手段
Wasmtime + Landlock<10ms<5 MBVM + LSMAgent 工具调用

对于 MCP Server 这种"高频、轻量、不可信"的 workload,Wasmtime + Landlock 是当前最优解。