콘텐츠로 이동

샌드박싱과 격리

에이전트가 코드를 실행하고 파일을 수정할 수 있다면, 하나의 잘못된 명령이 호스트 시스템 전체에 영향을 미칠 수 있습니다. 이를 막는 것이 샌드박싱(Sandboxing) — 에이전트의 실행 환경을 호스트와 격리하는 기술입니다.

샌드박싱에는 크게 두 가지 접근법이 있습니다.

| 방식 | 설명 | 강도 | |---|---|---| | 스키마 레벨 격리 | 위험한 도구를 에이전트 스키마에서 제거 | 중간 | | 런타임 권한 검사 | 도구 호출 시 실시간으로 권한 확인 | 중간 | | 컨테이너 격리 | 별도 OS 레벨 격리 환경에서 실행 | 강함 | | 전용 샌드박스 서비스 | E2B 같은 관리형 격리 환경 사용 | 매우 강함 |

에이전트에게 제공하는 도구 목록 자체에서 위험 도구를 제거합니다. 에이전트는 해당 도구의 존재를 알지 못하므로 호출 시도 자체가 불가능합니다.

// 읽기 전용 에이전트를 위한 도구 스키마
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)
ALLOWED_READ_PATHS = ["/workspace/src", "/workspace/tests"]
ALLOWED_WRITE_PATHS = ["/workspace/src", "/workspace/output"]
FORBIDDEN_PATHS = ["/etc", "/sys", "/proc", "/home", "~/.ssh"]
def validate_path(path: str, operation: str) -> None:
resolved = os.path.realpath(path) # symlink 해결
# 금지 경로 확인
for forbidden in FORBIDDEN_PATHS:
if resolved.startswith(forbidden):
raise SecurityError(f"금지된 경로: {path}")
# 쓰기 작업 화이트리스트 확인
if operation == "write":
allowed = any(resolved.startswith(p) for p in ALLOWED_WRITE_PATHS)
if not allowed:
raise SecurityError(f"쓰기 불가 경로: {path}")
Terminal window
# Docker로 작업 디렉터리만 마운트
docker run --rm \
-v /host/workspace:/workspace:rw \
-v /host/readonly-data:/data:ro \
--network none \
agent-image:latest

셸 명령 실행은 에이전트 도구 중 가장 강력하고 위험합니다.

import subprocess
import 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]

| 격리 수준 | 구현 방법 | 허용 범위 | |---|---|---| | 완전 차단 | --network none (Docker) | 없음 | | 내부망만 허용 | iptables / 방화벽 규칙 | 사내 API만 | | 도메인 화이트리스트 | 프록시 서버 경유 | 허용된 외부 API만 | | 전체 허용 | 제한 없음 | 모든 인터넷 (위험) |

FROM python:3.12-slim
# 최소 권한 사용자
RUN useradd -m -u 1000 agent
USER agent
WORKDIR /workspace
# 필요한 패키지만 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 실행 시 네트워크 · 마운트 옵션은 docker run에서 제어
CMD ["python", "agent_runner.py"]
Terminal window
docker run --rm \
--user 1000:1000 \
--memory 512m \
--cpus 1 \
--network none \
--read-only \
--tmpfs /tmp:size=100m \
-v $(pwd)/workspace:/workspace:rw \
agent-sandbox:latest

E2B는 에이전트를 위한 관리형 클라우드 샌드박스입니다. 직접 Docker를 관리하지 않고 안전한 코드 실행 환경을 제공합니다.

import { Sandbox } from '@e2b/code-interpreter';
const sandbox = await Sandbox.create();
// 에이전트가 생성한 코드를 격리된 환경에서 실행
const result = await sandbox.runCode(`
import pandas as pd
df = pd.read_csv('/data/input.csv')
print(df.describe())
`);
console.log(result.text);
await sandbox.kill();

샌드박싱은 스키마 레벨(도구 제거), 런타임 검사(실행 시 권한 확인), 컨테이너 격리(Docker), 관리형 서비스(E2B) 네 계층으로 구성됩니다. 파일 시스템 경로 제한, 셸 명령 화이트리스트, 네트워크 차단을 조합하여 에이전트가 의도치 않게 호스트 시스템에 영향을 미치는 것을 차단합니다.