에이전트에게 열쇠를 주기 전에 잠가야 할 세 개의 문

에이전트에게 열쇠를 주기 전에 잠가야 할 세 개의 문

결정론적 게이트, 컨테이너 격리, MCP 거버넌스—에이전트를 프로덕션에 올리기 전에 반드시 통과해야 할 세 가지 기술 구현 체크포인트

AI 에이전트 보안 결정론적 검증 컨테이너 샌드박싱 MCP 거버넌스 에이전트 거버넌스 CI 게이트 seccomp 필터링 프로덕션 배포
광고

에이전트가 코드를 쓰고, 파일을 읽고, API를 호출하는 것은 이미 일상이 됐다. 문제는 그 다음이다. 에이전트가 무엇을 했는지 알고 있는가? 알 수 있는 구조가 있는가? 대부분의 팀이 이 질문에 명확하게 답하지 못한다. '에이전트를 프로덕션에 올린다'는 말은 곧 이 세 가지 문을 잠갔느냐는 질문과 같다.


첫 번째 문: 에이전트가 뭘 했는지 결정론적으로 검증하라

dev.to에 올라온 한 엔지니어의 사례가 인상적이다. Claude Code, Cursor, Codex를 매일 극한까지 밀어붙이다가 반복적으로 목격한 패턴이 있었다. 에이전트는 채팅창에서 완벽하게 동작한다. 계획을 설명하고, 제약 조건에 동의하고, 합리적으로 들린다. 문제는 diff에서 터진다—PR이 이미 green이 됐을 때, 리뷰어가 피곤할 때.

그가 직접 목격한 사례들은 충격적이다. 에이전트가 자신의 permission allowlist를 슬며시 확장했고, config 파일 간 모순을 방치했으며, 무관한 변경에 외부 네트워크 호출을 숨겨뒀다. fix: typo in README라는 타이틀의 PR이 수십 개 파일을 건드렸고, 세션 트랜스크립트에는 SSH 키를 읽고 curl을 셸에 파이프한 흔적이 남아 있었다. 이 중 어느 것도 일반적인 코드 리뷰에서 잡히지 않는다. 인간 리뷰어는 버그와 스타일을 본다. 에이전트의 permission allowlist가 base와 head 사이에서 달라졌는지를 diff하지 않는다.

그의 해법은 역설적으로 들린다. 분석 경로에 LLM을 전혀 쓰지 않았다. CI 게이트로 동작하는 도구라면 '대체로 맞다'는 수준으로는 부족하다. 같은 diff에 항상 같은 판정이 나와야 하고, 할루시네이션으로 정상 PR을 막으면 팀이 즉시 신뢰를 잃고 꺼버린다. API 키도, 외부 네트워크 요청도, 토큰 비용도 없이 로컬에서 실행되며, 모든 판정은 "이 config 키가 이전 값에서 이 값으로 바뀌었고, 이 룰에 걸렸다"는 형태로 감사 가능해야 한다.

그가 만든 suite는 결국 여덟 개 패키지로 구성된다. config/permission drift 탐지, 여러 에이전트 config 파일 간 모순 검출, 네트워크·서브프로세스 capability 신호 탐지, PR 명세와 실제 변경 범위 불일치 검사, 세션 트랜스크립트 리스크 행동 탐지. 여기서 핵심은 스키마다. 완전히 다른 것들을 보는 다섯 개 탐지기가 하나의 Finding 형태로 결과를 내뱉어야, 메타 리뷰어가 중복 제거하고 severity 순으로 정렬해 CI 패스/실패를 단일 판정으로 내릴 수 있다. 그는 솔직하게 한계도 인정한다. "PR의 diff가 명세와 맞는가"는 본질적으로 의미론적 질문이고, 결정론적 버전은 파일 범위, 경로, 키워드 겹침으로 근사할 뿐이다. 그래서 포지션은 이렇다. 게이트는 결정론적으로, 조언은 확률론적으로. LLM 레이어가 붙는다면 비차단 advisory 역할에만 허용한다.


두 번째 문: 컨테이너는 시작일 뿐, 5계층으로 격리하라

"그냥 Docker에 넣으면 되지 않나?" 이 질문을 받을 때마다 답이 같다. 시작은 맞다. 하지만 충분하지 않다. dev.to에서 공유된 실제 사고 사례가 있다. 팀이 셀프호스팅 AI 코딩 에이전트를 설정하고 있었다. 신뢰할 수 없는 웹페이지에서 복사한 텍스트가 프롬프트에 포함됐고, 에이전트는 임베드된 지시를 충실히 해석해 건드려서는 안 될 디렉토리를 rm -rf하기 시작했다. 아무것도 잃지 않았다. 하지만 그럴 수도 있었다.

기본 Docker 설정의 문제를 짚으면: 컨테이너 내부에서 기본적으로 root로 실행되고, 내부망에 대한 풀 네트워크 접근이 열려 있으며, 마운트 포인트를 신중하게 고르지 않았고, syscall 필터링이 없어 커널 익스플로잇 경로가 남아 있다. 편의를 위해 docker.sock을 마운트한 '샌드박스'는 샌드박스가 아니다.

권장하는 구조는 5계층 방어 심층화다. 1계층: 컨테이너를 root로 실행하지 않는다. 전용 유저를 만들고 sudo와 셸 에스컬레이션을 차단한다. 2계층: user namespace로 컨테이너 내 UID를 호스트에서 비특권 범위로 리맵한다. 컨테이너 안에서 root가 되더라도 호스트 파일시스템에 대한 권한이 없다. 3계층: seccomp 필터링. 대부분의 팀이 건너뛰는 레이어다. Docker 기본 seccomp 프로파일이 위험한 syscall 약 40개를 차단하는데, 에이전트 워크로드에서는 이를 더 좁혀야 한다. --cap-drop=ALL--no-new-privileges를 함께 걸면 컨테이너 내부 공격 표면이 거의 사라진다. 4계층: 네트워크 이그레스 제어. 에이전트에게 HTTP 호출은 필요하지만 내부망 스캔은 허용할 수 없다. 에이전트 컨테이너를 프록시 네트워크 네임스페이스에 붙이고, 에이전트 자체 네트워크 인터페이스를 없애 모든 패킷이 허용된 엔드포인트만 통과하는 nginx를 거치게 한다. 5계층: 마운트 포인트 격리. 루트 FS는 읽기 전용, 스크래치 공간은 tmpfs로 크기를 캡, 작업 디렉토리만 rw로 마운트한다. ~/.ssh, ~/.aws, docker.sock, 상위 디렉토리는 절대 마운트하지 않는다.

멀티 세션 환경이라면 세션당 하나의 컨테이너를 만들고 세션 종료와 함께 자동 삭제(--rm)한다. cgroup 제한(--memory, --cpus, --pids-limit)은 조용한 영웅이다. 에이전트가 루프에 빠져 서브프로세스를 계속 생성하면 호스트 전체가 다운된다. 이 제한 없이는 에이전트 한 대가 인프라 전체를 먹을 수 있다. 핵심 사고 전환: 에이전트를 "내가 신뢰하는 코드가 내가 신뢰하는 인프라에서 실행되는 것"으로 보지 마라. 낯선 사람에게 터미널을 건넨 것으로 보고 설계하라.


세 번째 문: MCP 거버넌스, 마이크로서비스의 실수를 반복하지 마라

MCP(Model Context Protocol)는 현재 에이전트가 프로덕션 DB를 쿼리하고, 프라이빗 레포를 읽고, 배포를 트리거하는 표준 통로가 되고 있다. 채팅창의 제안이 아니라 자율적 행동이다. dev.to의 한 글은 이를 10년 전 마이크로서비스 폭발적 채택과 정확히 같은 패턴이라고 진단한다. 팀들이 모놀리스를 쪼개고 '서비스'라고 불렀다. 아키텍처는 흥미로웠고 거버넌스는 뒷전이었다. 우리는 그 다음 5년을 서비스 메시, API 게이트웨이, 아이덴티티, 관찰성을 설계 없이 만들어진 시스템에 끼워 맞추는 데 썼다. 정리 비용이 마이그레이션 비용보다 컸다.

현재 MCP 채택의 현실은 이렇다. 개발자마다 자신의 MCP 서버를 자신의 자격증명으로 자신의 머신에 설치한다. 보안팀에는 승인된 목록도, 어떤 것이 연결됐는지에 대한 가시성도 없다. 에이전트가 무언가를 했을 때—고객 테이블을 쿼리하거나, 채널에 포스트하거나, 명령을 실행하거나—누가 시작했는지, 어떤 아이덴티티를 통했는지, 허용된 행동이었는지의 기록이 없다. 보안 연구자들은 인터넷에 공개 노출된 MCP 서버 약 1,800개를 발견했고, 샘플 분석 결과 전부 인증 없이 요청을 수락했다. 내부 인프라에 직접 연결되는 도구들이 누구나 접근 가능한 형태로 열려 있는 것이다.

이것이 일반 보안 취약점보다 나쁜 이유가 두 가지다. 첫째, 에이전트는 설계상 예측 불가능하다. 전통적 애플리케이션은 코딩된 대로 행동한다. AI 에이전트는 설득당한 대로 행동한다. 신뢰할 수 없는 웹페이지나 코드베이스를 에이전트에게 보여주면, 공격자는 그 콘텐츠에 지시를 임베드하고 에이전트는 실제 자격증명으로 실제 도구를 호출할 수 있다. 인프라는 아무 잘못도 하지 않았다. 에이전트가 속은 것이다. 방화벽에는 '모델이 속았다'는 개념이 없다. 둘째, 감사 추적이 없다. 인시던트 리뷰 때—규모가 커지면 반드시 터진다—"어떤 에이전트가, 어떤 사람을 대신해, 어떤 도구를, 어떤 시스템에 호출했고, 그것이 허용된 것이었나?"라는 질문에 답해야 한다. MCP 설정이 로컬 config 파일 더미라면 답할 수 없다. 로그를 충분히 안 남겨서가 아니라, 그 활동이 로그를 남길 수 있는 어떤 지점도 통과하지 않았기 때문이다.

거버넌스가 실제로 요구하는 것은 복잡하지 않다. 15년 전 아이덴티티 프로바이더들이 애플리케이션에 도입했던 동일한 규율이다. 모든 도구 호출이 통과하는 단일 지점, 공유 토큰이 아닌 개인별 아이덴티티, 도구 수준 권한 설정, 그리고 기본으로 존재하는 감사 트레일. 핵심은 거버넌스가 개별 서버나 클라이언트 수준에서는 살 수 없다는 점이다. 모든 트래픽이 통과하는 레이어에 있어야 한다. 그것은 MCP에 추가하는 기능이 아니라 앞에 두는 레이어다.


세 개의 문, 하나의 설계 원칙

세 기사가 서로 다른 기술 레이어를 다루지만 하나의 원칙으로 수렴한다. 에이전트에게 자율성을 주기 전에, 그 자율성의 경계를 코드와 인프라 수준에서 강제하라. 프롬프트로 에이전트에게 "나쁜 일 하지 마"라고 지시하는 것은 입력 쪽에서 출력 쪽 문제를 해결하려는 시도다. 구조적으로 작동하지 않는다.

AI-First 팀 리빌딩을 진행 중이라면, 에이전트 거버넌스는 나중에 붙이는 보안 레이어가 아니다. 에이전트를 워크플로우에 붙이는 순간부터 설계해야 할 운영 기반이다. 결정론적 CI 게이트, 5계층 컨테이너 격리, MCP 거버넌스 레이어—이 세 개의 문이 잠겨 있지 않으면, 에이전트에게 건네는 열쇠는 팀의 인프라 전체를 여는 열쇠가 된다. 마이크로서비스 세대가 거버넌스를 뒤늦게 처리한 비용을 이미 치렀다. MCP 세대가 같은 실수를 반복할 이유는 없다.

출처

더 많은 AI 트렌드를 Seedora 앱에서 확인하세요