샌드박스 백엔드
백엔드 개요
섹션 제목: “백엔드 개요”| 백엔드 | 값 | 플랫폼 | 가용성 |
|---|---|---|---|
| 격리 VM | microvm |
macOS, Linux, Windows (microVM hypervisor) | microVM 런타임에 대해 탐지로 게이트됩니다. 대화형 터미널에 적용됩니다 — 셸은 호스트가 아니라 리그 안에서 실행됩니다 |
| 네이티브 | native |
macOS(Seatbelt), Linux와 WSL2(bubblewrap) | 부팅할 때 탐지됩니다. Linux는 bubblewrap이 강제할 수 없는 파일 이름 글로브 쓰기 거부를 요구하는 실행을 거부합니다 |
| 격리 없음 | none |
모두 | 파싱 기본값이자 터미널의 기본값이며, 사용할 수 있는 백엔드가 없거나 --sandbox off가 설정되었을 때의 폴백입니다 |
SandboxBackend.fromName은 저장된 값이 null이거나 인식할 수 없으면 none을 반환합니다. 저장된 "docker"는 native로 매핑됩니다.
microvm은 커널 경계를 가진 유일한 백엔드입니다 — 워크로드가
실제로 호스트에서 실행되지 않습니다. 어느 방향으로도 조용한 폴백이 되지
않습니다. VM을 부팅할 수 없는 호스트에서 이를 명시적으로 요청하면 크게
실패하며(“VM을 요청했는데 노트북이 나왔다”는 것이 인클로저가 감당할 수 없는
유일한 저하입니다), 지정되지 않은 기본값만이 그 사실을 로그 줄에 남기고
native로 내려갑니다. 세션의 실제 백엔드는 클라이언트에 보고되고 배지로
표시되므로, 터미널이 호스트에서 실행되면서 인클로저라고 주장할 수는 없습니다.
VM 자체의 수명주기, 이미지, 이그레스는 리그 레퍼런스에서 다룹니다.
Windows에는 네이티브 샌드박스가 없습니다. NativeSandboxAdapter.probe는 macOS나 Linux/WSL2가 아닌 모든 호스트에서 available: false를 반환합니다. 따라서 Windows의 에이전트 디스패치는 none을 사용합니다. microvm 백엔드는 별개입니다. microVM 런타임에 대해 탐지로 게이트되며, 호스트에서 bash를 감싸는 것이 아니라 대화형 터미널과 리그에 적용됩니다.
탐지 결과
섹션 제목: “탐지 결과”탐지가 강제를 게이트합니다. 서버 부팅 시 한 번 실행됩니다.
| 호스트 | available |
참고 / 힌트 |
|---|---|---|
| macOS | true |
sandbox-exec (Seatbelt) — 네임스페이스 격리, 커널 경계 없음 |
bwrap과 socat이 있는 Linux 또는 WSL2 |
true |
bubblewrap — 네임스페이스 격리, 커널 경계 없음 |
| 도구가 하나라도 없는 Linux 또는 WSL2 | false |
requiresInstall: true, 누락된 패키지를 알려 주는 설치 힌트가 함께 제공됩니다 |
| 그 밖의 모든 플랫폼 | false |
이 플랫폼에서는 네이티브 샌드박스를 제공하지 않습니다 |
서버는 그 결과를 시작 줄 하나에 기록합니다. agent sandbox ON에 해당 설명이 붙거나, no OS-native agent sandbox on this host에 힌트가 붙거나, agent sandbox DISABLED by --sandbox off입니다. GET /healthz에는 샌드박스 필드가 없습니다.
--sandbox / CC_SERVER_SANDBOX는 on(기본값) 또는 off를 받습니다. 이것은 옵트인이 아니라 옵트아웃입니다. on은 “호스트가 백엔드를 제공하면 감싼다”는 뜻이며, 결코 “백엔드를 요구한다”는 뜻이 아닙니다.
네이티브 백엔드
섹션 제목: “네이티브 백엔드”macOS: Seatbelt
섹션 제목: “macOS: Seatbelt”Apple의 Seatbelt 샌드박스 메커니즘을 사용합니다(macOS App Sandbox가 사용하는 것과 같은 시스템).
생성되는 프로필은 기본적으로 허용하는 방식((allow default))이며 거부 항목을 도려냅니다. 완전히 기본 거부인 프로필은 대화형 셸을 쓸 수 없게 만들기 때문입니다 — 열거해야 할 dyld, XPC, Mach 호출이 너무 많습니다. 실질적인 결과는 에이전트가 여전히 호스트의 대부분을 읽을 수 있다는 것입니다. 등록된 체크아웃은 보이지 않음으로써가 아니라 명시적인 쓰기 및 이름 바꾸기 거부로 보호됩니다.
- 파일시스템 읽기: 시크릿 경로(
~/.ssh,~/.aws,~/.gnupg,~/.config/gh,~/Library/Keychains및 나머지 읽기 거부 집합)에 대해 거부됩니다 - 파일시스템 쓰기: 거부로 재설정한 뒤 하위 경로를 명시적으로 허용하며, 필수 거부 경로(셸 rc 파일,
.git/hooks, Claude 구성, 보호된 체크아웃)는 쓰기 가능한 루트 안에서도 차단됩니다.file-write-unlink는 거부된 모든 경로와 그 조상에서 거부되므로 이름 바꾸기로 쓰기 거부를 우회할 수 없습니다 - 프로세스: 항상 위험한 바이너리에 대해
process-exec를 거부하고, 쓰기 가능한 디렉터리에서 무언가를 실행하는 것도 차단합니다 - 네트워크: 프로세스 내 프록시 포트와 DNS로 제한됩니다 — 루프백이 일괄 허용되지 않으며 Unix 소켓도 허용되지 않습니다. 프록시가 세션별 도메인 허용 목록을 강제합니다(네트워크 이그레스 참고)
- 구성: 프로필은 실행마다
SandboxSpec에서 생성됩니다
요구 사항:
sandbox-exec(/usr/bin/sandbox-exec)는 모든 macOS 설치에 포함되어 있으며 추가 도구가 필요하지 않습니다
Linux: bubblewrap
섹션 제목: “Linux: bubblewrap”bubblewrap(bwrap)을 사용해 마운트 및 PID 네임스페이스를 만듭니다.
- 파일시스템: 바인드된 경로만 보이는 마운트 네임스페이스를 만듭니다
- 네트워크: 공유되지 않은 네트워크 네임스페이스에서 실행되며, 트래픽은
socatUnix 소켓 브리지를 통해 호스트 프록시에 도달합니다 - 프로세스: PID 네임스페이스 격리
요구 사항:
bubblewrap과socat설치(Debian/Ubuntu에서는sudo apt-get install bubblewrap socat)- 커널 네임스페이스 지원(최신 Linux에서는 표준)
bubblewrap은 기존 경로를 가릴 수는 있지만 쓰기 가능한 바인드 안에 새
.env, *.key, *.pem 파일이 생기는 것을 금지할 수는 없습니다. 표준 디스패치
정책은 이런 파일 이름 글로브 거부를 요구하므로, 현재 Linux/WSL2의 네이티브
에이전트 디스패치는 탐지가 bubblewrap을 찾아내더라도 실패 시 차단됩니다.
조용히 더 약한 정책으로 실행되지 않습니다. Linux 네이티브 디스패치를 복원하려면
생성 시점의 파일 이름 규칙을 지원하는 백엔드가 필요합니다. 샌드박싱을 끄는 것은
명시적인 무제한 옵트아웃이며, 동등한 우회 방법이 아닙니다.
격리 없음 백엔드
섹션 제목: “격리 없음 백엔드”샌드박싱이 없습니다. 프로세스는 호스트의 파일시스템, 네트워크, 프로세스 접근을 그대로 가진 채 호스트에서 직접 실행됩니다. ACP와 하네스 bash 도구는 여전히 환경 정리와 명령 정책을 받습니다. NoSandboxAdapter를 거치는 Claude Code는 호스트 환경을 물려받습니다. 사용할 수 있는 백엔드가 없거나 --sandbox off인 호스트에서 에이전트 디스패치가 대체되는 대상이며, 클라이언트가 아무것도 지정하지 않았을 때 터미널의 기본값입니다.
서버 호스팅 터미널
섹션 제목: “서버 호스팅 터미널”터미널은 디스패치 탐지가 아니라 자체 기준에 따라 샌드박스로 감싸집니다. TerminalSessionService의 defaultBackend는 none이고 cc_server는 이를 재정의하지 않으므로, 클라이언트가 native를 명시적으로 요청하지 않는 한 터미널 세션은 호스트 셸을 실행합니다 — 데스크톱의 터미널 패널은 요청하지 않습니다.
native를 요청하면 고정된 터미널 프로필 아래에서 SandboxManager를 통해 셸이 감싸집니다. $HOME과 세션의 작업 디렉터리는 쓰기 가능하고, 네트워크는 열려 있으며 ~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gh, ~/Library/Keychains는 읽기가 거부됩니다. 읽기 전용 forge 토큰과 푸시 게이트웨이는 적용되지 않습니다 — 그것은 디스패치의 관심사입니다. 래핑이 지원되지 않으면 세션은 호스트 셸 PTY에 네이티브 배지를 표시하는 대신 실패합니다.
네트워크 이그레스
섹션 제목: “네트워크 이그레스”샌드박스가 시작되면 아웃바운드 네트워크 접근은 커널 방화벽 규칙이 아니라 프로세스 내 프록시가 강제합니다. 사용할 수 있는 백엔드가 없는 호스트에서는 이 중 어느 것도 적용되지 않으며, 스폰된 프로세스는 서버 사용자와 똑같이 네트워크에 접근합니다.
- 샌드박스 런타임은
cc_server프로세스 안에서127.0.0.1에 HTTP/HTTPS 프록시와 SOCKS5 프록시를 실행합니다. 제한된 세션마다 한 쌍입니다(http_proxy.dart,socks_proxy.dart). 동시 실행은 한 쌍을 공유하지 않습니다. - 샌드박스로 감싸진 프로세스는 프록시 환경 변수를 받습니다(
HTTP_PROXY/HTTPS_PROXY→ HTTP 프록시,ALL_PROXY→ SOCKS 프록시,GIT_SSH_COMMAND는 git-over-SSH를 SOCKS 프록시로 라우팅합니다). Linux에서는 네트워크 네임스페이스가 공유되지 않으므로 프록시가 유일한 출구이고, macOS에서는 샌드박스가 호스트 루프백을 공유하며 프록시와 직접 통신합니다. - 각 연결의 대상 호스트는 세션의 도메인 허용 목록과 대조되며, 그 밖의 모든 것은 거부됩니다. 와일드카드 형식은 두 가지이며 각각
*가 정확히 하나입니다. 맨 앞의*.example.com은 모든 하위 도메인(a.b.example.com포함)과 일치하지만 최상위 도메인 자체와는 일치하지 않습니다. 중간 레이블 형식bedrock.*.amazonaws.com은 정확히 레이블 하나와 일치합니다(bedrock.us-east-1.amazonaws.com은 일치하고bedrock.evil.attacker.amazonaws.com은 일치하지 않습니다). - 허용 목록은 세션마다 큐레이션된 기준선(
kBaselineAllowedDomains: LLM 공급자, Git 호스팅, 패키지 레지스트리)과 GitHub 도메인(kGithubDomains), 그리고SandboxSpec.egressAllowlist에서 해석됩니다. 디스패치 경로는 추가egressAllowlist항목을 결코 전달하지 않으므로 실제로는 모든 실행이 기준선과 GitHub를 받습니다. - 고정된 거부 목록(
kBaselineDeniedDomains: 클라우드 메타데이터 엔드포인트, 텔레메트리 싱크)이 모든 허용보다 우선합니다. networkEnabled: false에서도 세션별 프록시는 시작되지만 허용 목록이 비어 있으므로 모든 아웃바운드 연결이 거부됩니다. 디스패치는 실행이 시작될 때 네트워크 접근 규칙에서 이 값을 정합니다. 거부는 네트워크를 끄고, 허용과 먼저 묻기는 켜 둡니다.
SandboxSpec
섹션 제목: “SandboxSpec”SandboxPort.launch에 전달되는 명세입니다:
| 필드 | 타입 | 설명 |
|---|---|---|
sessionId |
String |
안정적인 세션 식별자(일반적으로 스페이스 ID) |
workspaceId |
String |
소유 워크스페이스 ID |
agentId |
String? |
현재 샌드박스에 바인딩된 에이전트(있는 경우) |
bindMounts |
List<SandboxBindMount> |
호스트→게스트 마운트. 샌드박스는 여기에 나열된 호스트 경로만 봅니다 |
networkEnabled |
bool (기본값 true) |
디스패치 시 네트워크 이그레스 규칙에서 설정됩니다(거부로 해석될 때만 꺼짐). 꺼져 있으면 세션별 프록시는 여전히 시작되지만 허용 목록이 비어 있어 모든 아웃바운드 연결이 거부됩니다 |
egressAllowlist |
List<String> (기본값 []) |
추가로 허용되는 이그레스 도메인으로, 와일드카드(*.example.com 및 레이블 하나짜리 중간 형식)를 지원합니다. 디스패치는 이 필드를 채우지 않으므로 감싸진 실행은 큐레이션된 기준선과 GitHub 도메인을 받습니다 |
guestWorkdir |
String? |
샌드박스 안의 기본 작업 디렉터리로, 개별 exec 호출이 재정의할 수 있습니다 |
mode |
Mode (기본값 chat) |
파일시스템 쓰기 규칙을 정합니다. chat은 바인드 마운트를 쓰기 가능하게 유지하고, review, plan, orchestrate는 읽기 전용입니다 |
protectedPaths |
List<String> (기본값 []) |
어떤 모드에서든 샌드박스 안에서 결코 쓰기 가능해서는 안 되는 호스트 경로 |
runnerStateDirs |
List<String> (기본값 []) |
러너가 자체 상태를 보관하는 호스트 디렉터리로, 모든 모드에서 쓰기 가능합니다(현재는 Control-Center가 관리하는 CLAUDE_CONFIG_DIR) |
execGrantRoots |
List<String> (기본값 []) |
macOS에서 쓰기 가능 디렉터리 실행 차단을 다시 여는, 운영자가 승인한 디렉터리 트리입니다. Linux에서는 무시됩니다 — bwrap에는 다시 열 $HOME 실행 차단이 없습니다 |
protectedPaths는 등록된 원본 리포지토리 체크아웃을 담으며, 디스패치마다 워크스페이스의 리포지토리에서 해석됩니다. 에이전트는 대화별 쓰기 시 복사 워크트리에서만 작업하므로, 원본은 샌드박스 안에서 결코 쓰기 가능해서는 안 됩니다 — 그렇지 않으면 macOS에서 Seatbelt의 일괄 $HOME 쓰기 허용 때문에 bash를 통해 원본에 쓸 수 있게 됩니다. 이 경로들은 chat을 포함한 모든 모드에서 시크릿 쓰기 거부 집합에 합류합니다.
모든 읽기 거부 및 쓰기 거부 경로는 리터럴 표기와 심볼릭 링크 해석 표기 두 가지로 내보내집니다. 커널은 거부 규칙을 해석된 경로에 대조하므로, 심볼릭 링크된 경로를 기준으로 작성된 규칙은 결코 발동하지 않습니다 — 그리고 macOS에서는 이것이 예외가 아니라 일반적인 경우인데, /tmp는 /private/tmp로, /var/folders/…는 /private/var/folders/…로 해석되기 때문입니다. *나 ?가 포함된 항목은 글로브로 취급되어 그대로 둡니다. 해석되지 않는 경로는 작성된 그대로 유지됩니다.
SandboxBindMount
섹션 제목: “SandboxBindMount”| 필드 | 타입 | 설명 |
|---|---|---|
hostPath |
String |
호스트의 절대 경로 |
guestPath |
String |
샌드박스 안의 경로(네이티브 샌드박스에서는 hostPath와 같으며, 어댑터 대칭을 위해 유지됩니다) |
readOnly |
bool (기본값 false) |
true이면 읽기 전용으로 마운트합니다 |
SandboxHandle
섹션 제목: “SandboxHandle”SandboxPort.launch가 반환합니다:
| 필드 | 타입 | 설명 |
|---|---|---|
sessionId |
String |
세션 식별자 |
backend |
SandboxBackend |
실행 중인 백엔드 |
state |
SandboxState |
수명주기 상태 |
error |
Object? |
state가 error일 때 설정됩니다 |
details |
Map<String, Object?> |
어댑터 내부의 부기 정보(작업 디렉터리, PID, 프로필 경로 등) |
SandboxState
섹션 제목: “SandboxState”| 상태 | 설명 |
|---|---|
created |
명세는 만들어졌지만 VM이 아직 시작되지 않음 |
warm |
실행 중이며 명령을 exec할 준비가 됨 |
active |
현재 명령을 실행 중 |
suspended |
RAM을 절약하기 위해 일시 중지되거나 디스크에 체크포인트됨 |
destroyed |
정리되었으며 핸들이 더 이상 유효하지 않음 |
error |
시작 실패 — SandboxHandle.error 참고 |
샌드박스 이벤트
섹션 제목: “샌드박스 이벤트”| 이벤트 유형 | 설명 |
|---|---|
stdout |
샌드박스의 표준 출력 |
stderr |
샌드박스의 표준 오류 |
exit |
샌드박스 프로세스가 종료됨(exitCode를 함께 전달) |
starting |
샌드박스가 시작 중 |
ready |
샌드박스가 exec 호출을 받을 준비가 됨 |
killed |
호스트가 프로세스를 종료함(OOM, 타임아웃, 수동 중지) |
violation |
샌드박스 정책이 위반됨(SandboxViolation을 함께 전달) |
SandboxViolation
섹션 제목: “SandboxViolation”| 필드 | 타입 | 설명 |
|---|---|---|
action |
String |
거부된 동작(예: file-read*, network-outbound) |
target |
String |
거부된 작업의 대상이 된 경로 또는 호스트 |
raw |
String? |
디버깅에 유용한 원시 로그 줄 |
함께 보기
섹션 제목: “함께 보기”- 샌드박스와 보안
- 샌드박스 정책
- 가드레일
- cc_server CLI:
--sandbox플래그