Skip to content
View as Markdown

隔离与放置 ​

四个彼此独立的控制,各管一件事。把它们混为一谈,多租户部署就会出现"某个租户的沙箱 跑在另一个租户机器上"。

一、放置按租户限定 ​

每个 worker 带一个工作区,每次创建带一个租户 —— 租户来自发起请求的凭据,不是 请求体。

沙箱(租户=A) → worker(租户=A)     ✓
沙箱(租户=A) → worker(租户=B)     ✗ 永不调度
沙箱(租户=A) → worker(租户="")    ✓ 自托管机队,故意不限定

这条规则在调度器里,所以对每一条创建路径都生效:原生 API、E2B 面,以及崩溃后恢复的 重新投递。

二、worker 角色只能做两件事 ​

worker 用 join token(jn_…)认证,拿到的角色只够到两条路由:隧道握手,和它自己的 心跳。它不能列机队、不能读沙箱、不能签发任何东西,其它一律 403。

这很重要,因为 join token 会发到一台你未必完全信任的机器上 —— 客户的 VM、外包的 笔记本。被攻陷的 worker 能跑沙箱,但看不到你部署的其余部分。

三、出网是策略,不是指望 ​

沙箱可以带白名单创建。在 Docker worker 上,这是结构性执行:每个沙箱一个没有出口 的内部网络,加一个挂在普通网络上的出网代理 sidecar。沙箱只挂在内部网络上,所以除了 代理它没有别的路。

json
{"allowed_domains": ["pypi.org", "github.com"]}

策略在容器创建时生效。对运行中的沙箱设置,只是记录下来给下一次创建用,无法就地 改造网络。细节与权衡见出网策略。

四、权限闸门(agent 与员工电脑模式) ​

在 agent 模式下,沙箱是某个真人的电脑,问题就变了:不是"这个容器能连到哪",而是 "这个智能体能对我的文件做什么"。那是一套六分支的决策引擎 —— 硬锁、强制拒绝、工作 目录、永久授权、会话授权、然后询问 —— 每次决策都写进审计日志,底下还可以叠一层操作 系统沙箱(Linux 的 bwrap、macOS 的 seatbelt),在内核层面执行同样的答案。

它是可选的(-permission-mode prompt|strict),因为在一次性的沙箱上加它只会多出 一堆弹窗。

平台会拒绝什么 ​

拒绝是特性。下面每条都是故意的:

请求回答为什么
在无机队具备出网能力时请求策略409 worker capability unavailable另一种做法是给你一个"号称被墙住、其实没有"的沙箱
超过工作区并发沙箱上限的创建准入闸门返回 403闸门在创建的唯一入口上,任何入口都绕不过
member 想删 worker 或签发 key403 owner_required花钱和授权属于工作区 owner
用名字访问别的租户的沙箱404与"不存在"不可区分,这是故意的

隔离做不到什么 ​

  • 不是虚拟机边界。 同一 worker 上的两个沙箱共享内核。需要硬件级隔离,就给工作区 分配专属 worker(或专属云主机)。
  • 不是资源保证。 cpu_cores 和 memory_mb 是调度提示;容器限额是 Docker 的, 一台繁忙的 worker 会拖慢上面的每个沙箱。
  • 挡不住 worker 本身。 worker 跑 Docker,能看到它承载的每个容器。这就是 join token 被限定作用域和时效、以及"客户自己的硬件是最强租户边界"的原因。

下一步 ​

Apache 2.0 — your clouds, your machines, your rules.