Agent 沙箱机制选型与拆解:以腾讯云 CubeSandbox 为例
为什么 Agent 必须有沙箱
AI Agent 跑在生产环境,意味着反复执行一段没有人审过的代码。LLM 生成一条 shell 命令、一段 Python、一条 SQL,agent 运行时直接拿去执行。传统 Docker 容器的安全模型是为可信工作负载设计的——它假设镜像里跑的代码是审计过的。这个假设在 agent 场景里彻底失效:你不知道模型下一秒会生成什么。比如一段 prompt injection 让 agent 生成 curl 把环境变量里的云凭据外带,或生成 DROP TABLE 把生产库清空——这些代码跑在哪、能碰什么,比代码本身更重要。
业界对此已经收敛出一个共识。Anthropic 在 2026 年披露的 Managed Agent 架构里,把一个 Agent 拆成三件套:Session(会话)、Harness(编排循环)、Sandbox(执行环境)。代码执行被明确隔离进一个独立的沙箱。Manus、Perplexity、Hugging Face、OpenAI Agents SDK 底层都依赖类似的「虚拟电脑」来承载代码执行与工具调用,接口也逐渐统一到 E2B 协议。
威胁其实分两层,沙箱只管其中一层:
- 执行隔离:防止 agent 生成的代码逃逸到宿主机。一个内核漏洞、一个没被 seccomp 过滤的 syscall,在共享内核的容器里就可能变成容器逃逸。
- Agent 层威胁
injection、tool poisoning 这类攻击在代码跑起来之前就劫持了 agent 的意图。沙箱挡不住这一层——它管的是「代码跑在哪」,不是「agent 想干什么」。
有效的 agent 安全要把两层都覆盖。本文聚焦执行隔离这一层,讲清楚沙箱怎么选、怎么实现,再拿腾讯云开源的 CubeSandbox 逐一拆。
选型:四种隔离原语
落地一个沙箱,本质是选一种隔离原语。原语决定了安全边界和性能下限。当前主流收敛到四种,各有取舍:
| 技术 | 启动时间 | 内存开销 | 内核暴露 | 适合场景 |
|---|---|---|---|---|
| Docker(runc) | ~10ms | ~10MB | 完全共享宿主内核 | 仅可信代码 |
| gVisor | ~100ms | ~20MB | 无(Sentry 拦截 syscall) | 计算密集、K8s |
| Firecracker microVM | ~125ms | ~5MB | 无(KVM 硬件隔离) | 不可信代码、最高隔离 |
| Kata Containers | ~200ms | ~30MB | 无(KVM 硬件隔离) | K8s 原生、K8s agent |
| WebAssembly/WASI | 亚毫秒 | 极低 | 无(能力模型) | 短时高频工具调用 |
Docker(runc) 是绝大多数人起步的选择。它用 Linux namespace 做资源视图隔离,加 seccomp 和 capabilities 限制 syscall。快、轻,但所有容器进程的 syscall 直接打进宿主内核,过滤配置容易出错,一个内核漏洞就可能导致逃逸。在 agent 场景(跑不可信 LLM 代码)里,这层隔离被普遍认为不够。
gVisor 换了个思路:核心组件 Sentry 是一个用 Go 写的、跑在用户态的「Linux 内核 API 重新实现」。沙箱里的进程调 open()/socket(),被 Sentry 拦截、自己模拟,而不是直接落到宿主内核。代价是不对称——I/O 密集任务掉 10–30%,计算密集任务几乎无感。对主要做计算、I/O 不多的 agent,gVisor 经常是性能/隔离比最好的选择。
Firecracker 是 AWS 开源的 microVM,走 KVM 硬件虚拟化。约 125ms 启动,单实例 5MB 以内,单机每秒能拉起 150 个。它的安全保证和 gVisor 质上不同:哪怕攻击者逃出 guest VM、又利用了 Firecracker 自身的 bug,落地的也是一个被 seccomp 严限(只放行最小 syscall 集)、jailer 关押(chroot + cgroups + namespace)、没有宿主文件系统、只有配置好的 tap 网络的环境。
Kata Containers 把 microVM 隔离包在标准 OCI 容器 API 后面,约 200ms 启动。好处是不用脱离 K8s 工具链就能拿到硬件级隔离,它和 kubernetes-sigs/agent-sandbox 有正式集成,是 K8s 原生 agent 部署的首选后端。
WebAssembly 是另一种范式。一个 Wasm 模块默认是惰性的——不显式授予能力,它碰不了文件系统、网络、环境变量。WASI 把这个能力模型正式化到 OS 边界,没有「环境权限」(ambient authority)这一说。启动亚毫秒、内存极低,适合高频短时的工具调用。微软 2025 年的 Wassette 把这套模型搬进 agent:基于 Wasmtime 跑 Wasm 组件,通过 MCP 协议暴露给 agent,权限默认拒绝、可交互细控。
选型不看「哪个最强」,看威胁模型和延迟预算。可信内部工具,Docker 够了;计算为主、要 K8s 集成,选 gVisor;跑不可信 LLM 代码、要最高隔离,选 microVM 类(Firecracker/Kata);高频短时工具调用、能接受能力模型重写,选 WASM。Kubernetes 社区 2025 年下半年推出的 kubernetes-sigs/agent-sandbox(SIG Apps 子项目)控制器,核心思路就是把「agent 工作负载生命周期」和「隔离后端选型」解耦——同一个 SandboxTemplate 可指向 gVisor、Kata 或普通容器,按威胁模型切换。
原理:隔离的三条线
一个合格的 agent 沙箱不止是「选个原语」,而是要在三条线上都立得住:
计算隔离——内核边界。共享内核(容器)还是独立内核(microVM/WASM)?这决定了逃逸的难度。独立内核意味着沙箱内代码即便利用了 guest 内核漏洞,崩的也只是 guest 内核,宿主毫发无损。
网络隔离——出口控制。沙箱里跑的代码默认应该不能任意联网。出口要白名单、要审计、要挡掉对宿主内网和其他沙箱的探测。这一层通常用 eBPF 在内核态做,避免用户态拦截的上下文切换开销。
状态生命周期——创建、快照、回滚、销毁。Agent 是按次调起、按毫秒计费的:一次对话里可能要拉起一个沙箱、跑段代码、拿结果、销毁,循环往复。启动慢一秒就是成本和体验双输。还要能快照回滚——agent 行为不可预测,出事了能「撤回」到上一个干净状态。最后是凭据
这三条线里,计算隔离由原语决定,网络和生命周期靠工程设计。CubeSandbox 的贡献主要在后两条——它把 microVM 这个「重」原语,通过资源池 + 快照 + CoW 做成了「轻」的,让硬件级隔离也能按毫秒起停。
CubeSandbox 是什么
腾讯云 2026 年 4 月以 Apache 2.0 完整开源的 agent 沙箱服务,官方定位是「兼顾硬件级强隔离与亚百毫秒启动」——按官方说法是首个把这两件事同时做进一个开源项目的项目。它原生兼容 E2B SDK 和 OpenAI Python SDK,从 E2B Cloud 迁过来只改一个环境变量。
关键指标:冷启动 < 60ms,单实例内存 < 5MB,单机数千实例,50 并发下 P95 90ms、P99 137ms。背后是腾讯云 Serverless 体系的生产验证——承载过百亿级调用,支撑 MiniMax 在 Agentic RL 训练里分钟级调度数十万沙箱实例。
它的核心设计决定:不直接用 Firecracker,而是用 Rust 从零写了一个 VMM(CubeVM),基于 RustVMM 和 KVM。理由是 Firecracker 是通用 microVM,启动流程里有不少对 agent 沙箱场景多余的步骤。CubeVM 针对这个场景做了三处裁剪:
- 最小设备模型:只留沙箱场景必需的虚拟设备(virtio-net、virtio-blk、serial),删掉所有非必要外设模拟。
- 定制 guest 内核:裁过的 Linux 内核,只保留 agent 执行必需的最小特性集,缩短内核启动路径。
- 用户态中断处理:关键 I/O 路径在用户态完成,减少内核态切换开销。
逐一拆:组件
CubeSandbox 分控制面和数据面。它是个 polyglot,按每层性能要求选语言:性能敏感的虚拟化和存储用 Rust,控制面编排用 Go,L7 代理用 OpenResty(nginx+Lua),网络数据面用 eBPF。控制面无状态,Redis 是唯一事实源,所以 CubeAPI/CubeMaster 可以随意水平扩。
| 组件 | 语言 | 职责 | 所属面 |
|---|---|---|---|
| CubeAPI | Rust(Axum) | E2B 兼容的 REST API 网关,翻译成内部 gRPC | 控制面 |
| CubeMaster | Go | 集群编排,选节点、分发到 Cubelet、发生命周期事件 | 控制面 |
| CubeProxy | OpenResty | 反向代理,按 Host 头或 URL 路径路由到目标沙箱 | 数据面 |
| Cubelet | Go | 节点本地调度,管该节点所有沙箱全生命周期 | 数据面 |
| CubeShim | Rust | 实现 containerd Shim v2,桥接容器运行时与 microVM | 数据面 |
| CubeHypervisor | Rust(RustVMM+KVM) | 管 microVM 生命周期,seccomp 加固最小 syscall 面 | 数据面 |
| CubeCoW | Rust | 存储引擎,FICLONE 实现 O(1) 快照/克隆 | 数据面 |
| CubeVS | eBPF | 内核态网络隔离与安全策略 | 数据面 |
| CubeEgress | OpenResty | L7 MITM 代理,出口域名白名单 + 凭据注入 | 数据面 |
CubeAPI 是入口,E2B 协议兼容是迁移成本低的根因——它实现的接口和 E2B Cloud 一样,SDK 不用改。
CubeMaster 是集群大脑,从 CubeAPI 接请求、选节点、分发到对应 Cubelet,生命周期事件发进 Redis。控制面本身无状态,Redis 是唯一事实源。
CubeProxy 解决的是「每请求都过编排层太慢」。它跑在 OpenResty 上,支持两种路由:按 Host 头里的 <port>-<sandbox_id>.<domain>,或按 URL 路径 /sandbox/<id>/...。业务请求直接走它进沙箱,绕过编排层。配套有个 cube-lifecycle-manager(Go)盯 Redis 里的生命周期事件,负责空闲沙箱自动挂起(AutoPause)、来请求时唤醒(AutoResume)。
Cubelet 跑在每个计算节点上,管沙箱从创建到销毁的全生命周期,协调 CubeShim 起 VM、协调 CubeVS 配网络。
CubeShim + CubeHypervisor 是虚拟化层。CubeShim 实现 containerd Shim v2,把沙箱塞进容器运行时,管 rootfs/内存文件/内核准备、VM 启动与恢复、vsock 通信;CubeHypervisor 基于 RustVMM+KVM 管 microVM 的 vCPU/内存/virtio 设备,自身 seccomp 加固(这点和 Firecracker 一样——即便 VMM 自身有 bug,落地也是个最小 syscall 的关押环境)。
CubeCoW 是存储引擎,用内核 FICLONE ioctl(XFS reflink)做 O(1) 的 rootfs 快照和克隆,不拷贝字节——和内存 CoW 是两套机制,一个管磁盘一个管内存。
CubeEgress 是凭据和出口控制的核心,下一节和 CubeVS 一起讲。
CubeVS 是网络数据面,单独拆一节。
60ms 冷启动是怎么来的
关键认知:60ms 不是「VM 本身启动快」(microVM 裸启动仍是百毫秒级),而是一套资源池 + 快照机制把启动过程整体跳过了。
传统 VM 启动: BIOS → 内核引导 → 用户态运行时初始化 → 就绪 (数百 ms ~ 秒)CubeSandbox: 从模板快照恢复(RustVMM restore)→ 就绪 (< 60ms)两步拆开看:
快照恢复:模板在构建时(下文三步里的 Boot+Snapshot)就预先打好内存+状态的完整快照。创建沙箱走 RustVMM 的 restore 路径从快照恢复,跳过整个 OS 引导和运行时加载。这是 < 60ms 的来源——不是 VM 跑得快,是根本没启动。这不是什么新发明——进程池、连接池都是这个思路,只是对象换成了 microVM 的快照恢复。
CoW 内存共享:这是单实例 < 5MB 的根因。从同一模板恢复的实例共享同一份物理内存页,只有被实际写入的页才分配新物理内存。agent 执行时大部分内存是只读的(运行时代码、库),真正写的页很少,所以单实例增量内存压到 5MB 以内,和基础镜像大小无关。磁盘侧另有 CubeCoW 用 FICLONE(XFS reflink)做 O(1) 的 rootfs 克隆,同样不拷贝字节——内存 CoW 和磁盘 CubeCoW 是两套机制,各管一摊。
模板本身有个三步生命周期:
1. Init(构建): 基于 OCI 基础镜像(Ubuntu 等)+ 可选 Dockerfile, Buildkit 打包成满足运行时要求的 rootfs2. Boot+Snapshot: rootfs 冷启动进 microVM,系统+语言运行时(Python/Node)全加载后, 对内存和状态打快照——sub-60ms 热启动就靠这份快照3. Deploy(注册): rootfs + 快照文件注册成可用模板, 之后新沙箱毫秒级从快照克隆出来贵的初始化(OS 引导、运行时加载)只在建模板时发生一次,之后每次创建沙箱都是克隆快照。这就是「亚百毫秒启动 + 硬件级隔离」能同时成立的工程答案。
CubeVS 网络隔离
光有 VM 隔离不够,网络层同样关键。CubeVS 用 eBPF 在内核态做包转发和策略,所有网络处理在内核空间完成,没有用户态上下文切换。
它在三个网络边界各挂一个 eBPF 程序:
- from_cube:挂在每个 TAP 设备的 TC ingress,处理沙箱出口流量——做 SNAT、策略检查、L7 代理选道、建会话、ARP 代理。
- from_world:挂在宿主网卡的 TC ingress,处理外部回包——查会话表、反向 NAT、转发到正确 TAP。
- from_envoy:挂在 cube-dev 的 TC egress,处理 overlay 流量——DNAT 到沙箱内网 IP。
策略评估有严格优先级:allow > deny > default-allow。可以设一条宽拒绝(比如 0.0.0.0/0 全挡),再用 allow 规则精确打洞。更关键的是,CubeVS 无条件拒绝私网和链路本地 CIDR,不管策略怎么配:10.0.0.0/8、127.0.0.0/8、169.254.0.0/16、172.16.0.0/12、192.168.0.0/16。这保证沙箱永远探不到宿主内网,也到不了其他沙箱的链路本地地址。
这里有个常被忽略的安全点:169.254.0.0/16 是链路本地段,云厂商的实例元数据端点 169.254.169.254 就落在这里——SSRF 打到它能偷宿主机的 IAM 凭据,是云上 SSRF 的头号目标。拒绝这个段,等于堵死了沙箱内代码去 curl 元数据端点外带云凭据的路。这不是 CubeSandbox 一家的做法kubernetes-sigs/agent-sandbox 默认 NetworkPolicy 也是同款逻辑——出口只放公网,挡掉 RFC1918 私网和 Metadata Server。沙箱自己的网关 169.254.68.5 也在这个段里,靠策略评估的第一步(目标是网关则放行)单独豁免,所以自身路由不受影响。
它还实现了完整 TCP 状态机(11 态,对齐 Linux 内核 nf_conntrack),保证超时和连接清理准确;UDP/ICMP 用更简单的两态模型;后台每 5 秒清一次过期会话。每个沙箱的 SNAT IP 由 jhash(sandbox_ip) % 4 确定性分配(同一沙箱的所有连接走同一个外网 IP),端口从 30000 起单调递增,冲突重试最多 10 次。
凭据也走这一层,机制值得单独说。agent 调外部 API 要密钥,但密钥不能进沙箱——否则一段 prompt injection 生成的代码就能把环境变量里的 key 读走。CubeEgress 解决这事:沙箱里发往 :80/:443 的流量被 from_cube 标成 L7_REQUIRED,经 TPROXY 重定向到 CubeEgress(OpenResty 做的 L7 MITM 代理)。CubeEgress 在出口处把凭据注入进请求头,沙箱代码只看到一个普通 HTTP 调用,从头到尾没碰到过密钥。凭据永不进入沙箱、模型上下文或日志——这是合规和防泄露的硬要求。
一个请求的完整流程
把前面拆的组件串起来,一个 SDK 请求的全链路:
1. SDK 发请求 → CubeAPI(E2B 协议网关)2. CubeAPI → CubeMaster 编排,选节点3. CubeMaster → 对应节点的 Cubelet4. Cubelet → CubeShim → CubeHypervisor:从模板快照恢复 microVM(RustVMM restore),CoW 共享内存5. Cubelet → CubeVS:配 TAP、挂 eBPF、下发出口策略6. microVM 就绪,注册路由7. 后续业务请求 → CubeProxy(解析 Host 头里的 sandbox_id)直连沙箱8. 沙箱内代码执行,出口流量过 from_cube eBPF → SNAT → 白名单校验 → 放行/拦截9. 销毁:Cubelet 回收,内存页归还(共享页不释放)注意第 7 步——建好之后的业务请求不再过编排层,直接走 CubeProxy 进沙箱,所以「建一次、用很多次」的 agent 循环里,延迟基本只剩代码本身和受控的出口网络。
它在选型谱系里的位置
把 CubeSandbox 放回前面四原语谱系:它本质是 Firecracker 这一档的硬件隔离,但靠快照恢复 + CoW 把启动延迟压到了 WASM 这一档的体感。代价是工程复杂度——你得维护快照、eBPF 数据面、定制内核、L7 代理。换来的收益是:既不共享内核(不像 Docker 有逃逸风险),又不用为每次 agent 调用付秒级 VM 启动(不像传统 VM)。CubeSandbox 切的位置很明确——不碰模型层、不改 agent 逻辑,专门替代「代码在哪跑」这一层;E2B Cloud 是它的闭源商业对照,接口兼容、可私有部署是它做开源平替的支点。(E2B/Kata/Docker 的逐项对比见前面选型表。)
何时用,何时不该用
用 CubeSandbox(或同类 microVM 沙箱)值得的场景:
- agent 跑的是不可信代码或 LLM 生成的代码,共享内核不可接受。
- 多租户场景,租户之间要强隔离。
- Agentic RL 训练,要分钟级拉起数十万实例,每个 episode 一个干净环境。
- 对出口网络有合规审计要求(凭据隔离、白名单、全审计)。
不该用的场景:
- 可信内部工具,代码是审计过的,共享内核的风险可接受——Docker 就够,不用上 microVM 的复杂度。
- 单租户、小规模、对启动延迟不敏感——维护预置池和快照基础设施不划算。
- 工具调用都是高频超短时、能接受把能力模型重写的——WASM 类方案更轻。
小结
沙箱不是一个「开关」,是 隔离原语 × 生命周期工程 的组合。原语决定安全边界(共享内核 / 拦截 syscall / 硬件虚拟化 / 能力模型),生命周期工程决定能不能塞进 agent 按毫秒起停的节奏。CubeSandbox 的贡献不在原语本身(KVM microVM 不是新东西),而在把生命周期工程做到位——这套组合让「硬件级隔离」从「太重、跑不动 agent」变成「能按毫秒起停」,这才是它值得拆开看的地方。
参考资料
- CubeSandbox GitHub README — 项目定位、核心指标、组件职责表
- CubeSandbox 架构总览 — 分层架构、控制面/数据面、组件语言与职责
- CubeSandbox 网络架构(CubeVS) — 三个 eBPF 程序、SNAT/会话追踪、网络策略与无条件拒绝 CIDR
- Cube Sandbox 开源公告(DEV) — 为什么自建 VMM、冷启动与 CoW 机制、E2B 兼容策略
- CubeSandbox 深度解析(PyShine) — CubeVS 三个 eBPF 程序、TCP 状态机、模板三步生命周期
- 腾讯云 Cube 沙箱开源新闻 — Apache 2.0 全栈开源、生产验证背景
- 60ms 启动一个安全沙箱(腾讯云开发者) — Docker 共享内核问题、KVM 硬件隔离对比
- AI Agent Sandboxing and Security Isolation(Zylos Research) — 四种隔离原语对比表、双层威胁模型、kubernetes-sigs/agent-sandbox
- Scaling Managed Agents: Decoupling the brain(Anthropic) — Session/Harness/Sandbox 三件套、凭据不进沙箱的结构约束
- kubernetes-sigs/agent-sandbox — SIG Apps 的 Sandbox CRD、SandboxTemplate、默认 NetworkPolicy 挡私网与元数据端点
- Firecracker SPECIFICATION — 125ms 启动、<5MiB 开销、150 microVM/秒、jailer + seccomp