
sandbox-runtime v0.0.71
一个轻量级沙箱工具,用于在操作系统层面强制执行任意进程的文件系统和网络限制,而无需使用容器。
Anthropic 沙箱运行时 (srt)
一个轻量级沙箱工具,用于在操作系统层面强制对任意进程实施文件系统和网络限制,而无需容器。
srt 使用操作系统原生沙箱原语(macOS 上的 sandbox-exec,Linux 上的 bubblewrap)以及基于代理的网络过滤。它可以对智能体、本地 MCP 服务器、bash 命令和任意进程的行为进行沙箱隔离。
Beta 研究预览
沙箱运行时是专为 Claude Code 开发的研究预览,旨在实现更安全的 AI 智能体。它作为早期开源预览发布,以帮助更广泛的生态系统构建更安全的智能体系统。由于这是早期研究预览,API 和配置格式可能会发展演变。我们欢迎反馈和贡献,共同让 AI 智能体默认更安全!
安装```bash
npm install -g @anthropic-ai/sandbox-runtime
## 基本用法```bash
# Network restrictions
$ srt "curl anthropic.com"
Running: curl anthropic.com
<html>...</html> # Request succeeds
$ srt "curl example.com"
Running: curl example.com
Connection blocked by network allowlist # Request blocked
# Filesystem restrictions
$ srt "cat README.md"
Running: cat README.md
# Anthropic Sandb... # Current directory access allowed
$ srt "cat ~/.ssh/id_rsa"
Running: cat ~/.ssh/id_rsa
cat: /Users/ollie/.ssh/id_rsa: Operation not permitted # Specific file blocked
概述
本包提供了一个独立的沙箱实现,既可用作 CLI 工具,也可用作库。它遵循默认安全的设计理念,专为常见的开发者使用场景而打造:进程以最小权限启动,而你只需显式打开所需的缺口。
主要能力:
- 网络限制:控制哪些主机/域名可通过 HTTP/HTTPS 及其他协议访问
- 文件系统限制:控制哪些文件/目录可被读取/写入
- Unix 套接字限制:控制对本地 IPC 套接字的访问
- 违规监控:在 macOS 上,接入系统沙箱违规日志存储以获得实时警报
示例用例:对 MCP 服务器进行沙箱隔离
一个关键用例是对模型上下文协议(MCP)服务器进行沙箱隔离以限制其能力。例如,要对文件系统 MCP 服务器进行沙箱隔离:
不进行沙箱隔离 (.mcp.json):```json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem"]
}
}
}
**使用沙箱** (`.mcp.json`):```json
{
"mcpServers": {
"filesystem": {
"command": "srt",
"args": ["npx", "-y", "@modelcontextprotocol/server-filesystem"]
}
}
}
然后在 ~/.srt-settings.json 中配置限制:```json
{
"filesystem": {
"denyRead": [],
"allowWrite": ["."],
"denyWrite": ["~/sensitive-folder"]
},
"network": {
"allowedDomains": [],
"deniedDomains": []
}
}
现在,MCP 服务器将被阻止写入被拒绝的路径:```
> Write a file to ~/sensitive-folder
✗ Error: EPERM: operation not permitted, open '/Users/ollie/sensitive-folder/test.txt'
工作原理
该沙箱使用操作系统级原语来实施适用于整个进程树的限制:
- macOS:使用
sandbox-exec和动态生成的 Seatbelt 配置文件 - Linux:使用 bubblewrap 进行容器化,并具有网络命名空间隔离
- Windows:在专用的
srt-sandbox本地用户帐户下运行沙箱进程,并使用 Windows 筛选平台 的出口围栏,该围栏以该帐户的 SID 为键,并在工作树上按会话设置显式 ACE
0d1c612947c798aef48e6ab4beb7e8544da9d41a-4096x2305
双重隔离模型
文件系统和网络隔离都是有效沙箱化所必需的。没有文件隔离,受感染的进程可能会泄露 SSH 密钥或其他敏感文件。没有网络隔离,进程可能会逃逸沙箱并获得不受限制的网络访问权限。
文件系统隔离 强制执行读写限制:
- 读取(先拒绝后允许模式):默认情况下,读取访问在所有位置均被允许。您可以拒绝大范围区域(例如
/Users),然后在该范围内重新允许特定路径(例如.)。allowRead优先于denyRead—— 与写入相反,写入时denyWrite优先于allowWrite。 - 写入(仅允许模式):默认情况下,写入访问在所有位置均被拒绝。您必须显式允许路径(例如
.、/tmp)。空的允许列表表示没有写入访问权限。
网络隔离(仅允许模式):默认情况下,所有网络访问均被拒绝。您必须显式允许域名。空的 allowedDomains 列表表示没有网络访问权限。网络流量通过主机上运行的代理服务器进行路由:
-
Linux:请求通过文件系统经 Unix 域套接字路由。沙箱进程的网络命名空间被完全移除,因此所有网络流量都必须通过主机上运行的代理(监听绑定挂载到沙箱中的 Unix 套接字)
-
macOS:Seatbelt 配置文件仅允许与特定 localhost 端口通信。代理监听该端口,为所有网络访问创建受控通道
-
Windows:机器范围的 WFP 筛选器集阻止来自
srt-sandbox帐户的所有出站连接,但回环到代理端口范围的连接除外。代理在该范围内监听,为所有网络访问创建受控通道
HTTP/HTTPS(通过 HTTP 代理)和其他 TCP 流量(通过 SOCKS5 代理)都由这些代理中介,代理会执行您的域名允许列表和拒绝列表。
有关 Claude Code 中沙箱化的更多详细信息,请参阅:
架构```
src/ ├── index.ts # Library exports ├── cli.ts # CLI entrypoint (srt command) ├── utils/ # Shared utilities │ ├── debug.ts # Debug logging │ ├── settings.ts # Settings reader (permissions + sandbox config) │ ├── platform.ts # Platform detection │ └── exec.ts # Command execution utilities └── sandbox/ # Sandbox implementation ├── sandbox-manager.ts # Main sandbox manager ├── sandbox-schemas.ts # Zod schemas for validation ├── sandbox-violation-store.ts # Violation tracking ├── sandbox-utils.ts # Shared sandbox utilities ├── http-proxy.ts # HTTP/HTTPS proxy for network filtering ├── socks-proxy.ts # SOCKS5 proxy for network filtering ├── linux-sandbox-utils.ts # Linux bubblewrap sandboxing ├── macos-sandbox-utils.ts # macOS sandbox-exec sandboxing └── windows-sandbox-utils.ts # Windows srt-win sandboxing
## 用法
### 作为 CLI 工具
`srt` 命令(Anthropic Sandbox Runtime)将任何命令包裹在安全边界内:```bash
# Run a command in the sandbox
srt echo "hello world"
# With debug logging
srt --debug curl https://example.com
# Specify custom settings file
srt --settings /path/to/srt-settings.json npm install
作为库```typescript
import { SandboxManager, type SandboxRuntimeConfig, } from '@anthropic-ai/sandbox-runtime' import { spawn } from 'child_process'
// Define your sandbox configuration const config: SandboxRuntimeConfig = { network: { allowedDomains: ['example.com', 'api.github.com'], deniedDomains: [], }, filesystem: { denyRead: ['~/.ssh'], allowWrite: ['.', '/tmp'], denyWrite: ['.env'], }, }
// Initialize the sandbox (starts proxy servers, etc.) await SandboxManager.initialize(config)
// Wrap a command with sandbox restrictions const sandboxedCommand = await SandboxManager.wrapWithSandbox( 'curl https://example.com', )
// Execute the sandboxed command const child = spawn(sandboxedCommand, { shell: true, stdio: 'inherit' })
// Handle exit and cleanup after child process completes
child.on('exit', async code => {
console.log(Command exited with code ${code})
// Cleanup when done (optional, happens automatically on process exit)
await SandboxManager.reset()
})
**违规归属 (`commandId` / `commandText`)。** 当被包装的命令运行时观察到的违规行为 (seatbelt 日志行、seccomp 事件、代理拒绝) 会存储在一个归属键下,而 `annotateStderrWithSandboxFailures(key, stderr)` / `getViolationsForCommand(key)` 会通过同一个键查找它们。默认情况下,该键就是被包装的字符串本身。请改为传入一个不透明的、每次调用唯一的 `commandId` (例如工具使用 ID) 作为键 —— 建议做法:键会基于其前 100 个字符进行比较;若不如此,共享相同前缀的长命令就会交叉归属,而同一文本的重新运行则会继承之前运行的事件。如果你实际*执行*的字符串并非该调用所*代表*的命令 (例如,你包装了组装好的 `source <snapshot> && eval '<cmd>'`),请同时传入 `commandText: '<cmd>'`:它既是 `ignoreViolations` 命令模式匹配的对象,也是每条违规报告为其 `command` 的内容。```typescript
const wrapped = await SandboxManager.wrapWithSandbox(
assembledCommand, // what actually runs
undefined,
undefined,
undefined,
{ commandId: invocationId, commandText: rawCommand },
)
// ... run it ...
const annotated = SandboxManager.annotateStderrWithSandboxFailures(invocationId, stderr)
可用的导出项```typescript
// Main sandbox manager export { SandboxManager } from '@anthropic-ai/sandbox-runtime'
// Violation tracking export { SandboxViolationStore } from '@anthropic-ai/sandbox-runtime'
// TypeScript types export type { SandboxRuntimeConfig, NetworkConfig, FilesystemConfig, IgnoreViolationsConfig, SandboxAskCallback, FsReadRestrictionConfig, FsWriteRestrictionConfig, NetworkRestrictionConfig, } from '@anthropic-ai/sandbox-runtime'
## 配置
### 设置文件位置
默认情况下,沙箱运行时会在 `~/.srt-settings.json` 中查找配置。您可以使用 `--settings` 标志指定自定义路径:```bash
srt --settings /path/to/srt-settings.json <command>
完整配置示例```json
{ "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"], "allowRead": [], "allowWrite": [".", "src/", "test/", "/tmp"], "denyWrite": [".env", "config/production.json"] }, "ignoreViolations": { "*": ["/usr/bin", "/System"], "git push": ["/usr/bin/nc"], "npm": ["/private/tmp"] }, "enableWeakerNestedSandbox": false, "enableWeakerNetworkIsolation": false, "allowAppleEvents": false }
### 配置选项
#### 网络配置
采用**仅允许模式** - 默认拒绝所有网络访问。
- `network.allowedDomains` - 允许的域名数组(支持通配符,如 `*.example.com`)。空数组 = 无网络访问。可选的 `:port` 后缀(`api.example.com:443`、`*.example.com:8443`)可将条目限制到该目标端口;不带端口的条目匹配任意端口。
- IPv6 字面量必须加方括号,采用 RFC 3986 风格:`[::1]`、`[2001:db8::1]:443`。未加括号的多冒号条目会被视为有歧义而拒绝(`2001:db8::1:443` 本身就是一个有效地址)。
- `network.deniedDomains` - 被拒绝的域名数组(先检查,优先级高于 allowedDomains)。支持相同的 `:port` 后缀,并接受裸 `*`(或 `*:22`)用于全拒绝。
- `network.deniedDomainReasons` - 可选映射,将 `deniedDomains` 条目(按精确字符串匹配)映射到一条面向模型的理由;当该条目拒绝连接时,此理由会出现在 `<sandbox_violations>` 行中 — 说明被阻止的内容以及认可的替代方案(例如 `{"github.com:22": "SSH pushes to GitHub are blocked; use an https:// remote"}`)。没有理由的条目会报告一条通用理由。对于 SSH 目标(端口 22),理由也会带内传递:通过无认证 SOCKS ProxyCommand(如 BSD `nc -X 5`)隧道传输的 SSH 客户端会收到一次密钥交换前的 SSH 断开连接,其描述即为该理由,OpenSSH 会原样打印 — 请将此类理由控制在约 400 个 ASCII 字符以内,并以祈使句开头,因为 OpenSSH 会截断并转义非 ASCII 字符。
- `network.allowLocalBinding` - 允许绑定本地端口(布尔值,默认:false)
**TLS 终止**(`network.tlsTerminate`,实验性):设置后,HTTPS CONNECT 会在进程内终止,以便 SRT 能够看到(并通过 `network.filterRequest` 过滤)解密后的请求。被沙箱化的进程会指向一个包含 MITM CA(`caCertPath`/`caKeyPath`,若省略则使用临时 CA)以及主机常规根证书的信任捆绑包,因此代理签发的证书和真实上游证书都能通过验证。
- `network.tlsTerminate.excludeDomains` - **不**终止的域名模式(语法与 `allowedDomains` 相同)。匹配的 CONNECT 改为不透明隧道传输:它们仍受域名白名单约束,但沙箱内的客户端会与真实上游自行完成 TLS 握手,且 `filterRequest` / 凭据注入不适用于它们的 HTTPS 流量。在 TLS 终止从根本上失效的两种场景下使用此选项:
- **mTLS 上游** - 只有沙箱内的客户端持有客户端证书,因此代理无法代表其重新发起连接。
- **证书固定客户端** - 客户端自行验证上游身份(自定义 CA、SAN 固定),并拒绝 MITM 证书。
- `network.tlsTerminate.extraCaCertPaths` - PEM CA 证书文件的路径,这些文件会追加到该信任捆绑包中(位于 MITM CA 和主机常规根证书之后)。被排除(不终止)的主机由沙箱内的客户端验证,而 SRT 设置的信任环境变量(`SSL_CERT_FILE`、`GIT_SSL_CAINFO`、...)会_替换_每个工具自身的信任配置,因此站点本地根证书(如内部 mTLS CA)必须位于捆绑包中,否则这些主机永远无法通过验证。每个文件中只有 `CERTIFICATE` 块会被复制到捆绑包中(其他任何内容,例如组合 PEM 中的私钥,绝不会暴露给沙箱);缺失、不可读或不含 PEM `CERTIFICATE` 块的文件会被跳过,因此列出仅存在于某些主机上的路径是安全的。```json
{
"network": {
"allowedDomains": ["*.example.com", "internal-mtls.example.net"],
"deniedDomains": [],
"tlsTerminate": {
"excludeDomains": ["internal-mtls.example.net"],
"extraCaCertPaths": ["/etc/internal-mtls-roots.pem"]
}
}
}
Unix Socket 设置(平台特定行为):
| 设置 | macOS | Linux |
|---|---|---|
allowUnixSockets: string[] | Socket 路径的允许列表 | 已忽略(seccomp 无法按路径过滤) |
allowAllUnixSockets: boolean | 允许所有 socket | 禁用 seccomp 阻止 |
Unix socket 在两种平台上默认均为阻止状态。
- macOS:使用
allowUnixSockets允许特定路径(例如["/var/run/docker.sock"]),或使用allowAllUnixSockets: true允许所有 socket。 - Linux:阻止功能使用 seccomp 过滤器(仅 x64/arm64)。如果 seccomp 不可用,则 socket 不受限制并显示警告。使用
allowAllUnixSockets: true可显式禁用阻止。
文件系统配置
使用两种不同的模式:
读取限制(先拒绝后允许模式)——默认允许所有读取:
filesystem.denyRead- 要拒绝读取访问的路径数组。空数组 = 完全读取访问。filesystem.allowRead- 在拒绝区域内重新允许读取访问的路径数组(优先于 denyRead)。注意: 这与写入相反,写入时denyWrite优先于allowWrite。
写入限制(仅允许模式)——默认拒绝所有写入:
filesystem.allowWrite- 允许写入访问的路径数组。空数组 = 无写入访问。filesystem.denyWrite- 在允许的路径内拒绝写入访问的路径数组(优先于 allowWrite)
路径语法(macOS):
macOS 上的路径支持类似 git 风格的 glob 模式,与 .gitignore 语法相似:
*- 匹配除/外的任意字符(例如*.ts匹配foo.ts,但不匹配foo/bar.ts)**- 匹配包括/在内的任意字符(例如src/**/*.ts匹配src/中的所有.ts文件)?- 匹配除/外的任意单个字符(例如file?.txt匹配file1.txt)[abc]- 匹配集合中的任意字符(例如file[0-9].txt匹配file3.txt)
示例:
"allowWrite": ["src/"]- 允许写入整个src/目录"allowWrite": ["src/**/*.ts"]- 允许写入src/及其子目录中的所有.ts文件"denyRead": ["~/.ssh"]- 拒绝读取 SSH 目录"denyRead": ["/Users"], "allowRead": ["."]- 拒绝读取/Users下的所有内容,但重新允许当前目录"denyWrite": [".env"]- 拒绝写入.env文件(即使当前目录被允许)
路径语法(Linux):
Linux 目前不支持 glob 匹配。 仅使用字面路径:
"allowWrite": ["src/"]- 允许写入src/目录"denyRead": ["/home/user/.ssh"]- 拒绝读取 SSH 目录"denyRead": ["/home"], "allowRead": ["."]- 拒绝读取/home下的所有内容,但重新允许当前目录
所有平台:
- 路径可以是绝对路径(例如
/home/user/.ssh),也可以是相对于当前工作目录的路径(例如./src) ~会展开为用户的主目录
其他配置
ignoreViolations- 将命令模式映射到路径数组的对象,用于指定在哪些路径下应忽略违规行为enableWeakerNestedSandbox- 为 Docker 环境启用较弱的沙箱模式(布尔值,默认:false)enableWeakerNetworkIsolation- 允许访问 macOS 沙箱中的com.apple.trustd.agent(布尔值,默认:false)。在使用httpProxyPort配合 MITM 代理和自定义 CA 时,Go 程序(gh、gcloud、terraform、kubectl等)需要此项来验证 TLS 证书。安全警告: 启用此选项会通过 trustd 服务引入潜在的数据外泄途径。allowAppleEvents- 允许从 macOS 沙箱发送 Apple Events 和 Launch Services 打开请求(布尔值,默认:false)。如果没有此选项,open、osascript以及通过 AppleScript 打开 URL 或脚本其他应用的命令都会失败,并出现 AppleScript 错误-600(“Application isn't running”)或 LaunchServices 错误(-10822、-54)。安全警告: 启用此选项意味着沙箱不再提供代码执行隔离。沙箱中的命令可以通过open在无用户提示的情况下启动其他应用,而它所启动的任何内容都在沙箱的文件系统和网络限制之外运行;通过 Apple Events 对已在运行的应用进行脚本控制还会额外受用户针对每个应用的 TCC 自动化同意限制。嵌入方只能从受信任的用户级配置中获取此选项——绝不能来自检出仓库中的项目本地文件,否则攻击者制作的项目可能会提升自身的沙箱权限。
常用配置示例
允许 GitHub 访问(所有必要端点):```json { "network": { "allowedDomains": [ "github.com", "*.github.com", "lfs.github.com", "api.github.com" ], "deniedDomains": [] }, "filesystem": { "denyRead": [], "allowWrite": ["."], "denyWrite": [] } }
**限制到特定目录:**```json
{
"network": {
"allowedDomains": [],
"deniedDomains": []
},
"filesystem": {
"denyRead": ["~/.ssh"],
"allowWrite": [".", "src/", "test/"],
"denyWrite": [".env", "secrets/"]
}
}
仅限工作区的文件系统访问 (拒绝读取工作区之外的内容):```json { "network": { "allowedDomains": [], "deniedDomains": [] }, "filesystem": { "denyRead": ["/Users"], "allowRead": ["."], "allowWrite": ["."], "denyWrite": [] } }
这会拒绝读取 `/Users`(Linux 上为 `/home`)下的任何内容,然后重新允许当前工作目录。系统路径(`/usr`、`/lib` 等)保持可读。
### 常见问题与提示
**运行 Jest:** 使用 `--no-watchman` 标志以避免沙箱违规:```bash
srt "jest --no-watchman"
Watchman 会访问沙盒边界之外的文件,从而触发权限错误。禁用它可让 Jest 改用内置文件监视器运行。
平台支持
- macOS:使用带有自定义配置文件的
sandbox-exec(无额外依赖) - Linux:使用
bubblewrap(bwrap)进行容器化 - Windows:Alpha — 使用捆绑的
srt-win.exe辅助程序(无额外依赖)。有关设置、安全模型和已知限制,请参阅下面的 Windows(Alpha)。
平台特定依赖
Linux 需要:
bubblewrap- 容器运行时- Ubuntu/Debian:
apt-get install bubblewrap - Fedora:
dnf install bubblewrap - Arch:
pacman -S bubblewrap
- Ubuntu/Debian:
socat- 用于代理桥接的套接字中继- Ubuntu/Debian:
apt-get install socat - Fedora:
dnf install socat - Arch:
pacman -S socat
- Ubuntu/Debian:
ripgrep- 用于拒绝路径检测的快速搜索工具- Ubuntu/Debian:
apt-get install ripgrep - Fedora:
dnf install ripgrep - Arch:
pacman -S ripgrep
- Ubuntu/Debian:
Ubuntu 24.04+ 注意事项: 这些版本默认启用 kernel.apparmor_restrict_unprivileged_userns,这允许 unshare(CLONE_NEWUSER),但会从生成的命名空间中剥离 capabilities。bubblewrap 和 seccomp 隔离层都需要具有 capabilities 的用户命名空间。使用以下命令禁用该限制:```bash
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
或者添加一个 AppArmor 配置,向相关二进制文件授予 `userns` 权限。
**可选 Linux 依赖(用于 seccomp 回退):**
该包包含针对 x86-64 和 arm 架构预生成的 seccomp BPF 过滤器。仅当您使用的架构不同且没有预生成过滤器时,才需要这些依赖:
- `gcc` 或 `clang` - C 编译器
- `libseccomp-dev` - Seccomp 库开发文件
- Ubuntu/Debian:`apt-get install gcc libseccomp-dev`
- Fedora:`dnf install gcc libseccomp-devel`
- Arch:`pacman -S gcc libseccomp`
**macOS 要求:**
- `ripgrep` - 用于拒绝路径检测的快速搜索工具
- 通过 Homebrew 安装:`brew install ripgrep`
- 或从以下地址下载:https://github.com/BurntSushi/ripgrep/releases
**Windows 要求:**
- 无需额外依赖。`srt-win.exe` 辅助程序(x64 和 arm64)已随 npm 包捆绑。需要一次性以提升权限执行 `windows-install` 步骤——见下文。
## Windows(alpha)
Windows 支持目前为 **alpha** 阶段。沙箱进程在专用的 `srt-sandbox` 本地用户帐户下运行,通过原生 Windows 安全基元与调用用户隔离——这些基元包括基于沙箱帐户 SID 的 Windows 筛选平台(WFP)出站屏障,以及按会话显式配置的 ACE,用于授予或拒绝该 SID 对已配置文件系统路径的访问权限。
### 设置
每台机器运行一次(自动提升权限;会弹出一次 UAC 提示):```powershell
npx @anthropic-ai/sandbox-runtime windows-install
这将配置 srt-sandbox 本地用户帐户(其随机密码以 DPAPI 加密方式存储于 %LOCALAPPDATA%\sandbox-runtime\state.db)、sandbox-runtime-users 本地组,并安装以 srt-sandbox SID 为键的计算机级 WFP 筛选器集。它是幂等的——重新运行会轮换沙箱帐户的密码并协调筛选器集。
无需注销。 WFP 筛选器以专用沙箱帐户的 SID 为键,因此您自己的网络、服务以及机器上的所有其他主体均不受影响。
安装后,SandboxManager.initialize() 和 srt CLI 的行为与其他平台一致。initialize() 会验证沙箱帐户和 WFP 围栏是否处于活动状态,若未生效则失败并给出可操作错误。
编程式安装/卸载以 installWindowsSandbox() / uninstallWindowsSandbox() 的形式导出。
安全模型
沙箱命令以 srt-sandbox 帐户身份运行,而非调用用户。捆绑的 srt-win.exe 辅助程序执行两跳启动:broker 调用 CreateProcessWithLogonW 以 srt-sandbox 身份启动 runner,runner 再在作业对象内以受限令牌生成目标进程。子进程继承沙箱帐户的隔离配置文件(%USERPROFILE%、%TEMP%、HKCU)以及全新的环境,仅叠加 broker 的 PATH 和生成的代理变量。
以不同的用户 SID 运行,在结构上封堵了 surrogate-spawn 类逃逸(任务计划程序、PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 指向 broker 拥有的进程、BITS、以及 RunAs="Interactive User" 的进程外 COM):子进程设法带外生成的任何进程仍携带 srt-sandbox SID,因此仍受 WFP 出站围栏约束,且对调用用户的文件没有权限。
网络隔离是在 FWPM_LAYER_ALE_AUTH_CONNECT_V4/V6 层设置的双筛选器 WFP 集合:对配置的代理端口范围(默认 60080–60089)内的回环目标 PERMIT,并对任何令牌携带 srt-sandbox SID 的连接 BLOCK。沙箱进程只能通过监听该范围的 JS HTTP/SOCKS5 代理访问互联网;移除代理环境后直接连接的进程会在内核层被阻止。
文件系统隔离由 NTFS 自由访问控制列表(DACL)强制实施。srt-sandbox 帐户对调用用户的文件没有固有权限,因此在 initialize() 时,沙箱仅写入追加式、可继承的、仅针对 srt-sandbox SID 的显式 ACE——它从不重写或替换路径现有的安全描述符:
filesystem.allowWrite→ 一个可继承的MODIFYALLOW ACE(READ|WRITE|EXECUTE|DELETE,但排除FILE_DELETE_CHILD)。沙箱进程可以在工作树内创建、修改和删除文件;在授权中排除FILE_DELETE_CHILD是对下方 deny 标记的纵深防御,而非对树根的防护。filesystem.allowRead→ 一个可继承的READ|EXECUTEALLOW ACEfilesystem.denyRead/filesystem.denyWrite→ 在目标上设置一个可继承的 DENY ACE,并同时在其父目录上设置一个可继承的FILE_DELETE_CHILDDENY——与工作树授权中排除FILE_DELETE_CHILD的做法相结合,可阻止沙箱进程通过其父目录重命名或删除被拒绝的路径
reset() 会移除本会话添加的所有 ACE(通过 state.db 在并发主机间进行引用计数;下一次 initialize() 时会进行一次崩溃恢复过程,以清理未正常退出的残留)。支持目录目标(ACE 会继承到整个子树)。Glob 模式会在 initialize() 时展开为具体路径——之后才出现的匹配路径不会被覆盖。
Windows 上的 TLS 终止
network.tlsTerminate 要求 MITM CA 存在于沙箱用户的 CurrentUser\Root 证书存储中(schannel——System32\curl.exe、PowerShell Invoke-WebRequest、.NET 以及默认后端的 git 所使用的 TLS 后端——只信任操作系统存储,而不信任环境变量)。这是一个安装时步骤,与 windows-install 分开:```typescript
import { windowsTrustCa } from '@anthropic-ai/sandbox-runtime'
windowsTrustCa('/path/to/mitm-ca.crt') // or: srt-win user trust-ca
`initialize()` 会将会话 CA 的指纹与已安装的 CA 指纹进行比较,若不匹配则以可操作的信息失败,因此过期的安装时 CA 无法在沙箱内静默破坏 TLS。
基于 OpenSSL 的客户端(msys2 `curl`、`git -c http.sslBackend=openssl`、Node、Python、cargo)由环境变量信任层覆盖:macOS/Linux 上使用的同一信任包通过 `NODE_EXTRA_CA_CERTS`、`SSL_CERT_FILE`、`CURL_CA_BUNDLE`、`GIT_SSL_CAINFO`、`CARGO_HTTP_CAINFO` 等传入沙箱,并将该信任包路径添加到会话的 `allowRead` 授权中,以便沙箱账户可以打开它。
### Windows 专属配置
跨平台的 `filesystem` 与 `network` 块如前所述适用。仅 Windows 的设置位于 `windows` 下:
- `windows.proxyPortRange` — JS 代理绑定在内的闭区间端口范围 `[low, high]`。**必须与**传给 `windows-install --proxy-port-range` 的范围(默认 `[60080, 60089]`)**匹配** — WFP 环回 PERMIT 仅覆盖该范围。
- `windows.sublayerGuid` — 安装过滤器所用的 WFP 子层 GUID。省略则使用编译期默认值;仅当企业工具在自定义子层下安装了过滤器时才进行设置。
- `windows.srtWin.path` — `srt-win` 二进制文件的路径。省略则解析打包的 `vendor/srt-win/<arch>/srt-win.exe`。在将 `srt-win` 的 CLI 嵌入多调用二进制文件时设置;生成的进程随后将 `--srt-win` 作为 `argv[1]` 传递,以便嵌入方的调度器可以路由到 `srt_win::run_from_args`。
### 已知限制
- **schannel 下的证书吊销。** CryptoAPI 的 CRL/OCSP 获取通过 WinHTTP 使用调用方令牌发出,忽略代理环境,因此会被 WFP 出口围栏阻止。默认开启吊销检查且使用 schannel 的工具会以 `CRYPT_E_REVOCATION_OFFLINE`(`0x80092013`)失败,除非按工具禁用吊销:`curl --ssl-no-revoke`、`git -c http.schannelCheckRevoke=false`、`CARGO_HTTP_CHECK_REVOKE=false`。`Invoke-WebRequest`、.NET `HttpClient` 和 `gh` 默认不检查吊销,因此不受影响。计划从环回代理提供 CRL 分发点以移除该变通方案。
- **无法访问按用户安装的工具。** 沙箱化进程以 `srt-sandbox` 身份运行,而非你的身份,因此你个人资料下安装的工具(nvm/fnm 管理的 Node、按用户的 `winget`/Scoop 包、`pip install --user`、`%LOCALAPPDATA%\Programs\…`)可以在继承的 `PATH` 中解析,但沙箱账户无法打开它们。请优先使用机器级安装(`Program Files`、`choco`/`winget --scope machine`),或将特定配置文件路径添加到 `filesystem.allowRead`。
- **不支持按执行的 `filesystem.allowRead` / `filesystem.allowWrite` 覆盖。** 会话级 `allowRead`/`allowWrite`(在传给 `initialize()` 的配置中)按前述方式工作;在 `wrapWithSandbox` 的 `customConfig` 中按命令传递则会抛出异常 — 授权通过 `srt-win acl grant` 在 `initialize()` 时会话级应用,而 `srt-win exec` 仅暴露按执行的拒绝。
- **`proxyAuthToken` 在运行器的命令行中可见。** 代理环境(包括 `HTTP_PROXY=http://srt:<token>@127.0.0.1:…`)作为 `--env` 参数通过 `srt-win exec` 的 argv 传给两跳运行器,因此任何能够以 `PROCESS_QUERY_LIMITED_INFORMATION` 打开运行器进程的本地主体都可读取该令牌。令牌的存在是为了让沙箱化进程能够向环回代理进行身份验证,因此它对沙箱本身并不是秘密;在单用户开发机上这通常可以接受,但在共享主机上,请将代理允许列表视为同一会话的其他主体也可访问。
- **通过系统解析器的 DNS 解析不被围栏隔离。** `getaddrinfo()` 由以 `NETWORK SERVICE` 身份运行的 `Dnscache` 服务处理,因此即使沙箱化进程随后的 `connect()` 被阻止,名称解析仍会成功。自行执行 UDP/53 的工具(`nslookup`、`dig`)会被围栏隔离。这与 macOS 行为一致。
### 卸载```powershell
npx @anthropic-ai/sandbox-runtime windows-uninstall
移除 WFP 筛选器集、srt-sandbox 帐户及其配置文件、sandbox-runtime-users 组,并清除 state.db 中的凭据/安装标记(一次 UAC 提示)。%LOCALAPPDATA%\sandbox-runtime\state.db 本身保留在原位(它被 ACL 标记为仅 broker 可访问);如需彻底清除,请手动删除该目录。
开发```bash
Install dependencies
npm install
Build the project
npm run build
Run tests
npm test
Type checking
npm run typecheck
Lint code
npm run lint
Format code
npm run format
### 构建 Seccomp 二进制文件
BPF 过滤器和 `apply-seccomp` 加载器是从 `vendor/seccomp-src/` 中的 C 源代码通过 `npm run build:seccomp` 编译的(仅限 Linux;需要 `gcc` 和 `libseccomp-dev`)。CI 在每种 Linux 架构的测试之前运行它,发布工作流会构建这两种架构并将它们打包进发布的包中。
## 实现细节
### 网络隔离架构
沙箱在宿主机上运行 HTTP 和 SOCKS5 代理服务器,根据权限规则过滤所有网络请求:
1. **HTTP/HTTPS 流量**:HTTP 代理服务器拦截请求,并根据允许/拒绝的域名对其进行验证
2. **其他网络流量**:SOCKS5 代理处理所有其他 TCP 连接(SSH、数据库连接等)
3. **权限执行**:代理强制执行配置中的 `permissions` 规则
**平台特定的代理通信:**
- **Linux**:请求通过文件系统上的 Unix 域套接字路由(使用 `socat` 进行桥接)。网络命名空间已从 bubblewrap 容器中移除,确保所有网络流量都必须经过代理。
- **macOS**:Seatbelt 配置文件仅允许与代理监听的特定 localhost 端口通信。所有其他网络访问均被阻止。
- **Windows**:WFP `ALE_AUTH_CONNECT` 过滤器会阻止来自 `srt-sandbox` 帐户的所有出站连接,除非是回环到已配置的代理端口范围。代理在该范围内绑定。环境变量(`HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY` 等)将工具指向代理,但 WFP 过滤器才是边界——忽略或取消设置这些变量的进程仍然会被限制。
### 文件系统隔离
文件系统限制在操作系统级别强制执行:
- **macOS**:使用 `sandbox-exec` 和动态生成的 Seatbelt 配置文件,这些文件指定允许读/写的路径
- **Linux**:使用带绑定挂载的 `bubblewrap`,根据配置将目录标记为只读或可读写
- **Windows**:为 `srt-sandbox` SID 向配置的路径写入附加的 `(OI)(CI)` 显式 ACE(在 `allowRead`/`allowWrite` 上 ALLOW,在 `denyRead`/`denyWrite` 上 DENY),然后在 `reset()` 时将其移除
**默认文件系统权限:**
- **读取**(先拒绝后允许):默认在所有地方允许。你可以拒绝大范围区域,然后重新允许其中的特定路径。`allowRead` 优先于 `denyRead`。
- 示例:`denyRead: ["~/.ssh"]` 用于阻止访问 SSH 密钥
- 示例:`denyRead: ["/Users"], allowRead: ["."]` 用于阻止 `/Users` 中除工作区外的所有内容
- 空的 `denyRead: []` = 完全读取权限(未拒绝任何内容)
- **写入**(仅允许):默认在所有地方被拒绝。你必须显式允许路径。
- 示例:`allowWrite: [".", "/tmp"]` 允许写入当前目录和 /tmp
- 空的 `allowWrite: []` = 无写入权限(未允许任何内容)
- `denyWrite` 在允许的路径内创建例外(拒绝优先)
**读取和写入的优先级有意相反:** `allowRead` 覆盖 `denyRead`,而 `denyWrite` 覆盖 `allowWrite`。这使你可以被拒绝的区域中划出可读区域,并在可写区域中划出受保护的区域。
### 强制拒绝路径(自动保护的文件)
某些敏感文件和目录**始终被阻止写入**,即使它们位于允许的写入路径内。这提供了针对沙箱逃逸和配置篡改的纵深防御。
**始终被阻止的文件:**
- Shell 配置文件:`.bashrc`、`.bash_profile`、`.zshrc`、`.zprofile`、`.profile`
- Git 配置文件:`.gitconfig`、`.gitmodules`
- 其他敏感文件:`.ripgreprc`、`.mcp.json`
**始终被阻止的目录:**
- IDE 目录:`.vscode/`、`.idea/`
- Claude 配置目录:`.claude/commands/`、`.claude/agents/`
- Git hooks 和配置:`.git/hooks/`、`.git/config`
这些路径会被自动阻止——你无需将它们添加到 `denyWrite`。例如,即使使用 `allowWrite: ["."]`,写入 `.bashrc` 或 `.git/hooks/pre-commit` 也会失败:```bash
$ 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): 在 Linux 上,强制拒绝路径只能阻止已存在的文件。对于这些模式中尚不存在的文件,bubblewrap 的绑定挂载方式无法阻止。macOS 使用 glob 模式,可以同时阻止已存在和新文件。
Linux 搜索深度: 在 Linux 上,沙箱使用 ripgrep 在允许的写入路径内的子目录中扫描危险文件。出于性能考虑,默认最多搜索 3 层深度。你可以通过 mandatoryDenySearchDepth 进行配置:```json
{
"mandatoryDenySearchDepth": 5,
"filesystem": {
"allowWrite": ["."]
}
}
- 默认值:`3`(最多搜索 3 层深度)
- 范围:`1` 到 `10`
- 数值越大提供更多保护,但性能更慢
- 无论该设置如何,CWD(深度 0)中的文件始终受到保护
### Unix 套接字限制(Linux)
在 Linux 上,沙箱使用 **seccomp BPF(伯克利包过滤器)** 在系统调用层面阻止 Unix 域套接字的创建。这提供了额外一层安全防护,防止进程为本地 IPC 创建新的 Unix 域套接字(除非明确允许)。
**工作原理:**
1. **内置 BPF 过滤器**:该软件包附带针对 x64 和 arm64 的静态 `apply-seccomp` 二进制文件,其中编译了 seccomp BPF 过滤器。该过滤器与架构相关但与 libc 无关,因此该二进制文件同时适用于 glibc 和 musl。
2. **运行时检测**:沙箱会自动检测您系统的架构,并使用匹配的 `apply-seccomp` 二进制文件。
3. **系统调用过滤**:BPF 过滤器拦截 `socket()` 系统调用,并通过返回 `EPERM` 阻止 `AF_UNIX` 套接字的创建。这可以防止沙箱代码创建新的 Unix 域套接字。
4. **使用 apply-seccomp 二进制文件进行两阶段应用**:
- 外层 bwrap 创建带有文件系统、网络和 PID 命名空间限制的沙箱
- 网络桥接进程(socat)在沙箱内部启动(需要 Unix 套接字)
- apply-seccomp 创建嵌套的 user+PID+mount 命名空间并重新挂载 `/proc`
- 在嵌套命名空间内部,apply-seccomp 充当 PID 1(不可转储的 init/回收进程)
- apply-seccomp 执行 fork,通过 `prctl()` 应用 seccomp 过滤器,并 exec 用户命令
- 用户命令在全部沙箱限制以及 Unix 套接字创建阻断下运行
**PID 命名空间隔离**:嵌套的 PID 命名空间确保用户命令无法看到或访问任何未在 seccomp 过滤器下运行的进程(bwrap 的 init、shell 包装器或 socat 辅助进程)。由于未过滤的辅助进程无法通过 `ptrace` 或 `/proc/N/mem` 访问,因此无论 `kernel.yama.ptrace_scope` 如何设置,seccomp 边界都保持完整。内部 PID 1 设置 `PR_SET_DUMPABLE=0`,因此它同样不可被 ptrace。如果嵌套命名空间创建失败,apply-seccomp 将中止运行,而不是在无隔离的情况下继续。
**安全限制**:该过滤器阻止 `socket(AF_UNIX, ...)` 以及 `io_uring_setup`/`io_uring_enter`/`io_uring_register` 系统调用(后三个是因为 Linux 5.19+ 上的 `IORING_OP_SOCKET` 会绕过 `socket()` 规则)。它无法阻止对从父进程继承或通过 `SCM_RIGHTS` 传递的 Unix 套接字文件描述符进行操作。对于大多数沙箱场景,阻止套接字创建足以防止未经授权的 IPC。
**零运行时依赖**:针对 x64 和 arm64 架构,已包含预构建的静态 apply-seccomp 二进制文件和预生成的 BPF 过滤器。运行时无需编译工具或外部依赖。
**架构支持**:x64 和 arm64 通过预构建的二进制文件得到全面支持。其他架构目前不受支持。要在不受支持的架构上使用不带 Unix 套接字阻断的沙箱功能,请在配置中设置 `allowAllUnixSockets: true`。
### 违规检测与监控
当沙箱进程尝试访问受限资源时:
1. 在操作系统层面**阻止该操作**(返回 `EPERM` 错误)
2. **记录违规行为**(平台特定的机制)
3. **通知用户**(在 Claude Code 中,这会触发权限提示)
**macOS**:沙箱运行时接入 macOS 的系统沙箱违规日志存储。这提供实时通知,包含尝试了什么操作以及为何被阻止的详细信息。这也是 Claude Code 用于违规检测的相同机制。```bash
# View sandbox violations in real-time
log stream --predicate 'process == "sandbox-exec"' --style syslog
Linux:Bubblewrap 不提供内置的违规报告功能。使用 strace 跟踪系统调用并识别被阻止的操作:```bash
Trace all denied operations
strace -f srt 2>&1 | grep EPERM
Trace specific file operations
strace -f -e trace=open,openat,stat,access srt 2>&1 | grep EPERM
Trace network operations
strace -f -e trace=network srt 2>&1 | grep EPERM
### 高级:自带代理
对于更复杂的网络过滤,你可以配置沙盒使用你自己的代理,而不是内置的代理。这可以实现:
- **流量检查**:使用像 [mitmproxy](https://mitmproxy.org/) 这样的工具来检查和修改流量
- **自定义过滤逻辑**:实现超越简单域名白名单的复杂规则
- **审计日志**:记录所有网络请求以用于合规或调试
**mitmproxy 示例:**```bash
# Start mitmproxy with custom filtering script
mitmproxy -s custom_filter.py --listen-port 8888
Note: 新的配置格式尚不支持自定义代理配置。此功能将在未来版本中添加。
重要的安全考虑: 即使设置了域名允许列表,仍可能存在数据外泄途径。例如,允许 github.com 会使进程能够向任意仓库推送内容。借助自定义 MITM 代理和适当的证书配置,您可以检查并过滤特定的 API 调用以防止这种情况。
安全限制
- 网络沙箱限制:网络过滤系统通过限制进程允许连接的域名来运作。它不会另外检查通过代理的流量,用户有责任确保其策略中只允许受信任的域名。
- 通过 Unix 套接字提升权限:
allowUnixSockets配置可能无意中授予对强大系统服务的访问权限,从而导致沙箱绕过。例如,如果用它允许访问/var/run/docker.sock,实际上将通过对 Docker 套接字的利用而授予对主机系统的访问权限。建议用户仔细考虑他们通过沙箱允许的任何 Unix 套接字。 - 文件系统权限提升:过于宽泛的文件系统写入权限可能促成权限提升攻击。允许写入包含
$PATH中可执行文件的目录、系统配置目录或用户 shell 配置文件(.bashrc、.zshrc)可能导致其他用户或系统进程访问这些文件时在不同安全上下文中执行代码。 - Linux 沙箱强度:Linux 实现提供了强大的文件系统和网络隔离,但包含一个
enableWeakerNestedSandbox模式,使其能够在没有特权命名空间的 Docker 环境中工作。此选项会显著削弱安全性,仅应在其他地方已强制实施额外隔离的情况下使用。 - 较弱的网络隔离(macOS):
enableWeakerNetworkIsolation选项会重新启用对com.apple.trustd.agent的访问,这是 Go 程序通过 macOS Security 框架验证 TLS 证书所必需的。这通过 trustd 服务打开了一条潜在的数据外泄途径,只有在需要 Go TLS 验证时才应启用(例如,在使用httpProxyPort配合 MITM 代理和自定义 CA 时)。 - Apple Events(macOS):
allowAppleEvents选项会重新启用发送 Apple Events 和 Launch Services 打开请求((allow appleevent-send)、(allow lsopen),以及对com.apple.coreservices.appleevents、com.apple.CoreServices.coreservicesd和com.apple.coreservices.quarantine-resolver的 mach-lookups),open、osascript和 URL 打开辅助程序需要这些权限。允许这些权限后,沙箱中的命令可以启动任意应用程序而无需用户提示,且被启动的应用程序完全在沙箱之外运行——因此该选项不是削弱代码执行隔离,而是彻底移除。通过 Apple Events 对已运行应用进行脚本控制还会受 macOS TCC 自动化同意的额外限制,但通过open启动则不受此限制。仅当沙箱内的命令确实需要打开 URL 或应用程序时才启用此选项。
已知限制与未来工作
Linux 代理绕过:目前使用环境变量(HTTP_PROXY、HTTPS_PROXY、ALL_PROXY)通过代理引导流量。这对大多数应用有效,但某些不遵循这些变量的程序可能会忽略它们,导致无法连接互联网。
未来改进:
-
Proxychains 支持:在 Linux 上增加对
proxychains与LD_PRELOAD的支持,以在更底层拦截网络调用,使绕过更加困难 -
Linux 违规监控:在 Linux 上实现基于
strace的自动违规检测,并与违规存储集成。目前,Linux 用户必须手动运行strace才能查看违规,而 macOS 则通过系统日志存储自动进行违规监控