CubeSandbox 是怎么让 AI Agent 安全跑代码的


前言

最近在看 Agent 怎么安全跑代码这件事,用了腾讯开源的 CubeSandbox,这个也是我们当前项目中在用的,记录一下。

事情本身很简单:Agent 要干活就得执行代码,而这段代码是 LLM 现写的。可能是老老实实 print("hello"),也可能顺手来个 rm -rf /。你没法像审微服务镜像那样审它,因为压根没人写过它。

那总得跑吧。CubeSandbox 就是这个”跑的地方”:60ms 起一个、内存开销 5MB 以内、单机跑几千个,接口兼容 E2B,换个环境变量就能切过去。

这篇不打算面面俱到,就记三个我比较关心的点:为什么能起这么快、靠什么做隔离。

一、先看速度:60ms 启动一个虚拟机

1.1 传统方案卡在哪

维度Docker 容器传统虚拟机CubeSandbox
隔离级别低(共享内核 Namespace)高(独立内核)极高(独立内核 + eBPF 网络隔离)
启动速度~200ms秒级毫秒级(< 60ms)
内存开销低(共享内核)高(完整 OS)低(极限裁剪,< 5MB)
部署密度高低极高(单机数千)
E2B SDK 兼容//✅ 完全兼容

传统虚拟机慢的根源在于:它必须走完整的 BIOS → Bootloader → Kernel → Init 引导流程。这个流程省不掉,几秒钟就没了,对需要实时交互的 Agent 来说完全不可接受。

CubeSandbox 的思路是:既然这段流程永远一样,那就别每次都走。

1.2 三个提速要点

  • 模板即”预快照”:模板不是静态镜像,而是系统完整启动后的内存现场存档。创建沙箱时直接 restore_vm() 恢复内存状态,跳过整段引导流程。
  • CoW 克隆,零字节拷贝:存储引擎 CubeCoW 借助 XFS reflink 的 FICLONE 实现 O(1) 克隆,只复制元数据,数据一字不拷,真实内存页首次写入时才复制——这是单沙箱基础开销能压到 5MB 以内的原因。
  • 网络设备池化:每个沙箱需要独立 TAP 设备,CubeSandbox 由 network-agent 预创建 500+ 个 TAP 形成池子按需取用,官方数据这一项省下 59ms+ 的创建耗时。

1.3 官方给的性能数据

下面这些数字都是官方基准测试的结果,不是我实测的,来源是 CubeSandbox 官方性能基准报告,测试环境为裸金属:

  • 单并发创建:平均 60ms
  • 50 并发创建:平均 67ms,P95 90ms,P99 137ms
  • 内存开销:32GB 规格沙箱的基础开销 < 5MB

(等我自己跑一遍再补实测数据。)

这里的工程哲学值得记一笔:把串行的初始化过程,提前变成并行的后台任务。 资源池按历史请求模式动态调整预热数量,请求到达时只是”取一个已经准备好的东西”。

二、再看安全:四层防御怎么叠

快只是入场券,Agent 沙箱的核心价值是安全。CubeSandbox 的防御不是单点,而是从硬件到应用层叠了四层。

2.1 第一层:硬件级隔离(MicroVM)

先说 Docker 为什么在 Agent 场景不够用。

Docker 的 Namespace 是共享宿主机内核的软隔离,做的是资源视图限制,不是真正的边界。一旦内核有漏洞,或者代码调用了 seccomp 没过滤掉的 syscall,容器逃逸就发生了。微服务场景还能靠镜像审计兜底,但 Agent 跑的代码是 LLM 现写的,没法审计。

所以 CubeSandbox 换成了 MicroVM:每个沙箱配一个独立的内核,跑在自己的虚拟机里。这样一来,沙箱内的代码就算打穿了自己的内核,崩掉的也只是它自己那个 VM,宿主机和邻居都还在。没有共享内核,就没有共享内核的逃逸面。

顺带一提,栈里负责虚拟化的 CubeHypervisor 本身也做了收窄:只保留 Agent 场景必需的设备,系统调用面用 seccomp 卡到最小——想从沙箱往上跳,面对的也是个小目标。

2.2 第二层:网络隔离(eBPF,无 iptables)

网络这块 CubeSandbox 换了套玩法:没有 iptables 规则、没有 Linux Bridge、没有 OVS,转发和策略全部由 eBPF 程序在内核态完成。

好处是每个沙箱独占一个 TAP 设备,点对点直连——没有共享网桥、没有软件交换机跳转,也没有共享 L2 域(不必处理 ARP 洪泛和 STP)。策略按沙箱独立存放,改一个沙箱的规则不影响别人。匹配用 LPM Trie(最长前缀匹配),查询接近 O(1),线速执行,不会出现 iptables 规则链越堆越长的老问题。

默认策略也挺有态度:允许访问 internet,但默认封掉内网网段——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。

这基本堵死了”Agent 从沙箱里探测宿主机内网、横向摸到别的服务”这条路。优先级是 allow > deny > default-allow,也支持先 0.0.0.0/0 全拒再逐条放行的白名单玩法。

2.3 第三层:域名维度的出网管控

只用 IP 做封禁有个天然缺陷:Agent 访问的目标通常是域名(比如 api.openai.com),而同一个域名可能解析到多个 IP,而且 IP 还会变。纯 CIDR 策略拦不住。

CubeSandbox 在 eBPF 层直接拦截 UDP/53 的 DNS 查询:

  1. 拿到查询的域名,检查是否在 allow_out 白名单中
  2. 如果在,建立一条域名追踪记录
  3. 收到 DNS 响应时,把响应里的 IP 动态加入 allow_out,放行后续访问
  4. 设置 TTL,到期自动清理陈旧 IP

这样就实现了域名维度的精细控制。注意它有个前提:依赖 Agent 走标准 DNS 查询,如果绕过 DNS 自己写 hosts 就不生效。

2.4 第四层:凭证托管(最值得抄的设计)

这一层是我个人认为整个方案里最有价值的部分。

核心原则:Agent 沙箱不应该直接持有 API Key / Token。

传统做法是把密钥通过环境变量或配置文件塞进容器——这意味着密钥就在沙箱内,Agent 写的代码可以读它、可以外传它,甚至可以把它带回模型上下文。一旦沙箱被攻破或遭遇提示注入,密钥就泄露了。

CubeEgress 的做法是:沙箱内代码发送不带凭证的请求,由沙箱外的代理层自动注入。

Agent 代码 → 发起无凭证请求 → CubeEgress 校验 → 注入 Authorization → 上游服务

注意”校验”两个字,凭证注入前有多道检查,任一失败就放弃注入(请求仍可继续,但不会带上凭证):

  1. 请求必须是 HTTPS
  2. SNI 必须匹配规则
  3. Host 头必须与 SNI 一致(防止通过改 Host 头把凭证送到别的域名)
  4. 上游证书验证通过
  5. 策略授权放通

这样一来,密钥既不在沙箱里,也不进模型上下文。Agent 全程”徒手”调用外部 API,凭证在沙箱外的代理层完成注入。这是零信任思路在凭证管理上的一个很漂亮的落地。

三、工程架构:组件各司其职

3.1 分层结构

CubeSandbox 采用控制面 / 数据面分离设计:

层组件职责
控制面CubeAPI、CubeMaster、WebUI、RedisAPI 网关、调度、状态协调、运维控制台
数据面Cubelet、CubeShim、CubeHypervisor、CubeCoW、CubeVS、CubeEgress、CubeProxyVM 生命周期、存储、网络、安全策略执行、请求路由

两条设计原则值得注意:

  • 控制面无状态:CubeAPI 和 CubeMaster 不保存本地状态,所有协调通过 Redis 完成,可以随意横向扩容
  • 数据面节点本地:每个计算节点运行自己的 Cubelet / CubeShim / CubeHypervisor / CubeVS / CubeEgress,只管自己主机上的沙箱

3.2 一次创建的完整交互链路

一次 Sandbox.create() 调用,从外到内大致走这么几步:

客户端 / SDK
   │  发一个 E2B 格式的创建请求
   ▼
CubeAPI          接口层:把 E2B 请求转成内部调用,处理鉴权
   │
   ▼
CubeMaster       调度层:挑一台资源够用的机器,把活派下去
   │
   ▼
Cubelet          节点层:在这台机器上准备沙箱的 rootfs 和资源
   │
   ▼
CubeShim         运行时层:把容器的创建指令翻译成虚拟机的启停
   │
   ▼
CubeHypervisor   虚拟化层:从内存快照恢复一个 MicroVM 出来
   │
   ▼
CubeVS           网络层:给新沙箱分配 TAP 设备、挂上网络策略
   │
   ▼
沙箱就绪,返回 sandbox_id

这条链路里,真正花时间的其实只有两件事:从内存快照恢复 VM(替代了完整的系统引导),和分配 TAP 网络设备(而这个设备是从预建池子里直接取的)。剩下的都是选机器、发事件、记路由这类轻量活。

这就是 60ms 的完整解释。

四、小结:三个可以立刻借鉴的设计

即使你不用 CubeSandbox,这套方案里有三个思路我认为可以直接搬到现有系统:

  1. 凭证永不进沙箱 —— 把密钥从”环境变量”改成”代理层注入”。这不需要 KVM,只需要在出口加一个代理,收益立竿见影。
  2. 默认拒绝内网段 —— 无论用什么隔离方案,都该默认封掉 10/8、172.16/12、192.168/16、169.254/16,别让不可信代码有机会摸到你的内网。
  3. DNS 层做域名白名单 —— 只拦 IP 是不够的,因为域名解析出来的 IP 会变。在 DNS 查询环节就把域名卡住,比事后维护 IP 列表可靠得多。

回头看 CubeSandbox 的技术选择,它做对了三件事:用 MicroVM 换来真正的隔离边界、用预快照 + CoW 把启动成本摊薄到接近零、用 eBPF 把网络策略做到内核态线速执行。三者缺一,就没法在”安全”和”快”之间同时站住。

参考链接:

  1. CubeSandbox 官网
  2. CubeSandbox GitHub 仓库
  3. Cube Sandbox 安全沙箱网络技术解析
  4. 架构概览 - CubeSandbox 文档