샌드박싱과 격리
왜 샌드박싱이 필요한가?
섹션 제목: “왜 샌드박싱이 필요한가?”에이전트가 코드를 실행하고 파일을 수정할 수 있다면, 하나의 잘못된 명령이 호스트 시스템 전체에 영향을 미칠 수 있습니다. 이를 막는 것이 샌드박싱(Sandboxing) — 에이전트의 실행 환경을 호스트와 격리하는 기술입니다.
샌드박싱에는 크게 두 가지 접근법이 있습니다.
| 방식 | 설명 | 강도 |
|---|---|---|
| 스키마 레벨 격리 | 위험한 도구를 에이전트 스키마에서 제거 | 중간 |
| 런타임 권한 검사 | 도구 호출 시 실시간으로 권한 확인 | 중간 |
| 컨테이너 격리 | 별도 OS 레벨 격리 환경에서 실행 | 강함 |
| 전용 샌드박스 서비스 | E2B 같은 관리형 격리 환경 사용 | 매우 강함 |
스키마 레벨 vs 런타임 검사
섹션 제목: “스키마 레벨 vs 런타임 검사”스키마 레벨 격리
섹션 제목: “스키마 레벨 격리”에이전트에게 제공하는 도구 목록 자체에서 위험 도구를 제거합니다. 모델이 정상적으로 선택할 도구의 범위를 줄입니다. 모델이 임의 이름을 출력하거나 다른 도구를 통해 같은 효과를 요청할 수 있으므로 디스패처가 허용 목록을 다시 검사합니다.
// 읽기 전용 에이전트를 위한 도구 스키마const readOnlyTools = [ { name: "read_file", description: "파일 내용 읽기" }, { name: "list_directory", description: "디렉터리 목록 조회" }, { name: "search_files", description: "파일 내용 검색" },];
// exec_shell, write_file, delete_file 은 스키마에 포함하지 않음const agent = new Agent({ tools: readOnlyTools });런타임 권한 검사
섹션 제목: “런타임 권한 검사”도구 스키마에는 포함되어 있지만, 실제 실행 시점에 권한을 확인하고 차단합니다.
def execute_tool(tool_name: str, params: dict, context: AgentContext) -> Any: # 런타임 권한 테이블 확인 if tool_name not in context.allowed_tools: raise PermissionError(f"{tool_name}은 이 컨텍스트에서 허용되지 않습니다")
# 경로 제한 검사 if tool_name == "write_file": path = params.get("path", "") if not is_within_allowed_paths(path, context.allowed_write_paths): raise PermissionError(f"허용되지 않은 경로: {path}")
return tools[tool_name](**params)파일 시스템 격리
섹션 제목: “파일 시스템 격리”허용 경로 화이트리스트
섹션 제목: “허용 경로 화이트리스트”from pathlib import Path
ALLOWED_READ_PATHS = [Path("/workspace/src"), Path("/workspace/tests")]ALLOWED_WRITE_PATHS = [Path("/workspace/src"), Path("/workspace/output")]
def validate_path(path: str, operation: str) -> Path: if operation not in {"read", "write"}: raise PermissionError("Unsupported operation") resolved = Path(path).expanduser().resolve() roots = ALLOWED_WRITE_PATHS if operation == "write" else ALLOWED_READ_PATHS if not any(resolved.is_relative_to(root.resolve(strict=True)) for root in roots): raise PermissionError(f"허용되지 않는 경로: {path}") return resolved문자열 startswith('/workspace/src')는 /workspace/src-other도 허용하므로 경로 경계를 검사해야 합니다. 실제 파일 연산에는 검사한 반환 경로를 사용합니다. 동시 변경 가능한 링크는 검사 후 바뀔 수 있으므로 격리된 마운트·OS 권한도 함께 적용합니다.
Chroot / 바인드 마운트
섹션 제목: “Chroot / 바인드 마운트”# Docker로 작업 디렉터리만 마운트docker run --rm \ -v /host/workspace:/workspace:rw \ -v /host/readonly-data:/data:ro \ --network none \ agent-image:latest셸 실행 격리
섹션 제목: “셸 실행 격리”셸 명령 실행은 에이전트 도구 중 가장 강력하고 위험합니다.
import subprocessimport shlex
ALLOWED_COMMANDS = {"python", "node", "npm", "pytest", "git"}SHELL_TIMEOUT = 30 # 초
def safe_exec(command: str) -> str: # 명령어 파싱 및 허용 목록 확인 parts = shlex.split(command) if not parts or parts[0] not in ALLOWED_COMMANDS: raise SecurityError(f"허용되지 않은 명령: {parts[0] if parts else 'empty'}")
result = subprocess.run( parts, capture_output=True, text=True, timeout=SHELL_TIMEOUT, cwd="/workspace", # 작업 디렉터리 제한 env={"PATH": "/usr/bin"}, # 환경 변수 제한 ) # 출력 크기 제한 (토큰 폭발 방지) return result.stdout[:10_000]허용 실행 파일에 python·node를 넣으면 해당 인터프리터가 임의 파일·네트워크 접근을 할 수 있습니다. shell=False와 실행 파일 목록은 셸 문자열 주입을 줄일 뿐, 코드의 효과를 제한하는 샌드박스가 아닙니다. 읽기·쓰기·네트워크 정책은 실행 환경에서 강제합니다.
네트워크 격리
섹션 제목: “네트워크 격리”| 격리 수준 | 구현 방법 | 허용 범위 |
|---|---|---|
| 완전 차단 | --network none (Docker) | 없음 |
| 내부망만 허용 | iptables / 방화벽 규칙 | 사내 API만 |
| 도메인 화이트리스트 | 프록시 서버 경유 | 허용된 외부 API만 |
| 전체 허용 | 제한 없음 | 모든 인터넷 (위험) |
Docker 기반 격리
섹션 제목: “Docker 기반 격리”FROM python:3.12-slim
# 최소 권한 사용자RUN useradd -m -u 1000 agentUSER agent
WORKDIR /workspace
# 필요한 패키지만 설치COPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txt
# 실행 시 네트워크 · 마운트 옵션은 docker run에서 제어CMD ["python", "agent_runner.py"]docker run --rm \ --user 1000:1000 \ --memory 512m \ --cpus 1 \ --network none \ --cap-drop ALL \ --security-opt no-new-privileges \ --pids-limit 128 \ --read-only \ --tmpfs /tmp:size=100m \ -v $(pwd)/workspace:/workspace:rw \ agent-sandbox:latest컨테이너는 커널을 공유하며 구성과 마운트에 따라 호스트에 영향을 줄 수 있습니다. 호스트 루트·Docker 소켓·운영 자격 증명을 마운트하지 않고, 불신 코드는 위협 모델에 맞는 별도 VM·관리형 격리 환경도 검토합니다.
E2B — 관리형 샌드박스 서비스
섹션 제목: “E2B — 관리형 샌드박스 서비스”E2B는 에이전트를 위한 관리형 클라우드 샌드박스입니다. 직접 Docker를 관리하지 않고 안전한 코드 실행 환경을 제공합니다.
import { Sandbox } from '@e2b/code-interpreter';
const sandbox = await Sandbox.create();
// 에이전트가 생성한 코드를 격리된 환경에서 실행const result = await sandbox.runCode(`import pandas as pddf = pd.read_csv('/data/input.csv')print(df.describe())`);
console.log(result.text);await sandbox.kill();샌드박싱은 스키마 레벨(도구 제거), 런타임 검사(실행 시 권한 확인), 컨테이너 격리(Docker), 관리형 서비스(E2B) 네 계층으로 구성됩니다. 파일 시스템 경로 제한, 셸 명령 화이트리스트, 네트워크 차단을 조합하여 에이전트가 의도치 않게 호스트 시스템에 영향을 미치는 것을 차단합니다.