Linux에서 AI 에이전트 샌드박싱
Source: Hacker News
Source: …
Sandboxing
표준적인 해결책은 에이전트를 샌드박스하는 것입니다 – 원격 머신(exe.dev, sprites.dev, daytona.io)에서든, 로컬에서 Docker나 다른 가상화 메커니즘을 사용하든 말이죠.
Linux에서 가벼운 대안으로는 bubblewrap이 있습니다. 이는 cgroups와 사용자 네임스페이스와 같은 Linux 커널 기능을 이용해 프로세스를 제한(감금)합니다.
결과적으로 bubblewrap은 AI 에이전트를 가볍게 샌드박스하는 데 좋은 솔루션입니다. 제가 이런 솔루션에서 필요로 하는 점은 다음과 같습니다:
- 일반적인 Linux 개발 머신 설정을 그대로 모방하고 싶습니다(여러 개발 환경을 관리하고 싶지 않음).
- 현재 프로젝트에 필요한 정보 외에는 최소/전혀 접근하지 않음.
- 현재 프로젝트에만 쓰기 권한을 부여.
- 프로젝트의 파일/폴더에 직접 접근해 IDE에서 쉽게 파일을 확인·수정하거나 직접 코드를 실행할 수 있음.
- 네트워크 접근 – AI 제공자에 연결하고 인터넷을 검색할 수 있어야 하며, 제가 연결할 수 있는 서버를 시작할 수 있어야 함.
Bubblewrap과 Docker는 강화된 보안 격리 메커니즘은 아니지만, 저는 괜찮습니다. 다음과 같은 위험에 대해서는 크게 우려하지 않습니다:
- 제로데이 Linux 커널 버그를 통한 탈출.
- 은밀한 사이드채널 통신.
- 현재 프로젝트의 데이터(프로젝트 전용 접근 키 포함) 유출.
- 코드베이스 손상(코드는
git으로 관리되고 GitHub 등에서 백업됨).
대안으로는 프로젝트별 API 키를 만들어 손상 가능성을 제한하는 방법이 있습니다. 이렇게 하면 키가 유출되더라도 피해 영역을 최소화할 수 있습니다.
Source:
실제 사용 예
내 bubblewrap 샌드박스 스크립트는 다음과 같습니다:
#!/usr/bin/bash
exec 3<$HOME/.claude.json
exec /usr/bin/bwrap \
--tmpfs /tmp \
--dev /dev \
--proc /proc \
--hostname bubblewrap --unshare-uts \
--ro-bind /bin /bin \
--ro-bind /lib /lib \
--ro-bind /lib32 /lib32 \
--ro-bind /lib64 /lib64 \
--ro-bind /usr/bin /usr/bin \
--ro-bind /usr/lib /usr/lib \
--ro-bind /usr/local/bin /usr/local/bin \
--ro-bind /usr/local/lib /usr/local/lib \
--ro-bind /opt/node/node-v22.11.0-linux-x64/ /opt/node/node-v22.11.0-linux-x64/ \
--ro-bind /etc/alternatives /etc/alternatives \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--ro-bind /etc/profile.d /etc/profile.d \
--ro-bind /etc/bash_completion.d /etc/bash_completion.d \
--ro-bind /etc/ssl/certs /etc/ssl/certs \
--ro-bind /etc/ld.so.cache /etc/ld.so.cache \
--ro-bind /etc/ld.so.conf /etc/ld.so.conf \
--ro-bind /etc/ld.so.conf.d /etc/ld.so.conf.d \
--ro-bind /etc/localtime /etc/localtime \
--ro-bind /usr/share/terminfo /usr/share/terminfo \
--ro-bind /usr/share/ca-certificates /usr/share/ca-certificates \
--ro-bind /etc/nsswitch.conf /etc/nsswitch.conf \
--ro-bind /etc/hosts /etc/hosts \
--ro-bind /etc/ssl/openssl.cnf /etc/ssl/openssl.cnf \
--ro-bind /usr/share/zoneinfo /usr/share/zoneinfo \
--ro-bind $HOME/.bashrc $HOME/.bashrc \
--ro-bind $HOME/.profile $HOME/.profile \
--ro-bind $HOME/.gitconfig $HOME/.gitconfig \
--ro-bind $HOME/.local $HOME/.local \
--bind $HOME/.claude $HOME/.claude \
--bind $HOME/.cache $HOME/.cache \
--file 3 $HOME/.claude.json \
--bind "$PWD" "$PWD" \
claude --dangerously-skip-permissions "$@"
이 스크립트가 다소 특이하게 보인다면, 그 이유는 바로 그렇기 때문입니다. 일반적인 규칙을 적용하기보다는 bwrap을 직접 실험하면서 내 시스템에 필요한 최소 설정을 찾아냈습니다.
몇 가지 흥미로운 세부 사항
/tmp,/proc,/dev는bwrap이 자동으로 처리합니다.- 파일과 디렉터리를 로컬 머신과 동일한 경로에 바인드‑마운트(즉, 노출)하므로 파일 위치나 프로젝트 경로에 차이가 없습니다.
- 전체
/etc를 노출하지 않고, 최소한의 항목만 제공합니다. $HOME/.claude.json의 내용은 샌드박스 안으로 주입되므로, 여기에서의 변경은 실제 파일에 영향을 주지 않습니다.$HOME/.claude/디렉터리는 읽기‑쓰기 방식으로 매핑되어 Claude가 세션 데이터를 저장할 수 있습니다./opt/node/node-v22.11.0-linux-x64/는 내가 직접 설치한 Node.js 위치입니다.- 호스트명을 바꾸면 호스트와 샌드박스를 쉽게 구분할 수 있습니다.
필요에 따라 스크립트를 조정할 예정이지만, 현재로서는 나에게 충분히 견고한 시작점이 됩니다.
사용자 정의 방법
다른 AI 에이전트나 시스템에 맞게 적용하고 싶다면, 스크립트를 bash를 실행하도록 수정한 뒤 에이전트를 수동으로 실행하고, 어떤 부분이 깨지는지 관찰한 뒤 그에 맞게 조정하는 것을 권장합니다.
이를 위해 유용한 명령은 strace이며, 파일 접근 시스템 호출을 추적하여 필요한 것이 무엇인지 확인할 수 있습니다:
strace -e trace=open,openat,stat,statx,access -o /tmp/strace.log codex
로그를 확인하여 필요한 파일을 찾아내고, 적절히 바인드 마운트하세요.