'샌드박스에서 돌려'는 하나의 지시처럼 들립니다. 실제로 사람들이 이 말로 뜻하는 흔한 경계는 네 가지이고, 막아 주는 것은 전혀 다릅니다. 잘못 고르면 무방비 상태로 남거나, 매번 실행이 필요 이상으로 느리고 비싸집니다.
이 비교는 코드가 정말로 신뢰할 수 없는 것이라고 가정합니다. 언어 모델이 작성했거나, 사용자가 제출했거나, 모르는 사람의 pull request로 도착한 코드입니다. 직접 작성한 코드는 필요한 것이 다릅니다.
무엇이 잘못될 수 있나
경계를 비교하기 전에, 그것이 무엇을 위한 것인지 짚어 두면 도움이 됩니다. 신뢰할 수 없는 코드는 다음과 같은 일을 할 수 있습니다.
- 읽어서는 안 되는 것을 읽기: 환경 변수, SSH 키, 홈 디렉터리의 파일, 클라우드 메타데이터 엔드포인트.
- 자원 고갈: 무한 루프, 머신이 멈출 때까지 fork, 디스크 채우기.
- 네트워크 악용: 다른 호스트 스캔, 스팸 발송, 암호화폐 채굴, 내부 서비스 호출.
- 눌러앉기: 작업보다 오래 살아남는 cron 작업이나 서비스 설치.
- 탈출: 아래 계층의 버그를 이용해 호스트 자체에서 코드 실행.
아래 각 수준은 이 중 일부를 처리하고 나머지는 처리하지 못합니다.
수준 1: 프로세스
가장 단순한 샌드박스는 자식 프로세스입니다. 타임아웃을 건 subprocess.run, 경우에 따라 별도 사용자로, 꼼꼼하다면 자원 제한과 seccomp 필터까지.
막는 것: 폭주하는 실행 시간(타임아웃), 일부 자원 고갈(제한), 일부 위험한 시스템 콜(seccomp). 못 막는 것: 그 사용자가 읽을 수 있는 모든 것의 읽기, 네트워크 접근, 그 사용자가 쓸 수 있는 곳을 통한 눌러앉기, 그리고 모든 커널 버그. 전형적인 실패: 읽을 수 있다는 걸 잊고 있던 파일을 코드가 읽거나, '제한된' 언어 인터프리터에 빠져나갈 구멍이 있었던 것으로 드러납니다. 언어 내부 샌드박스가 버티지 못한 사례는 역사에 수두룩합니다.
시작 시간은 사실상 0입니다. 직접 작성한 코드라면 괜찮지만, 남이 작성한 코드에 대해서는 경계가 되지 못합니다.
수준 2: 컨테이너
namespaces는 코드에 파일, 프로세스, 네트워크에 대한 자기만의 시야를 주고, cgroups는 CPU와 메모리를 제한하며, 제거된 capabilities와 seccomp는 커널에 요청할 수 있는 범위를 좁힙니다.
막는 것: 호스트 파일 읽기(마운트하지 않은 한), 다른 프로세스 보기, 무제한 자원 사용, 그리고 컨테이너를 쓰고 버린다면 눌러앉기 문제의 상당 부분. 못 막는 것: 공유된 호스트 커널의 버그. 머신의 모든 컨테이너가 같은 커널과 대화하므로, 닿을 수 있는 커널 취약점은 출구가 됩니다. 전형적인 실패: 기묘한 익스플로잇이 아니라 설정입니다. 특권 컨테이너, 안에 마운트된 컨테이너 런타임 소켓, '디버깅할 때만' 켠 호스트 네트워킹. 하나하나가 조용히 경계를 없앱니다.
시작 시간은 밀리초입니다. 시스템 콜이 호스트에 닿기 전에 가로채는 사용자 공간 커널 등, 속도와 호환성을 일부 희생한 강화 버전도 있습니다. 신뢰하는 사람이 작성한 코드라면, 잘 설정된 컨테이너가 합리적인 기본값입니다.
수준 3: microVM
microVM은 완전한 VM처럼 하이퍼바이저 아래에서 자체 커널을 실행하지만, 에뮬레이션하는 하드웨어를 거의 전부 없앴습니다. Firecracker가 가장 잘 알려진 예입니다.
막는 것: 컨테이너가 막는 모든 것에 더해, 공유 커널 문제까지. 호스트에 닿으려면 코드가 하이퍼바이저나 몇 안 되는 가상 디바이스 중 하나를 뚫어야 하는데, 이는 커널의 시스템 콜 인터페이스보다 훨씬 작은 표면입니다. 못 막는 것: 여러분이 건넨 것의 악용. 열린 아웃바운드는 여전히 악용될 수 있고, 넣어 준 비밀은 여전히 유출될 수 있습니다. 전형적인 실패: 격리가 아니라 정책입니다. 누군가 '테스트를 통과시키려고' 운영 환경 토큰을 넘기거나, 아웃바운드가 필요 없는 워크로드에서 그것을 무제한으로 열어 둡니다.
시작 시간은 1초 미만에서 약 1초이며, 오버헤드는 몇 메가바이트입니다. 작업마다 하나씩 만들고 끝나면 삭제할 수 있을 만큼 빠르고, 이것이 눌러앉기 문제도 해결합니다. 돌아올 곳이 아무것도 남지 않기 때문입니다.
수준 4: 완전한 가상 머신
일반 VM이나 VPS는 microVM과 같은 종류의 경계, 즉 하드웨어 가상화와 하이퍼바이저를 가집니다. 차이는 그 주변의 모든 것입니다. 완전한 디바이스 모델, OS 전체, 원하면 공인 IP, 그리고 일이나 월 단위로 재는 수명 주기.
막는 것: microVM과 같은 종류의 문제. 다만 에뮬레이션하는 하드웨어가 많아 공격 표면이 다소 큽니다. 못 막는 것: 자기 네트워크 신원의 악용. 공인 IP를 가진 VM에서 악성 코드를 돌리면 그 VM 자신의 IP가 블록리스트에 오르며, 그 IP로 메일까지 보내고 있다면 문제가 됩니다. (이미 그렇게 되었다면 IP가 블록리스트에 올랐을 때 해야 할 일을 참고하세요.) 전형적인 실패: 너무 오래 삽니다. 한 번의 실험을 위해 만든 VM이 자격 증명과 상태를 쌓아 가며, 지키려던 바로 그 신뢰받는 머신으로 서서히 변해 갑니다.
시작 시간은 수십 초에서 몇 분이고, 과금은 보통 월 단위입니다. 인바운드 포트가 필요한 장기 실행 서비스에는 맞는 형태이고, 20초짜리 코드 실행에는 맞지 않는 형태입니다.
비교
| 프로세스 | 컨테이너 | microVM | 완전한 VM | |
|---|---|---|---|---|
| 자체 커널 | 없음 | 없음 | 있음 | 있음 |
| 주요 경계 | 사용자 권한 | namespaces + 공유 커널 | 하이퍼바이저, 아주 작은 디바이스 모델 | 하이퍼바이저, 완전한 디바이스 모델 |
| 시작 시간 | 약 0 | ms | 약 0.1–1초 | 10초–몇 분 |
| 오버헤드 | 없음 | 작음 | 몇 MiB | 수십–수백 MiB |
| 작업마다 버리기 | 쉬움 | 쉬움 | 쉬움 | 느리고 비쌈 |
| 인바운드 포트 / 공인 IP | 호스트의 것 | 호스트의 것 | 보통 없음 | 있음 |
선택하기
- 본인이나 팀이 작성한 코드: 프로세스나 컨테이너면 충분합니다.
- 자기 브랜치의 CI: 자기 runner 위의 컨테이너.
- 에이전트가 생성한 코드, 사용자 제출물, 외부 pull request: 작업마다 microVM 하나, 끝나면 삭제.
- 연결을 받아야 하거나 몇 주 동안 실행되는 신뢰할 수 없는 서비스: 자체 IP를 가진 별도의 VM. 자격 증명을 가진 것이나 메일을 보내는 것과는 떨어뜨려 두세요.
무엇을 고르든 두 가지 규칙이 추가로 적용됩니다. 코드 작성자에게 건네지 않을 것은 안에 넣지 말 것, 그리고 네트워크가 무엇을 할 수 있는지 의도적으로 결정할 것.
우리 제품의 위치
수준 3을 위해 우리는 코드 샌드박스를 운영합니다. 약 1초 만에 시작되는 Firecracker microVM으로, 아웃바운드 인터넷이 되고 인바운드 포트는 없으며 초 단위로 과금됩니다. 위의 세 번째 항목을 정확히 겨냥한 것이며, 에이전트가 이를 가지고 할 수 있는 일을 제한하는 방법은 MCP 가드레일 페이지에서 다룹니다. 수준 4에는 AI agent 요금제 같은 VPS가 머신 한 대를 월 단위로 통째로 제공합니다. AI 에이전트용 샌드박스의 예제는 microVM 패턴을 처음부터 끝까지 보여 주고, AI 에이전트가 인프라에 필요로 하는 것은 격리를 여러 요구 사항 중 하나로 살펴봅니다.
댓글
아직 댓글이 없습니다. 첫 번째가 되세요.