← 返回信息流
AI 资讯Hacker News·6 小时前

大规模运行不可信客户代码的实践

原标题:How we run untrusted customer code at scale

速览

本文详细阐述了在大规模生产环境中执行不可信客户代码所面临的挑战及解决方案。内容涵盖沙箱隔离、资源限制、安全监控等关键技术手段,旨在平衡用户体验与系统安全性。该实践对于构建多租户SaaS平台及AI服务基础设施具有重要参考价值。

AI 深度解读

深度解读:Nango 如何大规模运行不受信任的客户代码

背景

Nango 是一个以代码为核心的平台,旨在帮助开发者构建产品级 API 集成。其核心业务模式是让客户的应用程序连接至 Salesforce、Google Calendar、Slack 以及数百种其他 API。在这些集成背后,大量的逻辑代码并非由 Nango 编写,而是由客户自己编写并部署到 Nango 的平台上执行。

这意味着 Nango 必须运行大量“不受信任”(untrusted)的代码。这些代码可能执行任何操作:调用外部 API、转换数据、抛出异常、泄漏内存,甚至恶意尝试突破沙箱限制。目前,Nango 每月要在不同的工作负载形态下运行超过 1.5 亿次此类函数。

这种场景带来了极高的技术挑战,因为运行不受信任的代码不仅需要满足常规的性能要求,还必须建立严格的安全边界,防止客户代码访问 Nango 的内部数据库、密钥或网络,同时确保不同租户(客户)之间的代码互不可见且资源互不干扰。

核心内容

Nango 在构建和执行环境的过程中,经历了一系列技术架构的演进,从最初的进程内沙箱逐步过渡到基于云原生的硬件隔离方案。

1. 工作负载的多样性与需求

Nango 主要运行三种截然不同的工作负载,每种都有特定的性能要求:

  • 按需调用(Actions):为用户或 AI 代理执行,要求极低的冷启动时间和快速的响应速度。
  • 长运行作业(Syncs):在后台复制数据,可能持续数小时并处理数百万条记录,需要具备可恢复执行(resumable execution)的能力。
  • 突发事件(Webhooks):到达时间不可预测,常出现流量尖峰,系统必须能够吸收突然的流量洪峰。

在此基础上,运行不受信任的代码引入了额外的安全与隔离需求:

  • 与系统隔离:函数绝不能访问 Nango 的数据库、密钥或内部网络。
  • 租户间隔离:一个客户的代码不能访问另一个客户的数据,且一个客户的重型作业不能导致其他客户资源饥饿。
  • 执行间隔离:同一客户下的长任务和短任务不应争夺同一资源。
  • 成本与弹性:系统需自动伸缩,避免为空闲计算资源付费,也无需人工维护服务器集群。

2. 第一阶段:进程内沙箱 (vm2)

在早期阶段,Nango 使用 vm2(一个 Node.js 沙箱库)在同一个进程中运行客户代码。

  • 机制:客户函数与 Nango 的工作进程代码共存,vm2 负责阻止客户代码访问数据库、密钥和其他客户数据。
  • 优势:实现简单,无需额外基础设施。
  • 危机:2023年,vm2 的维护者因一系列沙箱逃逸漏洞暂时归档了该项目。这些漏洞表明,沙箱内的代码可以逃逸并控制宿主机。Nango 意识到,与不受信任的代码共享进程并非真正的安全边界,一旦逃逸,后果严重。

3. 第二阶段:独立的 Runner 架构

吸取教训后,Nango 将系统拆分为两部分:调度器(Dispatcher)和执行器(Runner)。

  • 机制:调度器通过 HTTP 将客户代码分发给独立的 Runner。每个客户拥有独立的、长期存在的 Runner,可独立扩展 CPU 或内存。
  • 隔离:Runner 不直接访问数据库。客户代码通过 SDK 方法(如 nango.batchSave(...))与独立的持久化服务(persist service)通信,由该服务负责与数据库交互。
  • 部署:Runner 作为独立服务部署在 Render 上。
  • 局限:随着规模扩大,该模型在资源公平性和可观测性上遇到困难。一个 Runner 处理同一客户的所有执行,导致重型作业可能“饿死”该客户下的其他函数。此外,当 Runner 内存溢出时,难以定位具体是哪个函数导致的。

4. 第三阶段:迁移至 AWS Lambda

为了解决隔离性和可观测性问题,Nango 在 2025 年底迁移至 AWS Lambda。

  • 优势:Lambda 为每次执行提供硬件虚拟化的微虚拟机(microVM)和独立的内核,隔离性远强于共享进程。AWS 自动处理伸缩。
  • 改进
    • 精准定位:内存/CPU 问题现在可以精确指向单个连接函数,并显示在该函数的独立日志中。
    • 故障隔离:一个糟糕的代码函数不再影响其他函数。
    • 调试效率:对于支持数百个客户的小型团队,调试时间显著减少。
  • 挑战与解决方案
    • 超时限制:Lambda 函数最长运行 15 分钟,而 Nango 的同步作业可能更长。
    • 产品侧解决:将单次同步运行限制为 10 分钟,并引入“检查点”(checkpoints)功能,允许同步作业在多次运行之间恢复,而非依赖单次长执行。

5. 租户隔离的最终难题:Lambda 的环境复用

虽然 Lambda 提供了硬件隔离,但它为了速度会复用“热”环境(warm environment)。当 Lambda 函数执行完毕后,AWS 可能将下一次调用路由到同一个热环境中。

  • 风险:对于 Nango 而言,如果两个不同客户的代码被路由到同一个热 Lambda 环境,且客户代码存在逃逸漏洞,恶意代码可能访问下一个客户的凭证。硬件隔离无法解决同一环境内的时序竞争问题。
  • 解决方案按客户固定 Lambda 函数。每个客户的执行被绑定到其专属的 Lambda 函数上,确保热环境只服务于同一客户。这样,即使发生逃逸,影响范围也仅限于该客户已控制的内容。
  • 副作用
    • 冷启动增加:专属函数的冷启动率从不足 1% 上升至约 9%,每次增加数秒延迟。
    • 维持热度:在付费计划中,Nango 通过定期发送无操作(no-op)调用来保持函数处于热状态。

6. 行业对比与反思

Nango 指出,运行不受信任代码已不再是小众需求,AI 代理(Agents)的动态代码生成使其成为主流。其他玩家如 E2B、Fly 使用微 VM,Modal 使用 gVisor,均走向硬件级隔离。Cloudflare Workers 使用 V8-isolate,边界较弱,且 V8 难以安全沙箱化任意 npm 包。

Nango 团队内部对此架构存在分歧:

  • 观点一:按客户固定函数并维持热度,本质上重建了早期 Runner 的“始终在线”模型,是在扭曲 Lambda 以适应需求。应致力于构建足够强大的沙箱,使租户隔离变得不再必要。
  • 观点二:这是稳健的增量进步。利用 Lambda 提供的隔离,最小化其他变更,先解决最大风险,将更难的沙箱工作留在路线图后续阶段。

Nango 认为两者都有道理,目前的选择是在当前技术约束下的最佳权衡。

关键要点

  • 安全边界演进:从进程内沙箱(vm2)到独立 Runner,再到硬件隔离的 Lambda,Nango 的安全架构随着对“共享进程风险”认知的加深而不断升级。
  • 硬件隔离优于进程隔离:Lambda 提供的微 VM 和独立内核提供了比共享进程强得多的隔离性,解决了资源饥饿和故障定位难题。
  • 租户隔离的特殊性:云原生函数(如 Lambda)的环境复用机制在共享租户场景下存在安全隐患,必须通过“按客户固定函数”来对齐隔离边界。
  • 性能与安全的权衡:增强隔离(如固定函数)导致冷启动率上升(从 <1% 至 ~9%),需通过“保活”调用(keep-alive)来抵消延迟影响。
  • 长任务处理策略:面对函数执行时长限制,采用“短周期+检查点恢复”的产品级解决方案,而非依赖单次长运行。
  • 行业趋势:AI 代理时代的代码托管正普遍转向硬件级隔离(微 VM、gVis
查看原文 →nango.dev