一个轻量级的沙箱工具,可在操作系统层面为任意进程强制实施文件系统和网络访问限制,无需依赖容器。
srt 利用操作系统的原生沙箱机制(在 macOS 上使用 sandbox-exec,在 Linux 上使用 bubblewrap),并结合基于代理的网络过滤技术。它可以用于对智能体(agents)、本地 MCP 服务器、bash 命令以及任意进程的行为进行沙箱隔离。
Beta 研究预览版
Sandbox Runtime 是为 Claude Code 开发的一项研究性预览功能,旨在让 AI 智能体运行得更安全。我们将其作为早期开源预览版本发布,以帮助更广泛的开发者生态构建更安全的智能体系统。由于这是早期研究版本,其 API 和配置格式可能会发生变化。我们欢迎社区反馈与贡献,共同推动“默认安全”的 AI 智能体发展!
安装
npm install -g @anthropic-ai/sandbox-runtime
基础用法
网络限制示例
# 允许访问 anthropic.com
$ srt "curl anthropic.com"
Running: curl anthropic.com
<html>...</html> # 请求成功
# 阻止访问 example.com(不在白名单中)
$ srt "curl example.com"
Running: curl example.com
Connection blocked by network allowlist # 请求被拦截
文件系统限制示例
# 允许读取当前目录下的 README.md
$ srt "cat README.md"
Running: cat README.md
# Anthropic Sandb... # 成功读取
# 尝试读取敏感私钥文件(被禁止)
$ srt "cat ~/.ssh/id_rsa"
Running: cat ~/.ssh/id_rsa
cat: /Users/ollie/.ssh/id_rsa: Operation not permitted # 操作被拒绝
概览
本软件包提供了一个独立的沙箱实现,既可作为命令行工具(CLI),也可作为程序库(library)使用。它采用“默认安全(secure-by-default)”的设计哲学,专为常见的开发者场景量身打造:进程启动时拥有最小权限,你必须显式地打开所需权限的“小孔”(poke holes)。
核心能力:
- 网络限制:控制进程可通过 HTTP/HTTPS 或其他协议访问哪些主机或域名
- 文件系统限制:控制进程可读写哪些文件或目录
- Unix 套接字限制:控制对本地 IPC(进程间通信)套接字的访问
- 违规监控:在 macOS 上,可接入系统沙箱违规日志存储,实现实时告警
示例用例:沙箱化 MCP 服务器
一个关键应用场景是对 Model Context Protocol(MCP)。例如,对文件系统 MCP 服务器进行沙箱保护:
- 未启用沙箱(.mcp.json):
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem"]
}
}
}
- 启用沙箱(.mcp.json):
{
"mcpServers": {
"filesystem": {
"command": "srt",
"args": ["npx", "-y", "@modelcontextprotocol/server-filesystem"]
}
}
}
然后在 ~/.srt-settings.json 中配置限制策略:
{
"filesystem": {
"denyRead": [],
"allowWrite": ["."],
"denyWrite": ["~/sensitive-folder"]
},
"network": {
"allowedDomains": [],
"deniedDomains": []
}
}
现在,MCP 服务器将无法向被禁止的路径写入文件:
尝试写入 ~/sensitive-folder
✗ 错误:EPERM: operation not permitted, open '/Users/ollie/sensitive-folder/test.txt'
工作原理(How It Works)
沙箱利用操作系统级别的原语(primitives)对整个进程树实施限制:
- macOS:使用 sandbox-exec 配合动态生成的 Seatbelt 配置文件
- Linux:使用 bubblewrap 实现容器化,并通过网络命名空间隔离(network namespace isolation)
双重隔离模型(Dual Isolation Model)
有效的沙箱需要同时具备文件系统隔离和网络隔离:
- 没有文件隔离 → 被攻破的进程可能窃取 SSH 私钥等敏感文件
- 没有网络隔离 → 进程可能绕过沙箱,获得无限制的网络访问权
- 文件系统隔离策略:
读取(Read):采用 “仅拒绝”模式(deny-only)
- 默认允许读取所有路径
- 你可以通过 denyRead 明确禁止某些路径(如 ~/.ssh)
- 若 denyRead 为空,则表示完全开放读取权限
写入(Write):采用 “仅允许”模式(allow-only)
- 默认禁止所有写入
- 必须通过 allowWrite 显式授权路径(如 . 表示当前目录,/tmp)
- 若 allowWrite 为空,则完全禁止写入
网络隔离策略:
采用 “仅允许”模式(allow-only)
- 默认禁止所有网络访问
- 必须通过 allowedDomains 明确列出可访问的域名
- 若 allowedDomains 为空,则完全断网
所有网络流量都通过运行在宿主机上的代理服务器中转:
Linux:
- 沙箱进程的网络命名空间被完全移除
- 所有网络请求必须通过 Unix 域套接字(filesystem-based)转发给宿主机上的代理
- 代理通过 bind-mount 方式挂载进沙箱环境
macOS:
- Seatbelt 配置只允许连接到特定的 localhost 端口
- 代理监听该端口,形成受控的网络通道
这些代理支持两类流量: - HTTP/HTTPS → 通过 HTTP 代理处理
- 其他 TCP 流量(如数据库连接)→ 通过 SOCKS5 代理处理
代理会根据你的 allowedDomains / deniedDomains 规则强制执行域名过滤。
更多资料(延伸阅读)
- https://code.claude.com/docs/en/sandboxing
- Beyond Permission Prompts: Making Claude Code More Secure and Autonomous
架构概览(Architecture)
项目源码结构如下:
src/
├── index.ts # 库的主入口(供其他程序 import)
├── cli.ts # CLI 入口(即 `srt` 命令的实现)
├── utils/ # 公共工具模块
│ ├── debug.ts # 调试日志
│ ├── settings.ts # 读取 ~/.srt-settings.json 配置
│ ├── platform.ts # 操作系统检测(macOS/Linux)
│ └── exec.ts # 命令执行封装
└── sandbox/ # 沙箱核心逻辑
├── sandbox-manager.ts # 沙箱主控制器
├── sandbox-schemas.ts # 使用 Zod 定义配置校验规则
├── sandbox-violation-store.ts # 违规行为记录(macOS)
├── sandbox-utils.ts # 沙箱通用工具函数
├── http-proxy.ts # HTTP/HTTPS 代理(实现域名白名单)
├── socks-proxy.ts # SOCKS5 代理(用于非 HTTP 流量)
├── linux-sandbox-utils.ts # Linux 下调用 bubblewrap
└── macos-sandbox-utils.ts # macOS 下调用 sandbox-exec + Seatbelt
Usage(使用方式)
作为命令行工具(CLI)
srt 命令(即 Anthropic Sandbox Runtime)可以为任意命令加上安全边界:
# 在沙箱中运行命令
srt echo "hello world"
# 启用调试日志
srt --debug curl https://example.com
# 指定自定义配置文件
srt --settings /path/to/srt-settings.json npm install
作为程序库(Library)
import {
SandboxManager,
type SandboxRuntimeConfig,
} from '@anthropic-ai/sandbox-runtime'
import { spawn } from 'child_process'
// 1. 定义沙箱配置
const config: SandboxRuntimeConfig = {
network: {
allowedDomains: ['example.com', 'api.github.com'], // 允许访问的域名
deniedDomains: [], // 明确禁止的域名(可选)
},
filesystem: {
denyRead: ['~/.ssh'], // 禁止读取的路径
allowWrite: ['.', '/tmp'], // 允许写入的路径
denyWrite: ['.env'], // 禁止写入的路径(即使在 allowWrite 范围内也会被阻止)
},
}
// 2. 初始化沙箱(会启动代理服务器等)
await SandboxManager.initialize(config)
// 3. 将命令包装进沙箱环境
const sandboxedCommand = await SandboxManager.wrapWithSandbox(
'curl https://example.com',
)
// 4. 执行沙箱化后的命令
const child = spawn(sandboxedCommand, { shell: true, stdio: 'inherit' })
// 5. 处理退出状态
child.on('exit', code => {
console.log(`命令退出,状态码:${code}`)
})
// 6. 清理资源(可选,进程退出时会自动清理)
await SandboxManager.reset()
可导出的内容(Available exports)
// 主沙箱管理器
export { SandboxManager } from '@anthropic-ai/sandbox-runtime'
// 违规行为追踪器(用于监控沙箱突破尝试)
export { SandboxViolationStore } from '@anthropic-ai/sandbox-runtime'
// TypeScript 类型定义
export type {
SandboxRuntimeConfig, // 整体配置类型
NetworkConfig, // 网络配置
FilesystemConfig, // 文件系统配置
IgnoreViolationsConfig, // 忽略违规配置(高级)
SandboxAskCallback, // 交互式权限请求回调(未来扩展)
FsReadRestrictionConfig, // 读限制配置
FsWriteRestrictionConfig, // 写限制配置
NetworkRestrictionConfig, // 网络限制配置
} from '@anthropic-ai/sandbox-runtime'
Configuration(配置)
配置文件位置
默认情况下,沙箱运行时会在 ~/.srt-settings.json 查找配置文件。
你可以通过 --settings 标志指定自定义路径:
srt --settings /path/to/srt-settings.json <command>
完整配置示例
{
"network": {
"allowedDomains": [
"github.com",
"*.github.com",
"lfs.github.com",
"api.github.com",
"npmjs.org",
"*.npmjs.org"
],
"deniedDomains": ["malicious.com"],
"allowUnixSockets": ["/var/run/docker.sock"],
"allowLocalBinding": false
},
"filesystem": {
"denyRead": ["~/.ssh"],
"allowWrite": [".", "src/", "test/", "/tmp"],
"denyWrite": [".env", "config/production.json"]
},
"ignoreViolations": {
"*": ["/usr/bin", "/System"],
"git push": ["/usr/bin/nc"],
"npm": ["/private/tmp"]
},
"enableWeakerNestedSandbox": false
}
配置选项详解
- 🔌 网络配置(Network Configuration)
采用 “仅允许”模式(allow-only)—— 默认禁止所有网络访问。
| 字段 | 说明 |
|---|---|
network.allowedDomains | 允许访问的域名列表(支持通配符如 *.example.com)。空数组 = 完全断网。 |
network.deniedDomains | 明确禁止的域名列表(优先级高于 allowedDomains,先检查此项)。 |
network.allowUnixSockets | 允许访问的 Unix 套接字路径列表(仅 macOS 支持)。 |
network.allowLocalBinding | 是否允许进程绑定本地端口(如启动 HTTP 服务器),默认 false。 |
💡 示例:即使 allowedDomains 包含 *(实际不支持通配符全域),只要 deniedDomains 有 “evil.com”,访问仍会被拦截。
- 📁 文件系统配置(Filesystem Configuration)
采用两种不同策略:
✅ 读取限制(deny-only 模式)
默认允许读取所有路径
通过 denyRead 显式禁止敏感路径
| 字段 | 说明 |
|---|---|
filesystem.denyRead | 禁止读取的路径列表。空数组 = 全部可读。 |
✍️ 写入限制(allow-only 模式)
默认禁止所有写入
- 必须通过 allowWrite 显式授权目录
- 可通过 denyWrite 在允许范围内进一步禁止特定文件
| 字段 | 说明 |
|---|---|
filesystem.allowWrite | 允许写入的路径列表。空数组 = 完全禁止写入。 |
filesystem.denyWrite | 在 allowWrite 范围内额外禁止写入的路径(优先级更高)。 |
- 路径语法(Path Syntax)
🍏 macOS(支持类 .gitignore 的 glob 模式)
| 模式 | 含义 | 示例 |
|---|---|---|
* | 匹配任意字符(不含 /) | *.ts → 匹配 foo.ts,但不匹配 foo/bar.ts |
| `` | 匹配任意字符(含 /) | src//*.ts → 匹配 src/a/b/c.ts |
? | 匹配单个字符(不含 /) | file?.txt → 匹配 file1.txt |
[abc] | 匹配括号内任一字符 | file[0-9].txt → 匹配 file3.txt |
示例:
"allowWrite": ["src/"] // 允许整个 src/ 目录写入
"allowWrite": ["src/**/*.ts"] // 允许 src/ 下所有 .ts 文件写入
"denyRead": ["~/.ssh"] // 禁止读取 SSH 目录
"denyWrite": [".env"] // 禁止写入 .env(即使当前目录可写)
- 🐧 Linux(不支持 glob,仅支持字面路径)
必须使用完整路径或相对路径,不能用 *、 等通配符。
示例:
"allowWrite": ["src/"] // 允许 src/ 目录
"denyRead": ["/home/user/.ssh"] // 必须写绝对路径(~ 不被自动展开?注意:实际仍支持 ~)
- 🌐 所有平台通用规则
路径可以是:
- 绝对路径:/home/user/.ssh
- 相对路径:./src(相对于当前工作目录)
~ 会自动展开为当前用户的主目录(如 /Users/ollie 或 /home/ollie)
- 其他配置项
| 字段 | 说明 |
|---|---|
ignoreViolations | 对象,键为命令模式(如 "*" 或 "git push"),值为路径数组。当这些命令访问指定路径时,忽略沙箱违规告警(用于兼容某些工具的正常行为)。 |
enableWeakerNestedSandbox | 布尔值,默认 false。在 Docker 等嵌套容器环境中启用较弱沙箱模式(因 bubblewrap 在容器内可能受限)。 |
常用配置模板(Recipes)
- 允许访问 GitHub(含所有必要端点)
{
"network": {
"allowedDomains": [
"github.com",
"*.github.com",
"lfs.github.com",
"api.github.com"
],
"deniedDomains": []
},
"filesystem": {
"denyRead": [],
"allowWrite": ["."],
"denyWrite": []
}
}
- 严格限制:仅允许特定目录读写,完全断网
{
"network": {
"allowedDomains": [],
"deniedDomains": []
},
"filesystem": {
"denyRead": ["~/.ssh"],
"allowWrite": [".", "src/", "test/"],
"denyWrite": [".env", "secrets/"]
}
}
常见问题与技巧
❗ 运行 Jest 测试时报错?
Jest 默认使用 Watchman(Facebook 的文件监控工具),它会扫描大量系统路径,触发沙箱拒绝。
✅ 解决方案:禁用 Watchman
srt "jest --no-watchman"
这样 Jest 会回退到 Node.js 内置的文件监听器,只监控项目目录,符合沙箱限制。
Platform Support(平台支持)
平台支持概览
- macOS:使用 sandbox-exec 配合自定义配置文件(无需额外依赖)
- Linux:使用 bubblewrap(bwrap)实现容器化隔离
- Windows:暂不支持
各平台特定依赖项
🐧 Linux 必需依赖:
- bubblewrap —— 容器运行时工具
- Ubuntu/Debian: apt-get install bubblewrap
- Fedora: dnf install bubblewrap
- Arch: pacman -S bubblewrap
- socat —— 用于代理桥接的套接字中继工具
- Ubuntu/Debian: apt-get install socat
- Fedora: dnf install socat
- Arch: pacman -S socat
- ripgrep(rg)—— 高速文本搜索工具,用于检测“拒绝路径”(deny path detection)
- Ubuntu/Debian: apt-get install ripgrep
- Fedora: dnf install ripgrep
- Arch: pacman -S ripgrep
💡 注:ripgrep 在沙箱启动时用于快速扫描进程可能访问的敏感路径(如 ~/.ssh),以决定是否拦截。
🐧 Linux 可选依赖(仅在需要 seccomp 回退机制时使用):
项目已为 x86-64 和 ARM 架构预生成了 seccomp BPF 过滤器。
仅当你使用其他架构(如 RISC-V、PowerPC 等),才需要以下依赖来重新生成过滤器:
- C 编译器:gcc 或 clang
- libseccomp 开发库:libseccomp-dev(Debian/Ubuntu)或 libseccomp-devel(Fedora)
安装命令: - Ubuntu/Debian: apt-get install gcc libseccomp-dev
- Fedora: dnf install gcc libseccomp-devel
- Arch: pacman -S gcc libseccomp
✅ 大多数用户无需安装这些可选依赖,除非你在非主流架构上运行 srt。
🍏 macOS 必需依赖:
ripgrep(rg)—— 用于“拒绝路径检测”的高速搜索工具
安装方式:
- 通过 Homebrew:brew install ripgrep
- 或从 GitHub 发布页手动下载:
https://github.com/BurntSushi/ripgrep/releases
Development(开发)
开发命令速览
# 安装依赖
npm install
# 构建项目
npm run build
# 构建 seccomp 二进制文件(需 Docker)
npm run build:seccomp
# 运行单元测试
npm test
# 运行集成测试
npm run test:integration
# 类型检查
npm run typecheck
# 代码 lint
npm run lint
# 代码格式化
npm run format
构建 Seccomp 二进制文件
仓库中已包含预生成的 BPF 过滤器,但你也可以按需重新构建:
npm run build:seccomp
该脚本使用 Docker 跨平台编译以下架构的 seccomp 二进制文件:
- x64(x86-64)
- arm64(aarch64)
构建过程:
- 编译静态的 generator 二进制程序
- 生成约 104 字节 的 BPF 过滤器
- 存储到 vendor/seccomp/x64/ 和 vendor/seccomp/arm64/
- 删除 generator 二进制,以减小最终包体积
实现细节
一、网络隔离架构(Network Isolation Architecture)
srt 在宿主机上启动两个代理服务器,所有网络流量必须经过它们:
| 流量类型 | 代理类型 | 功能 |
|---|---|---|
| HTTP/HTTPS | HTTP 代理 | 拦截请求,验证域名是否在 allowedDomains / deniedDomains 中 |
| 其他 TCP(SSH、数据库等) | SOCKS5 代理 | 处理非 HTTP 流量,同样应用域名规则 |
平台通信机制差异:
Linux:
- 移除 bubblewrap 容器的网络命名空间(network namespace)
- 所有流量通过 Unix 域套接字 路由到宿主机代理
- 使用 socat 桥接容器内套接字 ↔ 宿主机代理端口
macOS:
- Seatbelt 沙箱配置仅允许连接 localhost 上的特定代理端口
- 其他所有网络访问被内核直接拒绝
二、文件系统隔离(Filesystem Isolation)
权限在操作系统层面强制执行:
| 平台 | 技术 |
|---|---|
| macOS | sandbox-exec + 动态生成的 Seatbelt 配置文件 |
| Linux | bubblewrap + bind mounts(只读/读写挂载) |
默认权限模型:
| 操作 | 策略 | 示例 |
|---|---|---|
| 读取 | deny-only(默认全开) | denyRead: ["~/.ssh"] → 禁止读 SSH 密钥[] → 全部可读 |
| 写入 | allow-only(默认全关) | allowWrite: [".", "/tmp"] → 仅当前目录和 /tmp 可写[] → 完全禁止写入 |
denyWrite 可在 allowWrite 范围内进一步禁止特定文件(优先级更高)
三、强制拒绝路径(Mandatory Deny Paths)
某些敏感文件/目录始终禁止写入,即使你在 allowWrite 中包含了它们!
自动保护的文件:
- Shell 配置:.bashrc, .zshrc, .profile 等
- Git 配置:.gitconfig, .gitmodules
- 工具配置:.ripgreprc, .mcp.json
自动保护的目录:
- IDE 配置:.vscode/, .idea/
- Claude 配置:.claude/commands/, .claude/agents/
- Git 钩子与配置:.git/hooks/, .git/config
✅ 示例(即使 allowWrite: [“.”]):
$ srt 'echo "malicious" >> .bashrc'
/bin/bash: .bashrc: Operation not permitted
$ srt 'echo "bad" > .git/hooks/pre-commit'
/bin/bash: .git/hooks/pre-commit: Operation not permitted
⚠️ Linux 与 macOS 差异:
| 平台 | 行为 |
|---|---|
| macOS | 使用 glob 模式,阻止创建新文件(如 .git/hooks/new-hook) |
| Linux | 仅能阻止已存在的文件(因 bubblewrap 依赖 bind mount) → 对不存在的文件无能为力 |
Linux 搜索深度控制:
srt 使用 ripgrep 在允许写入的目录中递归扫描已存在的敏感文件。
- 默认深度:3 层(如 ./a/b/c/file)
- 可配置:mandatoryDenySearchDepth(范围 1–10)
- 当前工作目录(CWD)中的文件始终受保护(深度 0)
四、Unix 套接字限制(Linux)
Linux 版本使用 seccomp BPF 在系统调用层阻止 Unix 域套接字创建。
工作原理:
- 预生成 BPF 过滤器(104 字节):
- 支持 x64 / arm64
- 与 libc 无关(兼容 glibc/musl)
- 存于 vendor/seccomp/
- 运行时自动加载对应架构的过滤器
- 拦截 socket() 系统调用:
- 若 domain == AF_UNIX,返回 EPERM
- 阻止创建新 Unix socket
- 两阶段应用:
- 第一阶段:bwrap 启动沙箱,启动 socat(需 Unix socket)
- 第二阶段:apply-seccomp 二进制通过 prctl() 应用过滤器,再 exec 用户命令
安全限制:
- 不阻止继承自父进程的 socket fd
- 不阻止通过 SCM_RIGHTS 传递的 socket
- 但对大多数场景,阻止创建已足够防止 IPC 滥用
无运行时依赖:
- 预编译的 apply-seccomp 静态二进制 + BPF 文件已包含
- 无需安装 gcc/libseccomp
不支持的架构?
- 可设置 allowAllUnixSockets: true 禁用此保护(不推荐)
五、违规检测与监控(Violation Detection)
当沙箱进程尝试越权操作:
- OS 层拦截(返回 EPERM)
- 记录违规日志
- 通知用户(如在 Claude Code 中弹出权限请求)
macOS:
- 利用系统内置的 sandbox violation log store
- 实时查看:
log stream --predicate 'process == "sandbox-exec"' --style syslog
Linux:
- bubblewrap 无内置日志
- 需手动用 strace 调试:
# 查看所有被拒操作
strace -f srt <command> 2>&1 | grep EPERM
# 仅查文件操作
strace -f -e trace=open,openat,stat,access srt <command> 2>&1 | grep EPERM
# 仅查网络操作
strace -f -e trace=network srt <command> 2>&1 | grep EPERM
六、高级功能:自带代理(Bring Your Own Proxy)
⚠️ 注意:此功能尚未在新配置格式中支持,将在未来版本加入。
你可以用自己的代理(如 mitmproxy)替代内置代理,实现:
- 流量审查:解密 HTTPS,检查内容
- 自定义过滤:基于 API 路径、Header 等精细控制
- 审计日志:记录所有外联请求
示例:
# 启动 mitmproxy
mitmproxy -s custom_filter.py --listen-port 8888
🔒 安全提示:即使限制了 github.com,攻击者仍可 git push 到任意仓库。只有通过 MITM 代理检查具体 API 调用,才能真正防止数据外泄。
⚠️ 安全限制与风险提示
1. 网络沙箱局限性
- 仅基于域名过滤,不检查请求内容
- 允许 github.com 可能导致数据外泄(通过 git push)
- 域前置(Domain Fronting)可能绕过过滤(尽管现代 CDN 已基本禁用)
2. Unix 套接字风险
- allowUnixSockets: [“/var/run/docker.sock”] = 授予 root 权限!
- Docker socket 可用于逃逸到宿主机
3. 文件系统提权风险
若 allowWrite 包含:
- $PATH 中的目录 → 可替换命令
- Shell 配置目录 → 下次登录执行恶意代码
- 系统配置目录 → 影响全局
4. Linux 嵌套沙箱弱化
- enableWeakerNestedSandbox: true 用于 Docker 内运行
- 大幅降低安全性,仅在已有额外隔离时使用
已知限制与未来工作
| 问题 | 当前状态 | 未来计划 |
|---|---|---|
| Linux 代理绕过 | 依赖 HTTP_PROXY 环境变量,部分程序(如 Go 默认客户端)可能忽略 | 支持 proxychains + LD_PRELOAD 拦截底层网络调用 |
| Linux 违规监控缺失 | 需手动 strace | 自动集成 strace 到 SandboxViolationStore |
| 自定义代理未支持 | 仅内部使用 | 开放配置选项 |
参考文献
https://github.com/anthropic-experimental/sandbox-runtime

被折叠的 条评论
为什么被折叠?



