업데이트로 돌아가기
New releaseSep 12, 2026

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 이그레스 펜스와 작업 트리에 대한 세션별 명시적 ACE를 사용합니다

0d1c612947c798aef48e6ab4beb7e8544da9d41a-4096x2305

이중 격리 모델

효과적인 샌드박싱을 위해서는 파일 시스템과 네트워크 격리가 모두 필요합니다. 파일 격리가 없으면 침해된 프로세스가 SSH 키나 기타 민감한 파일을 유출할 수 있습니다. 네트워크 격리가 없으면 프로세스가 샌드박스를 탈출하여 무제한 네트워크 접근 권한을 얻을 수 있습니다.

파일 시스템 격리는 읽기 및 쓰기 제한을 시행합니다:

  • 읽기 (거부 후 허용 패턴): 기본적으로 읽기 접근은 모든 곳에서 허용됩니다. 넓은 영역(예: /Users)을 거부한 다음 그 안의 특정 경로(예: .)를 다시 허용할 수 있습니다. allowReaddenyRead보다 우선합니다 — 쓰기와는 반대로, 쓰기에서는 denyWriteallowWrite보다 우선합니다. allowRead 영역 내부에 있으면서 그보다 더 구체적인 denyRead 항목(예: allowRead: ["."]와 함께 denyRead: ["**/.env"] 또는 ["./secrets"])은 여전히 거부된 상태로 유지됩니다.
  • 쓰기 (허용 전용 패턴): 기본적으로 쓰기 접근은 모든 곳에서 거부됩니다. 경로(예: ., /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)의 경우, 사유는 인밴드로도 전달됩니다: no-auth SOCKS ProxyCommand (예: BSD `nc -X 5`)를 통해 터널링된 SSH 클라이언트는 키 교환 전 SSH 연결 해제를 수신하며, 그 설명이 사유이고 OpenSSH가 이를 그대로 출력합니다 — OpenSSH가 비ASCII를 잘라내고 이스케이프하므로 이러한 사유는 ~400 ASCII 문자 이하로 유지하고 명령형을 먼저 사용하세요.
- `network.allowLocalBinding` - 로컬 포트 바인딩 허용 (불리언, 기본값: false)

**해석된 주소 검사.** 허용/거부 목록은 _이름_으로 일치하지만, 허용된 이름의 DNS(또는 허용된 와일드카드 아래의 모든 레이블)를 제어하는 사람은 그것이 해석되는 대상을 제어합니다. 따라서 허용된 **호스트 이름**에 직접 연결하기 전에, 프록시는 이를 한 번 해석하고, 거부 세트에 있는 주소를 버리고, 살아남은 주소에 연결합니다 (검사를 통과한 주소가 연결되는 주소입니다 — 두 번째 조회는 없습니다). 아무것도 살아남지 않으면, 연결은 다른 정책 거부와 마찬가지로 거부됩니다: 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, …)를 명시하며, 주소 자체는 디버그 로그에만 기록됩니다 — 이 위반 저장소에 기록됩니다.

거부 세트는 다음과 같습니다: loopback (`127.0.0.0/8`, `::1`), unspecified (`0.0.0.0/8`, `::`), link-local (`169.254.0.0/16`, `fe80::/10`), multicast (`224.0.0.0/4`, `ff00::/8`), broadcast, link-local 외부에 있는 클라우드 인스턴스 메타데이터 / 플랫폼 엔드포인트 (`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`에 바인딩된 서비스는 loopback에서와 정확히 동일하게 LAN 또는 글로벌 주소에서 응답합니다), `deniedDomains`에 나열된 모든 IP 리터럴 (`:port`가 있는 경우 이를 준수), 그리고 `deniedResolvedAddresses`에 있는 모든 것. IPv4 항목은 IPv4 주소를 포함하는 IPv6 형식과도 일치합니다 — IPv4-mapped, IPv4-compatible 및 IPv4-translated 주소, NAT64 well-known prefix (`64:ff9b::/96`) 및 6to4 (`2002::/16`)는 포함된 IPv4 주소로 판단됩니다. 로컬 사용 NAT64 prefix `64:ff9b:1::/48` 및 네트워크별 prefix는 디코딩되지 않습니다 — 그 레이아웃(RFC 6052는 여러 위치에서 IPv4를 허용)은 주소만으로는 인식할 수 없습니다. 이러한 네트워크에서는 거부하는 범위의 prefix 변환을 나열하세요 (예: `10.0.0.0/8`의 경우 `<prefix>::a00:0/104`). 이 호스트에 할당되지 않고 이 호스트에 도달하는 주소 — 클라우드 인스턴스의 1:1-NAT 공용 주소, 라우터 포트 포워드, 컨테이너 또는 VM 호스트 게이트웨이 별칭 — 는 자동으로 포함되지 않습니다. `deniedResolvedAddresses`에 나열하세요.

검사가 건드리지 않는 것: **IP 리터럴인** allowlist 항목 (`127.0.0.1:3000`을 허용 목록에 추가하는 것은 명시적 선택입니다) — 그리고 같은 맥락에서, 해당 IP 리터럴(해당 포트에서)이 `allowedDomains`에 있으면 호스트 이름이 다른 방식으로 거부된 주소로 해석될 수 있습니다. 이름으로 도달하는 것은 리터럴 항목이 부여하지 않는 것을 부여하지 않기 때문입니다 (`deniedDomains`의 IP 리터럴은 리터럴 요청에서와 정확히 동일하게 여전히 우선합니다). 따라서 `myapp.test`가 `/etc/hosts`를 통해 로컬 서버에 매핑되는 개발 설정은 `["myapp.test", "127.0.0.1:3000"]`을 허용 목록에 추가합니다. 별도의 예외 목록은 없습니다. `localhost` 및 `.localhost` 아래의 이름은 loopback(또는 허용 목록에 있는 리터럴)으로만 해석되며 그 외에는 아무것도 아닙니다. 이 검사는 `parentProxy` (srt 자체 환경의 `HTTP_PROXY` / `HTTPS_PROXY`에서 선택된 것 포함) 또는 `mitmProxy`를 통해 라우팅된 연결에 대해서는 평가되지 않습니다 — 해당 홉이 이름을 해석하고 자체 주소 정책을 소유합니다 — 그리고 프록시가 연결하는 대상만을 관장합니다: macOS에서 `allowLocalBinding`은 샌드박스된 프로세스가 프록시를 전혀 거치지 않고 loopback 포트에 연결할 수 있게 합니다.

- `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-mapped 블록(`::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 종료가 근본적으로 깨뜨리는 두 가지 경우에 사용하세요:
  - **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 소켓 설정 (플랫폼별 동작):

설정macOSLinux
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보다 우선함). 참고: 이는 denyWriteallowWrite보다 우선하는 쓰기와 반대입니다.

쓰기 제한 (허용 전용 패턴) - 기본적으로 모든 쓰기가 거부됩니다:

  • filesystem.allowWrite - 쓰기 접근을 허용할 경로 배열. 빈 배열 = 쓰기 접근 없음.
  • filesystem.denyWrite - 허용된 경로 내에서 쓰기 접근을 거부할 경로 배열 (allowWrite보다 우선함)

경로 구문 (macOS):

macOS에서 경로는 .gitignore 구문과 유사한 git 스타일 glob 패턴을 지원합니다:

  • * - /를 제외한 모든 문자와 일치 (예: *.tsfoo.ts와 일치하지만 foo/bar.ts와는 일치하지 않음)
  • ** - /를 포함한 모든 문자와 일치 (예: src/**/*.tssrc/ 내의 모든 .ts 파일과 일치)
  • ? - /를 제외한 임의의 단일 문자와 일치 (예: file?.txtfile1.txt와 일치)
  • [abc] - 집합 내의 임의의 문자와 일치 (예: file[0-9].txtfile3.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: JAVA_TOOL_OPTIONS를 통해 주입되는 JVM 에이전트인 srt-proxy-agent.jar의 절대 경로 (네트워크 격리 아래의 "JVM 도구" 참조). sandbox-runtime을 번들로 제공하고 jar를 별도로 배포하는 소비자에게만 필요합니다. 일반적인 npm 설치에서는 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 열기 요청 전송 허용 (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
  • socat - 프록시 브리징을 위한 소켓 릴레이
    • 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 격리 계층 모두 기능을 가진 사용자 네임스페이스가 필요합니다. 다음 명령으로 제한을 비활성화하세요:```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 패키지에 번들로 포함되어 있습니다. 일회성 관리자 권한 `windows-install` 단계가 필요합니다 — 아래를 참조하세요.

## Windows (알파)

Windows 지원은 **알파** 단계입니다. 샌드박스된 프로세스는 전용 `srt-sandbox` 로컬 사용자 계정으로 실행되며, 네이티브 Windows 보안 프리미티브를 통해 호출 사용자로부터 격리됩니다 — 샌드박스 계정의 SID를 키로 하는 WFP(Windows Filtering Platform) 이그레스 펜스와, 구성된 파일 시스템 경로에 대한 해당 SID의 접근을 허용하거나 거부하는 세션별 명시적 ACE입니다.

### 설정

머신당 한 번 실행하세요(자동 권한 상승, UAC 프롬프트 한 번):```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 아래에서 실행하는 것은 대리 실행(surrogate-spawn) 부류의 탈출(Task Scheduler, 브로커 소유 프로세스에 대한 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 → 상속되는 MODIFY ALLOW ACE (READ|WRITE|EXECUTE|DELETE, FILE_DELETE_CHILD는 제외됨). 샌드박스된 프로세스는 작업 트리 내부의 파일을 생성, 수정, 삭제할 수 있습니다; 부여에서 FILE_DELETE_CHILD를 제외하는 것은 아래의 거부 스탬프에 대한 심층 방어이며, 트리 루트에 대한 보호 장치가 아닙니다.
  • filesystem.allowRead → 상속되는 READ|EXECUTE ALLOW ACE
  • filesystem.denyRead / filesystem.denyWrite → 대상에 대한 상속되는 DENY ACE, 그리고 그 부모에 대한 상속되는 FILE_DELETE_CHILD DENY — 작업 트리 부여에서 제외된 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 이그레스 펜스에 의해 차단됩니다. 기본적으로 폐기 확인이 켜진 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 프롬프트 한 번. %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**: 구성된 경로에 `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`를 재정의합니다. 이를 통해 거부된 영역 내에 읽기 가능한 영역을 만들고, 쓰기 가능한 영역 내에 보호된 영역을 만들 수 있습니다.

### 필수 거부 경로 (자동 보호 파일)

특정 민감한 파일과 디렉터리는 허용된 쓰기 경로 내에 있더라도 **항상 쓰기가 차단됩니다**. 이는 샌드박스 탈출 및 구성 변조에 대한 심층 방어를 제공합니다.

**항상 차단되는 파일:**

- 셸 구성 파일: `.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가 포크하고 `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` 시스템 호출을 차단합니다(후자 세 가지는 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를 통해 나가는 연결은 동등한 검사를 해당 홉에 의존합니다.
사용자는 `github.com`과 같이 광범위한 도메인을 허용함으로써 발생할 수 있는 데이터 유출 위험을 인지해야 합니다. 또한 경우에 따라 [도메인 프론팅](https://en.wikipedia.org/wiki/Domain_fronting)을 통해 네트워크 필터링을 우회하는 것이 가능할 수 있습니다.
  • 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-lookup)을 다시 활성화하며, 이는 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 사용자는 시스템 로그 저장소를 통한 자동 위반 모니터링이 있는 macOS와 달리 위반을 확인하려면 수동으로 strace를 실행해야 합니다

카테고리