
sandbox-runtime v0.0.76
コンテナを必要とせず、OSレベルで任意のプロセスに対してファイルシステムとネットワークの制限を強制する軽量なサンドボックスツール。
Anthropic Sandbox Runtime (srt)
コンテナを必要とせず、OSレベルで任意のプロセスにファイルシステムとネットワークの制限を適用するための軽量サンドボックスツールです。
srt はネイティブOSのサンドボックスプリミティブ(macOSでは sandbox-exec、Linuxでは bubblewrap)とプロキシベースのネットワークフィルタリングを使用します。エージェント、ローカルMCPサーバー、bashコマンド、および任意のプロセスの動作をサンドボックス化するために使用できます。
ベータリサーチプレビュー
Sandbox Runtimeは、より安全なAIエージェントを実現するために Claude Code 向けに開発されたリサーチプレビューです。より広範なエコシステムがより安全なエージェントシステムを構築できるよう支援するため、早期のオープンソースプレビューとして公開されています。これは早期のリサーチプレビューであるため、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 ツールとしてもライブラリとしても使用できるスタンドアロンのサンドボックス実装を提供します。secure-by-default の哲学に基づいて設計されており、一般的な開発者のユースケースに合わせて調整されています。プロセスは最小限のアクセス権で起動し、必要な穴だけを明示的に開けます。
主な機能:
- ネットワーク制限: HTTP/HTTPS やその他のプロトコルを介してアクセスできるホスト/ドメインを制御
- ファイルシステム制限: 読み書きできるファイル/ディレクトリを制御
- Unix ソケット制限: ローカル IPC ソケットへのアクセスを制御
- 違反モニタリング: macOS では、システムのサンドボックス違反ログストアに接続してリアルタイムアラートを受信
ユースケース例: MCP サーバーのサンドボックス化
主要なユースケースの一つは、Model Context Protocol (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'
仕組み
サンドボックスは OS レベルのプリミティブを使用して、プロセスツリー全体に適用される制限を強制します:
- macOS: 動的に生成された Seatbelt プロファイル を使用して
sandbox-execを実行します - Linux: ネットワーク名前空間の分離を伴うコンテナ化のために bubblewrap を使用します
- Windows: 専用の
srt-sandboxローカルユーザーアカウントの下でサンドボックス化されたプロセスを実行し、そのアカウントの SID をキーとする Windows Filtering Platform の egress フェンスと、作業ツリー上のセッションごとの明示的な ACE を適用します
0d1c612947c798aef48e6ab4beb7e8544da9d41a-4096x2305
二重分離モデル
効果的なサンドボックス化には、ファイルシステムとネットワークの両方の分離が必要です。ファイル分離がなければ、侵害されたプロセスが SSH キーやその他の機密ファイルを窃取する可能性があります。ネットワーク分離がなければ、プロセスがサンドボックスを脱出して無制限のネットワークアクセスを取得する可能性があります。
ファイルシステム分離 は読み取りと書き込みの制限を強制します:
- 読み取り (deny-then-allow パターン): デフォルトでは、読み取りアクセスはどこでも許可されます。広い領域 (例:
/Users) を拒否し、その中の特定のパス (例:.) を再許可することができます。allowReadはdenyReadより優先されます — これは書き込みとは逆で、denyWriteがallowWriteより優先されます。allowRead領域の内側にある、それよりも具体的なdenyReadエントリ (例:allowRead: ["."]を伴うdenyRead: ["**/.env"]や["./secrets"]) は、依然として拒否されたままです。 - 書き込み (allow-only パターン): デフォルトでは、書き込みアクセスはどこでも拒否されます。パス (例:
.、/tmp) を明示的に許可する必要があります。許可リストが空の場合、書き込みアクセスはありません。
ネットワーク分離 (allow-only パターン): デフォルトでは、すべてのネットワークアクセスが拒否されます。ドメインを明示的に許可する必要があります。allowedDomains リストが空の場合、ネットワークアクセスはありません。ネットワークトラフィックは、ホスト上で実行されているプロキシサーバーを経由してルーティングされます:
-
Linux: リクエストはファイルシステムを介して Unix ドメインソケット経由でルーティングされます。サンドボックス化されたプロセスのネットワーク名前空間は完全に削除されるため、すべてのネットワークトラフィックはホスト上で実行されているプロキシ (サンドボックスにバインドマウントされた Unix ソケットでリッスン) を経由する必要があります
-
macOS: Seatbelt プロファイルは、特定の localhost ポートへの通信のみを許可します。プロキシはこのポートでリッスンし、すべてのネットワークアクセスのための制御されたチャネルを作成します
-
Windows: マシン全体の WFP フィルターセットは、プロキシポート範囲へのループバックを除き、
srt-sandboxアカウントから発信されるすべてのアウトバウンド接続をブロックします。プロキシはその範囲内でリッスンし、すべてのネットワークアクセスのための制御されたチャネルを作成します
HTTP/HTTPS (HTTP プロキシ経由) とその他の TCP トラフィック (SOCKS5 プロキシ経由) の両方が、ドメインの許可リストと拒否リストを強制するこれらのプロキシによって仲介されます。
Claude Code でのサンドボックス化の詳細については、以下を参照してください:
- Claude Code Sandboxing Documentation
- Beyond Permission Prompts: Making Claude Code More Secure and Autonomous
アーキテクチャ```
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 切断を受信し、その description が理由となり、OpenSSH がそれをそのまま出力します — OpenSSH は非 ASCII を切り詰めてエスケープするため、そのような理由は約 400 ASCII 文字未満に保ち、命令形を先頭にしてください。
- `network.allowLocalBinding` - ローカルポートへのバインドを許可する(ブール値、デフォルト: false)
**解決済みアドレスのチェック。** 許可/拒否リストは _名前_ で照合しますが、許可された名前の DNS(または許可されたワイルドカード配下の任意のラベル)を制御する者は、それが解決する先を制御します。そのため、許可された **ホスト名** を直接ダイヤルする前に、プロキシはそれを一度解決し、拒否セット内のアドレスを破棄し、生き残ったアドレスに接続します(チェックを通過したアドレスがダイヤルされるアドレスです — 2 回目のルックアップはありません)。何も生き残らない場合、接続は他のポリシー拒否と同様に拒否されます。HTTP/CONNECT は `403`(`X-Proxy-Error: blocked-by-sandbox-runtime`、理由はボディ内)を返し、SOCKS は「connection not allowed by ruleset」を返し、`deny network-outbound host:port (resolved to a loopback address)` 行 — アドレス自体ではなくアドレスのクラス(loopback、link-local、this host's、cloud metadata、deny-listed、listed、…)を名指しするもので、アドレス自体はデバッグログにのみ記録されます — が違反ストアに記録されます。
拒否セットは次のとおりです: ループバック(`127.0.0.0/8`、`::1`)、未指定(`0.0.0.0/8`、`::`)、リンクローカル(`169.254.0.0/16`、`fe80::/10`)、マルチキャスト(`224.0.0.0/4`、`ff00::/8`)、ブロードキャスト、リンクローカル外に存在するクラウドインスタンスメタデータ / プラットフォームエンドポイント(`100.100.100.200`、`168.63.129.16`、`192.0.0.192`、`fd00:ec2::/32`、`fd20:ce::254`、`fd00:c1::a9fe:a9fe`、`fd00:42::42`)、このホスト自身のネットワークインターフェースのいずれかに現在割り当てられているすべてのアドレス(`0.0.0.0` にバインドされたサービスは、ループバック上とまったく同様に LAN またはグローバルアドレスで応答します)、`deniedDomains` にリストされたすべての IP リテラル(`:port` がある場合はそれを尊重)、および `deniedResolvedAddresses` 内のすべて。IPv4 エントリは、IPv4 アドレスを運ぶ IPv6 形式にも一致します — IPv4 マップド、IPv4 互換、IPv4 変換アドレス、NAT64 ウェルノウンプレフィックス(`64:ff9b::/96`)、および 6to4(`2002::/16`)は、それらが埋め込む IPv4 アドレスによって判定されます。ローカル使用 NAT64 プレフィックス `64:ff9b:1::/48` およびネットワーク固有のプレフィックスはデコードされません — そのレイアウト(RFC 6052 は IPv4 を複数の位置に許可しています)はアドレスだけからは認識できません。そのようなネットワークでは、拒否する範囲のプレフィックスによる変換をリストしてください(例: `10.0.0.0/8` に対して `<prefix>::a00:0/104`)。このホストに割り当てられずに到達するアドレス — クラウドインスタンスの 1:1-NAT パブリックアドレス、ルーターポートフォワード、コンテナまたは VM のホストゲートウェイエイリアス — は自動的にはカバーされません。`deniedResolvedAddresses` にリストしてください。
チェックがそのままにするもの: **IP リテラルである** 許可リストエントリ(`127.0.0.1:3000` を許可リストに載せるのは明示的な選択です)— そして同様に、その IP リテラル(そのポート上)自体が `allowedDomains` にある場合、ホスト名はそうでなければ拒否されるアドレスに解決されることがあります。名前で到達することは、リテラルエントリが与えないものを何も与えないからです(`deniedDomains` 内の IP リテラルは、リテラルリクエストの場合とまったく同様に、依然として優先されます)。したがって、`myapp.test` が `/etc/hosts` 経由でローカルサーバーにマップされる開発セットアップでは、`["myapp.test", "127.0.0.1:3000"]` を許可リストに載せます。別個のカーブアウトリストはありません。`localhost` および `.localhost` 配下の名前はループバック(または許可リストに載ったリテラル)にのみ解決され、それ以外には解決されません。このチェックは、`parentProxy`(srt 自身の環境の `HTTP_PROXY` / `HTTPS_PROXY` から取得したものを含む)または `mitmProxy` 経由でルーティングされる接続に対しては評価されません — そのホップが名前を解決し、独自のアドレスポリシーを持ちます — そしてプロキシがダイヤルするものだけを統制します。macOS では、`allowLocalBinding` が別途、サンドボックス化されたプロセスがプロキシをまったく経由せずにループバックポートに接続することを許可します。
- `network.deniedResolvedAddresses` - 許可されたホスト名が解決してはならない追加の IP アドレス / CIDR 範囲(IPv4 または IPv6、ブラケットなし、任意のポート)。プライベート使用空間はデフォルトでは拒否されません。イントラネットのホスト名を許可リストに載せることは正当だからです。許可リストに載った名前がそこに入ってはならない場合はここにリストしてください(例: `["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10", "fc00::/7"]`)。IPv4 と IPv6 の範囲は別々にリストしてください — IPv4 マップドブロック(`::ffff:0:0/96`)をカバーするのに十分広い IPv6 範囲(`::/0` など)は、一部のランタイムでは IPv4 の応答に一致しますが、他のランタイムでは一致しません。したがって、IPv4 を拒否するためにそれに依存しないでください。
**TLS 終端**(`network.tlsTerminate`、実験的): 設定すると、HTTPS CONNECT がインプロセスで終端され、SRT が復号されたリクエストを確認(および `network.filterRequest` 経由でフィルタリング)できるようになります。サンドボックス化されたプロセスは、MITM CA(`caCertPath`/`caKeyPath`、省略時はエフェメラル CA)とホストの通常のルートを含むトラストバンドルを指し示されるため、プロキシが発行した証明書と実際のアップストリーム証明書の両方が検証されます。
- `network.tlsTerminate.excludeDomains` - 終端 **されない** ドメインパターン(`allowedDomains` と同じ構文)。一致する CONNECT は代わりに不透明にトンネルされます。それらは依然としてドメイン許可リストの対象ですが、サンドボックス内のクライアントが実際のアップストリームと自身の TLS ハンドシェイクを完了し、`filterRequest` / 認証情報注入はそれらの HTTPS トラフィックには適用されません。これは、TLS 終端が根本的に壊す 2 つのケースに使用してください:
- **mTLS アップストリーム** - サンドボックス内のクライアントだけがクライアント証明書を保持しているため、プロキシはその代理として接続を再発信できません。
- **証明書ピン留めクライアント** - アップストリームの ID を自身で検証し(カスタム 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 ソケット設定(プラットフォーム固有の動作):
| 設定 | macOS | Linux |
|---|---|---|
allowUnixSockets: string[] | ソケットパスの許可リスト | 無視される(seccomp はパスでフィルタできない) |
allowAllUnixSockets: boolean | すべてのソケットを許可 | seccomp ブロックを無効化 |
Unix ソケットは両プラットフォームでデフォルトでブロックされます。
- macOS:
allowUnixSocketsを使用して特定のパス(例:["/var/run/docker.sock"])を許可するか、allowAllUnixSockets: trueですべてを許可します。 - Linux: ブロックは seccomp フィルタを使用します(x64/arm64 のみ)。seccomp が利用できない場合、ソケットは制限されず、警告が表示されます。ブロックを明示的に無効にするには
allowAllUnixSockets: trueを使用します。
ファイルシステム設定
2 つの異なるパターンを使用します:
読み取り制限(deny-then-allow パターン) - デフォルトですべての読み取りが許可されます:
filesystem.denyRead- 読み取りアクセスを拒否するパスの配列。空の配列 = 完全な読み取りアクセス。filesystem.allowRead- 拒否された領域内で読み取りアクセスを再許可するパスの配列(denyRead より優先されます)。注意: これは書き込みとは逆で、書き込みではdenyWriteがallowWriteより優先されます。
書き込み制限(allow-only パターン) - デフォルトですべての書き込みが拒否されます:
filesystem.allowWrite- 書き込みアクセスを許可するパスの配列。空の配列 = 書き込みアクセスなし。filesystem.denyWrite- 許可されたパス内で書き込みアクセスを拒否するパスの配列(allowWrite より優先されます)
パス構文(macOS):
macOS ではパスは .gitignore 構文に似た git スタイルの glob パターンをサポートします:
*-/を除く任意の文字にマッチ(例:*.tsはfoo.tsにマッチするがfoo/bar.tsにはマッチしない)**-/を含む任意の文字にマッチ(例:src/**/*.tsはsrc/内のすべての.tsファイルにマッチ)?-/を除く任意の 1 文字にマッチ(例: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 環境向けのより弱いサンドボックスモードを有効化(boolean、デフォルト:false)javaAgentJarPath- macOS/Linux:srt-proxy-agent.jarへの絶対パス。JAVA_TOOL_OPTIONS経由で注入される JVM エージェント(ネットワーク分離の「JVM ツール」を参照)。sandbox-runtime をバンドルして jar を別途配布するコンシューマのみが必要とします。通常の npm install ではvendor/java-proxy-agent/の下にあります。enableWeakerNetworkIsolation- macOS サンドボックスでcom.apple.trustd.agentへのアクセスを許可(boolean、デフォルト:false)。これは Go プログラム(gh、gcloud、terraform、kubectlなど)が、MITM プロキシとカスタム CA でhttpProxyPortを使用する際に TLS 証明書を検証するために必要です。セキュリティ警告: これを有効にすると、trustd サービスを通じた潜在的なデータ流出ベクターが開かれます。allowAppleEvents- macOS サンドボックスから Apple Events および Launch Services の open リクエストの送信を許可(boolean、デフォルト:false)。これがないと、open、osascript、および URL を開いたり AppleScript 経由で他のアプリをスクリプト制御したりするコマンドは、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: アルファ版 — バンドルされた
srt-win.exeヘルパーを使用(追加の依存関係なし)。セットアップ、セキュリティモデル、既知の制限については、以下のWindows(アルファ版)を参照してください
プラットフォーム固有の依存関係
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)は許可されますが、結果として得られる名前空間からケーパビリティが剥奪されます。bubblewrapとseccomp分離レイヤーの両方で、ケーパビリティを持つユーザー名前空間が必要です。この制限を無効にするには:```bash
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 で必要なもの:**
- 追加の依存関係はありません。`srt-win.exe` ヘルパー(x64 および arm64)は npm パッケージに同梱されています。1 回限りの管理者権限での `windows-install` ステップが必要です — 以下を参照してください。
## Windows(アルファ)
Windows サポートは**アルファ**です。サンドボックス化されたプロセスは、専用の `srt-sandbox` ローカルユーザーアカウントの下で実行され、ネイティブの Windows セキュリティプリミティブによって呼び出し元ユーザーから分離されます — サンドボックスアカウントの SID をキーとする Windows Filtering Platform(WFP)の egress フェンス、および設定されたファイルシステムパスへのその SID のアクセスを許可または拒否するセッションごとの明示的な ACE です。
### セットアップ
マシンごとに 1 回実行します(自己昇格、UAC プロンプトは 1 回):```powershell
npx @anthropic-ai/sandbox-runtime windows-install
これにより、srt-sandbox ローカルユーザーアカウント(ランダムなパスワードは HKLM\SOFTWARE\sandbox-runtime に DPAPI 暗号化されて保存される — マシン全体で共有されるため、SYSTEM として実行されるフリートインストールでも機能し、あるユーザーがローテーションすると他のユーザーが読むコピーも更新される)、sandbox-runtime-users ローカルグループがプロビジョニングされ、srt-sandbox SID をキーとするマシン全体の WFP フィルターセットがインストールされる。これは 冪等 である — 再実行するとサンドボックスアカウントのパスワードがローテーションされ、フィルターセットが調整される。
ログアウトは不要である。 WFP フィルターは専用サンドボックスアカウントの SID をキーとするため、自身のネットワーク、サービス、およびマシン上の他のすべてのプリンシパルは影響を受けない。
インストール後、SandboxManager.initialize() と srt CLI は他のプラットフォームと同様に動作する。initialize() はサンドボックスアカウントと WFP フェンスが有効であることを検証し、そうでない場合は実行可能なエラーで失敗する。
プログラムによるインストール/アンインストールは installWindowsSandbox() / uninstallWindowsSandbox() としてエクスポートされる。
セキュリティモデル
サンドボックス化されたコマンドは 呼び出し元ユーザーとしてではなく、srt-sandbox アカウントとして 実行される。バンドルされた srt-win.exe ヘルパーは 2 ホップの起動を行う。ブローカーが CreateProcessWithLogonW を呼び出して srt-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 における 2 フィルターの WFP セットである。設定されたプロキシポート範囲(デフォルト 60080–60089)内のループバック宛先に対する PERMIT と、トークンが srt-sandbox SID を保持する接続に対する BLOCK である。サンドボックス化されたプロセスは、その範囲でリッスンしている JS HTTP/SOCKS5 プロキシを介してのみインターネットに到達できる。プロキシ環境を削除して直接接続するプロセスはカーネルでブロックされる。
ファイルシステム分離 は NTFS 随意 ACL によって強制される。srt-sandbox アカウントは呼び出し元ユーザーのファイルに対する固有の権限を持たないため、initialize() 時にサンドボックスは srt-sandbox SID のみに対する追加的で継承される明示的 ACE を書き込む — パスの既存のセキュリティ記述子を書き換えたり置き換えたりすることは決してない:
filesystem.allowWrite→ 継承されるMODIFYALLOW ACE(READ|WRITE|EXECUTE|DELETE、FILE_DELETE_CHILDは付与されない)。サンドボックス化されたプロセスは作業ツリー内のファイルを作成、変更、削除できる。付与からFILE_DELETE_CHILDを除外することは、以下の拒否スタンプに対する多層防御であり、ツリールートのガードではない。filesystem.allowRead→ 継承されるREAD|EXECUTEALLOW ACEfilesystem.denyRead/filesystem.denyWrite→ ターゲットに対する継承される DENY ACE、およびその親に対する継承されるFILE_DELETE_CHILDDENY — 作業ツリー付与におけるFILE_DELETE_CHILDの除外と合わせて、これによりサンドボックス化されたプロセスが親ディレクトリを介して拒否されたパスをリネームまたは削除することを防ぐ
reset() はこのセッションが追加したすべての ACE を削除する(このユーザーの同時ホスト間でユーザーごとのセッション DB を介して参照カウントされる。次回の initialize() 時のクラッシュリカバリパスが、クリーンでない終了後のクリーンアップを行う)。ディレクトリターゲットがサポートされる(ACE はサブツリー全体に継承される)。Glob パターンは initialize() 時に具体的なパスに展開される — 後から出現する一致パスはカバーされない。
Windows における TLS 終端
network.tlsTerminate は MITM CA が サンドボックスユーザーの CurrentUser\Root 証明書ストアに存在することを必要とする(schannel — System32\curl.exe、PowerShell Invoke-WebRequest、.NET、およびデフォルトバックエンドの git が使用する TLS バックエンド — は OS ストアのみを信頼し、環境変数は信頼しない)。これはインストール時のステップであり、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 がサンドボックス内で 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 の egress フェンスによってブロックされます。デフォルトで失効チェックが有効な 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` に追加してください。
- **exec ごとの `filesystem.allowRead` / `filesystem.allowWrite` オーバーライドはサポートされていません。** セッションレベルの `allowRead`/`allowWrite`(`initialize()` に渡される設定内)は上記のとおり機能しますが、`wrapWithSandbox` の `customConfig` でコマンドごとに渡すと例外が発生します — 許可は `initialize()` 時に `srt-win acl grant` を介してセッション全体に適用され、`srt-win exec` は exec ごとの拒否のみを公開します。
- **`proxyAuthToken` はランナーのコマンドラインで可視です。** プロキシ環境(`HTTP_PROXY=http://srt:<token>@127.0.0.1:…` を含む)は `srt-win exec` の argv 上の `--env` 引数として 2 ホップランナーに渡されるため、ランナープロセスを `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 グループを削除し、HKLM\SOFTWARE\sandbox-runtime キー(資格情報、マーカー、CA レコード)を削除する — UAC プロンプトが 1 回。%ProgramData%\sandbox-runtime(CA キーマテリアル)はそのまま残される。完全に一掃するには手動で削除する(およびユーザーごとの %LOCALAPPDATA%\sandbox-runtime も)。
開発```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フィルタが境界です — それらを無視したり解除したりするプロセスも依然としてフェンスで囲まれます。
**JVMツール(macOS/Linux):** JVMは`HTTPS_PROXY`/`NO_PROXY`を無視し、プロキシ認証情報用の環境変数を持ちません — プロキシの選択は`https.proxyHost`システムプロパティから行われ、認証情報は`java.net.Authenticator`を通じてのみ提供できます。そのため、JVMベースのツール(BazelのgRPCリモートキャッシュ、Gradle、Maven、…)はそうでなければターゲットに直接ダイヤルして失敗するか、トークンなしでプロキシに到達して407を受け取ります。このギャップを埋めるために、srtは`JAVA_TOOL_OPTIONS`を介して小さな`-javaagent`を注入します(環境変数はjarパスのみを運び、認証情報は`HTTPS_PROXY`に残ります)。JVM起動時に、エージェントはプロキシ環境変数から`http[s].proxyHost`/`Port`と`http.nonProxyHosts`を設定し、CONNECTトンネルのBasic認証を再有効化し、プロキシエンドポイント用のAuthenticatorをインストールします。JVMコマンドライン上の明示的な`-D`プロキシプロパティは依然として優先され、継承された`JAVA_TOOL_OPTIONS`は保持されます(拒否された認証情報環境変数でない限り)。結果として、すべてのJVMはstderrに`Picked up JAVA_TOOL_OPTIONS: …`行を出力します。`java.instrument`モジュールなしでビルドされたjlink'dランタイムはエージェントをロードできず、サンドボックス下での起動を拒否します — そのようなツールではコマンド内で`JAVA_TOOL_OPTIONS`を解除してください。jarはnpmパッケージに`vendor/java-proxy-agent/srt-proxy-agent.jar`として同梱されています(ソース:`vendor/java-proxy-agent-src/`、リリースワークフローでビルド、またはローカルで`npm run build:java-agent` — JDK ≥ 17が必要)。見つからない場合、`JAVA_TOOL_OPTIONS`はそのままにされ、JVMは以前と同様に動作します。バンドラーは`javaAgentJarPath`で独自のコピーを指定できます。
### ファイルシステム分離
ファイルシステムの制限はOSレベルで適用されます:
- **macOS**: 許可された読み取り/書き込みパスを指定する動的に生成されたSeatbeltプロファイルで`sandbox-exec`を使用します
- **Linux**: バインドマウントで`bubblewrap`を使用し、設定に基づいてディレクトリを読み取り専用または読み書き可能としてマークします
- **Windows**: 設定されたパスに`(OI)(CI)`明示的ACEを`srt-sandbox` SIDに対して追加書き込みし(`allowRead`/`allowWrite`でALLOW、`denyRead`/`denyWrite`でDENY)、`reset()`でそれらを削除します
**デフォルトのファイルシステム権限:**
- **読み取り**(deny-then-allow):デフォルトですべての場所で許可されます。広い領域を拒否してから、その中の特定のパスを再許可できます。`allowRead`は`denyRead`より優先されます。
- 例:`denyRead: ["~/.ssh"]`でSSHキーへのアクセスをブロック
- 例:`denyRead: ["/Users"], allowRead: ["."]`でワークスペースを除く`/Users`全体をブロック
- 空の`denyRead: []` = 完全な読み取りアクセス(何も拒否されない)
- **書き込み**(allow-only):デフォルトですべての場所で拒否されます。パスを明示的に許可する必要があります。
- 例:`allowWrite: [".", "/tmp"]`でカレントディレクトリと/tmpへの書き込みを許可
- 空の`allowWrite: []` = 書き込みアクセスなし(何も許可されない)
- `denyWrite`は許可されたパス内に例外を作成します(denyが優先)
**優先順位は読み取りと書き込みで意図的に逆になっています:** `allowRead`は`denyRead`を上書きし、`denyWrite`は`allowWrite`を上書きします。これにより、拒否された領域内に読み取り可能な領域を切り出し、書き込み可能な領域内に保護された領域を切り出すことができます。
### 必須拒否パス(自動保護ファイル)
特定の機密ファイルとディレクトリは、許可された書き込みパス内にある場合でも、**常に書き込みがブロックされます**。これはサンドボックスエスケープと設定改ざんに対する多層防御を提供します。
**常にブロックされるファイル:**
- シェル設定ファイル:`.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`への書き込みは失敗します:```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(Berkeley Packet Filter)** を使用して、システムコールレベルでUnixドメインソケットの作成をブロックします。これにより、プロセスがローカルIPC用に新しいUnixドメインソケットを作成することを防ぐ追加のセキュリティ層が提供されます(明示的に許可されている場合を除く)。
**仕組み:**
1. **組み込みのBPFフィルター**: パッケージには、seccomp BPFフィルターをコンパイル済みで含む、x64およびarm64用の静的 `apply-seccomp` バイナリが同梱されています。フィルターはアーキテクチャ固有ですがlibc非依存であるため、このバイナリはglibcとmuslの両方で動作します。
2. **実行時の検出**: サンドボックスはシステムのアーキテクチャを自動的に検出し、一致する `apply-seccomp` バイナリを使用します。
3. **システムコールのフィルタリング**: BPFフィルターは `socket()` システムコールをインターセプトし、`EPERM` を返すことで `AF_UNIX` ソケットの作成をブロックします。これにより、サンドボックス化されたコードが新しいUnixドメインソケットを作成することを防ぎます。
4. **apply-seccompバイナリを使用した2段階の適用**:
- 外側のbwrapが、ファイルシステム、ネットワーク、PID名前空間の制限を備えたサンドボックスを作成する
- ネットワークブリッジプロセス(socat)がサンドボックス内で起動する(Unixソケットが必要)
- apply-seccompがネストされたuser+PID+mount名前空間を作成し、`/proc` を再マウントする
- ネストされた名前空間内で、apply-seccompがPID 1(非ダンプ可能なinit/reaper)として動作する
- apply-seccompがforkし、`prctl()` を介してseccompフィルターを適用し、ユーザーコマンドをexecする
- ユーザーコマンドは、すべてのサンドボックス制限に加えてUnixソケット作成のブロックとともに実行される
**PID名前空間の分離**: ネストされたPID名前空間により、ユーザーコマンドはseccompフィルターなしで実行されるプロセス(bwrapのinit、シェルラッパー、または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` システムコールをブロックします(後者3つは、Linux 5.19以降の `IORING_OP_SOCKET` がそうでなければ `socket()` ルールをバイパスするためです)。親プロセスから継承された、または `SCM_RIGHTS` を介して渡されたUnixソケットファイルディスクリプターに対する操作は防ぎません。ほとんどのサンドボックスシナリオでは、ソケット作成をブロックするだけで不正なIPCを防ぐのに十分です。
**実行時依存関係ゼロ**: ビルド済みの静的apply-seccompバイナリと事前生成されたBPFフィルターがx64およびarm64アーキテクチャ用に含まれています。実行時にコンパイルツールや外部依存関係は不要です。
**アーキテクチャサポート**: x64およびarm64はビルド済みバイナリで完全にサポートされています。その他のアーキテクチャは現在サポートされていません。サポートされていないアーキテクチャでUnixソケットブロックなしでサンドボックス化を使用するには、設定で `allowAllUnixSockets: true` を設定してください。
### 違反の検出と監視
サンドボックス化されたプロセスが制限されたリソースにアクセスしようとすると:
1. OSレベルで**操作をブロック**する(`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
注: 新しい設定形式ではカスタムプロキシ設定はまだサポートされていません。この機能は将来のリリースで追加される予定です。
重要なセキュリティ上の考慮事項: ドメインの許可リストを使用していても、データ持ち出しの経路が存在する可能性があります。例えば、github.com を許可すると、プロセスが任意のリポジトリにプッシュできるようになります。カスタム MITM プロキシと適切な証明書設定を使用すれば、特定の API 呼び出しを検査およびフィルタリングしてこれを防ぐことができます。
セキュリティ上の制限
- ネットワークサンドボックスの制限: ネットワークフィルタリングシステムは、プロセスが接続を許可されるドメインを制限することによって動作します。それ以外にプロキシを通過するトラフィックを検査することはなく、ユーザーはポリシーで信頼できるドメインのみを許可する責任があります。許可されたホスト名は、直接ダイヤルする前に解決済みアドレスの拒否セットに対しても追加でチェックされるため(上記の解決済みアドレスチェックを参照)、許可された名前をループバック、リンクローカル、このホスト自身のアドレス、または
deniedDomainsに記載した IP に向けることはできません。その他のプライベート範囲は、deniedResolvedAddressesに記載した場合にのみ対象となります(DNS を制御していないドメインにワイルドカードエントリを設定すると、LAN 上のサービスを狙われる可能性があります)。また、parentProxy/mitmProxyを経由して出ていく接続は、同等のチェックをそのホップに依存します。
- Unix ソケットによる権限昇格:
allowUnixSockets設定は、サンドボックスバイパスにつながる可能性のある強力なシステムサービスへのアクセスを意図せず許可してしまうことがあります。例えば、/var/run/docker.sockへのアクセスを許可するために使用すると、docker ソケットを悪用して実質的にホストシステムへのアクセスを許可することになります。ユーザーは、サンドボックスを通じて許可する Unix ソケットを慎重に検討することが推奨されます。 - ファイルシステム権限の昇格: 過度に広範なファイルシステム書き込み権限は、権限昇格攻撃を可能にすることがあります。
$PATH内の実行可能ファイルを含むディレクトリ、システム設定ディレクトリ、またはユーザーシェル設定ファイル(.bashrc、.zshrc)への書き込みを許可すると、他のユーザーやシステムプロセスがこれらのファイルにアクセスした際に、異なるセキュリティコンテキストでコードが実行される可能性があります。 - Linux サンドボックスの強度: Linux 実装は強力なファイルシステムとネットワークの分離を提供しますが、特権名前空間なしで Docker 環境内で動作するための
enableWeakerNestedSandboxモードが含まれています。このオプションはセキュリティを大幅に弱めるため、追加の分離が別途強制されている場合にのみ使用すべきです。 - 弱められたネットワーク分離(macOS):
enableWeakerNetworkIsolationオプションは、Go プログラムが macOS Security フレームワークを介して TLS 証明書を検証するために必要なcom.apple.trustd.agentへのアクセスを再有効化します。これは 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 とは異なる