- Published on
AutoGPT에 MCP 서버 붙여 로컬툴 안전 실행하기
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
에이전트 기반 자동화(예: AutoGPT)는 “생각하고 실행한다”는 장점 때문에, 로컬 머신에서 곧바로 셸 명령을 때리거나 파일을 읽고 쓰는 순간부터 보안 사고 가능성이 급격히 커집니다. 특히 프롬프트 인젝션으로 의도치 않은 명령이 실행되거나, 토큰/SSH 키 같은 민감정보가 로그로 유출되는 케이스가 흔합니다.
이 글에서는 MCP(Model Context Protocol) 서버를 중간 게이트웨이로 두어, AutoGPT가 로컬 툴을 직접 실행하지 못하게 만들고, 허용된 기능만 정책적으로 노출하는 방식으로 “로컬툴 안전실행”을 구현하는 방법을 다룹니다. 목표는 다음 3가지입니다.
- 에이전트가 실행할 수 있는 툴을 명시적으로 제한(allowlist)
- 파일/네트워크/프로세스 권한을 샌드박스/격리
- 모든 툴 호출을 감사 로그로 남기고 재현 가능하게 운영
왜 AutoGPT에 MCP가 필요한가
AutoGPT류 에이전트는 보통 아래 중 하나로 로컬 실행을 합니다.
shell또는subprocess기반 커맨드 실행- 로컬 파일 시스템 직접 접근
- 브라우저 자동화/HTTP 호출
문제는 “에이전트가 똑똑해질수록” 실행 범위도 넓어지고, 그만큼 사고 표면이 커진다는 점입니다.
대표적인 위험 시나리오
- 프롬프트 인젝션으로 인한 임의 명령 실행
- 예: 웹페이지/README에 숨겨진 지시문을 따라
rm -rf계열 실행
- 예: 웹페이지/README에 숨겨진 지시문을 따라
- 민감정보 유출
- 예: 홈 디렉터리의
.env, SSH 키, 브라우저 쿠키를 읽어 외부로 전송
- 예: 홈 디렉터리의
- 권한 상승/지속성(Persistence)
- 예: 크론 등록, 로그인 스크립트 변조, 쉘 프로파일 수정
MCP를 붙이면, 에이전트는 “툴을 직접 실행”하는 대신 “MCP 서버가 제공하는 도구 목록”만 호출할 수 있습니다. 즉, 실행 지점을 한 군데로 모아 정책을 강제할 수 있습니다.
MCP 아키텍처: 에이전트와 로컬 실행 사이에 방화벽 세우기
구성은 단순합니다.
- AutoGPT(클라이언트): MCP를 통해
tools호출 - MCP 서버(게이트웨이): 허용된 도구만 노출, 파라미터 검증, 로깅
- 로컬 툴 실행기(샌드박스): 실제 명령/스크립트 실행, 권한 최소화
핵심은 MCP 서버가 “도구 카탈로그”와 “실행 정책”을 소유하도록 만드는 것입니다.
설계 원칙 6가지(안전실행 체크리스트)
1) Allowlist 기반 툴 노출
- “무엇이든 실행하는 shell”을 제공하지 않습니다.
- 반드시
git_status,run_tests,read_project_file같은 업무 단위 도구로 쪼갭니다.
2) 파라미터 스키마 검증
- 도구 입력은 JSON 스키마로 강제합니다.
- 경로는 워크스페이스 루트 하위만 허용하고,
..를 차단합니다.
3) 작업 디렉터리와 파일 접근 범위 제한
- 워크스페이스 경로를 예:
WORKSPACE=/repo로 고정 - 홈 디렉터리,
/etc, 키체인, 클라우드 크레덴셜 경로는 접근 불가
4) 네트워크 egress 제한
- 기본은 차단, 필요할 때만 도메인 allowlist
- 테스트/빌드 같은 로컬 작업은 네트워크 없이도 돌아가게 설계
5) 실행 시간/리소스 제한
- 타임아웃, CPU/메모리 제한
- 장기 실행은 잡 큐로 분리하고 상태 조회 API 제공
6) 감사 로그와 재현성
- 어떤 도구가 어떤 입력으로 호출됐는지 구조화 로그(JSON)
- 결과(표준출력/표준에러/exit code)와 실행 환경(커밋 해시 등) 기록
운영 관점에서 이런 “실행 경계”를 두면, 나중에 성능/장애 분석도 쉬워집니다. 예를 들어 런타임 블로킹 문제는 비슷한 패턴으로 재현되는데, Rust 기반 실행기를 쓴다면 이벤트 루프에서 blocking 호출로 패닉이 나는 이슈도 자주 겪습니다. 관련해서는 Rust Tokio runtime panic - blocking 호출 해결법 같은 글의 접근(격리/스레드풀 분리)이 도움이 됩니다.
구현 예시: Node.js MCP 서버로 “안전한 로컬 툴” 만들기
아래 예시는 MCP 서버가 두 개의 도구만 노출한다고 가정합니다.
read_file: 워크스페이스 내부 파일만 읽기run_cmd: 특정 커맨드만 실행(예:git status,npm test)
중요: 본문에서 부등호 문자가 그대로 나오면 MDX에서 JSX로 오인될 수 있으므로, 코드 블록/인라인 코드로만 표기합니다.
1) 환경 변수와 워크스페이스 고정
export WORKSPACE=/repo
export MCP_LOG_DIR=/repo/.mcp-logs
2) MCP 서버 코드(개념 예시)
// mcp-server.js
import fs from "node:fs/promises";
import path from "node:path";
import { spawn } from "node:child_process";
const WORKSPACE = process.env.WORKSPACE;
function assertWorkspacePath(p) {
const resolved = path.resolve(WORKSPACE, p);
if (!resolved.startsWith(path.resolve(WORKSPACE) + path.sep)) {
throw new Error("path_outside_workspace");
}
return resolved;
}
const ALLOWED_COMMANDS = new Map([
["git_status", { cmd: "git", args: ["status", "--porcelain"] }],
["npm_test", { cmd: "npm", args: ["test"] }]
]);
async function readFileTool({ relative_path, max_bytes = 200_000 }) {
const full = assertWorkspacePath(relative_path);
const buf = await fs.readFile(full);
if (buf.byteLength > max_bytes) throw new Error("file_too_large");
return { content: buf.toString("utf-8") };
}
async function runCmdTool({ name, timeout_ms = 60_000 }) {
const spec = ALLOWED_COMMANDS.get(name);
if (!spec) throw new Error("command_not_allowed");
return await new Promise((resolve, reject) => {
const child = spawn(spec.cmd, spec.args, {
cwd: WORKSPACE,
env: {
// 최소 환경만 전달
PATH: process.env.PATH
},
stdio: ["ignore", "pipe", "pipe"]
});
let out = "";
let err = "";
child.stdout.on("data", (d) => (out += d.toString("utf-8")));
child.stderr.on("data", (d) => (err += d.toString("utf-8")));
const timer = setTimeout(() => {
child.kill("SIGKILL");
reject(new Error("timeout"));
}, timeout_ms);
child.on("close", (code) => {
clearTimeout(timer);
resolve({ exit_code: code, stdout: out, stderr: err });
});
});
}
// 아래는 실제 MCP SDK에 맞춰 tools 등록/핸들러 연결을 해야 합니다.
// 여기서는 핵심 로직(allowlist, path 검증, timeout)을 보여주는 예시입니다.
export const tools = {
read_file: readFileTool,
run_cmd: runCmdTool
};
이 예시의 포인트는 다음과 같습니다.
assertWorkspacePath로 경로 탈출을 원천 차단ALLOWED_COMMANDS로 실행 가능한 명령을 “기능 이름”으로 제한spawn시 환경 변수 최소 전달(토큰/키 유출 위험 감소)- 타임아웃으로 무한 실행 차단
3) 도구 설계 팁: “run_cmd”를 더 쪼개라
실무에서 run_cmd 같은 범용 실행기는 결국 공격 표면이 됩니다. 가능하면 아래처럼 분해하세요.
git_status,git_diff,git_apply_patchrun_unit_tests,run_lintbuild_frontend,build_backend
도구가 세분화될수록 파라미터 검증이 쉬워지고, 로그도 의미가 생깁니다.
격리 강화: 샌드박스(컨테이너)로 “실행기” 분리
MCP 서버가 정책을 강제하더라도, 결국 실제 프로세스를 실행하는 머신이 뚫리면 끝입니다. 그래서 실행기는 컨테이너로 격리하는 편이 안전합니다.
Docker로 실행기 격리 예시
docker run --rm \
--read-only \
--cap-drop=ALL \
--network=none \
-v "$WORKSPACE":/repo:rw \
-w /repo \
node:20 \
node mcp-server.js
--read-only로 컨테이너 루트 파일시스템 쓰기 차단--cap-drop=ALL로 리눅스 capability 제거--network=none으로 기본 egress 차단- 워크스페이스만 마운트
네트워크가 필요한 도구(예: 패키지 설치)가 있다면, --network=none 을 유지한 채로 사내 프록시나 특정 도메인만 허용하는 별도 실행 경로를 두는 것이 좋습니다.
운영에서 자주 터지는 문제와 해결 포인트
1) 로그에 비밀이 섞인다
- 표준출력에 환경변수나 토큰이 찍히는 도구는 금지
- 로그 수집 전 마스킹(정규식) 적용
- 도구 실행 환경에서
AWS_*,GITHUB_TOKEN같은 변수를 아예 전달하지 않기
2) 파일 잠금/동시성 이슈
에이전트가 동시에 여러 작업을 돌리면 같은 파일을 수정하거나, 빌드 캐시를 건드리면서 경합이 납니다.
- 워크스페이스를 작업 단위로 복제(임시 디렉터리)하거나
- 도구별 mutex(락 파일)로 직렬화
데이터 레이스를 구조적으로 막는 사고방식은 Rust의 소유권 모델이 좋은 참고가 됩니다. 개념적으로는 Rust 소유권·빌림으로 데이터 레이스 0% 패턴 같은 접근을 “툴 실행 설계”에도 적용할 수 있습니다.
3) 장기 실행 작업이 에이전트를 멈춘다
run_build같은 도구는 비동기로 제출하고job_id를 반환get_job_status,get_job_logs도구로 조회
이렇게 하면 모델의 컨텍스트를 낭비하지 않고, 타임아웃 정책도 명확해집니다.
AutoGPT에 붙이는 연결 전략(개념)
AutoGPT 자체는 버전/포크에 따라 툴 인터페이스가 다르지만, 핵심은 동일합니다.
- AutoGPT가 직접
bash를 실행하지 못하게 설정 - “툴 호출”을 MCP 클라이언트로 라우팅
- MCP 서버가 제공하는 도구만 사용
즉, 에이전트의 권한을 “로컬 OS”가 아니라 “MCP 도구 목록”으로 축소합니다.
권장 정책 템플릿(바로 적용 가능한 형태)
- 파일 접근
- 허용: 워크스페이스 하위
src,tests,package.json등 - 금지: 홈 디렉터리, 숨김 파일, 키/인증서,
.env
- 허용: 워크스페이스 하위
- 명령 실행
- 허용:
git status,git diff,npm test,pytest -q처럼 고정된 커맨드 - 금지: 셸 해석이 필요한 문자열 실행(예:
sh -c), 파이프/리다이렉션
- 허용:
- 네트워크
- 기본 차단
- 예외: 패키지 레지스트리/사내 아티팩트 서버만 허용
- 리소스
- CPU/메모리 제한, 60초 타임아웃(도구별 조정)
- 로깅
- 요청/응답/exit code/실행시간/작업 디렉터리/커밋 해시 기록
마무리
AutoGPT에 MCP 서버를 붙이는 순간, “에이전트가 OS를 직접 만지는 구조”에서 “정책을 통과한 도구만 실행하는 구조”로 바뀝니다. 이 차이가 곧 안전성과 운영 가능성을 결정합니다.
정리하면 다음 순서로 접근하는 것이 가장 현실적입니다.
- 범용 셸 실행을 제거하고, 업무 단위 도구로 분해
- MCP 서버에서 allowlist, 파라미터 검증, 경로 고정, 타임아웃 강제
- 실행기는 컨테이너로 격리하고 네트워크/권한 최소화
- 감사 로그를 남겨 문제를 재현 가능하게 만들기
이 구성은 “로컬 자동화”뿐 아니라 CI 에이전트, 코드 리뷰 봇, 사내 운영 봇에도 그대로 확장할 수 있습니다.