필요한 정보 모아보기
테크

Docker 샌드박스 키트(Sandbox Kit) 스펙 정리 — AI 에이전트 권한을 이미지에 적는 오픈 포맷, 클로드 코드 샌드박스와 차이

#Docker#AI 에이전트#샌드박스#Claude Code#Codex#보안

도커(Docker)가 9월 24일 공개한 **샌드박스 키트 스펙(Docker Sandbox Kit Spec)**은 AI 에이전트가 "무엇에 접근해도 되는지"를 컨테이너 이미지 안에 적어 두는 오픈 포맷이다. 키트(Kit) 하나는 평범한 OCI 이미지이고, 그 안에 에이전트와 도구, 그리고 에이전트가 요청하는 접속 호스트·자격증명·볼륨 목록이 함께 들어간다. 스펙은 Apache 2.0으로 공개됐다. 도커는 이 스펙을 중립 재단인 CNCF(클라우드 네이티브 컴퓨팅 재단)로 가져가겠다고 밝혔다.

배경은 단순하다. Claude Code나 Codex 같은 코딩 에이전트는 패키지를 설치하고, API를 부르고, 사용자 대신 토큰을 쓴다. 그래서 팀마다 네트워크 규칙 하나, 토큰 하나, 볼륨 마운트 하나씩 권한을 덧붙이게 되는데 — 도커 발표 글의 표현을 빌리면 그런 규칙은 셸 히스토리와 대시보드, 누군가의 기억 속에 흩어져 있다가 몇 달 뒤엔 "이 에이전트가 뭘 할 수 있지?"라는 질문에 아무도 답하지 못하게 된다. 키트는 그 답을 이미지 하나, 다이제스트 하나에 묶자는 제안이다.

키트 하나에 무엇을 적나

키트는 두 종류다. **워크로드(workload)**는 실제로 실행되는 루트 파일시스템을 담고, 조합 하나에 정확히 하나만 들어간다. **믹스인(mixin)**은 그 위에 얹는 오버레이다. CLI 하나와 그 CLI의 네트워크 규칙, 자격증명 연결, 에이전트에게 줄 안내문 같은 것을 담으며 몇 개든 붙일 수 있다.

권한 요청은 전부 capabilities라는 한 목록에 들어간다. 항목마다 com.docker.sandbox/network-policy@2처럼 타입과 버전이 붙는다. 스펙 문서가 정의한 기본 타입은 현재 20개이고, 권한과 직접 관련된 것만 추리면 이렇다.

  • network-policy@1·@2 — 나가는 트래픽 허용·차단 목록. @2부터는 호스트뿐 아니라 HTTP 메서드와 경로까지 묶을 수 있다.
  • credential@1 — 어떤 서비스에 인증하는지. 비밀값은 호스트 쪽 저장소에서만 가져오고, proxyManaged나 OAuth 가짜 토큰(sentinel) 방식을 쓰면 샌드박스 안에는 진짜 값이 들어가지 않는다.
  • volume@1·host-mount@1 — 세션 사이에 남길 저장 공간. host-mount에는 호스트 경로를 고르는 필드가 아예 없다. 키트는 샌드박스 안에서 저장소가 나타날 위치만 요청할 수 있다.
  • port@1, usb-device@1, ssh-agent@1 — 포트 개방, USB 장치, SSH 에이전트 전달.
  • privileged@1 — 특권 모드 요청. 스펙은 이것을 "가장 강한 단일 확대"로 분류하고, 런타임이 기본값이 아닌 명시적 동의를 받아야 한다고(SHOULD) 적었다.

눈여겨볼 점이 하나 있다. 이 목록에는 셸 명령 단위로 허용·차단하는 타입이 없다. 키트는 "어떤 명령을 쳐도 되나"가 아니라 "샌드박스 밖 무엇에 닿아도 되나"를 다룬다. 도커 샌드박스는 자체 커널을 가진 마이크로VM이라, 그 안에서는 에이전트에게 루트를 줘도 피해가 샌드박스 경계에서 멈춘다는 게 도커의 설계 설명이다.

실제 정책 파일은 이렇게 생겼다

스펙 저장소(docker/sandbox-kit-spec)의 examples/gh/gh.yaml은 GitHub CLI를 얹는 믹스인 키트다. Apache 2.0으로 공개된 파일에서 권한 부분만 옮기면 다음과 같다(원문 주석은 생략).

capabilities:
  - type: com.docker.sandbox/network-policy@2
    config:
      runtime:
        allow:
          - github.com
          - hosts: [api.github.com]
            methods: [GET, HEAD, POST, PATCH, PUT, DELETE]
          - hosts: [uploads.github.com]
            methods: [POST, PUT]
        deny:
          - hosts: [api.github.com]
            methods: [DELETE]
            paths: [/repos/**]

  - type: com.docker.sandbox/credential@1
    description: GitHub API access for gh
    optional: true
    config:
      service: github
      phase: [runtime]
      apiKey:
        name: GH_TOKEN
        proxyManaged: true
        inject:
          - {domain: api.github.com, header: Authorization, format: "Bearer %s"}
          - {domain: uploads.github.com, header: Authorization, format: "Bearer %s"}
          - {domain: github.com, header: Authorization, format: "Bearer %s"}

읽는 법은 이렇다. GitHub 세 도메인 말고는 나갈 수 없다. api.github.com에는 DELETE까지 허용하지만, /repos/** 아래 DELETE는 deny가 다시 막는다. 스펙상 deny가 언제나 이기기 때문이다. 풀 리퀘스트는 열 수 있어도 저장소는 지울 수 없는 토큰이 되는 셈이다. 막힌 요청에 대해서는 연결을 끊지 말고 HTTP 403으로 답하라고 network-policy@2 문서가 정해 두었다.

proxyManaged: true도 중요하다. 진짜 토큰은 호스트에만 있고, 샌드박스 안 GH_TOKEN에는 가짜 값(sentinel)만 들어간다. 요청이 경계를 지나갈 때 런타임이 지정된 도메인에 한해 진짜 값을 헤더에 꽂아 준다. 에이전트가 환경변수를 몽땅 출력해도 쓸모 있는 토큰은 새지 않는 구조다.

이게 정말 이미지 안에 들어 있는지 궁금해서 도커 허브에 공개된 docker/sbx-kit-gh:2.100.0의 매니페스트를 레지스트리 API로 직접 열어 봤다(2026-10-09, macOS에서 curl로 익명 pull 토큰을 받아 조회, 이미지 내려받기는 하지 않음). 결과는 이렇다.

mediaType: application/vnd.oci.image.index.v1+json   (artifactType 없음)
linux/amd64 매니페스트 annotations:
  vnd.docker.sandbox.kit.schema-version: 3
  vnd.docker.sandbox.kit.capabilities:
    com.docker.sandbox/agent-context@1,com.docker.sandbox/credential@1,com.docker.sandbox/network-policy@2
  vnd.docker.sandbox.kit.descriptor: {"schemaVersion":"3","displayName":"GitHub CLI", ... "kind":"mixin" ...}

별도 아티팩트 타입 없이 평범한 이미지 인덱스였고, 위 YAML과 같은 허용·차단 규칙이 vnd.docker.sandbox.kit.descriptor 어노테이션에 JSON으로 그대로 들어 있었다. 다만 허브에 올라간 버전은 2.100.0이고 저장소 main의 예시는 [email protected]을 선언하고 있어, 버전 번호는 둘이 다르다. 같은 날 도커 허브 API로 docker 조직에서 이름에 sbx-kit가 들어간 저장소를 검색하니 102개가 나왔다. Claude Code·Codex·Cursor·Copilot·OpenCode 같은 에이전트 키트부터 Trivy, JFrog Xray, Grafana 같은 도구 키트까지 섞여 있다. Gemini 키트는 허브 목록에는 없고 GitHub 저장소 예시에만 있다.

업데이트가 권한을 넓히면 멈춘다

키트 스펙에서 개인적으로 가장 실용적이라고 본 부분은 업데이트 게이트다. 모든 런타임의 의무는 아니고, 업데이트를 게이트하는 런타임에 해당하는 규칙이다. 이런 런타임은 키트가 요구하는 권한을 정규화한 집합으로 기록해 두고, 새 버전이 나오면 그 집합과 비교한다. 이미 허락받은 범위 안이면 그냥 적용될 수 있고(MAY), 범위를 넓히면 멈추고 승인을 받아야 한다(MUST).

deny 규칙을 지우는 것도 확대로 친다. 도커 엔지니어링 블로그는 "다음 gh 키트가 DELETE /repos/** 차단을 빼면 런타임이 업그레이드를 붙잡는다"는 예를 든다. 리뷰어가 diff를 놓쳐도 기계가 한 번 더 거른다.

실패 방식도 보수적이다. 모르는 필드가 있으면 무시하지 않고 오류를 내고, 필수 권한을 호스트가 줄 수 없으면 실행 자체를 거부한다. 덜 받은 채로든 더 받은 채로든, 선언과 다른 권한으로 에이전트가 시작되지 않게 하려는 것이다.

아직 실험 단계, CNCF 이관도 '약속' 단계

정식 표준으로 보기엔 이르다. 저장소 README에는 "This specification is experimental"이라고 명시돼 있고, 최종 버전 목표 시점은 2026년 4분기(커뮤니티 피드백 반영 후)다. 릴리스도 전부 프리릴리스다 — 10월 9일 현재 최신은 10월 2일 나온 v3.0.0-m.8이다. 저장소는 9월 16일 만들어졌다.

CNCF 쪽도 마찬가지다. 발표 글은 "스펙을 CNCF로 가져온다"고 했고, 10월 1일 도커의 행사 정리 글도 "CNCF로 가져가겠다는 약속(commitment)"이라고 표현한다. 저장소의 GOVERNANCE.md는 지금도 "Docker maintains this specification"으로 시작한다. CNCF 프로젝트로 정식 채택됐다는 발표는 아직 찾지 못했다.

규칙을 실제로 집행하는 런타임은 도커가 밝힌 첫 구현인 자사 도커 샌드박스(sbx)이고, 다른 회사의 런타임 구현 발표는 찾지 못했다. 스펙은 런타임 의존성을 숨기지 않는다. 규칙을 지키는 런타임이 없으면 어노테이션은 "그냥 이미지일 뿐 아무 강제력이 없다"고 적었다. 도커가 AWS, Datadog, JFrog, 팔로알토 네트웍스, Snyk 등과 키트를 만들었다고 밝혔지만, 그건 키트 제작 파트너이지 다른 회사가 런타임을 구현했다는 뜻은 아니다.

런타임 쪽 보안 이력도 같이 봐야 한다

키트가 아무리 정교해도 결국 런타임이 경계를 지켜야 의미가 있다. 도커 샌드박스에는 올해 공개된 취약점이 여럿 있다.

식별자내용조치
CVE-2026-77179 (Critical)macOS의 virtio-fs 호스트 서버가 심볼릭 링크를 따라가, 악성 게스트가 공유 작업 폴더를 벗어나 호스트 파일을 읽거나 고칠 수 있었음. macOS 0.28.0 이상 0.42.0 미만9월 7일 0.42.0에서 수정
CVE-2026-79994 (High)게스트→호스트 유닉스 소켓 중계의 경로 확인과 연결 사이 경쟁 상태로 작업 폴더 밖 소켓에 연결 가능. 0.37.0 이상 0.42.0 미만0.42.0에서 수정
CVE-2026-17106파일 복사 시 지정한 목적지를 벗어나는 결함. 도커 샌드박스의 sbx cp(밖으로 꺼낼 때)와 Docker Desktop의 docker container cp에 모두 해당sbx는 8월 6일 0.38.0, Docker Desktop은 8월 10일 4.86.0에서 수정
CVE-2026-12039 (Medium)HTTP/S 허용 목록이 DNS 조회에는 적용되지 않아 DNS로 데이터를 빼낼 수 있었음6월 17일 0.33.0에서 수정(6월 18일 GitHub 권고 공개)

0.42.0으로 못 올리는 경우 도커는 clone 모드를 쓰고 읽기·쓰기 호스트 마운트를 피하라고 안내한다. DNS 쪽은 그 뒤 9월 21일 0.45.0에서도 "허용하는 네트워크 규칙이 없으면 DNS 조회를 막는다"는 변경이 추가됐다. 10월 9일 현재 정식 최신판은 10월 5일 나온 0.47.0이다. 지금 sbx를 쓰고 있다면 버전부터 보는 게 순서다.

Claude Code·Codex 샌드박스 설정과 무엇이 다른가

비교 대상이 되는 기존 방식은 각 에이전트에 내장된 샌드박스다. 공식 문서 기준으로 정리하면 이렇다.

Claude Code 샌드박스Codex 샌드박스Docker 샌드박스 키트
경계운영체제 기능(macOS Seatbelt, Linux bubblewrap)으로 셸 명령만 감쌈운영체제 수준 샌드박스 + 승인 정책자체 커널의 마이크로VM
기본 네트워크샌드박스 자체가 기본 꺼짐. 켜면 로컬 프록시 경유, 허용 목록은 빈 상태에서 시작하고 목록 밖 도메인은 권한 모드에 따라 승인 요청·허용·거부기본 차단키트가 선언한 호스트만
설정 위치settings.json의 sandbox 항목~/.codex/config.toml이미지 매니페스트 어노테이션
범위 밖·예외내장 파일 도구, MCP 서버, 훅은 샌드박스 밖에서 실행Auto 프리셋에선 작업 폴더 밖 편집·네트워크가 필요한 명령에 승인 요청에이전트 프로세스 전체가 VM 안

핵심 차이는 "설정이 어디에 사느냐"다. Claude Code와 Codex의 설정은 그 도구, 그 사용자의 설정 파일에 묶여 있다. 키트는 권한 목록을 이미지와 함께 배포하고 버전을 매긴다. 대신 키트는 규칙을 지키는 런타임이 있어야만 효력이 있고, 지금은 그게 도커 샌드박스다. 둘은 대체 관계라기보다 층이 다르다 — 실제로 도커는 Claude Code와 Codex를 키트로 감싸 샌드박스 안에서 돌리는 예시를 저장소에 함께 올려 두었다.

우리 팀에 적용한다면

당장 표준으로 갈아타기보다는, 키트를 "에이전트 권한을 문서화하는 형식"으로 먼저 빌려 쓰는 쪽이 현실적이다. 실험 단계 스펙이라 필드가 바뀔 수 있다는 점은 README가 직접 경고한다. 다만 기능 버전이 @1, @2처럼 따로 올라가는 구조라 기존 키트는 계속 해석되도록 설계했다는 게 저장소 설명이다.

시도해 본다면 이런 순서가 무난하다.

  1. 지금 에이전트에 준 권한을 적어 본다. 열어 둔 도메인, 넣어 둔 토큰, 마운트한 폴더. 키트의 network-policy·credential·volume 칸에 맞춰 적으면 빠진 게 금방 보인다.
  2. 토큰은 환경변수로 직접 넣지 말고 proxyManaged 방식처럼 샌드박스 밖에서 주입하는 구조를 검토한다. 에이전트가 볼 수 없는 토큰이 가장 안전한 토큰이다.
  3. 허용 규칙만 쓰지 말고 deny로 위험한 동작을 따로 깎는다. gh 예시의 DELETE /repos/**처럼, 쓰기 권한이 필요해도 삭제는 막을 수 있다.
  4. 키트 파일을 저장소에 커밋해 권한 변경을 코드 리뷰에 올린다. 도구 버전업이 아니라 권한 변경이라는 점을 리뷰어가 알아볼 수 있게.
  5. sbx를 실제로 쓴다면 0.42.0 이상인지, 호스트 마운트를 읽기·쓰기로 열어 두지 않았는지부터 본다.

셸 명령 단위 통제가 필요하면 키트만으로는 안 된다. 그 부분은 여전히 Claude Code의 권한 규칙이나 Codex의 승인 정책이 맡는다. 키트가 겨냥하는 건 그보다 바깥, 에이전트가 손댈 수 있는 세계의 테두리다.

출처 docker.com/…/docker-sandbox-kit-spec-cncfdocker.com/…/docker-sandbox-kit-specgithub.com/…/sandbox-kit-specgithub.com/…/[email protected]docs.docker.comgithub.com/…/GHSA-4crj-qc6g-g3xjgithub.com/…/releasescode.claude.comlearn.chatgpt.comdocker.com/…/docker-cloud-sandboxes-wearedevelopers-recapgithub.com/…/GOVERNANCE.mdgithub.com/…/SPEC-v3.mdgithub.com/…/[email protected]github.com/…/[email protected]github.com/…/gh.yamlgithub.com/…/[email protected]hub.docker.comgithub.com/…/releases

내용이 틀렸나요? 정정 요청

관련 글