콘텐츠로 이동

샌드박스와 보안

에이전트는 사용자를 대신해 파일을 읽고, 셸 명령을 실행하고, 외부 API를 호출합니다. 걱정해야 할 실패는 악의인 경우가 드뭅니다. 경로를 잘못 읽어 작업 중인 체크아웃을 덮어쓰는 에이전트거나, 아직 리뷰를 끝내지 않은 브랜치를 푸시하는 에이전트입니다. 그래서 제품이 답해야 하는 질문은 “에이전트를 신뢰할 수 있는가”가 아니라 “에이전트가 잘못했을 때 피해 범위는 어디까지인가”입니다.

Control Center는 이 질문에 계층으로 답하며, 계층은 의도적으로 서로 독립적입니다. 하나가 빠져도 나머지는 여전히 유효합니다. 이것이 중요한 이유는 가장 바깥 계층인 OS 샌드박스가 유일하게 조건부이기 때문입니다. 호스트가 백엔드를 제공하는지에 달려 있고, Windows에서는 어떤 호스트도 제공하지 않습니다.

에이전트를 실제로 제약하는 것

섹션 제목: “에이전트를 실제로 제약하는 것”

샌드박스가 있든 없든, 모든 호스트에서 에이전트 실행을 제약하는 다섯 가지가 있습니다. 어느 것도 커널 경계는 아니지만 모두 실재합니다. 호스트가 백엔드를 제공하면 OS 샌드박스가 그 위에 놓이며, 그것이 무엇을 바꾸는지는 아래의 “샌드박스가 더하는 것과 멈추는 곳”에서 다룹니다.

자체 쓰기 시 복사 워크트리. 모든 대화는 연결된 각 리포지토리의 체크아웃을 자체적으로 준비합니다. 바이트 단위로 복사하는 대신 번들로 제공되는 rift 네이티브를 통해 쓰기 시 복사로 클론하므로, 격리가 기본값이 될 만큼 저렴합니다. 워크스페이스에 추가한 등록 리포지토리는 에이전트가 작업하는 디렉터리가 아닙니다. 같은 리포지토리를 다루는 두 대화는 서로의 편집을 볼 수 없고, 어느 쪽도 사용자의 체크아웃을 편집하지 않습니다.

단위를 분명히 할 필요가 있습니다. 워크트리는 에이전트별이 아니라 대화별입니다. 같은 대화의 두 에이전트는 그 대화의 repos/를 공유 하는 서로 다른 오버레이 작업 디렉터리를 받습니다. 이는 의도된 것으로, 둘이 하나의 변경을 함께 작업하기 때문입니다. 반면 각자의 비공개 에이전트 디렉터리는 분리된 채로 남습니다. rift가 제공되는 곳에서는 쓰기 시 복사가 유일한 백엔드입니다. reflink를 할 수 없는 파일시스템이나 리포지토리와 다른 볼륨에 있는 데이터 디렉터리는 대체 없이 프로비저닝에 실패합니다. Windows는 예외입니다. 그곳에는 쓰기 시 복사 백엔드가 없으므로 git worktree가 백엔드이며, 조용한 성능 저하가 아닙니다.

기본 제공 하네스 파일 도구의 경로 제한. 하네스의 read, write, edit, apply_patch, search, find, search_files 도구는 모든 경로를 실행의 작업 디렉터리와 공유 루트 — 대화의 repos/ 디렉터리(워크트리가 있는 곳)와 존재하는 경우 스페이스의 첨부 파일 디렉터리 — 를 기준으로 해석하고, 그 밖으로 벗어나는 것은 모두 거부합니다. 절대 경로는 이미 내부에 있던 경우에만 허용됩니다. 워크트리 안에서 밖을 가리키는 심볼릭 링크도 해석한 뒤 거부합니다. 샌드박스가 전혀 없는 상황에서도 에이전트가 ~/.ssh에 write할 수 없는 이유가 여기 있습니다.

모드의 도구 표면. 대화의 모드는 실행에 어떤 도구가 아예 구체화되는지를 결정합니다. 읽기 전용 모드(plan, review, orchestrate)에서는 변경 도구가 레지스트리에 아예 없고, 그 모드의 셸 명령 정책은 git commit, git push, npm install, docker 등 모든 변경 명령을 묻지도 않고 즉시 거부합니다. 존재하지 않는 도구는 호출할 수 없으며, 이는 존재하되 게이트가 걸린 도구보다 강한 보장입니다.

통합 동작 가드레일. 모든 변경 도구는 만들어 낼 수 있는 효과 클래스를 선언하고, 정책 엔진은 호출이 실행되기 전에 각각을 허용, 먼저 묻기, 거부 중 하나로 해석합니다. 승인자가 연결되지 않은 prompt는 거부 로 해석됩니다. 아무도 답할 수 없는 질문은 결코 긍정이 되지 않습니다. 해석 모델은 가드레일을 참고하세요.

중개된 자격 증명과 정리된 환경. 자격 증명은 실행마다 발급되고 실행이 끝나면 폐기되며, 실행이 가진 GitHub 토큰은 읽을 수는 있어도 푸시할 수는 없습니다. 아래의 “푸시는 Control Center를 거칩니다”를 참고하세요. ACP, 하네스 bash, 네이티브 래핑 아래의 Claude Code는 프로세스가 시작되기 전에 동적 링커와 인터프리터 주입 벡터(LD_PRELOAD, DYLD_INSERT_LIBRARIES, NODE_OPTIONS, GIT_SSH_COMMAND 및 이와 유사한 것들)를 제거한 환경을 받습니다. NoSandboxAdapter를 통한 Claude Code는 호스트 환경을 그대로 물려받습니다.

하네스의 파일 도구는 자식 프로세스가 아니라 서버 프로세스 안에서 실행되므로, 샌드박스 프로필을 붙이더라도 제약할 수 없습니다. 샌드박스가 적용된 명령 실행기를 거치는 것은 bash뿐입니다. 이들의 격리는 위에서 설명한 경로 검사이며, 따라서 그 검사는 두 번째 방어선이 아니라 핵심적인 지지대입니다.

bash 도구는 정확히 짚어야 할 또 다른 지점입니다. 그 명령은 모드의 명령 정책에 따라 평가된 뒤 정리된 환경과 워크트리를 작업 디렉터리로 삼아 스폰됩니다. 백엔드가 있으면 그 스폰이 래핑되므로, 셸 명령이 워크트리 밖에 쓰지 못하게 막는 것은 샌드박스입니다. 백엔드가 없으면 — Windows, 또는 bwrap과 socat이 없는 Linux — 명령 정책이 명령과 디스크의 나머지 사이에 있는 유일한 것입니다. chat 모드에서 정책은 고정된 목록(권한 상승, 시스템 및 디스크 파괴, 컨테이너와 네임스페이스 탈출)을 강하게 거부하고, 원격 상태를 바꾸는 동작(git push, gh pr create, 패키지 게시와 설치)에는 먼저 묻습니다. 어느 목록에도 없는 것은 실행됩니다. 파이프라인, 체인, 서브셸은 파싱되어 각 하위 명령이 개별적으로 평가되고, 가장 제한적인 결정이 이깁니다.

어댑터로 사용될 때의 Claude Code는 샌드박스가 가장 큰 비중을 지는 사례입니다. Control Center는 비대화형 claude -p가 자체 승인 프롬프트에서 영원히 멈추지 않도록 --dangerously-skip-permissions와 함께 이를 실행합니다. Claude Code의 읽기, 쓰기, 편집 호출은 자체 프로세스 안에서 일어나고 Control Center는 이를 보지 못합니다. Bash 호출은 실행되기 전에 서버의 훅으로 보내지며, 훅이 명령줄을 분류해 해당 규칙에 묻습니다. Control Center가 제공하는 mcp__* 도구는 평소처럼 게이트를 거칩니다. 백엔드가 있으면 보이지 않는 그 호출들도 여전히 샌드박스 래핑 안에서 일어나므로 OS가 그 주위의 경계입니다. 백엔드가 없으면 아무것도 아닙니다. 각 어댑터는 이를 자체 규칙 적용 매트릭스에 명시합니다. 어댑터 정직성 매트릭스를 참고하세요.

에이전트별 권한 플래그는 없습니다. 에이전트가 커밋 생성, 푸시, 풀 리퀘스트 열기, 트래커에 쓰기, 네트워크 접근을 할 수 있는지는 가드레일이 결정합니다. 허용, 먼저 묻기, 거부 중 하나이며, 어떤 어댑터가 에이전트를 실행하든 같은 규칙이 적용됩니다.

푸시는 규칙을 우회하려는 에이전트에 맞서서도 지켜져야 하는 작업이므로, 에이전트가 무엇을 실행하느냐가 아니라 무엇을 가지고 있느냐로 강제됩니다. GitHub App이 구성되어 있으면 GitHub 워크트리에서의 실행은 저장소 범위의 설치 토큰을 받습니다. 이 토큰은 콘텐츠를 읽고 풀 리퀘스트를 다룰 수 있지만 푸시할 수는 없습니다. 실행의 git 구성은 모든 GitHub 푸시 URL(https://github.com/, git@github.com:, ssh://git@github.com/)을 cc_server의 루프백 엔드포인트인 에이전트 실행 게이트웨이로 다시 씁니다. 게이트웨이는 푸시가 어떤 ref를 업데이트하는지 읽고 리모트에 푸시 규칙에 묻습니다. 따라서 “main으로의 푸시 거부” 같은 브랜치 한정 규칙도 적용됩니다. 그런 다음에야 이 용도로 발급하고 에이전트에게는 절대 넘기지 않는 쓰기 토큰으로 푸시를 GitHub에 전달합니다. 거부된 푸시는 git 자체 출력에 나타납니다.

! [remote rejected] main -> main (Control Center policy: …)

게이트웨이는 워크트리 자체의 저장소나 워크스페이스에 연결된 GitHub 저장소로만 푸시를 전달합니다. 페치는 읽기 토큰으로 GitHub에 직접 갑니다.

다른 셸 명령은 더 느슨한 게이트를 만납니다. 하네스의 bash 도구와, 훅을 통한 Claude Code의 Bash 도구는 각 명령줄을 분류해 해당하는 규칙에 묻습니다. git commit, merge, rebase, cherry-pick, revert, am은 커밋 생성, gh pr create는 풀 리퀘스트 열기, gh pr merge, review, comment, close는 리뷰 게시 또는 병합, gh issue create, edit, comment, close는 외부 트래커에 쓰기, 패키지 설치는 패키지 설치, curl과 wget은 네트워크 접근입니다. 이는 작업의 일반적인 표기를 알아보는 것이며, 숨기기로 작정한 에이전트(eval, 스크립트 파일, 직접 API 호출)는 빠져나갈 수 있습니다. 푸시는 이에 의존하지 않습니다.

샌드박스에는 티켓팅 API 키가 놓이지 않습니다. 에이전트는 Control Center의 MCP 도구로 티켓을 쓰며, 그 도구들이 외부 트래커에 쓰기 규칙에 묻습니다.

네트워크 접근 규칙은 실행이 시작될 때 한 번 읽힙니다. 샌드박스의 네트워크는 켜짐 또는 꺼짐뿐이므로 거부는 이를 끄고, 허용과 먼저 묻기는 켜 둡니다. 하네스의 web_fetch와 web_search 도구는 호출마다 계속 묻습니다. 자격 증명은 실행이 정리될 때 게이트웨이가 발급한 모든 쓰기 토큰과 함께 폐기되므로, 핸들이 실행보다 오래 살아남지 않습니다.

샌드박스가 더하는 것과 멈추는 곳

섹션 제목: “샌드박스가 더하는 것과 멈추는 곳”

SandboxBackend 열거형에는 세 가지 값이 있습니다. native(macOS의 Seatbelt, Linux와 WSL2의 bubblewrap), none, 그리고 microvm입니다. 에이전트 명령 래핑은 처음 두 개만 사용합니다. microvm은 대화형 터미널과 리그를 위한 격리 VM 백엔드이며, bash 래핑의 조용한 업그레이드가 아닙니다. 네이티브 형태를 이해하면 하나가 붙었을 때 무엇을 얻는지, 그리고 그것이 처음부터 줄 수 없었던 것이 무엇인지 분명해집니다.

cc_server는 부팅할 때 호스트를 한 번 탐지하고, 백엔드가 응답하면 디스패치 경로가 가진 두 이음매에 모두 연결합니다. 전송 계층이 하나를 공유하지 않기 때문입니다. 첫 번째(SandboxPort)는 Claude Code CLI를 래핑합니다. 두 번째(SandboxManager)는 ACP 어댑터와 기본 제공 하네스의 bash 도구를 래핑합니다. 하나에만 연결하면 나머지는 래핑 없이 스폰되며, 예전에는 그런 일이 있었습니다. 백엔드가 없는 호스트나 --sandbox off 아래에서는 모두 래핑 없이 스폰되고, 서버는 시작 줄에서 어느 경우인지 알려줍니다. ACP와 하네스 bash는 여전히 환경을 정리합니다. NoSandboxAdapter를 통한 Claude Code는 호스트 환경을 그대로 물려받습니다.

한 표면은 의도적으로 빠집니다. 서버가 호스팅하는 터미널은 세션마다 백엔드 인수를 받고 cc_server는 호스트 셸을 기본값으로 이를 구성하므로, 앱에서 연 터미널은 클라이언트가 native를 명시하지 않는 한 샌드박스 없이 실행됩니다. 그것은 에이전트 실행이 아니라 서버 호스트의 실제 셸입니다.

Linux에서 bubblewrap은 마운트 네임스페이스를 만들어내므로 에이전트는 실제로 그 안에 바인드된 것만 봅니다. macOS에서 sandbox-exec는 기본적으로 허용하는 Seatbelt 프로필을 적용합니다. 완전히 기본 거부인 프로필은 대화형 셸을 쓸 수 없게 만드는데, 열거해야 할 dyld, XPC, Mach 호출의 수가 비현실적이기 때문입니다. 그래서 macOS 프로필은 (allow default)에서 시작해 거부 항목을 도려냅니다. 시크릿 경로 읽기, 명시적 허용을 위에 얹은 채 거부로 재설정된 쓰기 정책, 거부된 모든 경로와 그 조상에 대한 unlink 차단(이름 바꾸기로 쓰기 거부를 우회할 수 없도록), 항상 위험한 바이너리와 쓰기 가능한 디렉터리로 복사된 모든 것에 대한 exec 거부, 그리고 루프백 프록시 포트와 DNS로 제한된 네트워크입니다.

실질적인 결과는 이렇습니다. Linux에서는 에이전트가 보는 파일시스템이 좁아집니다. macOS에서는 에이전트가 여전히 호스트의 대부분을 읽을 수 있지만, 등록된 체크아웃은 보이지 않음으로써가 아니라 명시적인 쓰기 및 이름 바꾸기 거부로 보호됩니다. 둘 다 커널 보안 경계가 아니라 네임스페이스 수준의 격리이며, 백엔드들은 자체 기능 노트에서 그렇게 밝힙니다.

에이전트 간 경계가 그어지는 곳이 바인드 마운트 집합입니다. 오버레이 디스패치(Claude Code와 ACP)는 실행마다 해석되는 최대 네 개의 경로를 마운트합니다. 에이전트의 오버레이 작업 디렉터리(읽기-쓰기), 에이전트의 구성 디렉터리(읽기 전용), 존재하는 경우 대화의 공유 repos/ 디렉터리(읽기-쓰기), 그리고 존재하는 경우 스페이스의 첨부 파일 디렉터리(읽기 전용)입니다. 형제 에이전트의 디렉터리는 결코 마운트되지 않습니다. 하네스 bash 래핑은 더 좁습니다. 실행의 작업 디렉터리만 바인드 마운트합니다. 이는 하네스의 프로세스 내 경로 검사가 소프트웨어로 강제하는 것과 같은 형태이므로 둘은 일치합니다. 하지만 하네스 자체의 파일 도구는 마운트 네임스페이스에 전혀 도달하지 않으므로 경로 검사만이 이를 감쌉니다.

등록된 체크아웃은 그 위에 쓰기 거부 집합으로 합류하며, 이는 모든 모드에서, 래핑된 세 경로(Claude Code, ACP, 하네스 bash) 모두에 적용됩니다. 각 규칙은 리터럴 표기와 심볼릭 링크 해석 표기 두 가지로 내보내집니다. 두 번째 표기는 사치가 아닙니다. 커널은 거부 규칙을 해석된 경로에 대조하고, macOS에서는 해석이 예외가 아니라 일반적인 경우입니다. /tmp는 실제로 /private/tmp이고, /var/folders/…는 실제로 /private/var/folders/…입니다. 따라서 둘 중 어느 쪽으로 접근하든 프로필이 금지하는 것처럼 보이면서도 체크아웃에 쓸 수 있었을 것입니다.

샌드박스 아래에서 네트워크는 세션별 도메인 허용 목록이며 프로세스 내 루프백 프록시가 이를 강제합니다. 이그레스는 기본 거부입니다. 네트워크가 꺼져 있으면 프록시는 여전히 시작하지만 허용 목록이 비어 있으므로 모든 아웃바운드 연결이 거부됩니다. 켜져 있으면 리졸버가 선별된 기준선 — LLM API 공급자, git 호스팅, 패키지 레지스트리, 모델 카탈로그 — 에 GitHub 도메인을 더해 허용 목록을 만들고, 모든 연결의 대상 호스트를 이에 대조합니다. 와일드카드가 동작합니다 (*.example.com은 하위 도메인과 일치하며 최상위 도메인은 일치하지 않습니다). 별도의 항상 거부 목록이 허용 목록보다 우선하며 클라우드 메타데이터 엔드포인트와 텔레메트리 싱크를 포함합니다. 구성할 수 없는 것은 목록 자체입니다. 스펙에는 추가 도메인을 위한 egressAllowlist 필드가 있지만 디스패치 경로의 어느 것도 이를 채우지 않으므로, 실제로는 모든 실행이 기준선과 GitHub만 받고 그 이상은 받지 않습니다.

macOS에서 거부는 관측 가능합니다. 모니터가 시스템 로그를 훑어 Sandbox 거부를 찾아내고, 알려진 잡음을 걸러내고, 재시도 폭주를 중복 제거한 뒤, 무엇을 시도했고 무엇이 차단되었는지 밝히는 SandboxViolation으로 각각을 드러냅니다. Linux에서는 래핑 경로가 stderr에서 EPERM, Operation not permitted, Permission denied를 파싱해 같은 이벤트 유형을 내보냅니다. 시스템 로그 탭은 없습니다. 이들은 실행 자체의 이벤트 스트림으로 도착하므로, 거부된 시스템 콜이 로그뿐 아니라 트랜스크립트에도 나타납니다. 텔레메트리는 네이티브 샌드박스가 실제로 프로세스를 래핑하는 동안에만 존재합니다. 백엔드가 없는 호스트에서는 관측할 것이 없습니다. 또한 가드레일과는 무관합니다. 위반은 규칙 조회가 아니라 거부된 시스템 콜에서 비롯됩니다.

확인은 샌드박스가 아니라 가드레일에서 온다

섹션 제목: “확인은 샌드박스가 아니라 가드레일에서 온다”

샌드박스에는 승인 경로가 없습니다. 실행하고, 수행하고, 이벤트를 내보내고, 정리할 뿐이며, 사람이 질문에 답할 자리가 없습니다. 스페이스에서 확인을 보게 될 때, 이를테면 이 푸시를 승인하라거나 이 삭제를 승인하라는 것이라면, 그것은 동작이 런타임에 도달하기 전에 가드레일 엔진(또는 하네스의 명령 정책)이 제기한 것입니다.

이 순서는 의도된 것입니다. 시스템 콜 이후에 일어난 승인은 게이트가 아니라 감사 기록일 뿐입니다. 실패 시 거부하는 규칙이 가드레일에 있는 이유이기도 합니다. 승인자가 연결되지 않으면 무엇이든 실행되기 전에 결정이 내려지고, 그것은 거부입니다.

에이전트 진단 실행은 손으로 확인할 수 있는 것을 밝히는 유일한 곳입니다.