sandbox-runtime:基于 OS 原生隔离技术的轻量级进程沙箱工具

A lightweight sandboxing tool for enforcing filesystem and network restrictions on arbitrary processes at the OS level, without requiring a container.

分支220Tags35
当前项目代码仓暂无内容

Anthropic Sandbox Runtime (srt)

一款轻量级沙箱工具,可在操作系统层面为任意进程实施文件系统和网络限制,无需容器支持。

srt 采用原生操作系统沙箱原语(macOS 上为 sandbox-exec,Linux 上为 bubblewrap)和基于代理的网络过滤技术。它可用于对智能体、本地 MCP 服务器、bash 命令及任意进程的行为进行沙箱隔离。

Beta 研究预览版

Sandbox Runtime 是为 Claude Code 开发的研究预览版,旨在实现更安全的 AI 智能体。我们将其作为早期开源预览版发布,以帮助更广泛的生态系统构建更安全的智能体系统。由于这是早期研究预览版,API 和配置格式可能会不断演变。我们欢迎大家提供反馈和贡献,共同打造默认更安全的 AI 智能体!

安装

npm install -g @anthropic-ai/sandbox-runtime

基本用法

# 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):

{
  "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 服务器将被阻止写入被拒绝的路径:

> Write a file to ~/sensitive-folder
✗ Error: EPERM: operation not permitted, open '/Users/ollie/sensitive-folder/test.txt'

工作原理

sandbox-runtime 利用操作系统级原语来实施适用于整个进程树的限制:

  • macOS:使用 sandbox-exec 配合动态生成的 Seatbelt 配置文件
  • Linux:使用 bubblewrap 进行容器化,并提供网络命名空间隔离
  • Windows:在专用的 srt-sandbox 本地用户账户下运行沙箱进程,结合 Windows 筛选平台 出口防护(基于该账户的 SID)以及工作目录树的每会话显式 ACE

0d1c612947c798aef48e6ab4beb7e8544da9d41a-4096x2305

双重隔离模型

有效的沙箱防护需要同时实现文件系统隔离和网络隔离。若缺乏文件隔离,受感染的进程可能会窃取 SSH 密钥或其他敏感文件;若缺乏网络隔离,进程则可能突破沙箱限制,获得无限制的网络访问权限。

文件系统隔离 实施读写限制:

  • 读取(先拒绝后允许模式):默认允许读取所有位置。您可以拒绝访问广泛区域(例如 /Users),然后重新允许其中的特定路径(例如 .)。allowRead 优先于 denyRead —— 这与写入权限相反,denyWrite 优先于 allowWrite。如果 denyRead 条目比其所在的 allowRead 区域更具体(例如 denyRead: ["**/.env"] 或在 allowRead: ["."] 时使用 ["./secrets"]),则该条目仍保持拒绝状态。
  • 写入(仅允许模式):默认拒绝所有位置的写入权限。您必须显式允许路径(例如 ./tmp)。空的允许列表意味着无写入权限。

网络隔离(仅允许模式):默认拒绝所有网络访问。您必须显式允许域名。空的 allowedDomains 列表意味着无网络访问权限。网络流量通过主机上运行的代理服务器路由:

  • Linux:请求通过 Unix 域套接字经由文件系统路由。沙箱进程的网络命名空间被完全移除,因此所有网络流量必须通过主机上运行的代理(监听绑定挂载到沙箱内的 Unix 套接字)
  • macOS:Seatbelt 配置文件仅允许与特定本地主机端口通信。代理监听此端口,为所有网络访问创建受控通道
  • 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

使用方法

作为命令行工具

srt 命令(Anthropic Sandbox Runtime)可在任意命令外包裹安全边界:

# 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

作为库

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 的内容。

const wrapped = await SandboxManager.wrapWithSandbox(
  assembledCommand, // what actually runs
  undefined,
  undefined,
  undefined,
  { commandId: invocationId, commandText: rawCommand },
)
// ... run it ...
const annotated = SandboxManager.annotateStderrWithSandboxFailures(invocationId, stderr)

可用导出项

// 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'

配置

设置文件位置

默认情况下,sandbox runtime 会在 ~/.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"],
    "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 推送到 GitHub 已被阻止;请使用 https:// 远程仓库"})。没有原因的条目将报告一个通用原因。对于 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 过滤)解密后的请求。沙箱进程会指向一个信任 bundle,其中包含 MITM CA(caCertPath/caKeyPath,如果省略则使用临时 CA)以及主机的常规根证书,因此代理生成的证书和真实的上游证书都能通过验证。

  • network.tlsTerminate.excludeDomains - 不进行终止的域名模式(语法与 allowedDomains 相同)。匹配的 CONNECT 请求会被透明地隧道传输:它们仍然受域名允许列表的限制,但沙箱内的客户端会与真实的上游服务器完成自己的 TLS 握手,并且 filterRequest/凭据注入不适用于它们的 HTTPS 流量。在以下两种 TLS 终止从根本上会破坏连接的情况下使用此选项:
    • mTLS 上游——只有沙箱内的客户端持有客户端证书,因此代理无法代表其重新发起连接。
    • 证书固定客户端——自行验证上游服务器身份(自定义 CA、SAN 固定)并拒绝 MITM 证书的客户端。
  • network.tlsTerminate.extraCaCertPaths - PEM 格式 CA 证书文件的路径,这些文件会附加到上述信任 bundle 中,位于 MITM CA 和主机常规根证书之后。被排除(未终止)的主机由沙箱内的客户端进行验证,SRT 设置的信任环境变量(SSL_CERT_FILEGIT_SSL_CAINFO 等)会替换各个工具自己的信任配置,因此站点本地根证书(例如内部 mTLS CA)必须包含在 bundle 中,否则这些主机将永远无法通过验证。每个文件中只有 CERTIFICATE 块会被复制到 bundle 中(其他任何内容,例如组合 PEM 中的私钥,绝不会暴露给沙箱);对于缺失、不可读或不包含 PEM CERTIFICATE 块的文件会被跳过,因此可以安全地列出仅在某些主机上存在的路径。
{
  "network": {
    "allowedDomains": ["*.example.com", "internal-mtls.example.net"],
    "deniedDomains": [],
    "tlsTerminate": {
      "excludeDomains": ["internal-mtls.example.net"],
      "extraCaCertPaths": ["/etc/internal-mtls-roots.pem"]
    }
  }
}

Unix 套接字设置(平台特定行为):

设置项 macOS Linux
allowUnixSockets: string[] 套接字路径允许列表 忽略(seccomp 无法按路径过滤)
allowAllUnixSockets: boolean 允许所有套接字 禁用 seccomp 阻止

Unix 套接字在两个平台上均默认阻止

  • macOS:使用 allowUnixSockets 允许特定路径(例如 ["/var/run/docker.sock"]),或设置 allowAllUnixSockets: true 允许所有。
  • Linux:阻止功能使用 seccomp 过滤器(仅支持 x64/arm64)。如果 seccomp 不可用,套接字将不受限制并显示警告。使用 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)。当使用带有 MITM 代理和自定义 CA 的 httpProxyPort 时,Go 程序(ghgcloudterraformkubectl 等)需要此选项来验证 TLS 证书。安全警告: 启用此选项会通过 trustd 服务打开潜在的数据泄露渠道。
  • allowAppleEvents - 允许从 macOS 沙箱发送 Apple 事件和 Launch Services 打开请求(布尔值,默认:false)。如果没有此选项,openosascript 等命令以及任何通过 AppleScript 打开 URL 或其他应用程序脚本的操作将失败,并出现 AppleScript 错误 -600(“应用程序未运行”)或 LaunchServices 错误(-10822-54)。安全警告: 启用此选项意味着沙箱不再提供代码执行隔离。沙箱中的命令可以通过 open 启动其他应用程序,且无需用户提示,其启动的任何内容都在沙箱的文件系统和网络限制之外运行;通过 Apple 事件脚本化已运行的应用程序还受到用户的每应用 TCC 自动化许可的限制。嵌入程序应仅从受信任的用户级配置中获取此选项——绝不要从检出仓库中的项目本地文件获取,因为这会让攻击者编写的项目提升自身的沙箱权限。

常见配置方案

允许 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/"]
  }
}

仅工作区文件系统访问(拒绝读取工作区外内容):

{
  "network": {
    "allowedDomains": [],
    "deniedDomains": []
  },
  "filesystem": {
    "denyRead": ["/Users"],
    "allowRead": ["."],
    "allowWrite": ["."],
    "denyWrite": []
  }
}

这会禁止读取 /Users(在 Linux 上为 /home)下的任何内容,然后重新允许当前工作目录。系统路径(/usr/lib 等)仍保持可读。

常见问题与提示

运行 Jest: 使用 --no-watchman 标志以避免沙箱违规:

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
  • socat - 用于代理桥接的 socket 中继
    • Ubuntu/Debian:apt-get install socat
    • Fedora:dnf install socat
    • Arch:pacman -S socat
  • ripgrep - 用于拒绝路径检测的快速搜索工具
    • Ubuntu/Debian:apt-get install ripgrep
    • Fedora:dnf install ripgrep
    • Arch:pacman -S ripgrep

Ubuntu 24.04+ 注意事项: 这些版本默认启用 kernel.apparmor_restrict_unprivileged_userns,它允许 unshare(CLONE_NEWUSER) 但会从生成的命名空间中剥离权限。bubblewrap 和 seccomp 隔离层都需要具有权限的用户命名空间。通过以下命令禁用此限制:

sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

或添加一个 AppArmor 配置文件,向相关二进制文件授予 userns 权限。

可选的 Linux 依赖项(用于 seccomp 回退):

该软件包包含为 x86-64 和 arm 架构预生成的 seccomp BPF 过滤器。仅当您使用的是预生成过滤器不可用的其他架构时,才需要这些依赖项:

  • gccclang - C 编译器
  • libseccomp-dev - Seccomp 库开发文件
    • Ubuntu/Debian:apt-get install gcc libseccomp-dev
    • Fedora:dnf install gcc libseccomp-devel
    • Arch:pacman -S gcc libseccomp

macOS 需要:

Windows 需要:

  • 无其他依赖项。srt-win.exe 辅助程序(x64 和 arm64)已捆绑在 npm 软件包中。需要执行一次性的 elevated windows-install 步骤 — 见下文。

Windows (alpha)

Windows 支持目前为 alpha 版本。沙箱进程在专用的 srt-sandbox 本地用户账户下运行,通过原生 Windows 安全原语与调用用户隔离 — 一个基于沙箱账户 SID 的 Windows 筛选平台 (WFP) 出口防护,以及为该 SID 授予或拒绝访问已配置文件系统路径的每会话显式 ACE。

安装

每台机器运行一次(会自行提升权限;出现一个 UAC 提示):

npx @anthropic-ai/sandbox-runtime windows-install

此操作会配置 srt-sandbox 本地用户账户(其随机密码通过 DPAPI 加密存储在 HKLM\SOFTWARE\sandbox-runtime 中——此为计算机范围设置,因此以 SYSTEM 身份运行的批量安装可正常工作,且一名用户更新密码后,其他用户读取的副本也会同步更新)、sandbox-runtime-users 本地组,并安装一个以 srt-sandbox SID 为关键字的计算机范围 WFP 筛选器集。此操作具有幂等性——重新运行时会轮换沙箱账户的密码并协调筛选器集。

无需注销。WFP 筛选器基于专用沙箱账户的 SID,因此您自己的网络、服务以及计算机上的所有其他主体均不受影响。

安装完成后,SandboxManager.initialize()srt 命令行工具的工作方式与在其他平台上一致。initialize() 会验证沙箱账户和 WFP 防护是否处于活动状态,如未处于活动状态,则会返回可操作的错误信息。

程序化安装/卸载功能通过 installWindowsSandbox() / uninstallWindowsSandbox() 导出。

安全模型

受沙箱限制的命令srt-sandbox 账户身份运行,而非调用用户的身份。捆绑的 srt-win.exe 辅助程序执行两跳启动:代理程序调用 CreateProcessWithLogonWsrt-sandbox 身份启动运行器,然后运行器在作业对象内使用受限令牌生成目标进程。子进程继承沙箱账户的隔离配置文件(%USERPROFILE%%TEMP%HKCU)以及一个全新的环境,该环境仅叠加了代理程序的 PATH 和生成的代理变量。

在不同的用户 SID 下运行,从结构上阻止了代理生成类别的逃逸(任务计划程序、对代理拥有的进程使用 PROC_THREAD_ATTRIBUTE_PARENT_PROCESS、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 → 可继承的 MODIFY 允许 ACE(READ|WRITE|EXECUTE|DELETE,但不包含 FILE_DELETE_CHILD)。受沙箱限制的进程可以在工作树内创建、修改和删除文件;在授予中不包含 FILE_DELETE_CHILD 是对下文拒绝标记的纵深防御,而非对树根的保护。
  • filesystem.allowRead → 可继承的 READ|EXECUTE 允许 ACE
  • filesystem.denyRead / filesystem.denyWrite → 目标上的可继承拒绝 ACE,以及其父目录上的可继承 FILE_DELETE_CHILD 拒绝 ACE——结合工作树授予中不包含的 FILE_DELETE_CHILD,这可以阻止受沙箱限制的进程通过其父目录重命名或删除被拒绝的路径

reset() 会移除本次会话添加的所有 ACE(通过每用户会话数据库对该用户的并发主机进行引用计数;下次 initialize() 时的崩溃恢复过程会清理异常退出后的残留)。支持目录目标(ACE 会继承到整个子树)。通配符模式在 initialize() 时会扩展为具体路径——之后出现的匹配路径不在覆盖范围内。

Windows 上的 TLS 终止

network.tlsTerminate 要求 MITM CA 存在于沙箱用户CurrentUser\Root 证书存储中(schannel——System32\curl.exe、PowerShell Invoke-WebRequest、.NET 和默认后端 git 所使用的 TLS 后端——仅信任 OS 存储,而不信任环境变量)。这是一个安装时步骤,与 windows-install 分开:

import { windowsTrustCa } from '@anthropic-ai/sandbox-runtime'
windowsTrustCa('/path/to/mitm-ca.crt') // or: srt-win user trust-ca <path>

initialize() 会将会话 CA 的指纹与已安装的指纹进行比较,如果不匹配则会失败并显示可操作的消息,这样安装时的陈旧 CA 就不会在沙箱内无声地破坏 TLS。

基于 OpenSSL 的客户端(msys2 curlgit -c http.sslBackend=openssl、Node、Python、cargo)由环境变量信任层覆盖:macOS/Linux 上使用的相同信任束通过 NODE_EXTRA_CA_CERTSSSL_CERT_FILECURL_CA_BUNDLEGIT_SSL_CAINFOCARGO_HTTP_CAINFO 等传入沙箱,并且该信任束路径会添加到会话的 allowRead 授权中,以便沙箱账户可以打开它。

Windows 特定配置

跨平台的 filesystemnetwork 块如上文所述适用。Windows 专用设置位于 windows 下:

  • windows.proxyPortRange — JS 代理在内部绑定的 [low, high] 包含端口范围。必须匹配 传递给 windows-install --proxy-port-range 的范围(默认 [60080, 60089])——WFP 环回 PERMIT 仅覆盖该范围。
  • windows.sublayerGuid — 安装筛选器时所在的 WFP 子层 GUID。省略则使用编译时默认值;仅当企业工具在自定义子层下安装筛选器时才设置。
  • windows.srtWin.pathsrt-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-revokegit -c http.schannelCheckRevoke=falseCARGO_HTTP_CHECK_REVOKE=falseInvoke-WebRequest、.NET HttpClientgh 默认不检查吊销,因此不受影响。计划通过环回代理提供 CRL 分发点以移除此解决方法。
  • 每用户工具安装不可访问。 沙箱进程以 srt-sandbox 身份运行,而非当前用户,因此安装在用户配置文件下的工具(nvm/fnm 管理的 Node、每用户 winget/Scoop 包、pip install --user%LOCALAPPDATA%\Programs\…)会在继承的 PATH 中解析,但沙箱账户无法打开它们。建议使用计算机范围的安装(Program Fileschoco/winget --scope machine),或将特定的配置文件路径添加到 filesystem.allowRead
  • 不支持每个执行的 filesystem.allowRead / filesystem.allowWrite 覆盖。 会话级别的 allowRead/allowWrite(在传递给 initialize() 的配置中)如上文所述工作;在 wrapWithSandboxcustomConfig 中按命令传递它们会抛出错误——授权在 initialize() 时通过 srt-win acl grant 应用于整个会话,而 srt-win exec 仅公开每个执行的拒绝项。
  • proxyAuthToken 在运行器的命令行中可见。 代理环境(包括 HTTP_PROXY=http://srt:<token>@127.0.0.1:…)作为 srt-win exec 命令行上的 --env 参数传递给两跳运行器,因此任何能够以 PROCESS_QUERY_LIMITED_INFORMATION 权限打开运行器进程的本地主体都可以读取该令牌。该令牌的存在是为了让沙箱进程能够向环回代理进行身份验证,因此对于沙箱本身而言它不是秘密;在单用户开发机器上,这通常是可接受的,但在共享主机上,应将代理允许列表视为可被其他同会话主体访问。
  • 通过系统解析器的 DNS 解析不受防护。 getaddrinfo() 由以 NETWORK SERVICE 身份运行的 Dnscache 服务提供服务,因此即使沙箱进程后续的 connect() 被阻止,名称解析仍然会成功。自行进行 UDP/53 通信的工具(nslookupdig)会受到防护。这与 macOS 的行为一致。

卸载

npx @anthropic-ai/sandbox-runtime windows-uninstall

移除 WFP 筛选器集、srt-sandbox 账户及其配置文件、sandbox-runtime-users 组,并删除 HKLM\SOFTWARE\sandbox-runtime 项(凭据、标记、CA 记录)——需要一个 UAC 提示。%ProgramData%\sandbox-runtime(CA 密钥材料)会保留;若要完全清理,请手动删除它(以及每个用户的 %LOCALAPPDATA%\sandbox-runtime)。

开发

# 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 加载器通过 npm run build:seccompvendor/seccomp-src/ 目录中的 C 源代码编译而来(仅限 Linux;需要 gcclibseccomp-dev)。CI 会在每个 Linux 架构上的测试前运行此命令,发布工作流则会构建两种架构的二进制文件并将其打包到发布的软件包中。

实现细节

网络隔离架构

沙箱在主机上运行 HTTP 和 SOCKS5 代理服务器,这些服务器会根据权限规则过滤所有网络请求:

  1. HTTP/HTTPS 流量:HTTP 代理服务器拦截请求,并根据允许/拒绝的域名进行验证
  2. 其他网络流量:SOCKS5 代理处理所有其他 TCP 连接(SSH、数据库连接等)
  3. 权限执行:代理强制执行配置中的 permissions 规则

特定平台的代理通信方式

  • Linux:请求通过 Unix 域套接字经由文件系统路由(使用 socat 进行桥接)。bubblewrap 容器的网络命名空间被移除,确保所有网络流量都必须通过代理。

  • macOS:Seatbelt 配置文件仅允许与代理监听的特定本地端口通信。所有其他网络访问均被阻止。

  • Windows:WFP ALE_AUTH_CONNECT 过滤器阻止 srt-sandbox 账户的所有出站连接,但允许回环到配置的代理端口范围。代理绑定在该范围内。环境变量(HTTP_PROXYHTTPS_PROXYALL_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 钩子和配置:.git/hooks/.git/config

这些路径会被自动阻止——无需将它们添加到 denyWrite 中。例如,即使设置了 allowWrite: ["."],写入 .bashrc.git/hooks/pre-commit 也会失败:

$ 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 进行配置:

{
  "mandatoryDenySearchDepth": 5,
  "filesystem": {
    "allowWrite": ["."]
  }
}
  • 默认值:3(最多搜索 3 层深度)
  • 范围:110
  • 值越高,保护效果越好,但性能会相应降低
  • 当前工作目录(CWD,深度 0)中的文件始终受到保护,不受此设置影响

Unix 套接字限制(Linux)

在 Linux 系统上,沙箱使用 seccomp BPF(Berkeley 包过滤器) 在系统调用级别阻止 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 创建嵌套的用户+PID+挂载命名空间并重新挂载 /proc
    • 在嵌套命名空间内,apply-seccomp 充当 PID 1(不可转储的 init/收割进程)
    • apply-seccomp 进行分叉,通过 prctl() 应用 seccomp 过滤器,然后执行用户命令
    • 用户命令在所有沙箱限制以及 Unix 套接字创建阻止的条件下运行

PID 命名空间隔离:嵌套的 PID 命名空间确保用户命令无法看到或访问任何未运行 seccomp 过滤器的进程(bwrap 的 init、shell 包装器或 socat 辅助进程)。这保持了 seccomp 边界的完整性,而不受 kernel.yama.ptrace_scope 的影响,因为未过滤的辅助进程无法通过 ptrace/proc/N/mem 访问。内部 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:sandbox-runtime 接入 macOS 的系统沙箱违规日志存储。这能提供实时通知,包含关于尝试访问的内容以及被阻止原因的详细信息。这与 Claude Code 用于违规检测的机制相同。

# View sandbox violations in real-time
log stream --predicate 'process == "sandbox-exec"' --style syslog

Linux:Bubblewrap 不提供内置的违规报告功能。可使用 strace 跟踪系统调用并识别被阻止的操作:

# Trace all denied operations
strace -f srt <your-command> 2>&1 | grep EPERM

# Trace specific file operations
strace -f -e trace=open,openat,stat,access srt <your-command> 2>&1 | grep EPERM

# Trace network operations
strace -f -e trace=network srt <your-command> 2>&1 | grep EPERM

高级用法:使用自定义代理

若需更复杂的网络过滤,您可以将沙箱配置为使用自定义代理,而非内置代理。这支持以下功能:

  • 流量检查:使用 mitmproxy 等工具检查和修改流量
  • 自定义过滤逻辑:实现简单域名允许列表之外的复杂规则
  • 审计日志:记录所有网络请求以满足合规要求或进行调试

mitmproxy 示例:

# Start mitmproxy with custom filtering script
mitmproxy -s custom_filter.py --listen-port 8888

注意:新配置格式暂不支持自定义代理配置。此功能将在未来版本中添加。

重要安全注意事项:即使设置了域名允许列表,仍可能存在数据泄露途径。例如,允许 github.com 会使进程能够推送到任何仓库。通过自定义 MITM 代理和正确的证书设置,您可以检查并过滤特定 API 调用以防止此类情况。

安全限制

  • 网络沙箱限制:网络过滤系统通过限制进程允许连接的域名来运行。它不会检查通过代理的流量,用户负责确保在其策略中只允许受信任的域名。
用户应注意允许 `github.com` 等宽泛域名可能带来的数据泄露风险。此外,在某些情况下,可能通过[域名前置](https://en.wikipedia.org/wiki/Domain_fronting)绕过网络过滤。
  • 通过 Unix 套接字进行权限提升:allowUnixSockets 配置可能会无意中授予对强大系统服务的访问权限,从而导致沙箱绕过。例如,如果用于允许访问 /var/run/docker.sock,这将通过利用 docker 套接字有效地授予对主机系统的访问权限。建议用户仔细考虑允许通过沙箱访问的任何 unix 套接字。
  • 文件系统权限提升:过于宽泛的文件系统写入权限可能会启用权限提升攻击。允许写入包含 $PATH 中的可执行文件的目录、系统配置目录或用户 shell 配置文件(.bashrc.zshrc),可能会在其他用户或系统进程访问这些文件时导致在不同安全上下文中执行代码。
  • Linux 沙箱强度:Linux 实现提供了强大的文件系统和网络隔离,但包含一个 enableWeakerNestedSandbox 模式,使其能够在没有特权命名空间的 Docker 环境中工作。此选项会显著削弱安全性,仅应在通过其他方式强制执行额外隔离的情况下使用。
  • 较弱的网络隔离(macOS):enableWeakerNetworkIsolation 选项重新启用对 com.apple.trustd.agent 的访问,这是 Go 程序通过 macOS 安全框架验证 TLS 证书所必需的。这会通过 trustd 服务打开一个潜在的数据泄露途径,仅应在需要 Go TLS 验证时启用(例如,当将 httpProxyPort 与 MITM 代理和自定义 CA 一起使用时)。
  • Apple 事件(macOS):allowAppleEvents 选项重新启用发送 Apple 事件和 Launch Services 打开请求((allow appleevent-send)(allow lsopen),以及对 com.apple.coreservices.appleeventscom.apple.CoreServices.coreservicesdcom.apple.coreservices.quarantine-resolver 的 mach 查找),这些是 openosascript 和 URL 打开助手所必需的。启用这些后,沙箱内的命令可以无用户提示地启动任意应用程序,并且启动的应用程序完全在沙箱外运行——因此此选项会移除代码执行隔离,而不仅仅是削弱它。通过 Apple 事件编写已运行应用程序的脚本还受到 macOS TCC 自动化许可的限制,但通过 open 启动则不受限制。仅当沙箱内的命令确实需要打开 URL 或应用程序时才启用此选项。

已知限制与未来工作

Linux 代理绕过:目前通过环境变量(HTTP_PROXYHTTPS_PROXYALL_PROXY)将流量引导至代理。此方式对大多数应用有效,但可能会被不遵循这些变量的程序忽略,导致它们无法连接互联网。

未来改进

  • Proxychains 支持:在 Linux 上通过 LD_PRELOAD 添加对 proxychains 的支持,在更低层级拦截网络调用,增加绕过难度

  • Linux 违规监控:为 Linux 实现基于 strace 的自动违规检测,并与违规存储集成。目前,Linux 用户必须手动运行 strace 才能查看违规情况,而 macOS 则通过系统日志存储实现了自动违规监控

项目介绍

一款轻量级沙箱工具,用于在操作系统层面为任意进程强制执行文件系统和网络限制,无需容器支持。【此简介由AI生成】

定制我的领域
215.2 K434访问 GitHub