ZeroClaw — Sandboxing 시스템
Layer 3 학습. 실제 소스 코드 기반. 파일:
src/security/traits.rs,detect.rs,firejail.rs,landlock.rs,docker.rs,bubblewrap.rs
핵심 개념
Sandboxing = shell 명령 실행 시 OS 레벨에서 프로세스를 격리하는 것.
SecurityPolicy가 "이 명령을 실행해도 되는가?"를 결정한다면, Sandbox는 "실행 허가된 명령이 실행될 때 피해 범위를 제한"한다.
두 레이어가 독립적으로 동작:
validate_command_execution() ← SecurityPolicy (허용 여부)
→ sandbox.wrap_command() ← Sandbox (격리된 환경에서 실행)
→ 실제 OS 실행
Sandbox trait
pub trait Sandbox: Send + Sync {
fn wrap_command(&self, cmd: &mut Command) -> std::io::Result<()>;
fn is_available(&self) -> bool;
fn name(&self) -> &str;
fn description(&self) -> &str;
}
wrap_command()가 핵심. Command를 받아서 in-place로 수정해 샌드박스 래퍼로 교체.
예: echo hello → firejail --private=home ... echo hello
백엔드 4종 비교
| 백엔드 | 방식 | 플랫폼 | 의존성 | 격리 강도 |
|--------|------|--------|--------|----------|
| Landlock | 커널 LSM (kernel 5.13+) | Linux only | 없음 (커널 기능) | 파일시스템 접근 제어 |
| Firejail | SUID 래퍼 | Linux only | firejail 바이너리 | 홈/dev/사운드/비디오 격리 |
| Bubblewrap | user namespace | Linux/macOS | bwrap 바이너리 | 네임스페이스 완전 격리 |
| Docker | 컨테이너 | 크로스 플랫폼 | Docker 데몬 | 메모리/CPU/네트워크 제한 |
| Noop | 없음 (폴백) | 모든 플랫폼 | 없음 | 없음 |
자동 감지 우선순위 (Auto 모드)
Linux:
1. Landlock (feature = "sandbox-landlock", kernel 5.13+)
2. Firejail (`firejail --version` 성공 여부)
macOS:
1. Bubblewrap (feature = "sandbox-bubblewrap")
공통:
3. Docker (`docker --version` 성공 여부)
4. Noop (폴백)
// detect.rs
fn detect_best_sandbox() -> Arc<dyn Sandbox> {
// Linux: Landlock 시도
if let Ok(sandbox) = LandlockSandbox::probe() { return Arc::new(sandbox); }
// Linux: Firejail 시도
if let Ok(sandbox) = FirejailSandbox::probe() { return Arc::new(sandbox); }
// macOS: Bubblewrap 시도
if let Ok(sandbox) = BubblewrapSandbox::probe() { return Arc::new(sandbox); }
// 공통: Docker 시도
if let Ok(sandbox) = DockerSandbox::probe() { return Arc::new(sandbox); }
// 폴백
Arc::new(NoopSandbox)
}
각 백엔드 상세
Landlock (Linux 커널 LSM)
// wrap_command()가 실제로 하는 일:
// child process를 감싸는 게 아니라
// "현재 프로세스"에 Landlock 규칙셋을 적용
// → child는 부모의 제한을 상속
self.apply_restrictions() // 파일 접근 규칙 적용
허용되는 경로:
workspace_dir→ 읽기/쓰기/디렉토리 나열/tmp→ 읽기/쓰기/usr,/bin→ 읽기 전용 (명령 실행용)
특징: 의존성 없음. 순수 커널 기능. landlock Rust 크레이트로 구현.
Firejail (Linux user-space)
firejail --private=home # 새 임시 홈 디렉토리
--private-dev # 최소 /dev (실제 장치 차단)
--nosound # 오디오 장치 차단
--no3d # 3D 가속 차단
--novideo # 비디오 장치 차단
--nowheel # 입력 장치 차단
--notv # TV 장치 차단
--noprofile # 프로파일 로딩 스킵
--quiet # 경고 억제
<original command>
apt install firejail로 설치. SUID 바이너리.
Bubblewrap (user namespace)
bwrap --ro-bind /usr /usr # /usr 읽기 전용 마운트
--dev /dev # 가상 /dev
--proc /proc # 가상 /proc
--bind /tmp /tmp # /tmp 바인드
--unshare-all # 모든 네임스페이스 분리 (네트워크 포함)
--die-with-parent # 부모 종료 시 자동 종료
<original command>
--unshare-all이 핵심 — 네트워크 네임스페이스도 분리돼서 네트워크 완전 차단.
--die-with-parent — 부모 프로세스 종료 시 자식도 같이 종료 (고아 프로세스 방지).
Docker
docker run --rm # 종료 후 컨테이너 자동 삭제
--memory 512m # 메모리 512MB 제한
--cpus 1.0 # CPU 1코어 제한
--network none # 네트워크 완전 차단
alpine:latest # 기본 이미지
<original command>
크로스 플랫폼이지만 Docker 데몬이 필요해서 무거움. 컨테이너 이미지 커스터마이즈 가능.
config.toml 설정
[security.sandbox]
enabled = true # true / false / null(auto)
backend = "auto" # auto / landlock / firejail / bubblewrap / docker / none
# firejail 추가 인수 (선택)
firejail_args = ["--net=none", "--caps.drop=all"]
백엔드 명시 + 설치 안 됨 → 경고 로그 + Noop 폴백:
WARN: Firejail requested but not available, falling back to application-layer
Sandbox가 "없을 때" (Noop)
pub struct NoopSandbox;
impl Sandbox for NoopSandbox {
fn wrap_command(&self, _cmd: &mut Command) -> std::io::Result<()> {
Ok(()) // 아무것도 하지 않음
}
fn is_available(&self) -> bool { true }
fn name(&self) -> &str { "none" }
}
Noop이어도 SecurityPolicy의 허용/차단 로직(allowlist, risk gate, path check)은 여전히 동작. Sandbox는 허용된 명령의 실행 환경을 격리할 뿐, 허용 여부 자체는 SecurityPolicy가 담당.
격리 범위 비교
| 보호 대상 | Landlock | Firejail | Bubblewrap | Docker | |----------|----------|----------|------------|--------| | 파일시스템 | ✅ (규칙 기반) | ✅ (홈 격리) | ✅ (ro-bind) | ✅ (컨테이너) | | 네트워크 | ❌ | ❌ | ✅ (--unshare-all) | ✅ (--network none) | | 프로세스 네임스페이스 | ❌ | 부분 | ✅ | ✅ | | 메모리/CPU 제한 | ❌ | ❌ | ❌ | ✅ | | 루트 권한 필요 | ❌ | SUID | ❌ | 데몬 권한 |
내 환경 (로컬 Linux)에서 권장 설정
[security.sandbox]
enabled = true
backend = "landlock" # 의존성 없음, 커널 5.13+
kernel 버전 확인: uname -r
5.13 미만이면 firejail 또는 none.
Landlock이 자동 감지되면 로그에:
INFO zeroclaw: Landlock sandbox enabled (Linux kernel 5.13+)
설계 원칙
Defense in depth (심층 방어):
Layer 1: allowed_commands allowlist → 허용된 명령만
Layer 2: command_risk_level + approval gate → 위험 명령 차단/승인
Layer 3: forbidden_path_argument() → 위험 경로 인수 차단
Layer 4: Sandbox.wrap_command() → OS 레벨 격리
Layer 5: is_resolved_path_allowed() → 심볼릭 링크 탈출 방지
각 레이어가 독립적으로 작동. 하나가 뚫려도 다음 레이어가 방어.
관련
- security-policy — SecurityPolicy, allowed_commands, 경로 검증
- tool-system — all_tools_with_runtime()에서 sandbox 전달
- config-schema — [security.sandbox] 설정 섹션
- overview — ZeroClaw 학습 지도