Electric Agents 沙箱实战指南:用 Sandbox Profiles 隔离 LLM 工具的文件、进程与网络访问
发布时间:2026/9/16 18:58:45来源:尧图网络
Electric Agents 沙箱实战指南用 Sandbox Profiles 隔离 LLM 工具的文件、进程与网络访问【免费下载链接】electricThe agent platform built on sync.项目地址: https://gitcode.com/GitHub_Trending/el/electricElectric Agents 将 LLM 驱动的文件读写、Shell 执行与网络请求统一收口到ctx.sandbox这一抽象中由运行时主机注册命名沙箱 ProfileSandbox Profiles并在实体Entity被 spawn 时按需选择。本文以 website/docs/agents/usage/sandboxing.md 为骨架结合electric-ax/agents-runtime的源码实现完整讲解 Profile 注册、内置 Provider 的取舍、ctx.sandbox的 API 用法、spawn 时的选择语义、身份与生命周期解析以及网络策略与安全边界。读完你将能为自有运行时配置本地/容器/远程 VM 三类沙箱在 Handler 与自定义工具中安全地访问文件系统与网络并为多实体协作设计正确的沙箱共享与持久化方案。沙箱是什么一次 wake 会话内文件、进程、网络的统一出入口沙箱Sandbox是 Electric Agents 为 LLM 驱动的工具提供隔离能力的核心原语。在 沙箱接口定义 中Sandbox被设计为同时拥有三方面能力的对象文件系统访问readFile/writeFile/mkdir/readdir/exists/remove/stat路径解析与包含containment由沙箱自身负责进程执行exec(opts)运行一条 Shell 命令并返回退出码、输出与超时/中止状态网络出口fetch(input, init)从沙箱内部发起 HTTP 请求受沙箱创建时声明的网络策略约束。这三个能力对应 LLM 驱动工具读文件、跑 bash、抓取 URL最常见的三类危险面。源码注释明确指出隔离强度因 Provider 而异每个 Provider 都会说明自己保护什么、不保护什么——这是理解沙箱设计的第一原则它是一套尽力而为 分层纵深的机制而不是一个统一的强安全边界。沙箱的生命周期按wake 会话wake session组织运行时在每个 wake 会话开始时调用 Profile 的 factory 构造ctx.sandbox会话结束由运行时统一负责销毁。正因为如此Handler 不应自行调用ctx.sandbox.dispose()——处置权在运行时。沙箱的配置由运行时主机runtime host完成通过命名 Profile的形式注册到 agents-server实体 spawn 时按名字选取 Profile。这样设计的好处是真正持有资源Docker 守护进程、E2B 凭证的 factory 闭包只存在于运行时进程内跨网络传输的仅仅是name、label、description、remote等描述性元数据见 SandboxProfile 定义。注册 Runtime Profiles在运行时上声明沙箱能力在运行时创建处通过sandboxProfiles数组注册 Profile。每个 Profile 由四部分组成name稳定 wire 标识、labelUI 选择器中的人类可读名称、description可选长描述、factory构造Sandbox的工厂函数。原文档给出了一个同时注册本地与远程两种 Profile 的完整示例这也是多数场景的标准模板import { createRuntimeHandler } from electric-ax/agents-runtime import { remoteSandbox, unrestrictedSandbox, } from electric-ax/agents-runtime/sandbox const runtime createRuntimeHandler({ baseUrl: http://localhost:4437, registry, sandboxProfiles: [ { name: local, label: Local, description: Trusted local development sandbox, factory: ({ args }) unrestrictedSandbox({ workingDirectory: typeof args.workingDirectory string ? args.workingDirectory : process.cwd(), }), }, { name: e2b, label: E2B, description: Remote VM sandbox, remote: true, factory: ({ sandboxKey, persistent, owner }) remoteSandbox({ provider: e2b, sandboxKey, persistent, owner, initialNetworkPolicy: { mode: allow-all }, }), }, ], })要点解读factory 接收的args来自 spawn 时传入的 Profile 级参数这里用来动态决定workingDirectory未提供时回退到process.cwd()远程 Profileremote: true会被 agents-server 用来放宽共置约束co-location guard一个共享的远程沙箱可以从任意 runner 访问而共享的本地沙箱必须把协作者钉在同一个 runner 上容器只存在于单台主机。默认值为false即 Profile 一律被视为主机本地除非显式声明remote: true从 SandboxFactoryParams 可以看到factory 还会拿到sandboxKey、persistent、owner、entityUrl、entityType等已解析的身份与生命周期信息——它们由上游从实体的沙箱配置 当前 wake 解析而来。Profile 的名字在运行时注册 type/runtime 时随描述符一并上报给 agents-server实体定义中通过名字引用 Profilespawn 时从该实体允许的 Profile 集合中挑选。内置 Sandbox Provider 一览与选型沙箱包从以下两个入口导出内置实现见 sandbox.ts 导出清单import { chooseDefaultSandbox, unrestrictedSandbox, remoteSandbox, } from electric-ax/agents-runtime/sandbox import { dockerSandbox } from electric-ax/agents-runtime/sandbox/docker注意dockerSandbox放在sandbox/docker子路径导出见 sandbox-docker.ts这样只使用进程内unrestrictedSandbox的调用方如被 Vite 打包的桌面渲染进程不会把dockerode及其原生依赖拉进 bundle。仅在真正使用 Docker Provider 时才从子路径导入。内置 Provider 的选型对照沿用原文档表格并补充实现细节Provider适用场景说明unrestrictedSandbox()受信任的本地开发共享宿主机文件系统与进程命名空间。便捷但不是安全边界。dockerSandbox()多实体主机的本地隔离需要 Docker 与dockerode。推荐用于不受信任或租户隔离的本地负载。remoteSandbox({ provider: e2b })远程 VM 隔离需要可选的e2b包与 Provider 凭证Profile 需标记remote: true。chooseDefaultSandbox()内置本地默认为内置的 Horton 与 Worker 运行时选择默认本地 Profile。unrestrictedSandbox便捷但非边界的默认实现chooseDefaultSandbox的实现见 default.ts总是返回unrestrictedSandbox更强的隔离需要显式构造 Docker 或远程沙箱。这一默认适用于单租户、受信任代码的场景但源码注释明确警告它无法阻止宿主机级访问如读取/proc/ppid/environ窃取密钥或来自fetch_url的 SSRF。在实现层面见 unrestricted.ts它仍然做了三层值得注意的防御路径包含检查resolveWithinL269-L307通过realpath规范化最深存在的祖先再与规范化后的工作目录根做比较同时拦截..逃逸与符号链接逃逸对应 CVE-2025-53109/53110-shape 一类路径看起来干净但组件是指向工作区外的符号链接的绕过方式进程组级超时exec用detached: true创建独立进程组超时或外部signal中止时对整个进程树发SIGTERM、500ms 后升级SIGKILLL104-L133避免child.kill(SIGTERM)只杀sh、遗留sleep等孙进程占据 stdio 管道的问题环境变量白名单子进程环境只继承PATH、HOME、USER、LANG、TERM五个变量再叠加调用方传入的env宿主process.env不会整包透传——这正是内置文件工具不再向 Shell 命令转发宿主环境变量这一安全说明的实现基础。dockerSandbox本地容器级隔离Docker Provider 是多实体主机上的本地隔离推荐选项其选项面非常丰富见 DockerSandboxOpts镜像默认固定为node:20-alpinesha256:fb4cd12c...L139跨 amd64/arm64 可复现自定义镜像必须带 digest pin除非显式allowFloatingTag: trueresolveImage会强制校验见 L879-L888运行时runc默认兼容广或runscgVisor加固资源限制memoryBytes默认 2 GiB、cpus默认 2、pidsLimit默认 1024对应HostConfig中的Memory、NanoCpus、PidsLimit端口暴露exposedPorts仅绑定到 loopback127.0.0.1避免沙箱内服务意外暴露到局域网额外挂载extraMounts默认只读挂载且拒绝挂载 Docker socket——无论字面路径还是经realpath解析后的符号链接指向 socket 都会被SandboxError(policy)拒绝L940-L975否则沙箱代码可创建容器直接逃逸。创建容器时写入的HostConfig是一套调用方无法覆盖的硬化配置L449-L470CapDrop: [ALL]、SecurityOpt: [no-new-privileges:true]、Privileged: false、IpcMode: none、禁 swap、严格的 nofile/nproc ulimit。容器由sandboxKey确定性命名electric-sbx-slug-sha256[:12]见 L283-L289所有解析到同一 key 的调用方收敛到同一个容器——这既是重连reattach的基础也用于跨 wake/跨实体共享文件系统状态。生命周期方面Docker Provider 维护一个进程内注册表sandboxContainers以引用计数refs跟踪每条租约空闲后经去抖定时器执行 teardownpersistent为 true 时stop可写层保留可重连恢复否则remove彻底抹除默认空闲宽限期为 2 分钟DEFAULT_IDLE_GRACE_MS。进程崩溃遗留的孤儿容器由启动时的sweepOrphanedDockerSandboxes清理依据 owner-pid 标签与容器内收养标记判断归属。remoteSandbox远程 VM 隔离远程沙箱把文件系统与进程搬进 SaaS Provider 的微 VM / 容器中默认工作目录为/workremote.ts L91所有 FS 方法经 Provider SDK 往返每次调用一次网络 RTT。当前支持的唯一 Provider 是e2bRemoteProvider类型定义为e2bL20。远程沙箱的两条关键语义读取范围更宽工作区外的读取是被允许的VM 内的系统二进制、语言标准库等本来就在工作区外而 VM 相对宿主机已经是隔离的但工作区外的写入一律policy拒绝assertWritableL277-L284fetch在 VM 内执行sandbox.fetch()通过exec在 VM 内跑一个 HTTP 客户端完成fetchInSandbox请求从工作区出口、受创建时施加到 VM 的网络策略管辖策略声明后会话中途不可变更。远程 Provider 对allowlist是在 VM 边界强制的与 Docker 的仅表面防护形成对比。在 Handler 与自定义工具中使用 ctx.sandbox原文档给出的 Handler 用法是标准姿势async handler(ctx) { const result await ctx.sandbox.exec({ command: ls -la, timeoutMs: 10_000, signal: ctx.signal, }) const readme await ctx.sandbox.readFile(README.md) const res await ctx.sandbox.fetch(https://example.com) }使用时的黄金规则是把路径原样交给沙箱不要在宿主进程里预先解析。因为沙箱可能是容器或远程 VM根文件系统与宿主不同——在宿主侧stat/realpath会指向错误的文件系统。路径解析与包含检查是沙箱的职责见 Sandbox 接口注释写入类writeFile/mkdir/remove所有 Provider 一律包含解析到工作区外的路径以SandboxError(policy)拒绝读取类readFile/stat/readdir/existsunrestricted与docker包含remote允许 VM 内任意读取符号链接逃逸仅unrestricted会跟随 realpath 并拒绝它共享宿主 FSrealpath 是唯一边界docker/remote依赖字符串前缀检查 容器/VM 根作为隔离边界。exec的可选参数见 SandboxExecOpts还包括cwd、env合并到沙箱允许的 env 基座之上、stdin、maxOutputBytes按流截断 stdout/stderr、signal外部取消信号与timeoutMs谁先触发谁生效。返回的SandboxExecResult中aborted与timedOut是区分调用方取消与超时到期的两个独立字段。内置的文件与 bash 工具正是通过ctx.sandbox完成实际 IO 的相关实现位于 packages/agents-runtime/src/tools 下的read-file.ts、write.ts、edit.ts、bash.ts、fetch-url.ts这保证了无论沙箱是本地进程还是远程 VM工具层看到的都是同一套受约束的抽象。Spawn 时选择沙箱inherit 与对象形式在 spawn 子实体时选择或继承沙箱。字符串形式inherit直接采用父实体 wake 已解析的沙箱Profile 解析后的 key persistent父实体无沙箱时优雅降级为无await ctx.spawn( worker, analysis, { systemPrompt: Inspect the workspace, tools: [read, bash] }, { initialMessage: Start with package.json, sandbox: inherit, } )对象形式提供更细的控制await client.spawnEntity({ type: worker, id: isolated, sandbox: { profile: docker, scope: entity, persistent: true, }, })SpawnSandboxOption的类型定义types.ts L868-L870显示它要么是inherit要么是SandboxSelectionConfig { profile?: string; inherit?: boolean }——即对象形式既可以直接指定profile也可以通过inherit: true或key等字段组合出更复杂的语义。选择字段的完整语义原文档表格字段含义profile要使用的命名运行时 Profile。inherit复用父级已解析的沙箱选择。key显式的共享沙箱身份标识。scopeentity表示按实体身份默认wake表示按单次 wake 身份。persistent支持时在 wake 会话之间保留沙箱状态。owner该实体是否拥有沙箱的生命周期拆除权。身份、持久化与所有权的解析规则spawn 时传入的配置会在 wake 路径与inherit路径共用的纯函数resolveSandboxIdentityidentity.ts L62-L78中被解析为三个正交事实Key身份显式key是跨实体 rendezvous 句柄永远优先无 key 时按scope派生——wake⇒${entityUrl}#${wakeId}每次 wake 完全隔离entity默认⇒entityUrl该实体所有 wake 共享的稳定工作区持久化persistent决定owner的空闲拆除动作——true 保留stop/suspend供后续重连false 抹除remove/kill。未显式设置时按 scope 默认per-wake 沙箱是临时的显式 key 或 per-entity 沙箱是持久的所有权ownerowner 会创建沙箱并以其生命周期驱动拆除非 owner 只能attach到已存活的沙箱绝不创建或拆除——子代理无法在共享 key 下凭空造出一个全新的空沙箱。默认trueinherit解析为false。这一模型意味着完全隔离纯粹来自唯一的 per-wake key而不是单独的代码路径——Provider 只会看到解析后的sandboxKey、persistent、owner三个值。拆除动作的共享判定函数是sandboxWipesOnDispose(reclaim, persistent)L94-L98实体进入终止态reclaim或沙箱是临时的 → 抹除否则 → 保留。网络策略allow-all / deny-all / allowlist沙箱的网络策略类型type NetworkPolicy | { mode: allow-all } | { mode: deny-all } | { mode: allowlist; allow: string[] }各模式在 Provider 上的真实强度见 NetworkPolicy 注释deny-all隔离 Provider 上的硬边界。Docker 直接以NetworkMode: none创建容器容器没有任何网络接口远程在 VM 边界拒绝沙箱内无论 exec、fetch 还是其他任何方式都无法出口。需要网络隔离时请用deny-allallow-all无出口限制allowlistProvider 相关。远程 Provider 在 VM 边界强制而 Docker 目前只在宿主侧fetch()工具路径上做主机匹配见 net-policy.ts——经exec/bash 运行的代码拥有直接 bridge 出口不受 allowlist 约束所以docker allowlist不能当作网络隔离这是原文档反复强调的关键坑。matchesHost支持精确主机名、localhost环回别名127.0.0.1/::1以及*.suffix通配符。此外无论 allowlist 如何配置宿主侧都会拒绝字面意义上的私网 / 链路本地 / 环回 / 云元数据 IPisPrivateOrLinkLocalL120这是对 LLM 最常尝试的 SSRF 外泄模式内网探测、云元数据窃取的主动防御IP 会先经canonicalizeHost规范化——inet_aton接受的一整套松散 IPv4 形式简写127.1、整数2130706433、八进制0177.0.0.1、十六进制、::ffff:映射 IPv6、带括号 IPv6都会被折叠成点分十进制再分类防止绕过。在remoteSandbox中未指定策略时的默认是deny-all旧字段allowedHosts已废弃同时提供时initialNetworkPolicy优先见 remote.ts L96-L101dockerSandbox未指定时同样默认deny-allL375-L377。而原文档的 e2b 示例显式使用{ mode: allow-all }——需要出口访问的远程沙箱应明确声明策略而不是依赖默认值。安全注意事项与实践建议综合原文档的安全说明与源码实现给出如下实践清单unrestrictedSandbox()只用于受信任的本地代码。它能减少意外的路径逃逸realpath 包含检查但共享宿主文件系统与进程命名空间不是安全边界——无法阻止宿主级访问如读/proc/ppid/environ窃取密钥或来自fetch的 SSRF。内置文件工具依赖当前激活的沙箱做包含且不再把宿主process.env转发进 Shell 命令unrestricted仅透传五个基础变量。这是默认配置下的一道重要纵深。远程与 Docker 沙箱隔离更强但凭证与挂载数据仍需谨慎限定范围Docker 的extraMounts默认只读并拒绝挂载 Docker socket远程 Provider 的apiKey、template等属于运行方机密不应落入不可信的 Profile 参数。需要跨 wake 存活的状态使用 per-entity 或显式key沙箱默认 per-entity 即持久需要完全隔离则用scope: wake或每次 spawn 传入唯一 key需要协作者共享则给父子实体传同一key并让子实体以owner: false如inherit仅 attach。网络隔离请用deny-all不要在 Docker 上依赖allowlist作为进程级出口边界需要白名单出口时远程 VME2B才是能真正在边界强制的选择。dispose 交给运行时Handler 不应调用ctx.sandbox.dispose()Owner 实体到达终止态时框架会通过dispose({ reclaim: true })语义抹除沙箱非 owner 的释放只会 detach永远不会拆除 owner 的资源。相关文档与代码位置沙箱官方文档website/docs/agents/usage/sandboxing.md沙箱接口与类型定义packages/agents-runtime/src/sandbox/types.ts沙箱包统一导出packages/agents-runtime/src/sandbox.ts本地默认实现与身份解析packages/agents-runtime/src/sandbox/default.ts、packages/agents-runtime/src/sandbox/identity.tsDocker Providerpackages/agents-runtime/src/sandbox/docker.ts子路径导出见 packages/agents-runtime/src/sandbox-docker.ts远程E2BProviderpackages/agents-runtime/src/sandbox/remote.ts、packages/agents-runtime/src/sandbox/remote/e2b.tsDocker 网络策略与 SSRF 防护packages/agents-runtime/src/sandbox/docker/net-policy.tsspawn 沙箱选项类型packages/agents-runtime/src/types.ts基于沙箱的内置工具实现packages/agents-runtime/src/tools【免费下载链接】electricThe agent platform built on sync.项目地址: https://gitcode.com/GitHub_Trending/el/electric创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网