서론 — 또 하나의 흐름, 또 하나의 함정
13년 전 클라우드 업계에 서버리스라는 바람이 불었고, 8년 전에는 쿠버네티스가 그 자리를 차지했습니다. 그리고 지금 필자는 또 하나의 새로운 흐름 앞에 서 있습니다. AI 에이전트 입니다. 단순히 질문에 답하는 챗봇이 아니라, 사용자 대신 작업을 수행하는 자율 실행체가 엔터프라이즈 워크로드 안으로 들어오기 시작했습니다.
필자 또한 익숙한 패턴대로 도입부터 시작했습니다. Anthropic Claude Desktop을 Azure VM 위에 올리고, MCP 서버를 통해 카카오톡, 주식 API, 파일 시스템과 같은 로컬 도구들을 붙여 본격적으로 운영을 시작했죠. 그리고 모바일에서 명령을 던지면 VM의 에이전트가 그 명령을 받아 실제 작업을 수행하는 Dispatch 모드를 활용하기 시작했습니다.
그런데 운영을 시작하자마자 가장 먼저 떠오른 질문은 기능이 아니라 보안이었습니다.
VM의 인바운드 포트는 닫혀 있는데 어떻게 폰에서 보낸 메시지가 VM 안의 에이전트까지 도달하는가. 이 통신은 어디서 끊어야 하고 어디서 막아야 하는가. 방화벽은 어디에 위치해야 의미가 있는가.
이 질문은 단순한 호기심이 아닙니다. 엔터프라이즈에서 AI 에이전트를 운영한다는 것은 곧 사용자 권한 위임을 의미하며, 이는 곧 새로운 형태의 공격 표면을 의미합니다. 우리가 쿠버네티스 도입 초기에 RBAC, NetworkPolicy, Pod Security Policy를 뒤늦게 학습했던 것과 똑같은 실수를, AI Agent 도입에서 반복해서는 안 됩니다.
Claude Dispatch는 어떻게 동작하는가
먼저 흐름부터 정리합니다. Claude Desktop의 Dispatch는 흔히 오해하기 쉬운 구조를 가집니다. 많은 사람들이 클라우드가 VM에 직접 명령을 내린다고 생각하지만, 실제로는 그렇지 않습니다.
제어 평면(Control Plane) — Anthropic Cloud
Anthropic 클라우드는 메시지를 라우팅할 뿐, 실제로 파일을 읽거나 셸 명령을 실행하지 않습니다. 제어 평면은 메시지의 전달자 역할에 한정됩니다.
실행 평면(Data Plane) — Azure VM
모든 실제 작업은 VM 로컬에서만 일어납니다. 파일 입출력, 셸 실행, MCP 도구 호출(카카오톡, 주식 API 등)이 모두 여기에 해당합니다. 에이전트의 본체는 클라우드가 아니라 VM 안에 있는 Claude Desktop 프로세스입니다.
통신은 항상 Outbound
VM은 부팅 직후 Anthropic 클라우드를 향해 WebSocket 또는 HTTP long-poll로 outbound 연결을 먼저 엽니다. 이 연결은 사용자가 명시적으로 종료하기 전까지 유지됩니다. 폰에서 메시지가 들어오면 클라우드는 이미 열려 있는 그 outbound 채널을 거꾸로 타고 VM에게 메시지를 푸시합니다. 따라서 VM의 NSG에는 인바운드 포트를 단 한 개도 열 필요가 없습니다.
이는 NAT 환경에서 내부 호스트가 먼저 외부에 연결을 맺으면 그 응답 패킷이 방화벽을 거꾸로 통과해 들어오는 것과 같은 원리입니다. Reverse Tunnel, 또는 Long-lived Outbound Channel이라고 부르는 패턴입니다.
다이어그램으로 보는 흐름
전체 흐름을 다이어그램으로 정리하면 아래와 같습니다.
좌측은 클라이언트(폰/브라우저)로 사용자 입력 진입점입니다. 중앙은 Anthropic Cloud로 Control Plane 역할만 수행하며 세션 라우팅에 한정됩니다. 우측은 Azure VM으로 Data Plane이며 에이전트 본체와 모든 실제 실행이 이곳에서 일어납니다. 흐름은 다음과 같습니다. 0번 VM이 outbound 채널을 선개통, 1번 폰에서 메시지 송신, 2번 클라우드 라우팅, 3번 기존 채널로 VM에 푸시, 4번 VM 로컬에서 도구 호출 및 실행, 5번 결과를 outbound로 반환.
여기서 반드시 주목해야 할 보안적 함의는 두 가지입니다.
하나는 공격자 입장에서 VM이 직접 노출되지 않는다는 점입니다. Public IP가 없거나 인바운드가 모두 닫혀 있어도 Dispatch는 동작합니다. 이것은 장점이지만 동시에 함정입니다. 노출되지 않았으니 안전하다는 착각을 줍니다.
다른 하나는 그러나 outbound는 활짝 열려 있다는 점입니다. 그리고 그 outbound 채널을 통해 들어오는 메시지가 곧 임의의 코드 실행 트리거가 됩니다. 누군가 사용자의 Anthropic 계정을 탈취한다면, 그는 VM의 인바운드를 한 줄도 건드리지 않고 VM 안에서 임의의 셸 명령을 실행시킬 수 있습니다.
이 지점에서 우리는 다시 묻게 됩니다. 그럼 보안은 어디서 막아야 하는가.
---
본론 — AI Agent VM의 보안을 어떻게 끌어올릴 것인가
전통적인 클라우드 보안은 인바운드 통제 중심으로 설계되어 있습니다. NSG에서 22, 3389, 443을 닫고, WAF로 L7을 걸러내고, Bastion으로 관리자 접근을 제어합니다. 그러나 Claude Dispatch와 같은 Outbound-First 아키텍처에서는 이 설계가 절반밖에 작동하지 않습니다. 보안 설계의 무게중심을 인바운드에서 아이덴티티와 아웃바운드로 옮겨야 합니다.
필자는 이를 위해 6단계 방어선을 설계했습니다. 1차부터 5차까지는 엔터프라이즈 환경에서 권장하는 표준 방어선이며, 6차는 개인 개발자나 소규모 팀이 Azure Firewall을 도입할 수 없을 때 활용할 수 있는 비용 효율적 대안입니다.
1차 방어선 — Egress Firewall (엔터프라이즈)
Dispatch 채널이 outbound로 열린다는 사실은 곧 outbound 통제가 1차 방화벽이 된다는 뜻입니다. 인바운드를 다 닫아도 outbound가 자유롭다면 보안은 0점입니다.
Azure Firewall 또는 NVA를 Hub VNet에 배치하고, Spoke의 Agent VM은 모든 outbound 트래픽을 강제로 Hub Firewall을 경유하도록 UDR로 강제합니다.
Firewall 정책 (Application Rules) 예시는 다음과 같습니다.
| 우선순위 | 동작 | 대상 FQDN | 목적 |
|---------|------|----------|------|
| 100 | Allow | `*.anthropic.com` | Claude API/Dispatch 연결 |
| 110 | Allow | `*.azure.com`, `*.windowsupdate.com` | OS 패치 |
| 120 | Allow | 지정된 MCP 엔드포인트 | 업무용 MCP 서버 화이트리스트 |
| 9999 | Deny | `*` | 그 외 모든 outbound 차단 |
핵심 원칙은 Default Deny, Whitelist Allow 입니다. Anthropic 도메인 외에는 어디로도 나가지 못하게 하는 것이 가장 중요합니다. 만약 에이전트가 탈취되어 사내 데이터를 외부로 빼내려 한다면 이 Egress Firewall이 마지막 방어선이 됩니다.
다만 Azure Firewall Standard SKU는 시간당 약 1.25 USD, 월 약 900 USD 수준이며, 데이터 처리 비용까지 합치면 월 1,000 USD를 쉽게 넘깁니다. 엔터프라이즈에서는 합리적인 비용이지만 개인 사용자에게는 도입 자체가 불가능한 수준입니다. 이 부분은 6차 방어선에서 별도로 다루겠습니다.
2차 방어선 — Identity & Account Hardening
Dispatch는 본질적으로 Anthropic 계정 기반 라우팅입니다. 계정이 곧 키이며, 계정이 뚫리면 VM이 뚫린 것과 같습니다.
MFA를 반드시 활성화하고, 가능하다면 YubiKey와 같은 Hardware Security Key를 사용합니다. 개인 계정과 Enterprise 운영 계정은 분리합니다. Dispatch 운영용 계정은 절대 일반 채팅용으로 사용하지 않습니다. Anthropic 콘솔의 Active Sessions를 정기적으로 감사하고 비정상 IP나 지역에서의 접속을 차단합니다. Microsoft Entra ID와 SSO 통합이 가능한 환경이라면 지역과 디바이스 기반 Conditional Access 정책으로 접속 자체를 제한합니다.
3차 방어선 — VM Hardening 및 최소 권한
Agent VM은 개발자 데스크탑이 아닌 운영 서버로 다뤄야 합니다.
Azure Defender for Cloud의 JIT VM Access를 켜서 RDP/SSH 인바운드는 평소 닫혀 있고, 관리자가 명시적으로 요청한 시간에만 열리도록 설정합니다. VM에서 다른 Azure 리소스(Storage, Key Vault 등)에 접근할 때는 Access Key를 코드에 박지 말고 System-assigned Managed Identity를 사용합니다. Claude Desktop을 실행하는 사용자는 Local Administrator가 아닌 일반 사용자로 운영합니다. MCP 서버가 시스템 파일을 건드리지 못하게 합니다.
`claude_desktop_config.json`에서 Filesystem MCP의 접근 경로를 명시적으로 좁은 디렉토리로 제한합니다. 예를 들어 `D:AgentWorkspace`만 허용하고, `C:` 루트나 사용자 프로파일 전체는 절대 노출하지 않습니다.
4차 방어선 — MCP Server 자체에 대한 통제
MCP는 강력하지만 그만큼 위험합니다. 외부에서 만든 MCP 서버를 무비판적으로 설치하는 것은 낯선 사람의 USB를 서버에 꽂는 행위와 같습니다.
회사 차원에서 사용 가능한 MCP 서버 목록을 화이트리스트로 관리하고, 사용자가 임의로 추가할 수 없도록 `claude_desktop_config.json` 파일을 GPO 또는 Intune으로 잠급니다. 오픈소스 MCP를 사용할 때는 반드시 소스 코드 리뷰를 거칩니다. 특히 child_process, exec, 외부 네트워크 호출이 있는지 확인합니다. 카카오톡 MCP는 카카오 도메인만, 주식 MCP는 해당 데이터 제공자 도메인만 허용하는 식으로 MCP별로 Egress Firewall 정책을 세분화합니다.
5차 방어선 — Logging, Monitoring & Incident Response
뚫릴 수 있다는 전제 하에 탐지 체계를 갖춰야 합니다.
Azure Monitor Agent와 Log Analytics를 통해 VM 내 프로세스 생성 이벤트(EventID 4688)를 모두 수집합니다. Claude Desktop이 어떤 자식 프로세스를 spawn했는지 전부 기록합니다. Microsoft Defender for Cloud에서 EDR을 활성화하여 비정상적인 PowerShell 실행과 LOLBins를 탐지합니다. Azure Firewall의 모든 Allow/Deny 로그는 Sentinel 또는 Splunk로 전송하여, 안 가야 할 곳으로 가려는 시도를 매일 분석합니다. Anthropic 콘솔에서 제공하는 사용 로그는 정기적으로 다운로드하여 보관합니다(향후 SOC2 감사 대비).
Incident Playbook도 준비합니다. 만약 계정이 탈취되었다고 판단되면 우선 Anthropic 콘솔에서 모든 세션을 강제 종료하고, VM을 즉시 격리하며(NSG로 outbound 전체 차단), 포렌식을 수행한 뒤 새로운 VM으로 재구축합니다. 이 절차를 문서화하고 분기별로 훈련해야 합니다.
6차 방어선 — 개인/소규모 환경의 비용 효율적 대안
지금까지의 방어선은 엔터프라이즈를 가정한 것이며, 1차 Azure Firewall만 해도 월 1,000 USD 수준의 운영비가 발생합니다. 개인 개발자나 소규모 스타트업은 이 비용을 감당하기 어렵습니다. 그렇다고 outbound 통제를 포기할 수는 없습니다. 보안 무게중심이 outbound로 옮겨갔다는 사실은 비용과 무관하게 동일하기 때문입니다.
여기서 솔직하게 짚고 넘어가야 할 기술적 사실이 하나 있습니다. NSG의 ASG(Application Security Group)로 outbound FQDN 필터링이 가능하다는 오해가 흔하지만, 실제로 ASG는 NSG 규칙의 source/destination을 IP 대신 논리적 그룹명으로 표현해주는 라벨일 뿐입니다. NSG 자체가 5-tuple(IP/포트/프로토콜) 기반 L4 필터링만 지원하기 때문에 ASG를 사용하더라도 FQDN 차단은 불가능합니다. Microsoft 공식 문서도 FQDN 필터링은 Azure Firewall에서만 지원된다고 명시하고 있습니다. 이 점을 명확히 하지 않으면 잘못된 보안 설계를 하게 됩니다.
그럼 개인 환경에서 outbound를 어떻게 통제할 것인가. 필자는 다음 4가지 계층을 조합한 무비용 방어 스택을 제안합니다.
계층 1 — NSG + Service Tag (L4 outbound 통제)
NSG는 FQDN을 모르지만 Service Tag는 압니다. Service Tag는 Microsoft가 관리하는 IP 묶음을 이름으로 부르는 기능으로, 무료이고 자동 갱신됩니다.
```
Outbound Rule 100 Allow Destination=AzureCloud Port=443
Outbound Rule 110 Allow Destination=Storage Port=443
Outbound Rule 120 Allow Destination=AzureMonitor Port=443
Outbound Rule 130 Allow Destination=<Anthropic IP> Port=443 (수동 관리)
Outbound Rule 4096 Deny Destination=Internet Port=*
```
Anthropic은 Service Tag가 없으므로 IP 대역을 수동으로 추출해 등록해야 합니다. nslookup이나 dig로 `api.anthropic.com`의 IP를 주기적으로 확인하고, 변동이 있으면 NSG를 갱신하는 PowerShell 스크립트를 Azure Automation으로 스케줄 등록합니다. 갱신 주기는 일 1회면 충분합니다. 이 방식은 완벽하지 않지만 인터넷 전체를 열어두는 것보다 100배 안전합니다.
계층 2 — Windows Defender Firewall (Host-based, 프로세스 단위)
Azure Firewall이 못 하는 것 중 하나가 프로세스 단위 통제입니다. Windows Defender Firewall은 무료이며, 프로세스 단위 outbound 룰을 만들 수 있습니다.
```powershell
# Claude Desktop만 outbound 443 허용, 나머지 모든 프로세스는 차단
New-NetFirewallRule -DisplayName "Allow Claude Desktop Outbound" `
-Direction Outbound -Action Allow `
-Program "C:Users<user>AppDataLocalAnthropicClaudeClaude.exe" `
-Protocol TCP -RemotePort 443
# 기본 outbound 정책을 Block으로 변경 (Default Deny)
Set-NetFirewallProfile -Profile Domain,Public,Private -DefaultOutboundAction Block
```
이 정책을 적용하면 Claude Desktop과 명시적으로 허용한 MCP 프로세스만 인터넷에 나갈 수 있고, 만약 멀웨어가 VM에 침투해 cmd.exe나 powershell.exe로 외부에 데이터를 빼내려 해도 차단됩니다. 다만 Default Deny는 Windows Update나 OS 텔레메트리도 막아버리므로 예외 룰을 충분히 추가해두어야 운영에 지장이 없습니다.
계층 3 — Cloudflare Zero Trust (FQDN 기반 DNS 필터링, 무료 티어)
진짜 FQDN 기반 outbound 통제가 필요하다면 Cloudflare Zero Trust의 무료 티어가 가장 현실적인 선택지입니다. 50명까지 무료이며, WARP 클라이언트를 VM에 설치하면 모든 DNS 쿼리가 Cloudflare를 거치게 됩니다. 여기에 Gateway 정책으로 FQDN 화이트리스트를 적용합니다.
```
Cloudflare Gateway Policy:
- Allow: *.anthropic.com, *.claude.ai
- Allow: *.kakao.com (KakaoTalk MCP)
- Allow: *.windowsupdate.com, *.microsoft.com
- Block: Categories = Malware, Phishing, Newly Registered Domains
- Block: Default (everything else)
```
이 방식은 사실상 Azure Firewall의 Application Rule과 동일한 기능을 무료로 제공합니다. 다만 Cloudflare에 DNS 쿼리 로그가 남는다는 프라이버시 트레이드오프는 인지하고 사용해야 합니다.
계층 4 — Pi-hole 또는 AdGuard Home (자체 호스팅 DNS Sinkhole)
외부 SaaS에 의존하기 싫다면 같은 VNet 내에 작은 Linux VM(B1s, 월 8 USD)을 띄워 Pi-hole을 운영하는 방법도 있습니다. Agent VM의 DNS 서버를 Pi-hole로 지정하고, allowlist에 등록되지 않은 도메인은 모두 sinkhole(0.0.0.0)로 응답하게 설정합니다. 운영 부담은 있지만 모든 통제권을 직접 보유할 수 있습니다.
개인 환경에서 권장하는 최소 조합은 다음과 같습니다.
| 통제 영역 | 도구 | 월 비용 |
|---------|------|--------|
| L4 outbound (IP/포트) | NSG + Service Tag | 0 USD |
| 프로세스 단위 통제 | Windows Defender Firewall | 0 USD |
| FQDN 기반 통제 | Cloudflare Zero Trust Free | 0 USD |
| 합계 | | 0 USD |
이 조합은 Azure Firewall의 기능을 100% 대체하지는 못하지만, outbound 통제의 핵심 원칙인 Default Deny + Whitelist Allow를 무비용으로 구현할 수 있습니다. 엔터프라이즈로 성장한 후에 1차 방어선의 Azure Firewall로 자연스럽게 전환할 수 있도록, 처음부터 화이트리스트 도메인 목록을 문서화해두는 것이 중요합니다.
보안 아키텍처 통합도
6단계 방어선을 통합하면 다음과 같은 그림이 됩니다.
```
[Phone/Browser]
│ (outbound HTTPS, MFA + Conditional Access)
▼
[Anthropic Cloud — Control Plane]
│ (account_id 기반 라우팅)
▼
═══════════════════════════════════════════
엔터프라이즈 — Hub VNet (보안 경계)
┌─────────────────────────────────┐
│ ① Azure Firewall (Egress) │ ← Default Deny
│ - Allow: *.anthropic.com │
│ - Allow: 화이트리스트 MCP │
│ - Deny: 나머지 전부 │
└─────────────────────────────────┘
│ UDR 강제 경유
▼
Spoke VNet (Agent Workload)
┌─────────────────────────────────┐
│ ② Agent VM (JIT, Managed ID) │
│ ├─ Claude Desktop │
│ ├─ MCP (제한된 scope) │
│ └─ Defender for Cloud Agent │
└─────────────────────────────────┘
│
▼
③ Log Analytics + Sentinel (SIEM)
═══════════════════════════════════════════
═══════════════════════════════════════════
개인/소규모 — VNet (6차 방어선)
┌─────────────────────────────────┐
│ NSG + Service Tag │ ← L4 통제 (무료)
├─────────────────────────────────┤
│ Agent VM │
│ ├─ Windows Defender Firewall │ ← 프로세스 단위 (무료)
│ ├─ Cloudflare WARP Client │ ← FQDN 통제 (무료)
│ └─ Claude Desktop + MCP │
└─────────────────────────────────┘
│
▼
Cloudflare Zero Trust Gateway
(FQDN allowlist + Threat Block)
═══════════════════════════════════════════
```
---
결론 — 새로운 패러다임에는 새로운 보안 모델이 필요하다
쿠버네티스가 처음 등장했을 때 우리는 컨테이너가 격리되어 있으니 안전하다고 믿었지만, 곧 RBAC와 NetworkPolicy 없이는 클러스터가 무방비라는 사실을 깨달았습니다. AI Agent가 등장한 지금, 인바운드가 닫혀 있으니 안전하다는 착각에 빠져서는 안 됩니다.
Claude Dispatch는 인바운드를 우회하는 합법적 채널입니다. 보안의 무게중심은 Egress와 Identity, 그리고 MCP Scope로 이동해야 합니다. 인바운드 NSG 한 줄 닫는 것보다 outbound에 Default Deny 정책 한 줄 추가하는 것이 100배 더 효과적입니다.
다만 보안에는 비용이 따릅니다. 엔터프라이즈는 Azure Firewall로 정공법을 쓸 수 있지만 개인은 그럴 수 없습니다. 그렇다고 보안을 포기할 수는 없습니다. NSG + Service Tag, Windows Defender Firewall, Cloudflare Zero Trust 무료 티어를 조합하면 0원으로도 outbound Default Deny를 구현할 수 있습니다. 처음부터 완벽하게 만들 필요는 없습니다. 화이트리스트 도메인 목록을 문서화해두고, 조직이 성장하면 자연스럽게 Azure Firewall로 마이그레이션하면 됩니다.
엔지니어로서 새로운 기술을 빠르게 도입하는 것은 어렵지 않습니다. 그러나 그 기술이 만드는 새로운 위험을 인식하고, 자신의 환경과 예산에 맞는 방어선을 그리는 것, 이것이 진짜 엔터프라이즈 아키텍트의 역할입니다.
]]>