大模型代码执行的安全沙箱:OpenSandbox 隔离 AI 生成代码的架构与实践

发布时间:2026/10/5 17:11:44
大模型代码执行的安全沙箱:OpenSandbox 隔离 AI 生成代码的架构与实践 先说一个前两天发生在团队里的真实场景。同事为了让 AI 编程助手能自己写测试、自己跑测试把模型生成的命令行直接丢进了本地终端。第一次跑得很顺利模型自动补了一个依赖、执行完测试、返回了结果。第二次就没那么幸运了模型读到项目里一份 READMEREADME 里有一段看似人畜无害的 base64 字符串模型居然解码后当成命令执行了。虽然最终没造成损失但这件事让我们意识到——大模型会写代码这件事真正的难点从来不是生成而是执行。更准确地说是让一个不可完全信任的程序在可控的边界内运行。OpenSandbox 这个名字说的就是这件事。它要解决的命题很具体大模型LLM/AI Agent生成了一段代码我们需要让它安全地把这段代码跑起来同时不去影响宿主机、不去盗取数据、不去无限消耗资源。它不是让 AI 更会写代码而是给 AI 的手戴上手套——让它可以操作但不能乱摸。如果你正在做 AI 编程助手、AI Agent、数据分析自动化或者任何模型自动执行代码的功能这篇内容应该能帮你少走一些弯路。我会从为什么需要一个专门的沙箱、沙箱的边界怎么设计、OpenSandbox 这类项目在架构上怎么落地、实际接入会踩哪些坑、以及它到底拦不住什么这几个角度完整拆一遍。1. 大模型会写代码之后真正的难题变成了在哪跑1.1 代码生成只是第一步执行才是落脚点现在的大模型已经能写出相当完整的 Python 脚本、Shell 命令、SQL 查询甚至整套 CI 配置。当这些产物只是给用户看的时候风险是可控的因为最终决定权在用户手里用户会审视、修改、再决定是否运行。但过去一年里产品形态明显发生了变化从生成代码给人看变成生成代码给机器跑。AI 编程助手直接帮你执行命令、跑测试AI Agent 为了完成任务自动调用 shell 工具操作文件系统数据分析平台让模型自己写 pandas 代码处理数据并返回图表自动化测试工具让模型生成用例后直接提交执行。这些场景的共同点是人工审查环节被大幅压缩甚至完全消失。模型输出变成了一次真实的系统操作。问题在于大模型的生成过程是概率性的同样的 prompt 可能输出完全不同的代码同一段代码这次没问题下次可能因为对话上下文里混入的某些内容多出一句os.system调用。当执行动作由机器自动完成代码质量问题就升级成了系统安全问题。所以在哪跑这个问题的权重实际上已经超过了怎么写。OpenSandbox 这一类沙箱方案核心就是接管模型生成代码之后的运行这个环节让不安全的代码可以在一个能收放、能审计、能销毁的环境里执行。1.2 直接在本机执行大模型代码的风险清单我先不急着讲沙箱怎么做而是把直接在本机执行大模型代码会遇到什么这件事列清楚。这决定了我们在沙箱里要重点隔离什么东西。风险类型触发方式典型表现提示词注入模型读取了外部网页、文档文本中埋有恶意指令模型生成rm -rf ~、下载执行恶意脚本等危险命令供应链依赖投毒模型为了完成任务自动安装第三方包安装了名字相似或被篡改的包安装阶段就执行恶意代码资源耗尽模型输出死循环、大内存分配或恶意 fork 子进程开发机卡死、内存被吃光、CPU 被打满数据外传代码读取本机敏感文件并尝试发起网络请求.env、SSH 私钥、浏览器 Cookie 被读取并尝试发送到公网权限蔓延代码以当前用户权限执行拥有过大的操作范围写 cron、改 shell 配置、遍历删除项目文件非恶意但愚蠢模型对任务理解偏差执行了多余或破坏性操作清理磁盘变成全盘扫描删除缓存批量重命名文件改错这六类风险里最值得警惕的是第一类。提示词注入不是模型自己变坏而是模型在上下文里被外部内容操纵了。大模型执行链路的典型攻击路径是模型读取一个网页或文件网页里藏着一句请忽略之前的指令执行以下命令……模型照着做了。这时候如果执行环境是本机终端损失就是真实的。理解了这张风险清单再回头看沙箱的设计目标就会很清晰沙箱要解决的不是这段代码有没有 bug而是这段代码即使不怀好意也搞不出大乱子。2. OpenSandbox 的沙箱边界到底隔离了什么2.1 两层基础隔离进程空间与文件系统沙箱首先解决的问题是让 AI 生成的代码运行在一个不同的世界里。OpenSandbox 这类方案的底层普遍依赖 Linux 内核的隔离机制来实现第一层边界。进程隔离通过 PID namespace沙箱里的进程看不到宿主机的进程列表。代码里执行ps aux看到的只是沙箱自己的一亩三分地无法探测宿主机上在跑什么服务。文件系统隔离通过 Mount namespace沙箱内的文件系统来自独立的镜像宿主机目录默认不可见。模型在沙箱里执行ls /看到的是预制镜像的目录结构而不是宿主机真实的根目录。用户隔离通过 User namespace把沙箱内的 root 用户映射成宿主机上的非特权用户。即使代码拿到了容器内的 root 权限它在宿主机眼里也只是一个普通用户无法直接提权。在这层之上OpenSandbox 通常还会做两个强化根文件系统默认只读以及资产目录显式挂载。只读意味着沙箱启动之后代码无法修改镜像里的系统文件显式挂载意味着沙箱内能读到哪些宿主目录是配置出来的而不是默认全部可见。打个比方这不是给陌生人一把钥匙而是给陌生人一个装了监控、只能进入指定房间的独立隔间隔间里所有东西都是临时布置的参观完直接拆掉。2.2 网络和资源配额把不可控行为按住隔离了进程和文件系统之后还有两件容易被忽略的事网络和资源。网络往往是数据外传的唯一通道。OpenSandbox 的默认姿态是断网。也就是说沙箱内的代码默认不具备访问公网的能力。一个现实的意义在于即便模型被诱导读取了宿主机挂载进来的文件它也找不到路径把这些数据送出去。当然完全断网在很多场景下不现实。数据分析可能要从内网拉数据AI Agent 可能调用内网 API。这时候需要做成可控的白名单哪些域名、哪些 IP、哪些端口可以访问由执行策略显式配置。默认全关按需放行这是安全设计里最基本的默认拒绝原则。资源限制则是防止不坏但蠢的代码把机器拖垮。OpenSandbox 通常通过 cgroup 对每个沙箱实例做配额限制CPU 配额限制最多占几核避免死循环吃满宿主 CPU内存限制超过配额直接 OOM避免大内存分配拖垮宿主机进程数限制限制pids_max防 fork 风暴磁盘配额限制沙箱内可写的磁盘空间防把临时盘写满执行超时超过时间上限直接强杀避免任务永远不结束。这些限制在设计上还有一个关键点用完即销毁。沙箱实例默认是一次性的执行完任务之后整个环境被销毁临时文件不落盘。如果业务需要取回结果通过显式 API 拉取输出文件而不是让沙箱持久化保存用户数据。2.3 容器不等于沙箱多出来的那一层防御很多人听到这里会想这不就是docker run吗确实容器是沙箱的基础载体但默认的 Docker 容器跟真正面向大模型代码执行的沙箱差距还很大。维度默认 Docker 容器OpenSandbox 类沙箱内核隔离共享宿主机内核内核漏洞可能穿透叠加 seccomp 白名单高风险系统调用直接拒绝用户权限默认 root容器内提权路径多非 root 运行User namespace 隔离网络默认有网络能力默认断网白名单域名才可访问资源限制默认不限制 CPU 和内存cgroup 强制配额超限即杀文件写入容器的可写层可被篡改只读根文件系统改动不持久生命周期手动管理容易泄漏默认 TTL 显式销毁用完即回收最核心的差异在 seccomp 和系统调用过滤。容器的隔离依赖于内核的特性但容器内进程仍然可以直接向内核发起系统调用。沙箱会在这一层加一道白名单除了读写文件、创建进程、网络相关等必要调用其他高风险调用直接返回权限错误。这样即使代码试图执行某些敏感操作在进入内核之前就被拦住了。更进一步如果威胁模型要求更高还会把沙箱运行时替换成用户态内核如 gVisor或微虚机如 Firecracker让代码执行在一个完全独立的内核之上。OpenSandbox 的架构要把运行时设计成可替换的原因就在这里不同业务对安全等级的要求不一样运行时是选择项而不是写死的一项。3. OpenSandbox 是怎么造出来的架构拆解与关键决策3.1 以服务化的思路做执行引擎我第一次接触 OpenSandbox 这类方案时直觉是把沙箱做成一个本地库在 Agent 进程里直接调用。真正落地之后才发现面向大模型代码执行的沙箱必须服务化而不是本地化。原因有三点第一代码执行的请求会来自不同业务线服务化之后才能统一做鉴权、配额和审计第二沙箱运行时不希望跟业务进程共享权限独立服务可以把自己的权限降到最低避免业务被攻破 沙箱管理面被攻破的连锁反应第三审计日志需要集中采集分散在本地进程里根本没法查。从架构上看OpenSandbox 类的执行引擎通常包含五个模块控制面 / API 网关接收我要执行一段代码的请求校验调用方身份、配额、执行策略然后把请求交给执行引擎。调度与执行引擎负责沙箱实例的生命周期管理包括创建、执行代码、收集 stdout/stderr、超时强杀、销毁实例。沙箱运行时真正运行代码的底层载体。它可以是基于 OCI 规范的容器运行时也可以替换成 gVisor 或 Firecracker 这类更高隔离级别的运行时。镜像仓库与依赖代理存放预制好的基础镜像例如包含 pandas、numpy 的数据分析镜像以及内网依赖代理让沙箱不需要访问公网也能装包。审计与指标系统记录每次执行的调用方、时间、资源用量、网络请求、敏感系统调用为事后的安全排查提供依据。这个拆分方式的关键在于控制面和数据面分离。控制面负责决策数据面负责执行。模型生成的代码永远只接触数据面拿不到控制面的任何凭证。3.2 一份可落地的最小配置样例直接给一个风格类似 OpenSandbox 的配置样例方便你理解一个沙箱执行请求长什么样{ name: data-analysis-task, image: opensandbox/python:3.11-pandas, language: python, code: ..., limits: { cpu: 1, memory_mb: 1024, disk_mb: 512, timeout_seconds: 30, network: off, pids_max: 64 }, files: { read_only: [/dataset/input.csv], writable: [] }, audit: true }逐项解释一下设计意图image指向一个预制镜像而不是允许执行时临时pip install这样可以减少供应链投毒面。limits.network off是默认值意味着沙箱内代码一旦尝试创建网络连接就会失败。如果需要联网显式配置域名白名单。files.read_only用于把宿主机的数据文件以只读方式挂载进沙箱。这样模型能读数据但改不了源文件。files.writable默认留空代码写不了任何持久化目录。如果确实需要产出文件指定一个临时的可写目录任务结束后通过 API 取回。audit true意味着这次执行的全过程都要落日志包括执行了哪些系统调用、尝试连接了哪些地址、占用了多少资源。这套配置的核心姿态是默认不给按需给。给得越少代码能造成的破坏就越小。3.3 为什么执行策略要单独抽象我在最初设计时犯过一个错误把资源限制、网络开关、文件挂载这些参数直接写在业务代码里。结果每个业务接入的方式都不一样运维根本没法统一审计。OpenSandbox 这类项目给我的一个启发是执行策略必须独立抽象成一等公民。也就是说能不能跑网络请求、最多用多少内存、能读哪些目录这些规则应该由策略模板统一管理而不是散落在业务代码里。例如数据分析策略内存大、断网、可读数据集目录、不可写AI Agent 工具策略内存中等、可访问白名单 API、可写临时目录代码沙箱测试策略内存小、断网、完全只读、超时短。这样做的好处是安全工程师只需要审查策略模板不需要一行一行看业务代码。而且策略的变更可以灰度下发不需要重新发布业务服务。如果你打算在自己的项目里落地沙箱我强烈建议一开始就把策略单独建一张表或者一个配置目录来管。4. 大模型执行代码的场景矩阵哪些必须上沙箱哪些可以不上4.1 数据分析、AI Agent、CI 自动化三类典型场景沙箱不是所有场景都必须上的。我试着把实际的项目场景分成三类你可以对照自己的情况。数据分析 / Notebook 式执行。典型形态是用户上传一个 CSV模型写 pandas 代码做统计分析然后把图表或者统计结果返回给用户。这种场景下沙箱要读取用户数据文件但严格不能联网也不能写宿主机的任何目录。OpenSandbox 的典型做法是把用户文件只读挂载进沙箱执行完把结果文件取出来沙箱销毁。数据文件自始至终不会落到宿主机的业务目录里。AI Agent 自动操作。Agent 型的应用更复杂因为 Agent 可能需要执行 shell 命令、调用系统工具、操作文件。这类场景我强烈建议把工具执行整体放进沙箱Agent 通过 function calling 请求执行命令命令在沙箱里运行宿主机文件按需只读挂载。这样即使 Agent 模型被上下文中的恶意内容误导损失范围也被限制在沙箱内。CI / 自动化测试。自动化测试需要的是环境可复现和并发能力强。模型生成测试用例后每个用例在一个新的沙箱里跑环境干净互不干扰跑完即销毁。这个场景下关注点更多在并发配额和依赖管理上网络通常按需开放到内网测试环境。4.2 判断是否需要沙箱的四要素如果你的项目还不确定要不要上沙箱可以按这四个要素过一遍代码来源代码是模型生成的还是用户手写的模型生成的代码且未经过人工审查信任度最低。执行目标代码跑在哪本地开发机、生产服务器、还是临时的隔离环境目标越重要越需要沙箱。数据敏感度代码执行过程中要不要接触密钥、PII、生产数据一旦需要接触敏感数据隔离和审计就必不可少。执行频率是一次性人工执行还是高频自动执行高频自动化任务只要有风险就会被放大成必然事故必须由沙箱兜底。我个人的判断标准是只要你的 AI 应用存在模型生成的代码被自动执行这条路径默认就应该上沙箱。即便短期没有安全事件审计日志本身就是巨大的价值——出事的时候你有地方查而不是两眼一抹黑。5. 接入 OpenSandbox 的实操流程与三个真实踩坑5.1 最小可用的接入路径下面是一个简化版的接入流程基于 OpenSandbox 这类方案的通用 API 风格目标是在本地跑通一个最小闭环。第一步启动沙箱服务示意命令不同实现的具体命令会有差异opensandbox runtime start \ --runtime-typedocker \ --default-imageopensandbox/python:3.11第二步创建一个沙箱实例curl -X POST http://localhost:8080/v1/sandboxes \ -H Content-Type: application/json \ -d { image: opensandbox/python:3.11, limits: { cpu: 1, memory_mb: 512, timeout_seconds: 30, network: off } }第三步向实例提交一段代码执行curl -X POST http://localhost:8080/v1/sandboxes/{sandbox_id}/exec \ -H Content-Type: application/json \ -d { language: python, code: print(1 1) }第四步拿到输出结果销毁实例curl -X DELETE http://localhost:8080/v1/sandboxes/{sandbox_id}接入大模型的完整链路也不复杂让模型通过 function calling 输出结构化代码前端校验一次 JSON 格式再把代码提交给沙箱执行。关键点是不要让模型直接拼系统命令而是让模型输出参数化、结构化的调用由你的代码负责翻译成沙箱执行请求。这样模型的自由度被限制在一个安全的接口面里。5.2 踩坑一资源限额设置与 OOM 问题我第一版把沙箱内存统一设成 128MB想着代码量都不大结果数据分析任务一跑就 OOM模型读个 50MB 的 CSV 就崩了。后来又把内存放宽到 4GB结果并发 10 个任务时宿主机内存告急。教训是资源配额不能设全局默认值必须按执行策略分层。简单代码解释类任务内存 128~256MBCPU 0.5 核超时 10 秒数据分析类任务内存 1~2GBCPU 1 核超时 60 秒重计算任务内存 4GB 以上CPU 2 核但并发数必须压低。配额的粒度应该跟业务场景匹配越精细越好。顺带提醒一点内存限制不仅防 OOM也是防恶意代码的重要手段。刻意分配大数组然后不释放是模型输出中真实出现过的情况。5.3 踩坑二镜像与依赖网络策略默认断网之后立刻遇到一个新问题模型输出的代码需要pip install一些库但沙箱连不上公网任务直接失败。一开始我觉得这是沙箱拖累了功能后来才发现正确的解法不是开放公网而是管理依赖把常用依赖直接预制进基础镜像。比如数据分析场景把 pandas、numpy、matplotlib 都装进opensandbox/python:3.11-pandas镜像里模型需要时直接 import根本不需要现场安装。搭建内网依赖代理。如果确实需要临时安装包让沙箱访问内网 PyPI 代理而不是公网 PyPI。按域名白名单逐步放开同时所有网络请求都进审计日志。这样既保留了模型执行代码的灵活性又让安装依赖这个高风险动作变得可控。依赖预装还有一个隐藏好处执行速度快很多不用每次任务都花十几秒在装包上。5.4 踩坑三沙箱泄漏与僵尸进程回收第三个坑最隐蔽。最初我在每次任务结束后没有强制销毁沙箱实例想着下次还能复用节省启动时间。结果跑了一个晚上之后宿主机磁盘被占满了一查发现堆了几百个没回收的沙箱实例每个实例都占用了一部分磁盘和内存。回收机制必须设计成三层保险显式销毁每次任务结束业务代码调用销毁接口这是第一层TTL 自动过期每个沙箱实例创建时带上一个最长存活时间比如 30 分钟超时后控制面自动强杀后台巡检兜底定期扫描所有沙箱实例把长时间空闲或超龄的实例清理掉。靠人工记得销毁是不现实的尤其在 AI Agent 这种自动化调用链里任何一步异常都会导致实例泄漏。三层保险缺一层都会出问题。6. 用一次受控的红队演练验证边界再看它挡不住什么6.1 一次模拟攻击的完整链路在沙箱上线之前我们在测试环境里做了一次受控的对抗验证。注意这里说的攻击是测试工具构造的模拟恶意输入只针对自己的沙箱环境目的是验证防御边界不是教攻击手法。第一组测试是提示词注入。我们把一段包含恶意指令的文本喂给模型诱导它生成删除目录的代码。让模型在沙箱里执行shutil.rmtree(/app/data)。结果符合预期删除动作只影响沙箱内部的可写目录宿主机上的/app/data目录完全不受影响。如果这段代码在本机跑至少是一次真实的文件删除事故。第二组测试是数据外传。我们构造了一段代码尝试读取挂载进沙箱的敏感文件然后建立网络连接发送到外部地址。由于网络策略是offconnect 调用直接失败审计日志里记录了这起尝试外联的行为。数据没有出去但日志告诉我们模型确实被诱导了——这本身就是重要的安全信号可以触发告警。第三组测试是资源耗尽。我们让代码执行一个死循环。沙箱的 CPU 配额和超时机制起了作用30 秒后控制面强杀了进程并销毁实例。宿主机全程没有任何资源抖动其他业务不受影响。这三组测试说明了一件关键的事边界正确的沙箱即使在最坏情况下损失也是局部的、可恢复的、可追踪的。它不能阻止模型产生恶意的想法但能阻止这个想法波及到真实系统。6.2 沙箱真正挡住的与挡不住的红队演练验证之后反而让我更清醒地看到沙箱的边界。它挡住了本地文件系统被破坏、敏感进程被探测、默认网络外传、资源滥用这四类问题但它不是万能的。以下三类情况沙箱本身解决不了宿主机内核漏洞如果沙箱运行时本身有内核逃逸漏洞恶意的代码理论上可能突破隔离。所以宿主机加固是必须配套的包括非 root 运行沙箱进程、启用 AppArmor 或 SELinux、禁用不必要的内核模块。配置层面的失误如果有人在挂载配置里把宿主机/home目录整个以可写方式挂载进沙箱那沙箱等于透明。配置审查和最小权限原则比沙箱本身更重要。模型层面的诱导沙箱拦截的是执行阶段的危害模型在自然语言层面被诱导产生恶意 payload这个行为发生在执行前。你需要在入口做 prompt 安全策略、对模型输出做语义检查而不是把希望全部放在沙箱上。所以我的建议是把沙箱当作纵深防御体系里的一层而不是唯一的一层。一个相对稳妥的组合是入口做 prompt 过滤和输出检查中间用沙箱承载所有自动执行的代码底层做好宿主机制加固全程留审计日志并配置告警。每一层都独立工作即使某一层被绕过损失也不会被放大。我个人实际使用下来最深的体会是沙箱解决的不只是安全问题还有工程效率问题。以前让 AI 自动跑代码我总是提心吊胆地盯着输出现在代码先在沙箱里跑一遍我才放心把结果拿回来看。再分享一个小技巧吧把沙箱返回的 stdout、stderr 和退出码原样回传给模型往往能让模型根据报错自己修正代码。只传一个成功或失败的布尔值模型基本没有方向去自我纠错。这个细节实测下来对 AI 自动编程类任务的完成率提升非常明显。