WorkBuddy_AI Harness架构解析8_执行沙箱
执行沙箱摘要在现代软件系统尤其是 AI Agent、CI/CD 平台、在线代码评测系统以及低代码平台中让不可信代码或命令安全运行是一个绕不开的核心问题。本文以Harness为例深入剖析一个执行沙箱Execution Sandbox应该如何设计如何在授权目录范围内安全地执行文件读写、命令运行与代码执行。文章将从威胁模型、目录授权模型、路径规范化、文件读写隔离、命令执行隔离、代码执行沙箱、资源限制与审计等角度给出完整的原理讲解与可运行的代码示例帮助读者构建一个可落地的最小可用沙箱实现。引言为什么需要执行沙箱设想以下几个典型场景AI Agent / Copilot用户让 Agent “帮我写个脚本跑一下”Agent 需要真的在机器上执行 Python、Shell 代码甚至读写文件。在线代码评测OJ成千上万个陌生用户提交的代码要在同一台机器上运行。CI/CD Runner流水线中的构建脚本来自不同团队可能包含恶意或误操作命令。低代码/自动化平台如 Harness Pipeline用户通过可视化方式配置脚本步骤平台负责执行。这些场景的共同点是执行者不可信但执行环境是共享的。如果没有任何隔离一段rm -rf /或open(/etc/passwd)就足以让整个系统崩溃或数据泄露。什么是执行沙箱执行沙箱是一种受控的执行环境它对被执行的代码/命令施加约束典型约束包括文件系统约束只能访问授权目录如/workspace不能越界读写。命令约束只能执行白名单命令禁止危险操作。网络约束禁止或限制外联。资源约束限制 CPU、内存、磁盘、进程数、执行时间。审计约束所有操作可记录、可追溯。本文将以Harness 风格的沙箱设计为线索从零构建一个授权目录内安全执行的模型并给出 Python 实现示例。一、威胁模型与设计目标在写代码之前我们必须先明确要防什么。1.1 威胁模型威胁类型示例后果路径穿越open(../../etc/passwd)读取敏感文件符号链接逃逸授权目录内有指向/etc的软链绕过目录限制命令注入os.system(ls; rm -rf /)任意命令执行资源耗尽while True: pass拖垮主机网络外联下载恶意载荷数据外泄权限提升通过sudo或 setuid 程序获取 root1.2 设计目标一个合格的执行沙箱应该满足最小权限只给执行者完成任务所需的最小权限。默认拒绝未明确允许的操作一律拒绝。纵深防御即使一层被绕过还有下一层兜底。可观测所有关键操作有日志。可恢复执行失败或超时后能干净地回收资源。注意语言层面的沙箱如 Python 的exec永远不是安全边界。真正的安全边界来自操作系统容器、namespace、seccomp、cgroup或虚拟机。本文的代码示例用于展示逻辑层的约束生产环境必须叠加 OS 级隔离。二、授权目录模型Workspace 抽象2.1 核心思想沙箱的一切文件操作都必须**相对于一个根目录Workspace Root**进行。所有路径在真正访问前都要经过规范化 前缀校验。/workspace - 授权根目录 ├── input/ - 只读输入 ├── output/ - 可写输出 └── tmp/ - 临时目录2.2 路径规范化与校验关键点是先解析符号链接、再比较绝对路径前缀否则..和软链都会绕过检查。importosclassPathViolationError(Exception):路径越界异常passclassWorkspace:def__init__(self,root:str,writable_subdirs(output,tmp)):# 使用 realpath 解析所有符号链接得到规范化的绝对路径self.rootos.path.realpath(root)self.writable{os.path.realpath(os.path.join(self.root,d))fordinwritable_subdirs}os.makedirs(self.root,exist_okTrue)fordinself.writable:os.makedirs(d,exist_okTrue)defresolve(self,user_path:str,write:boolFalse)-str: 将用户提供的路径解析为绝对路径并校验是否在授权范围内。 :param user_path: 用户传入的相对路径 :param write: 是否为写操作写操作还需限定在可写子目录 :return: 规范化后的绝对路径 # 1. 拼接为绝对路径不允许用户传绝对路径ifos.path.isabs(user_path):raisePathViolationError(f不允许使用绝对路径:{user_path})candidateos.path.realpath(os.path.join(self.root,user_path))# 2. 前缀校验必须以 root 开头且是路径边界ifnotself._is_within(candidate,self.root):raisePathViolationError(f路径越界:{user_path}-{candidate})# 3. 写操作额外限制ifwrite:ifnotany(self._is_within(candidate,w)forwinself.writable):raisePathViolationError(f该路径不可写:{user_path})returncandidatestaticmethoddef_is_within(path:str,parent:str)-bool:判断 path 是否在 parent 目录内含自身# 加上 os.sep 防止 /workspace-evil 被误判为 /workspace 的子路径returnpathparentorpath.startswith(parentos.sep)2.3 为什么必须用realpath考虑如下攻击ln-s/etc /workspace/output/evil如果只用os.path.abspath/workspace/output/evil/passwd看起来在授权目录内但realpath会把它解析成/etc/passwd从而被前缀校验拦截。三、文件读写隔离有了Workspace抽象文件读写就变得简单可控。3.1 安全的读操作classSandboxedFS:def__init__(self,workspace:Workspace,max_read_bytes10*1024*1024):self.wsworkspace self.max_read_bytesmax_read_bytesdefread_text(self,user_path:str,encodingutf-8)-str:real_pathself.ws.resolve(user_path,writeFalse)ifnotos.path.isfile(real_path):raiseFileNotFoundError(user_path)# 防止读取超大文件导致 OOMsizeos.path.getsize(real_path)ifsizeself.max_read_bytes:raiseValueError(f文件过大:{size}bytes)withopen(real_path,r,encodingencoding)asf:returnf.read()3.2 安全的写操作写操作除了路径校验还要注意避免 TOCTOUTime-of-Check to Time-of-Use竞态。简单做法是以O_NOFOLLOW打开文件禁止跟随软链。importosclassSandboxedFS:# ... 省略 read_text ...defwrite_text(self,user_path:str,content:str,encodingutf-8):real_pathself.ws.resolve(user_path,writeTrue)# 确保父目录存在parentos.path.dirname(real_path)os.makedirs(parent,exist_okTrue)# O_NOFOLLOW: 若目标本身是软链则报错避免写到链接指向的位置flagsos.O_WRONLY|os.O_CREAT|os.O_TRUNC|os.O_NOFOLLOW fdos.open(real_path,flags,0o600)try:withos.fdopen(fd,w,encodingencoding)asf:f.write(content)exceptException:os.close(fd)raise3.3 使用示例wsWorkspace(/tmp/sandbox_root)fsSandboxedFS(ws)fs.write_text(output/hello.txt,hello sandbox)print(fs.read_text(output/hello.txt))# hello sandbox# 以下调用会被拒绝try:fs.read_text(../../etc/passwd)exceptPathViolationErrorase:print(拦截:,e)try:fs.write_text(input/x.txt,data)# input 不在可写列表exceptPathViolationErrorase:print(拦截:,e)四、命令执行隔离4.1 危险做法 vs 安全做法# ❌ 危险shellTrue 会导致命令注入os.system(fcat{user_input})# ❌ 危险拼接字符串subprocess.run(fls{user_input},shellTrue)# ✅ 安全参数列表 shellFalsesubprocess.run([ls,user_input],shellFalse)4.2 白名单命令 参数校验importsubprocessimportshlex ALLOWED_COMMANDS{ls,cat,echo,python3,grep}classCommandSandbox:def__init__(self,workspace:Workspace,timeout10):self.wsworkspace self.timeouttimeoutdefrun(self,argv:list,cwd_rel:str.,env:dictNone):ifnotargv:raiseValueError(空命令)programos.path.basename(argv[0])ifprogramnotinALLOWED_COMMANDS:raisePermissionError(f命令未授权:{program})# cwd 也必须在 workspace 内cwdself.ws.resolve(cwd_rel,writeFalse)# 构造最小环境变量避免泄露宿主机信息safe_env{PATH:/usr/bin:/bin,HOME:cwd,LANG:C.UTF-8,}ifenv:safe_env.update(env)try:resultsubprocess.run(argv,cwdcwd,envsafe_env,shellFalse,# 关键禁用 shellcapture_outputTrue,textTrue,timeoutself.timeout,# 关键超时控制checkFalse,)returnresult.returncode,result.stdout,result.stderrexceptsubprocess.TimeoutExpired:return-1,,执行超时4.3 使用示例cmdCommandSandbox(ws)code,out,errcmd.run([echo,hello],cwd_reloutput)print(code,out)# 尝试注入会被拒绝try:cmd.run([rm,-rf,/],cwd_reloutput)exceptPermissionErrorase:print(拦截:,e)4.4 仍需注意的问题参数中的路径cat ../../etc/passwd仍然能读文件所以命令参数中的路径也要过一遍Workspace.resolve或干脆禁止用户传路径参数。命令本身的逃逸python3允许执行任意代码因此它必须交给下一节的代码执行沙箱处理。真正的隔离生产环境应结合chroot、bubblewrap、firejail、Docker、seccomp等。五、代码执行沙箱5.1 为什么不能只靠execPython 的exec/eval在同一进程内运行攻击者可以通过__builtins__、__import__、().__class__.__bases__等逃逸。同进程内没有真正的沙箱。安全做法子进程 受限环境 资源限制。5.2 基于子进程的代码执行importsubprocessimporttempfileimportosclassCodeSandbox:def__init__(self,workspace:Workspace,timeout5,max_memory_mb256):self.wsworkspace self.timeouttimeout self.max_memory_mbmax_memory_mbdefrun_python(self,code:str,stdin:str)-dict:# 将用户代码写入 workspace 内的临时文件script_reltmp/user_script.pyreal_scriptself.ws.resolve(script_rel,writeTrue)os.makedirs(os.path.dirname(real_script),exist_okTrue)withopen(real_script,w,encodingutf-8)asf:f.write(code)# 使用 prlimitLinux限制内存与进程数cmd[prlimit,f--as{self.max_memory_mb*1024*1024},# 地址空间--nproc32,# 进程数--nofile64,# 文件描述符python3,-I,-S,real_script,# -I: 隔离模式]try:resultsubprocess.run(cmd,cwdself.ws.root,inputstdin,capture_outputTrue,textTrue,timeoutself.timeout,shellFalse,env{PATH:/usr/bin:/bin,HOME:self.ws.root},)return{returncode:result.returncode,stdout:result.stdout,stderr:result.stderr,}exceptsubprocess.TimeoutExpired:return{returncode:-1,stdout:,stderr:执行超时}5.3 使用示例code_sbCodeSandbox(ws,timeout3,max_memory_mb128)# 正常代码print(code_sb.run_python(print(hello from sandbox)))# 死循环会被超时杀掉print(code_sb.run_python(while True: pass))# 内存炸弹会被 prlimit 拦截print(code_sb.run_python(x a * (10**9)))5.4 关键参数说明参数作用python3 -I隔离模式忽略环境变量、不加载用户 site-packagespython3 -S不加载 site 模块减少攻击面prlimit --as限制虚拟内存防止内存炸弹prlimit --nproc限制进程数防止 fork 炸弹timeout防止死循环cwdworkspace.root保证相对路径落在授权目录5.5 进一步加固生产建议# 使用 bubblewrap 做文件系统隔离bwrap\--ro-bind /usr /usr\--ro-bind /lib /lib\--bind/tmp/sandbox_root /workspace\--chdir/workspace\--unshare-net\--unshare-pid\--die-with-parent\python3 /workspace/tmp/user_script.pybwrap会创建新的 mount namespace让沙箱内根本看不到宿主机其他目录这比路径校验强得多。六、资源限制与审计6.1 资源限制清单资源手段说明CPU 时间resource.setrlimit(RLIMIT_CPU)/ cgroup防死循环内存RLIMIT_AS/ cgroup防内存炸弹进程数RLIMIT_NPROC防 fork 炸弹文件大小RLIMIT_FSIZE防磁盘写满文件描述符RLIMIT_NOFILE防耗尽 fd网络net namespace / iptables防外联6.2 审计日志每次执行都应该记录谁、什么时候、在哪个目录、执行了什么、结果如何。importjsonimporttimeimportlogging loggerlogging.getLogger(sandbox.audit)defaudit(event:str,**kwargs):record{ts:time.time(),event:event,**kwargs,}logger.info(json.dumps(record,ensure_asciiFalse))# 使用audit(file_read,pathoutput/hello.txt,useralice,size12)audit(cmd_run,argv[ls,-l],cwdoutput,returncode0)audit(code_run,langpython,duration0.42,returncode0)审计日志的价值在于事后追溯出现安全事件时能定位。行为分析发现异常模式如频繁越界尝试。合规要求很多行业标准强制要求操作留痕。七、把它们组装起来下面是一个完整的、可运行的沙箱示例把所有组件串起来。importosimportsubprocessimportlogging logging.basicConfig(levellogging.INFO,format%(message)s)classExecutionSandbox:

相关新闻

PHP音乐分享系统源码深度解析:从本地部署到音频全栈处理

PHP音乐分享系统源码深度解析:从本地部署到音频全栈处理

简介:这是一套基于ASP.NET开发的原创音乐分享平台完整源码,面向Web开发者与创业团队,用于快速搭建在线音乐社区、原创音乐推广站或伴奏资源分发网站。系统采用Web 2.0架构,集成用户中心、音乐上传发布、日志相册、资讯管理及原创音…

2026/10/15 3:34:41 阅读更多 →
在浏览器中零代码训练机器学习模型:Teachable Machine v1 开发指南与源码剖析

在浏览器中零代码训练机器学习模型:Teachable Machine v1 开发指南与源码剖析

AI 应用机器学习计算机视觉前端 【免费下载链接】teachable-machine-v1 Explore how machine learning works, live in the browser. No coding required. 项目地址: https://gitcode.com/gh_mirrors/te/teachable-machine-v1 点击查看 免费下载 本篇技术指南以仓库…

2026/10/15 3:34:41 阅读更多 →
仓颉 std.net SocketAddress 类详解:协议无关 Socket 地址的抽象基类与实战用法

仓颉 std.net SocketAddress 类详解:协议无关 Socket 地址的抽象基类与实战用法

人工智能AI 技能文档开发工具 【免费下载链接】DocAgent 项目地址: https://gitcode.com/Cangjie-SIG/DocAgent 点击查看 免费下载 SocketAddress 是仓颉标准库 std.net 包中表示协议无关 Socket 地址的抽象基类,它为 TCP、UDP、Unix Domain Socket 等所…

2026/10/15 3:34:41 阅读更多 →

最新新闻

IDEA 2025.3 新功能实战:多光标、调试数据流与临时文件提升编码效率

IDEA 2025.3 新功能实战:多光标、调试数据流与临时文件提升编码效率

IDEA 2025.3 的更新日志刚出来那会儿,我真没当回事,毕竟每个大版本都爱说“更快、更智能、更好用”。直到上周把开发环境从旧版本升上来,试了一圈新功能,才意识到这波“骚操作”是真的多,甚至有点跟不上了。这篇文章不…

2026/10/15 4:27:50 阅读更多 →
贝叶斯网络学习指南:从结构、参数到推断的完整实践

贝叶斯网络学习指南:从结构、参数到推断的完整实践

简介:《贝叶斯网络学习指南》是一份面向机器学习、概率建模与数据科学初学者的PDF电子书,系统覆盖从概率论基础到贝叶斯网络建模、推理与参数学习的完整知识链。全书从概率函数与空间、条件概率与独立性、贝叶斯定理、随机变量及联合概率分布讲起&#x…

2026/10/15 4:27:50 阅读更多 →
STM32F072C8T6裸机开发实战:从寄存器到USB/CAN外设

STM32F072C8T6裸机开发实战:从寄存器到USB/CAN外设

简介:这份资料面向嵌入式初学者与有一定经验的开发者,围绕意法半导体STM32F072C8T6(ARM Cortex-M0内核、48MHz主频、32KB闪存与2KB RAM)学习板展开,帮助读者快速掌握该芯片的初始化配置、中断处理、内存管理及驱动编写…

2026/10/15 4:27:50 阅读更多 →
C/C++不同位数数据互转的进制转换实现与避坑指南

C/C++不同位数数据互转的进制转换实现与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 4:27:50 阅读更多 →
NC65销售订单批量导入全攻略:从模板准备到成功入库的避坑指南

NC65销售订单批量导入全攻略:从模板准备到成功入库的避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 4:27:50 阅读更多 →
Linux下C/C++动静态库制作与使用实战指南

Linux下C/C++动静态库制作与使用实战指南

长期以来在Linux下做C/C开发,动静态库始终是绕不开的一个核心技能。无论是自己封装模块、分发二进制产物,还是处理第三方依赖,理解库的制作与使用直接决定了你在编译链接这块能不能少掉头发。我见过不少开发者写业务逻辑很熟练,一…

2026/10/15 4:26:50 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →