anthropic-experimental/sandbox-runtime

GitHub: anthropic-experimental/sandbox-runtime

一款轻量级 OS 级沙盒运行时工具,无需容器即可对任意进程强制执行文件系统和网络访问限制。

Stars: 4730 | Forks: 367

# Anthropic Sandbox Runtime (srt) 一款轻量级的沙盒工具,用于在操作系统层面对任意进程强制执行文件系统和网络限制,且无需使用容器。 `srt` 使用原生的 OS 沙盒原语(macOS 上的 `sandbox-exec`,Linux 上的 `bubblewrap`)以及基于代理的网络过滤机制。它可用于对 agents、本地 MCP 服务器、bash 命令及任意进程进行沙盒化处理。 ## 安装 ``` npm install -g @anthropic-ai/sandbox-runtime ``` ## 基本用法 ``` # 网络限制 $ srt "curl anthropic.com" Running: curl anthropic.com ... # Request succeeds $ srt "curl example.com" Running: curl example.com Connection blocked by network allowlist # Request blocked # 文件系统限制 $ 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 # Specific file blocked ``` ## 概述 本包提供了一个独立的沙盒实现,既可以作为 CLI 工具使用,也可以作为库使用。它的设计秉持了针对常见开发者用变量身定制的**默认安全**理念:进程启动时具有最低的访问权限,您只需显式开放必要的权限缺口。 **核心功能:** - **网络限制**:控制可以通过 HTTP/HTTPS 及其他协议访问哪些主机/域名 - **文件系统限制**:控制可以读取/写入哪些文件/目录 - **Unix socket 限制**:控制对本地 IPC socket 的访问 - **违规监控**:在 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 服务器将被阻止写入被拒绝的路径: ``` > Write a file to ~/sensitive-folder ✗ Error: EPERM: operation not permitted, open '/Users/ollie/sensitive-folder/test.txt' ``` ## 工作原理 该沙盒使用操作系统级别的原语来强制执行适用于整个进程树的限制: - **macOS**:使用 `sandbox-exec` 以及动态生成的 [Seatbelt 配置文件](https://reverse.put.as/wp-content/uploads/2011/09/Apple-Sandbox-Guide-v1.0.pdf) - **Linux**:使用 [bubblewrap](https://github.com/containers/bubblewrap) 进行容器化,并结合网络 namespace 隔离 ![0d1c612947c798aef48e6ab4beb7e8544da9d41a-4096x2305](https://github.com/user-attachments/assets/76c838a9-19ef-4d0b-90bb-cbe1917b3551) ### 双重隔离模型 要实现有效的沙盒化,文件系统和网络隔离都是必不可少的。如果没有文件隔离,被攻破的进程可能会窃取 SSH 密钥或其他敏感文件。如果没有网络隔离,进程可能会逃逸出沙盒并获得不受限制的网络访问权限。 **文件系统隔离**强制执行读取和写入限制: - **读取**(先拒绝后允许模式):默认情况下,所有位置都允许读取访问。您可以先拒绝大范围区域(例如 `/Users`),然后再允许其中的特定路径(例如 `.`)。`allowRead` 的优先级高于 `denyRead` —— 这与写入正好相反,写入时 `denyWrite` 的优先级高于 `allowWrite`。 - **写入**(仅允许模式):默认情况下,所有位置都拒绝写入访问。您必须显式允许路径(例如 `.`、`/tmp`)。空的允许列表意味着没有任何写入权限。 **网络隔离**(仅允许模式):默认情况下,所有网络访问都被拒绝。您必须显式允许域名。空的 allowedDomains 列表意味着没有任何网络访问权限。网络流量通过运行在主机上的代理服务器进行路由: - **Linux**:请求通过文件系统经由 Unix domain socket 路由。沙盒进程的 network namespace 被完全移除,因此所有网络流量都必须通过运行在主机上的代理(监听绑定挂载到沙盒中的 Unix socket) - **macOS**:Seatbelt 配置文件仅允许与特定的 localhost 端口进行通信。代理监听此端口,为所有网络访问创建一个受控的通道 HTTP/HTTPS(通过 HTTP 代理)和其他 TCP 流量(通过 SOCKS5 代理)均由这些代理进行中介,代理会强制执行您的域名允许列表和拒绝列表。 有关 Claude Code 中沙盒的更多详细信息,请参阅: - [Claude Code 沙盒文档](https://docs.claude.com/en/docs/claude-code/sandboxing) - [超越权限提示:让 Claude Code 更安全、更自主](https://www.anthropic.com/engineering/claude-code-sandboxing) ## 架构 ``` 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 ``` ## 用法 ### 作为 CLI 工具 `srt` 命令(Anthropic Sandbox Runtime)使用安全边界包装任何命令: ``` # 在 sandbox 中运行命令 srt echo "hello world" # 启用 debug 日志 srt --debug curl https://example.com # 指定自定义 settings 文件 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() }) ``` #### 可用的导出 ``` // 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` 标志指定自定义路径: ``` srt --settings /path/to/srt-settings.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` 等通配符)。空数组 = 无网络访问。 - `network.deniedDomains` - 拒绝的域名数组(最先检查,优先级高于 allowedDomains) - `network.allowLocalBinding` - 允许绑定到本地端口(布尔值,默认值:false) **TLS termination** (`network.tlsTerminate`, 实验性):设置后,HTTPS CONNECT 会在进程内被终止,以便 SRT 能够查看(并通过 `network.filterRequest` 过滤)解密后的请求。沙盒进程会被指向一个信任束,其中包含 MITM CA(`caCertPath`/`caKeyPath`,如果省略则使用临时 CA)以及主机的常规根证书,因此代理生成的证书和真实的上游证书都能通过验证。 - `network.tlsTerminate.excludeDomains` - **不**被终止的域名模式(语法与 `allowedDomains` 相同)。匹配的 CONNECT 将以不透明的方式进行隧道传输:它们仍然受域名允许列表的约束,但沙盒内部的客户端会与真实的上游服务器完成自己的 TLS 握手,且 `filterRequest` / 凭据注入不适用于其 HTTPS 流量。将此用于 TLS termination 根本上会破坏的两种情况: - **mTLS 上游** - 只有沙盒内的客户端持有客户端证书,因此代理无法代表它重新发起连接。 - **证书绑定客户端** - 自行验证上游身份(自定义 CA、SAN 绑定)并拒绝 MITM 证书的客户端。 - `network.tlsTerminate.extraCaCertPaths` - 附加到信任束(在 MITM CA 和主机的常规根证书之后)的 PEM CA 证书文件路径。被排除(未终止)的主机由沙盒内部的客户端验证,并且 SRT 设置的信任环境变量(`SSL_CERT_FILE`, `GIT_SSL_CAINFO` 等)会 _替换_ 每个工具自身的信任配置,因此站点本地根(例如内部 mTLS CA)必须位于信任束中,否则这些主机将永远无法通过验证。仅将每个文件中的 `CERTIFICATE` 块复制到信任束中(其他任何内容,例如组合 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 Socket 设置**(特定于平台的行为): | 设置 | macOS | Linux | | ------------------------------ | ------------------------- | ---------------------------------------- | | `allowUnixSockets: string[]` | Socket 路径白名单 | _被忽略_ (seccomp 无法按路径过滤) | | `allowAllUnixSockets: boolean` | 允许所有 socket | 禁用 seccomp 阻断 | Unix socket 在两个平台上**默认都被阻断**。 - **macOS**:使用 `allowUnixSockets` 允许特定路径(例如 `["/var/run/docker.sock"]`),或使用 `allowAllUnixSockets: true` 允许所有。 - **Linux**:阻断使用 seccomp 过滤器(仅限 x64/arm64)。如果 seccomp 不可用,则 socket 不受限制并会显示警告。使用 `allowAllUnixSockets: true` 显式禁用阻断。 #### 文件系统配置 采用两种不同的模式: **读取限制**(先拒绝后允许模式) - 默认允许所有读取: - `filesystem.denyRead` - 拒绝读取访问的路径数组。空数组 = 完全读取访问。 - `filesystem.allowRead` - 在被拒绝的区域内重新允许读取访问的路径数组(优先级高于 denyRead)。**注意:** 这与写入正好相反,写入时 `denyWrite` 的优先级高于 `allowWrite`。 **写入限制**(仅允许模式) - 默认拒绝所有写入: - `filesystem.allowWrite` - 允许写入访问的路径数组。空数组 = 无写入访问。 - `filesystem.denyWrite` - 在允许的路径内拒绝写入访问的路径数组(优先级高于 allowWrite) **路径语法:** 路径在 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 目前不支持 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 程序(`gh`、`gcloud`、`terraform`、`kubectl` 等)需要此项来验证 TLS 证书。**安全警告:** 启用此项会打开一个通过 trustd 服务进行潜在数据泄露的向量。 - `allowAppleEvents` - 允许从 macOS 沙盒发送 Apple Events 和 Launch Services 打开请求(布尔值,默认值:false)。如果没有此项,像 `open`、`osascript` 等命令,以及任何通过 AppleScript 打开 URL 或脚本其他应用程序的操作,都会因 AppleScript 错误 `-600`(“应用程序未运行”)或 LaunchServices 错误(`-10822`、`-54`)而失败。**安全警告:** 启用此项意味着沙盒不再提供代码执行隔离。沙盒命令可以通过 `open` 在没有用户提示的情况下启动其他应用程序,并且它启动的任何内容都在沙盒的文件系统和网络限制之外运行;通过 Apple Events 对已运行应用程序进行脚本控制,还会额外受到用户基于应用程序的 TCC 自动化同意的限制。嵌入者应仅从受信任的用户级配置中获取此选项——切勿从检出仓库中的项目本地文件中获取,否则会让攻击者编写的项目提升其自身的沙盒权限。 ### 常见配置方案 **允许 GitHub 访问**(所有必需的 endpoint): ``` { "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 版 —— 请参阅下方的 [Windows (alpha)](#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)`,但会剥夺生成 namespace 的 capabilities。bubblewrap 和 seccomp 隔离层都需要具有 capabilities 的 user namespace。使用以下命令禁用该限制: ``` sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 ``` 或添加一个向相关二进制文件授予 `userns` 的 AppArmor 配置文件。 **可选的 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 (alpha) Windows 支持**处于 alpha 阶段,设计尚在变动中**。目前的实现提供了网络出站过滤(通过 Windows Filtering Platform)和文件读/写拒绝(通过 ACL 标记),但它**不能作为抵御故意对抗的沙盒进程的安全边界** —— 请参阅下方的[威胁模型](#threat-model)。该机制将在未来版本中进行大幅修改(沙盒进程将在单独的沙盒用户账户下运行)。 ### 设置 在每台机器上运行一次 `npx @anthropic-ai/sandbox-runtime windows-install`(需要管理员权限;创建 `sandbox-runtime-net` 本地组并安装 WFP 出站过滤器)。**注销并重新登录**,使组成员身份在您的令牌上生效。之后,`SandboxManager.initialize()` 和 `srt` CLI 将像在其他平台上一样工作。 ### 威胁模型 **重要提示:** Windows 沙盒以与主机**相同的用户**身份运行子进程,使用受限令牌(判别组被翻转为 deny-only)。这可以防止规规矩矩的工具**意外**访问,并防止沙盒进程本身**在令牌内故意**访问 —— 在受限令牌下运行的代码无法打开被拒绝的文件或发起直接的出站连接。 它**无法**防止沙盒进程通过系统服务代理故意逃逸。任务计划程序、`PROC_THREAD_ATTRIBUTE_PARENT_PROCESS`(重新设定父进程为不受限制的进程,例如 `explorer.exe`)、BITS 以及某些 COM 进程外服务器将在用户的完全不受限制的交互式令牌下生成进程。然后该进程可以读取或写入任何“被拒绝”的文件,并建立不受限制的网络连接。针对此类逃逸没有同一用户的解决方案;根本性的解决办法是在**单独的沙盒用户账户**下运行沙盒进程,这也是计划中的下一次修订。 在该修订落地之前,请将 Windows 沙盒视为**防止意外访问和良好行为工具的尽力而为防御**,而不是针对对抗性进程的容器。 ### 已知限制 - **不是针对故意对抗的子进程的安全边界** —— 参见上方的[威胁模型](#threat-model)。 - Windows 上**不支持** `filesystem.allowWrite`(写入白名单)和 `filesystem.allowRead`(在 denyRead 内重新允许) —— `initialize()` 会拒绝设置其中任何一项的配置。仅应用 `denyRead` / `denyWrite`。 - Windows 上尚不支持 `network.tlsTerminate`。 - `filesystem.denyRead` / `filesystem.denyWrite` 中的目录目标尚不支持(仅限单个文件)。 ## 开发 ``` # 安装依赖 npm install # 构建项目 npm run build # 运行测试 npm test # 类型检查 npm run typecheck # Lint 代码 npm run lint # 格式化代码 npm run format ``` ### 构建 Seccomp 二进制文件 BPF 过滤器和 `apply-seccomp` 加载器是通过 `npm run build:seccomp`(仅限 Linux;需要 `gcc` 和 `libseccomp-dev`)从 `vendor/seccomp-src/` 中的 C 源码编译而来。CI 会在每个 Linux 架构上测试之前运行它,而发布工作流会构建这两种架构,并将它们打包到发布的程序包中。 ## 实现细节 ### 网络隔离架构 沙盒在主机上运行 HTTP 和 SOCKS5 代理服务器,根据权限规则过滤所有网络请求: 1. **HTTP/HTTPS 流量**:HTTP 代理服务器拦截请求,并根据允许/拒绝的域名对其进行验证 2. **其他网络流量**:SOCKS5 代理处理所有其他 TCP 连接(SSH、数据库连接等) 3. **权限强制执行**:代理强制执行您配置中的 `permissions` 规则 **特定于平台的代理通信:** - **Linux**:请求通过文件系统经由 Unix domain socket 路由(使用 `socat` 进行桥接)。network namespace 被从 bubblewrap 容器中移除,确保所有网络流量都必须通过代理。 - **macOS**:Seatbelt 配置文件仅允许与代理监听的特定 localhost 端口进行通信。所有其他网络访问都被阻止。 ### 文件系统隔离 文件系统限制在操作系统层面被强制执行: - **macOS**:使用 `sandbox-exec` 以及动态生成的 Seatbelt 配置文件,指定允许读/写的路径 - **Linux**:使用带有 bind mount 的 `bubblewrap`,根据配置将目录标记为只读或读写 **默认文件系统权限:** - **读取**(先拒绝后允许):默认允许所有。您可以拒绝大范围区域,然后重新允许其中的特定路径。`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` 也会失败: ``` $ 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 上,强制拒绝路径只会阻止已存在的文件。由于 bubblewrap 的 bind mount 机制,这些模式中不存在的文件无法被阻止。macOS 使用 glob 模式,这可以同时阻止现有文件和新文件。 **Linux 搜索深度:** 在 Linux 上,沙盒使用 `ripgrep` 扫描允许写入路径子目录中的危险文件。默认情况下,出于性能考虑,它最多搜索 3 层深度。您可以使用 `mandatoryDenySearchDepth` 配置此项: ``` { "mandatoryDenySearchDepth": 5, "filesystem": { "allowWrite": ["."] } } ``` - 默认值:`3`(最多搜索 3 层深度) - 范围:`1` 到 `10` - 值越高提供的保护越多,但性能越慢 - CWD(深度 0)中的文件无论此设置如何,始终受保护 ### Unix Socket 限制 在 Linux 上,沙盒使用 **seccomp BPF (Berkeley Packet Filter)** 在 syscall 级别阻止创建 Unix domain socket。这提供了额外的安全层,防止进程为本地 IPC 创建新的 Unix domain socket(除非被显式允许)。 **工作原理:** 1. **内置 BPF 过滤器**:该包附带了一个静态的 `apply-seccomp` 二进制文件(适用于 x64 和 arm64),其中内置了编译好的 seccomp BPF 过滤器。该过滤器是特定于架构的,但与 libc 无关,因此该二进制文件在 glibc 和 musl 下均可运行。 2. **运行时检测**:沙盒会自动检测您的系统架构,并使用匹配的 `apply-seccomp` 二进制文件。 3. **Syscall 过滤**:BPF 过滤器拦截 `socket()` syscall,并通过返回 `EPERM` 阻止创建 `AF_UNIX` socket。这可以防止沙盒代码创建新的 Unix domain socket。 4. **使用 apply-seccomp 二进制文件进行两阶段应用**: - 外部 bwrap 创建带有文件系统、网络和 PID namespace 限制的沙盒 - 网络桥接进程 启动于沙盒内部(需要 Unix socket) - apply-seccomp 创建一个嵌套的 user+PID+mount namespace,并重新挂载 `/proc` - 在嵌套的 namespace 内,apply-seccomp 充当 PID 1(不可转储的 init/reaper) - apply-seccomp 进行 fork,通过 `prctl()` 应用 seccomp 过滤器,并 exec 用户命令 - 用户命令在所有沙盒限制以及 Unix socket 创建阻止下运行 **PID namespace 隔离**:嵌套的 PID namespace 确保用户命令无法看到或寻址任何在没有 seccomp 过滤器的情况下运行的进程(bwrap 的 init、shell 包装器或 socat 助手)。这使得 seccomp 边界保持完整,而不受 `kernel.yama.ptrace_scope` 的影响,因为未经过滤的助手无法通过 `ptrace` 或 `/proc/N/mem` 访问。内部 PID 1 设置了 `PR_SET_DUMPABLE=0`,因此它也是不可被 ptrace 的。如果嵌套 namespace 创建失败,apply-seccomp 将中止,而不是在没有隔离的情况下运行。 **安全限制**:该过滤器阻止 `socket(AF_UNIX, ...)` 以及 `io_uring_setup`/`io_uring_enter`/`io_uring_register` syscall(后三者被阻止是因为 Linux 5.19+ 上的 `IORING_OP_SOCKET` 否则将绕过 `socket()` 规则)。它不能阻止对从父进程继承或通过 `SCM_RIGHTS` 传递的 Unix socket 文件描述符的操作。对于大多数沙盒场景,阻止创建 socket 足以防止未经授权的 IPC。 **零运行时依赖**:包含了用于 x64 和 arm64架构的预构建静态 apply-seccomp 二进制文件和预生成的 BPF 过滤器。运行时不需要编译工具或外部依赖。 **架构支持**:完全支持 x64 和 arm64,提供预构建的二进制文件。目前不支持其他架构。要在不受支持的架构上使用沙盒而不阻止 Unix socket,请在您的配置中设置 `allowAllUnixSockets: true`。 ### 违规检测和监控 当沙盒进程尝试访问受限资源时: 1. **在 OS 级别阻止操作**(返回 `EPERM` 错误) 2. **记录违规**(特定于平台的机制) 3. **通知用户**(在 Claude Code 中,这会触发权限提示) **macOS**:沙盒运行时接入了 macOS 的系统沙盒违规日志存储。这提供了实时通知,其中包含关于尝试了什么以及为什么被阻止的详细信息。这与 Claude Code 用于违规检测的机制相同。 ``` # 实时查看 sandbox 违规行为 log stream --predicate 'process == "sandbox-exec"' --style syslog ``` **Linux**:Bubblewrap 不提供内置的违规报告。使用 `strace` 跟踪系统调用并识别被阻止的操作: ``` # 跟踪所有被拒绝的操作 strace -f srt 2>&1 | grep EPERM # 跟踪特定文件操作 strace -f -e trace=open,openat,stat,access srt 2>&1 | grep EPERM # 跟踪网络操作 strace -f -e trace=network srt 2>&1 | grep EPERM ``` ### 进阶:自带代理 对于更复杂的网络过滤,您可以将沙盒配置为使用您自己的代理,而不是内置代理。这可以实现: - **流量检查**:使用 [mitmproxy](https://mitmproxy.org/) 等工具检查和修改流量 - **自定义过滤逻辑**:实现超越简单域名白名单的复杂规则 - **审计日志记录**:记录所有网络请求以进行合规性检查或调试 **使用 mitmproxy 的示例:** ``` # 使用自定义过滤脚本启动 mitmproxy mitmproxy -s custom_filter.py --listen-port 8888 ``` 注意:新配置格式尚不支持自定义代理配置。此功能将在未来版本中添加。 **重要的安全考量:** 即使有域名白名单,也可能存在数据泄露向量。例如,允许 `github.com` 会让进程推送到任何存储库。通过自定义 MITM 代理和正确的证书设置,您可以检查并过滤特定的 API 调用以防止这种情况。 ### 安全限制 - 网络沙盒限制:网络过滤系统通过限制允许进程连接的域名来运作。它不会以其他方式检查通过代理的流量,用户有责任确保其策略中仅允许受信任的域名。 用户应意识到允许广泛域名(如 `github.com`)可能带来的潜在风险,这可能会导致数据泄露。此外,在某些情况下,可能会通过[域名前置](https://en.wikipedia.org/wiki/Domain_fronting)绕过网络过滤。 - 通过 Unix Socket 的权限提升:`allowUnixSockets` 配置可能会无意中授予对强大系统服务的访问权限,从而导致沙盒被绕过。例如,如果它被用于允许对 `/var/run/docker.sock` 的访问,这将有效地通过利用 docker socket 授予对主机系统的访问权限。我们鼓励用户仔细考虑他们允许通过沙盒的任何 unix socket。 - 文件系统权限提升:过于宽泛的文件系统写入权限可能导致权限提升攻击。允许对包含 `$PATH` 中的可执行文件、系统配置目录或用户 shell 配置文件(`.bashrc`、`.zshrc`)的目录进行写入,在其他用户或系统进程访问这些文件时,可能会导致在不同安全上下文中执行代码。 - Linux 沙盒强度:Linux 实现提供了强大的文件系统和网络隔离,但包含一个 `enableWeakerNestedSandbox` 模式,使其能够在没有特权 namespace 的 Docker 环境中运行。此选项会大大削弱安全性,应仅在其他方式已强制执行额外隔离的情况下使用。 - 较弱的网络隔离:`enableWeakerNetworkIsolation` 选项重新启用了对 `com.apple.trustd.agent` 的访问,这是 Go 程序通过 macOS 安全框架验证 TLS 证书所必需的。这打开了一个通过 trustd 服务进行潜在数据泄露的向量,仅当需要 Go TLS 验证时才应启用(例如,当使用带有 MITM 代理和自定义 CA 的 `httpProxyPort` 时)。 - 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 上添加对带有 `LD_PRELOAD` 的 `proxychains` 的支持,以在更低级别拦截网络调用,使绕过变得更加困难 - **Linux 违规监控**:为 Linux 实现基于 `strace` 的自动违规检测,并与违规存储集成。目前,Linux 用户必须手动运行 `strace` 来查看违规,这与通过系统日志存储具有自动违规监控的 macOS 不同
标签:Claude, CVE检测, MITM代理, 子域名枚举, 文件系统权限, 沙箱环境, 知识库安全, 系统安全, 网络访问控制, 自动化攻击, 进程隔离