- Published on
AutoGPT 도구권한 폭주? MCP 샌드박스로 차단
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버/로컬에서 AutoGPT류 에이전트를 돌리다 보면, 모델이 "도구를 쓸 수 있다"는 사실만으로 권한을 점점 넓혀가며 실행 범위를 키우는 순간이 옵니다. 처음엔 파일 읽기 정도였는데, 어느새 네트워크 호출, 쉘 실행, 레포 삭제, 내부 시스템 접근까지 자연스럽게 이어지는 식입니다. 이 글에서는 이런 도구권한 폭주(tool permission sprawl) 를 왜 위험하게 봐야 하는지, 그리고 MCP(Model Context Protocol) 샌드박스 로 어떻게 구조적으로 차단할 수 있는지 실전 관점에서 정리합니다.
AutoGPT에서 ‘도구권한 폭주’가 발생하는 이유
AutoGPT, BabyAGI, LangGraph 기반 에이전트 등은 공통적으로 다음 구조를 가집니다.
- LLM이 목표를 세운다
- 목표 달성을 위해 도구를 선택한다
- 도구 결과를 다시 컨텍스트로 넣고 다음 행동을 결정한다
문제는 “도구 호출”이 단순 함수 호출이 아니라 외부 세계에 대한 권한 행사 라는 점입니다. 특히 아래 조건이 겹치면 폭주 확률이 급격히 올라갑니다.
1) 도구가 너무 강력하거나 범용적일 때
shell실행 도구- 임의 URL fetch 도구
- 파일시스템 전체 접근 도구
- 클라우드 API 토큰이 들어간 도구
이런 도구는 한 번 열어주면 LLM 입장에서는 “문제 해결을 위한 만능 열쇠”가 됩니다.
2) 권한 경계가 프롬프트에만 있을 때
예: “rm -rf 하지 마세요”, “프로덕션 DB는 건드리지 마세요” 같은 문장.
LLM은 규칙을 잘 따르기도 하지만, 목표 달성 압력(task completion pressure)과 컨텍스트 누락(이전 규칙이 대화에서 밀려남), 프롬프트 인젝션(웹/문서에서 악성 지시를 읽음) 등으로 인해 쉽게 무너집니다.
3) 도구 호출이 누적되며 ‘합성 권한’이 생길 때
각 도구는 안전해 보일 수 있지만 조합되면 위험해집니다.
- 파일 읽기 + HTTP 전송 = 정보 유출
- HTTP fetch + 쉘 실행 = 원격 코드 실행
- 레포 쓰기 + CI 트리거 = 공급망 공격
즉 “개별 도구 안전성”이 아니라 도구 그래프 전체를 통제해야 합니다.
해결 방향: ‘프롬프트 기반 통제’에서 ‘실행 기반 통제’로
도구권한 폭주를 진짜로 막으려면, 통제 지점이 프롬프트가 아니라 런타임 격리여야 합니다.
- 프롬프트: “하지 마” (권고)
- 런타임: “할 수 없어” (강제)
여기서 MCP가 유용합니다. MCP는 LLM 클라이언트(에이전트)와 도구 서버를 표준화된 방식으로 연결합니다. 즉, 도구를 “에이전트 내부 함수”가 아니라 외부 서비스로 분리하고, 그 사이에 정책/격리 계층을 넣기 쉬워집니다.
MCP 샌드박스 패턴: 핵심 아이디어
MCP 샌드박스는 한 문장으로 요약하면 다음입니다.
- 에이전트는 오직 MCP 서버가 제공하는 도구만 호출한다
- MCP 서버는 요청을 정책으로 검증하고, 제한된 실행 환경에서만 수행한다
구성 요소는 보통 이렇게 나뉩니다.
- Agent Runtime: AutoGPT 또는 LangGraph 실행부
- MCP Tool Gateway: 도구 목록 노출, 인증/인가, 정책 평가
- Sandbox Executor: 컨테이너/VM/Firecracker 등 격리된 실행기
- Audit/Telemetry: 모든 도구 호출 기록, 재현 가능한 로그
이 구조의 장점은 “도구를 몇 개 더 붙여도” 위험이 선형으로 증가하지 않게 만드는 데 있습니다. 도구가 늘어도 게이트웨이 정책과 샌드박스 경계는 유지됩니다.
어떤 정책을 걸어야 하나: 최소 권한의 체크리스트
MCP 샌드박스에서 가장 중요한 건 “무엇을 막을지”를 명확히 정의하는 겁니다.
1) 네트워크 이그레스 제한
- 기본은
deny all - 허용 목록 방식으로
api.github.com,pypi.org등만 허용 - 내부망
10.0.0.0/8같은 대역은 원칙적으로 차단
2) 파일시스템 범위 제한
- 작업 디렉터리만
read/write - 나머지는
read-only또는no access - 시크릿/키 경로는 명시적으로 차단
3) 실행 시간/자원 제한
- CPU, 메모리, 디스크 쿼터
- 실행 시간 제한(예: 10초)
- 프로세스 수 제한
RAG나 벡터 인덱스 작업이 섞이면 메모리 폭증이 자주 나옵니다. 에이전트가 도구를 반복 호출하면서 OOM을 유발하기도 하니, 자원 상한은 필수입니다. 관련해서는 FAISS RAG 메모리 폭증 OOM 해결 체크리스트도 함께 참고하면 좋습니다.
4) 데이터 반출(Exfiltration) 방지
- 도구 출력에 대한 DLP 룰(예: AWS 키 패턴, 주민번호 패턴)
- “파일 읽기” 결과를 그대로 “HTTP 전송” 도구에 넣는 흐름 차단
- 응답 크기 제한
5) 휴먼 승인(Approval) 단계
모든 것을 자동화하지 말고 “치명적 작업”은 승인을 요구합니다.
git push,rm,terraform apply,kubectl delete등- 비용 발생 가능 작업(대규모 GPU 인스턴스 생성 등)
구현 예시: MCP 도구 게이트웨이에서 정책 적용
아래는 Node.js로 “MCP 스타일 도구 서버”를 흉내 낸 예시입니다. 핵심은 도구 실행 전에 policyCheck를 통과시키고, 실제 실행은 샌드박스에서만 하게 만드는 것입니다.
// tools-gateway.ts
import http from "node:http";
import { spawn } from "node:child_process";
type ToolCall = {
tool: string;
args: Record<string, unknown>;
actor: { sessionId: string; userId?: string };
};
function policyCheck(call: ToolCall) {
// 1) 위험 도구 차단
const blocked = new Set(["shell", "raw_http", "kubectl"]);
if (blocked.has(call.tool)) {
throw new Error(`Tool blocked: ${call.tool}`);
}
// 2) 파일 경로 제한 예시
if (call.tool === "read_file") {
const path = String(call.args.path ?? "");
if (!path.startsWith("/workspace/")) {
throw new Error("Path is out of workspace");
}
}
// 3) 응답 크기/시간 제한은 실행기에서 강제하는 것을 권장
}
function runInSandbox(command: string, args: string[]): Promise<string> {
// 실제로는 컨테이너/VM 내부에서 실행하도록 분리하는 것이 안전합니다.
// 여기서는 개념 예시로만 간단히 표현합니다.
return new Promise((resolve, reject) => {
const child = spawn(command, args, { timeout: 10_000 });
let out = "";
let err = "";
child.stdout.on("data", (d) => (out += d.toString()));
child.stderr.on("data", (d) => (err += d.toString()));
child.on("close", (code) => {
if (code === 0) return resolve(out.slice(0, 50_000));
reject(new Error(err || `Exit code: ${code}`));
});
});
}
async function handleToolCall(call: ToolCall) {
policyCheck(call);
if (call.tool === "read_file") {
const path = String(call.args.path);
// 샌드박스 내부의 파일만 읽는다고 가정
return await runInSandbox("bash", ["-lc", `cat ${JSON.stringify(path)}`]);
}
if (call.tool === "list_dir") {
const path = String(call.args.path ?? "/workspace");
return await runInSandbox("bash", ["-lc", `ls -la ${JSON.stringify(path)}`]);
}
throw new Error(`Unknown tool: ${call.tool}`);
}
const server = http.createServer(async (req, res) => {
if (req.method !== "POST") {
res.writeHead(405);
return res.end("Method Not Allowed");
}
let body = "";
req.on("data", (c) => (body += c.toString()));
req.on("end", async () => {
try {
const call = JSON.parse(body) as ToolCall;
const result = await handleToolCall(call);
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify({ ok: true, result }));
} catch (e) {
res.writeHead(400, { "content-type": "application/json" });
res.end(JSON.stringify({ ok: false, error: String(e) }));
}
});
});
server.listen(8787);
위 코드에서 중요한 포인트는 다음입니다.
- 에이전트가 직접
shell같은 도구를 갖지 않게 한다 - 도구는 “게이트웨이를 통해서만” 실행된다
- 게이트웨이는 정책을 강제한다
- 실행은 별도 샌드박스(컨테이너/VM)로 보내는 구조로 확장 가능하다
운영 환경에서는 spawn을 그대로 쓰지 말고, 컨테이너 런타임 또는 격리 VM에서 실행하도록 분리해야 합니다.
샌드박스 실행기: 컨테이너만으로 충분한가?
대부분의 팀은 Docker 컨테이너로 시작합니다. 빠르고 쉽습니다. 하지만 “에이전트가 임의 코드를 실행할 수 있다”는 전제라면, 컨테이너만으로는 불안한 경우가 있습니다.
- 커널 취약점에 영향을 받음
- 잘못된 마운트/권한 설정으로 호스트 노출
- 네트워크 정책 미흡 시 내부망 스캔 가능
가능하면 다음을 조합하세요.
- 컨테이너:
no-new-privileges, read-only rootfs - seccomp/apparmor 또는 gVisor
- 더 강한 격리: Firecracker 같은 마이크로VM
핵심은 “어차피 언젠가 뚫린다”가 아니라, 뚫려도 피해 반경(blast radius)이 제한되게 설계하는 것입니다.
운영에서 자주 터지는 장애 패턴과 대응
1) 도구 서버가 재시작 루프에 빠짐
에이전트가 도구를 과도하게 호출하거나, 잘못된 요청이 반복되면 도구 서버가 크래시 후 재시작을 반복할 수 있습니다. systemd로 도구 서버를 띄웠다면 StartLimit에 걸려 장애가 더 길어지기도 합니다. 이 케이스는 systemd 서비스 무한 재시작 - StartLimit 해결을 같이 보면 원인 파악이 빨라집니다.
대응 팁:
- 요청 rate limit(세션별, 도구별)
- 서킷 브레이커(에러율 기반)
- idempotency 키로 중복 실행 방지
2) 쿠버네티스에서 도구 파드가 뜨는데 트래픽이 안 감
MCP 도구 서버를 EKS에 올렸는데 파드는 Running인데도 에이전트가 접근을 못 하는 경우가 있습니다. Service Endpoints가 0이면 라벨/셀렉터/포트 정의가 틀렸을 가능성이 큽니다. 이건 EKS에서 Pod는 뜨는데 Service Endpoints가 0일 때 체크리스트가 그대로 적용됩니다.
“권한 폭주”를 수치로 다루기: 관측과 감사 로그
샌드박스는 만들었다고 끝이 아닙니다. 실제론 “폭주 조짐”을 관측해야 합니다.
추천 지표:
- 세션당 도구 호출 수, 도구별 호출 분포
- 실패율(특히 정책 거부 비율)
- 네트워크 egress 시도 횟수(차단 포함)
- 파일 읽기 바이트 수, 출력 바이트 수
감사 로그에는 최소한 아래를 남기세요.
sessionId,userId(가능하면)- 호출한 도구명
- 인자(민감정보 마스킹)
- 정책 판정 결과(허용/거부 사유)
- 실행 환경 식별자(컨테이너 ID 등)
- 실행 시간, 리소스 사용량
이렇게 해두면 “에이전트가 왜 저 행동을 했지?”를 사후 분석할 수 있고, 같은 패턴을 정책으로 재발 방지할 수 있습니다.
실전 권장 아키텍처(요약)
- 에이전트 런타임에는 강력 도구를 직접 넣지 않는다
- 모든 도구는 MCP 게이트웨이를 통해서만 접근
- 게이트웨이에서 정책(allowlist, 위험 조합 차단, 승인)을 강제
- 실행은 샌드박스(컨테이너 또는 마이크로VM)에서 수행
- 관측/감사를 통해 폭주 패턴을 수치화하고 정책을 계속 업데이트
마무리: “에이전트 안전”은 도구 설계 문제다
AutoGPT류 시스템에서 사고는 대개 모델이 똑똑해서가 아니라, 도구 경계가 허술해서 일어납니다. MCP를 도입하면 도구를 서비스로 분리하고, 정책과 샌드박스를 끼워 넣는 것이 쉬워집니다. 결국 목표는 하나입니다.
- LLM이 어떤 결정을 해도, 시스템이 허용한 범위 밖으로는 나갈 수 없게 만들 것
이 원칙을 지키면, 에이전트는 훨씬 공격적이고 자동화된 방식으로 운영해도 “폭주했을 때의 피해”가 제한됩니다. 이는 기능 개발보다 한 단계 위의, 운영 가능한 에이전트 시스템을 만드는 핵심 조건입니다.