Claude Code + CubeSandbox 集成实战:用 PreToolUse Hook 实现透明 MicroVM 隔离的 Bash 执行
2026/9/16 22:14:29 网站建设 项目流程

Claude Code + CubeSandbox 集成实战:用 PreToolUse Hook 实现透明 MicroVM 隔离的 Bash 执行

【免费下载链接】CubeSandboxInstant, Concurrent, Secure & Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox

导读

本文基于 CubeSandbox 仓库中的 claude-code-integration 示例,讲解如何让 Claude Code 继续运行在宿主机上,却把它发出的每一条 Bash 命令透明转发进隔离的 CubeSandbox MicroVM 执行:文件编辑留在本地、shell 命令跑在独立内核/文件系统/网络里,模型完全感知不到沙箱层,也不需要改变任何使用方式。读完本文,你将掌握 hook 的安装配置、改写与执行两个核心脚本的工作原理、按会话复用沙箱与 shell 状态持久化的机制、宿主项目只读挂载的约束,以及完整的安全设计要点和排错方法。

Claude Code(宿主机) ├── Read / Write / Edit ─────────────► 宿主机项目文件 │ └── Bash ──► PreToolUse hook ──► cubesandbox_exec ──► CubeAPI ──► MicroVM (cubesandbox_rewrite.py) (:3000) └─ 按会话复用

为什么用 PreToolUse Hook 而不是 MCP / SDK

基于 MCP 或 SDK 的沙箱方案依赖 agent主动选择沙箱工具;一条普通的Bash调用仍会落到宿主机上。PreToolUsehook 堵住了这个缺口:它在工具调用执行之前拦截工具调用本身,因此对 Bash 而言隔离是透明且完整的——没有任何命令能绕过它。

特性行为
透明模型发起普通的Bash调用,由 hook 改写。无需改 prompt 或工具。
按会话复用沙箱每个 Claude Codesession_id复用一个 MicroVM,同一会话的命令共享状态。
shell 状态延续同一会话内,cd和导出的环境变量在多次 Bash 调用间保留。
只读挂载宿主项目会话首次调用可把项目按相同路径只读挂载,沙箱命令可读取(不可修改)宿主文件。
fail-closed若无法安全改写,hook 以非零退出阻断命令,而不是放它到宿主机执行。
防注入原命令作为单个shlex引用参数传入,shell 元字符和换行都无法越出到宿主机。
无条件改写每条 Bash 调用都会被改写;已包裹的执行器调用若再次经过 hook,只会在沙箱内失败,绝不会落到宿主机。
自动批准hook 对改写后的 Bash 调用返回permissionDecision: "allow",Claude Code 的逐命令确认提示被抑制;请相应使用--permission-mode/ hooks 策略。

从源码结构看,cubesandbox_rewrite.py对 payload 的改写逻辑在 examples/claude-code-integration/hooks/cubesandbox_rewrite.py 中实现,cubesandbox_exec.py承担执行与状态管理,二者通过install.sh注册进~/.claude/settings.json

前置条件

  • 运行中的 CubeSandbox 部署(CubeAPI 可达,如http://127.0.0.1:3000
  • 运行 Claude Code 的宿主机上有 Python 3.9+
  • 一个用于创建沙箱的 CubeSandbox 模板(cubemastercli tpl list可列出可用模板)

快速开始

1 —— 安装依赖并配置

python3 -m pip install -r requirements.txt cp .env.example .env # 编辑 .env:设置 CUBE_API_URL 和 CUBE_TEMPLATE_ID

.env中的核心配置项及其说明(见 .env.example):

变量说明默认值
CUBE_API_URLCubeAPI 的 base URL,SDK 通过它访问控制面http://127.0.0.1:3000
CUBE_TEMPLATE_IDhook 创建沙箱使用的模板 ID(必填,缺失时执行器报CUBE_TEMPLATE_ID is not set
CUBE_SANDBOX_USER沙箱内执行命令的用户root
CUBE_SANDBOX_TIMEOUT沙箱存活 TTL(秒)1800
CUBE_EXEC_TIMEOUT单条命令执行超时(秒)120
CUBE_HOOK_STATE_DIR会话→沙箱映射状态目录~/.cache/cubesandbox-hook
CUBE_PROXY_NODE_IP/CUBE_PROXY_PORT_HTTP/CUBE_SANDBOX_DOMAIN远程/自建部署下的 envd 路由覆盖,可选

其中CUBE_SANDBOX_USERCUBE_SANDBOX_TIMEOUTCUBE_EXEC_TIMEOUTCUBE_HOOK_STATE_DIR在 cubesandbox_exec.py 中通过环境变量读取,且CUBE_SANDBOX_TIMEOUT/CUBE_EXEC_TIMEOUT必须是正整数/正有限浮点数,否则执行器以BootstrapError拒绝启动。

2 —— 安装 hook

cd hooks ./install.sh

安装脚本(install.sh)会:

  • cubesandbox_rewrite.pycubesandbox_exec.py复制到~/.claude/hooks/(权限 0755);
  • 将 hook 注册进~/.claude/settings.jsonhooks.PreToolUse下的Bashmatcher 分组,不覆盖你其它设置;
  • 只把../.env里白名单内的CUBE_*值复制进 hook 配置cubesandbox.env(权限 0600),provider API key(如ANTHROPIC_AUTH_TOKENOPENAI_API_KEY不会被写入 hook 配置。

重启 Claude Code 以加载 hook。

3 —— 照常使用 Claude Code

直接用 Claude Code。它发出的每条 Bash 命令现在都在 MicroVM 内执行:

> 执行 `uname -a && whoami`,告诉我它在哪运行

命令在沙箱里运行(不同内核、沙箱用户),而 Claude Code 的文件编辑仍在你的宿主机上。

工作原理

1. 改写阶段:cubesandbox_rewrite.py

hook 接收PreToolUseJSON payload。对Bash调用,它把tool_input.command改写为:

<python> <hooks>/cubesandbox_exec.py --session=<id> --mount=<cwd> --timeout=<秒> -- <原命令>

并通过updatedInput返回。关键实现细节(cubesandbox_rewrite.py):

  • 原命令是单个引用参数:经shlex.join引用,里面的 shell 元字符和换行无法在宿主机执行,--分隔符保证以-开头的命令不会被误解析为执行器参数;
  • session_id 复用:从 payload 的session_id字段取值,缺失时回退到default
  • 超时透传有校验:仅当timeout是正有限数值且不超过MAX_TIMEOUT_MS(1 小时)时才追加--timeout,非法值(0、负数、naninf、字符串等)一律不转发;
  • fail-closed:payload 不是 JSON 对象、tool_name缺失或tool_input非法时,hook 打印错误并以退出码 2阻断命令,绝不放行到宿主机;
  • 非 Bash 工具原样放行Read/Write/Edit等返回None,不产生任何改动;
  • 无条件改写:即使命令文本本身就是执行器调用(例如从 transcript 粘贴回来),也照常包裹——它只会在沙箱内失败(沙箱内不存在宿主 hook 路径),绝不会落到宿主机。

这些行为在 test_cubesandbox_rewrite.py 中有系统化验证,包括控制操作符保持为单个引用参数、session_id/cwd中的注入字符被隔离、非法 payload 被拒绝、超时边界校验,以及“仅命名执行器脚本也必须被包裹”等测试用例。

2. 执行阶段:cubesandbox_exec.py

执行器(cubesandbox_exec.py)按session_id复用一个沙箱:

  • 会话映射状态存放在~/.cache/cubesandbox-hook/(可用CUBE_HOOK_STATE_DIR覆盖),session_id 经 SHA-256 哈希作为文件名,状态目录权限 0700、状态文件权限 0600,并用每会话文件锁(fcntl.flock)保护并发;
  • 重用或新建:优先通过Sandbox.connect(sandbox_id)重连已缓存的沙箱(失败自动回退新建),首次调用时通过Sandbox.create(TEMPLATE_ID, ...)创建并记录sandbox_id与随机state_token
  • shell 状态延续:每条命令被包裹进一段状态包装脚本(_state_shell)——恢复上次持久化的 cwd 与环境变量,运行原命令,再把pwdexport -p写回状态文件。其中BASH_ENVENVLD_PRELOADPROMPT_COMMAND等会自执行恶意代码的变量被显式清洗;
  • 返回结果:命令结束后缓冲返回 stdout/stderr 与退出码(非流式),并将退出码透传给 Claude Code;
  • 串行执行:同一会话内并发的 Bash 调用经每会话锁串行执行——一次只跑一条,不并行。

沙箱 SDK 侧的支撑:SandboxConfig来自 sdk/python/cubesandbox/sandbox.py,Configapi_url/template_id等字段均可通过环境变量注入(见 sdk/python/cubesandbox/_config.py),与 hook 的.env白名单机制一一对应。

宿主项目挂载

会话的首次 Bash 调用可把 Claude Code 的项目目录按相同绝对路径只读挂载进沙箱——沙箱命令可读取宿主项目文件,但不能修改或往里写构建产物。挂载请求以host-mountmetadata 形式传入Sandbox.create(见 cubesandbox_exec.py):

[{"hostPath": "/path/to/project", "mountPath": "/path/to/project", "readOnly": true}]

注意挂载的生效条件hostPath在被调度到的 Cubelet 节点上解析,而非运行 Claude Code 的机器。因此只有当 Claude Code 与该 Cubelet 同机、或项目已经以相同绝对路径存在于每个可调度 Cubelet 上时,这种共享视图才成立。hook绝不会把本地项目上传或同步到远端部署——不要把一个仅客户端存在、在 Cubelet 上可能指向无关数据的路径加入白名单。

项目路径必须被 CubeMaster 允许:

extra_conf: allowed_host_mount_prefixes: - "/data/shared/" - "/home/you/projects/"

若挂载被拒,执行会回退到无挂载的隔离沙箱:Bash 仍然隔离,但不再与 Claude Code 宿主侧文件工具共享文件视图。回退时会打印警告:

[cubesandbox-exec] warning: read-only host mount '/xxx' was rejected (...); creating a sandbox without the mount

重置与卸载

# 丢弃某会话绑定的沙箱(下次调用重新创建) python3 ~/.claude/hooks/cubesandbox_exec.py --reset --session <session-id> # 从 ~/.claude/settings.json 移除 hook cd hooks ./install.sh --uninstall

--reset会尽力 kill 已缓存的沙箱并清空该会话的状态(即使 API 不可达,状态也会被清除,避免会话被卡死),并回收累积的锁文件;卸载则从 settings.json 移除 Bash matcher 分组、删除已安装的 hook 文件,并保留~/.cache/cubesandbox-hook中的既有状态供用户自行处置。

目录结构

claude-code-integration/ ├── hooks/ │ ├── cubesandbox_rewrite.py # PreToolUse hook:把 Bash 改写为沙箱执行 │ ├── cubesandbox_exec.py # 执行器:按会话复用 MicroVM + 状态持久化 │ └── install.sh # 幂等安装 / 卸载 ├── tests/ │ ├── conftest.py │ ├── test_cubesandbox_rewrite.py │ ├── test_cubesandbox_exec.py │ └── test_hook_install.py ├── requirements.txt ├── .env.example ├── TROUBLESHOOTING.md ├── README.md └── README_zh.md # 本文件

测试

python3 -m pip install -r requirements.txt pytest pytest tests

测试覆盖了三个层面:改写 hook 的注入防护与 fail-closed 行为(test_cubesandbox_rewrite.py)、执行器的会话复用/状态持久化/并发串行化/超时处理(test_cubesandbox_exec.py),以及安装脚本的幂等性、配置白名单与卸载还原(test_hook_install.py)。其中值得关注的安全验证包括:状态文件拒绝符号链接、BASH_ENV等自执行变量被清洗、同会话并行调用严格串行(max_active_commands == 1)、安装脚本不向 hook 配置泄漏 provider API key。

排错

现象可能原因处理
Bash 命令仍在宿主机执行hook 未注册 / Claude Code 未重启重新执行hooks/install.sh并重启 Claude Code
CUBE_TEMPLATE_ID is not set.env缺模板设置CUBE_TEMPLATE_ID(见cubemastercli tpl list),然后重新运行hooks/install.sh
the cubesandbox SDK is required未装依赖pip install -r requirements.txt(需要cubesandboxpython-dotenv
Template not found模板 ID 错误检查cubemastercli tpl list
挂载被拒(警告)路径不在allowed_host_mount_prefixes在 CubeMasterextra_conf加前缀,或接受无挂载回退

更多详细踩坑记录(包括模板快照与 CPU 特性绑定导致的CpuidCheckCompatibility报错及其重建方法、npm/pip 安装类命令超时的参数调优等)见 TROUBLESHOOTING.md。

安全设计要点

  • fail-closed:hook 无法安全改写时以非零退出阻断命令,绝不放行到宿主机;
  • 防注入:原命令作为单个shlex引用参数传入执行器,shell 元字符和换行无法越出;
  • 无条件改写:每条 Bash 调用都会被改写;已包裹的执行器调用若再次经过 hook,只会在沙箱内失败(沙箱内不存在宿主 hook 路径),绝不会落到宿主机;
  • 自动批准:hook 对改写后的 Bash 调用返回permissionDecision: "allow",Claude Code 的逐命令确认提示被抑制;请相应使用--permission-mode/ hooks 策略;
  • 凭据不外泄:安装脚本只复制白名单CUBE_*值,不会把 provider API key 写进 hook 配置;
  • 状态文件防护:会话状态目录与文件分别以 0700/0600 权限创建,读写均拒绝符号链接,持久化环境会清洗BASH_ENV/ENV/LD_PRELOAD/PROMPT_COMMAND等自执行变量(见 cubesandbox_exec.py 与其测试)。

这套方案的核心价值在于:它不要求模型学习任何新工具或新 prompt,而是在 Claude Code 的工具调用边界上做了一层透明的强制隔离——Bash 永远进沙箱,文件操作留在宿主机,二者互不越界。

【免费下载链接】CubeSandboxInstant, Concurrent, Secure & Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询