在 Modal,我们的客户依赖 Sandboxes 来执行由其下游用户编写的不可信代码,或者如今几乎完全是由智能体编写的代码。运行不可信代码并不是一个新问题:每个云提供商从第一天起就必须这样做,以将其平台与用户隔离,并将用户彼此隔离。幸运的是,像 gVisor 和 Firecracker 这样的技术在大约八年前就“解决”了“隔离”问题。对我们来说不幸的是,它们解决的是一个如今已经过时的信任单元。你如何保护用户免受他们“自己”代码的伤害?
今天我们很高兴推出 Sidecars,这是我们针对这个问题的更广泛答案。Sidecars 是隔离的容器,与你的主 Sandbox 在同一主机上并行运行,并在可信或不可信代码之间提供真正的安全边界。与使用单独的 Sandboxes 相比,Sidecars 使跨信任边界的通信速度提升 3 倍——这对操作密集型工作负载尤其有帮助。
Sandboxes 中充满了可信代码
智能体需要能力,但不应持有凭证。我希望智能体检查我的 Slack 消息,并替我向同事道歉,因为审阅他们发来的内容花了太长时间。我不希望这个智能体在偶然访问 exfiltrate-my-credentials.com 的落地页、并被指示记录我的密码时,知道我的 Slack 密码。
如果 Sandboxes 主要是作为不可信代码的执行环境而存在,那么你不得不问:为什么 Sandboxes 里有这么多可信代码?
这种反模式很大程度上源于早期编码智能体(如 Claude Code)的设计方式——它们是为在用户笔记本电脑上运行而设计的,而不是在远程 Sandbox 中运行。执行框架从未与其工具调用分离,而这种设计模式在迁移到云端后依然延续了下来。
将执行框架和工具调用托管在一起是一种安全风险。Simon Willison 称之为致命三要素:一个能够访问私有数据、接触不可信内容并具备对外通信能力的智能体,可能被诱骗泄露这些数据。一个编码智能体,如果其 harness 运行在 Sandbox 中,默认就同时具备这三点。harness 的凭证就放在生成代码旁边,智能体可以从网络上读取不可信内容,比如软件包和代码仓库,而 Sandbox 中的任何东西都可以发起网络调用。
如今的修复方案以延迟或灵活性换取安全性
我们不是第一个思考这个问题的人,也不是第一个提出解决方案的人。如今流行的解决方案各自去掉了这个三要素中的一条腿:把 harness 移出 Sandbox 就移除了私有数据,而加入高级网络出口控制则限制了对外通信。
像Anthropic和OpenAI这样的公司提倡将智能体的 harness 与其工具调用分离,或者用 Anthropic 的话说:把“大脑”与“手”分离,我们也一直倡导这种模式。
然而,无论 harness 运行在控制平面还是单独的 Sandbox 中,现在每一次智能体操作都要发起一次网络调用。对于要进行大量工具调用的智能体来说,每次调用带来的微小延迟会累积起来。安全性最终以延迟为直接代价。而且,对于编码智能体来说,代码库本身就是私有数据,所以即使把 harness 移出去,仍然存在数据外泄风险,这就引出了第二种方案。
第二种方案是构建高级网络出口控制,比如域名允许列表、凭证注入以及动态出口策略更新。包括我们在内的 Sandbox 提供商一直在快速推出这些功能,以解决最常见的泄露风险。
这些方案符合人体工程学,对大多数客户来说非常有效,但它们并不像许多客户需要的那样灵活。例如,我们的一个客户 Ramp,需要在其智能体旁边运行一个复杂服务,该服务既注入密钥,又监控外部调用——现成的密钥注入并不能完全满足他们的用例。随着智能体能力越来越强,新需求涌现的速度超过了任何提供商推出新功能的速度,而前沿团队需要一种构建自己解决方案的方式。
一个能避免任一权衡的完整解决方案将:
- 完全分离不受信任和受信任的代码
- 为操作密集型智能体最小化延迟
- 足够灵活,以适应尚未想到的程序
Sidecar 作为一种“新”的隔离原语
Sidecar 是我们以低延迟和高可编程性隔离受信任与不受信任代码的答案。Sidecar 与 Sandbox 并行运行,同时保持与之隔离,为受信任软件提供一个位置来注入凭证、代理流量、脱敏数据、运行 harness 逻辑或观察智能体,而无需让每个操作都跨越远程服务边界。
该模式从现有设计中汲取灵感,例如Kubernetessidecar 容器或Datadog Agent:小型、专用的容器,与主应用程序并行运行以提供支持功能。Sidecar 将同样的理念应用于智能体基础设施,并具备建立容器之间信任边界所需的隔离。
隔离:Sidecar 与主 Sandbox 以与独立 Sandbox 相同的隔离边界分隔——gVisor 或 VM,取决于你传递给 `Sandbox.create()` 的运行时。这意味着智能体生成的代码可以在主 Sandbox 中运行,而凭证、代理、harness 逻辑或其他受信任操作则在智能体无法直接访问的 Sidecar 中运行。
延迟:由于容器运行在同一主机上,它们之间的通信保持在本地。主 Sandbox 及其 Sidecar 通过一个基于 TCP/UDP 的内部桥接网络连接,并且每个容器都可以通过名称访问。原本需要从 Sandbox 到远程控制平面的工具调用,现在可以由运行在其旁边的 Sidecar 处理,从而避免在执行密集型智能体循环中累积起来的网络往返。我们的内部基准测试显示,Sandbox 与其 Sidecar 之间的通信速度至少比同一区域内不同 Sandbox 之间的通信快 3 倍。
| Sidecar | 独立 Sandbox(HTTPS) | |
|---|---|---|
| 每次调用传输时间,p50 | 0.58ms | 2.6ms |
| 每次调用传输时间,p99 | 1.4ms | 44ms |
| 100 次调用,总时间 | 0.6s | 1.2s |
创建:Sidecar 通过 Modal SDK 动态创建,因此你可以在运行时启动所需的受信任服务,而不必预先定义固定的拓扑结构。Sandbox 内运行的代码无法创建或修改它们。
资源:Sandbox 和 Sidecar共享 CPU 和内存资源,这使得使用 Sidecar 比跨多个 Sandbox 运行进程更具成本效益。单个 Sandbox 最多可运行 250 个 Sidecar。
网络策略:每个 Sidecar 都有自己的出站网络策略,独立于主 Sandbox,你可以强制 Sandbox 的出站流量通过代理 Sidecar。
生命周期:Sidecar 可以在 Sandbox 生命周期的任意时间点被终止和替换,其文件系统可以被快照,并用于启动另一个 Sandbox 或 Sidecar。终止 Sandbox 会停止其所有 Sidecar。
Sidecar 实战:在 Ramp 构建自定义出口代理
Ramp 的后台编码智能体,Inspect,编写了 Ramp 所有已合并拉取请求中超过 75% 的内容,每个会话都运行在自己的 Modal Sandbox 中。Ramp 在 Sidecar 之上构建了自己的自定义出口代理,运行在每个 Inspect Sandbox 旁边,以监控和控制智能体的出站流量。
“我们需要确切知道我们的编码智能体正在访问什么,并能够在它访问不该访问的内容时阻止它。Sidecar 让我们能够在智能体旁边运行我们自己的出口代理,使用我们自己的规则。我们可以强制出站流量通过代理,这样就没有任何东西能绕过它,而且由于它在同一台主机上,这不会给我们带来额外的网络跳数。”——Zach Bruggeman,Ramp 首席软件工程师
开始使用
Sidecars 现已推出 Beta 版,关于如何开始使用以及已知限制的更多细节,请参阅我们的文档。
我们有意将 Sidecars 设计为灵活的构建模块,但为了帮助你快速上手,我们还为一些具体用例编写了示例:
不过,我们预计最有趣的用例会是那些我们尚未想到的场景。所以,如果你用 Sidecars 构建了什么,我们很想听听!