728x90

전체 목표와 핵심 원칙
목표
- MCP 서버는 “도구 실행/리소스 읽기”라는 강력한 권한을 다루므로,
접속 관문(NGINX)에서 강하게 걸러내고, MCP 내부에서는 역할/스코프/입력검증으로 “행동”을 통제합니다. - 툴/리소스를 코드 하드코딩이 아니라 DB/JSON으로 관리하고, 관리용 Web API로 추가/수정하면, MCP 서버는 이를 실시간 반영합니다.
원칙
- 외부에서 MCP로 직통 접근 금지 (MCP는 내부망/loopback에만 바인딩)
- mTLS로 “클라이언트 단위” 강제 식별 (토큰보다 앞선 1차 관문)
- Zero Trust = 기본 차단 + 최소 허용
- Guardrail은 2단
- (A) 서버 레벨: role/scope 기반 Tool/Resource 필터
- (B) 툴 레벨: arguments 검증/수정/차단 (예: analyze_tool_request)
- 감사 로깅은 필수: 누가(인증서 DN) 언제 어떤 메서드로 무엇(uri/tool/args)을 시도했는지
아키텍처 전체 구성
[Client / Agent / IDE Plugin]
|
| (1) TLS + mTLS (클라 인증서)
v
[NGINX Reverse Proxy]
- mTLS verify
- IP allowlist / geo / time window (옵션)
- rate limit / conn limit
- request size limit
- (옵션) OIDC / JWT 검증
- 헤더에 인증 컨텍스트 주입(역할/주체)
|
v
[MCP Server (WebSocket JSON-RPC)]
- dynamic registry (DB/JSON 캐시)
- server-level RBAC (tools/resources 허용목록)
- tool-level guardrail (args 검사)
- audit log
|
+--> [Admin API (FastAPI)]
- tools/resources CRUD
- roles/guardrail rules CRUD
- change log/versioning
권장 분리
/mcp/ws(WebSocket): mTLS 필수 + rate limit/admin/*(관리 API): mTLS + 추가로 IP allowlist + (가능하면) 별도 포트/별도 서브도메인 + 접근자 최소화
mTLS 구성 단계 (CA/서버/클라이언트 인증서)
내부 CA 생성(예시)
# CA 키/인증서 생성
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
-subj "/C=KR/O=PagesKR/OU=Security/ CN=PagesKR-MCP-CA" \
-out ca.crt
NGINX 서버 인증서
openssl genrsa -out server.key 4096
openssl req -new -key server.key \
-subj "/C=KR/O=PagesKR/OU=MCP/CN=mcp.example.internal" \
-out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out server.crt -days 825 -sha256
클라이언트 인증서 (역할을 OU로 구분 추천)
SOC 에이전트용
openssl genrsa -out soc_client.key 4096
openssl req -new -key soc_client.key \
-subj "/C=KR/O=PagesKR/OU=SOC_ASSISTANT/CN=soc-agent-01" \
-out soc_client.csr
openssl x509 -req -in soc_client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out soc_client.crt -days 365 -sha256
DEV 에이전트용
openssl genrsa -out dev_client.key 4096
openssl req -new -key dev_client.key \
-subj "/C=KR/O=PagesKR/OU=DEV_ASSISTANT/CN=dev-agent-01" \
-out dev_client.csr
openssl x509 -req -in dev_client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out dev_client.crt -days 365 -sha256
실무 팁
- OU(조직단위) 또는 인증서 확장 필드에 role을 심어 두면, NGINX/MCP에서 role 매핑이 쉬워집니다.
- 인증서 폐기는 CRL/OCSP 도입 또는 “짧은 만료 + 자동 갱신” 전략이 운영상 편합니다.
NGINX 앞단 구성 (mTLS + WebSocket + Zero Trust)
핵심: MCP 서버는 내부에만 바인딩
- MCP 서버:
127.0.0.1:8765(또는 내부 전용 VLAN IP) - 외부에서 8765 접근은 방화벽/보안그룹으로 차단
300x250
NGINX http 블록(WebSocket reverse proxy) 예시
# /etc/nginx/nginx.conf (중요 부분만 예시)
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# (A) Rate limit (클라이언트별 요청 제한)
limit_req_zone $binary_remote_addr zone=mcp_req:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=mcp_conn:10m;
upstream mcp_ws_backend {
server 127.0.0.1:8765;
}
upstream mcp_admin_backend {
server 127.0.0.1:8000;
}
server {
listen 9443 ssl;
server_name mcp.example.internal;
# 서버 인증서
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
# mTLS: 클라이언트 인증서 검증
ssl_client_certificate /etc/nginx/certs/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# (B) 요청 크기 제한(과도한 payload 방지)
client_max_body_size 1m;
# (C) 기본 보안 헤더(필요 시)
add_header X-Content-Type-Options nosniff;
# ---------------------------
# MCP WebSocket
# ---------------------------
location /mcp/ws {
# Zero Trust: 접속망 제한(예: 사내망/VPN만)
# allow 10.0.0.0/8;
# deny all;
limit_req zone=mcp_req burst=20 nodelay;
limit_conn mcp_conn 20;
proxy_pass http://mcp_ws_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# ★ 헤더 스푸핑 방지: 클라이언트가 보내더라도 항상 덮어쓰기
proxy_set_header X-Client-Verify $ssl_client_verify;
proxy_set_header X-Client-DN $ssl_client_s_dn;
proxy_set_header X-Client-CN $ssl_client_s_dn_cn;
proxy_set_header X-Client-OU $ssl_client_s_dn_ou;
# (옵션) 요청 추적
proxy_set_header X-Request-ID $request_id;
}
# ---------------------------
# Admin API (훨씬 더 엄격하게)
# ---------------------------
location /admin/ {
# 강력 권장: Admin은 별도 Bastion/VPN IP만 허용
# allow 10.10.10.10;
# deny all;
# 추가 인증(옵션): Basic Auth / JWT / OIDC 등
# auth_basic "Restricted";
# auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://mcp_admin_backend;
proxy_set_header X-Client-DN $ssl_client_s_dn;
proxy_set_header X-Request-ID $request_id;
}
}
}
NGINX Zero Trust 체크 포인트
- mTLS on이 1차 필수
/admin/은 별도 포트/호스트로 분리하면 더 안전proxy_set_header로 인증 컨텍스트를 서버가 주입하고, MCP는 이 헤더만 신뢰 (클라가 보내는 값 신뢰 금지)
MCP 서버 설계: “동적 레지스트리 + RBAC + Guardrail”
동적 구성 데이터 모델
권장 구성 파일/테이블(예: DB/JSON)
tools
- name, description, inputSchema, handlerType, handlerConfig, tags, riskLevel
resources
- uri, name, mimeType, backendType(file/db/http), backendConfig, sensitivity
roles(RBAC)
- roleName, allowedTools[], allowedResourcePrefixes[], denyRules 등
guardrail_rules
- toolName별 allow/deny 조건, arg validation, max limit, forbidden patterns 등
audit_logs
- request_id, client_dn, role, method, tool/resource, args hash, result status, latency
세션 role 결정 로직
- NGINX가 넣어주는
X-Client-OU를 role로 매핑- OU=SOC_ASSISTANT → role=SOC_ASSISTANT
- 또는 DN/CN을 키로 내부 DB에서 role lookup (더 유연)
중요한 점: “initialize params로 role을 받는 방식”은 조작 가능하므로
반드시 mTLS 인증서/NGINX 주입 헤더 기반으로 role을 결정하세요.
Server-level Guardrail (RBAC)
tools/list에서 role 허용 목록만 노출tools/call에서 role 허용 여부 재검증resources/list/read에서도 prefix 기반 허용 여부 확인
Tool-level Guardrail: analyze_tool_request + 입력정책 엔진
서버 레벨이 “어떤 툴/리소스를 쓸 수 있나”를 막는다면, 툴 레벨은 “어떤 입력으로 실행할 수 있나”를 통제합니다.
Guardrail 정책 예시(JSON)
예: guardrail.json
{
"defaults": {
"maxLimit": 1000,
"maxTimeRangeDays": 30
},
"tools": {
"search_logs": {
"rules": [
{"type": "limit_max", "value": 200},
{"type": "time_range_days_max", "value": 14},
{"type": "deny_pattern", "field": "query", "pattern": "(?i)secret|password|api[_-]?key"}
]
},
"compare_policies": {
"rules": [
{"type": "allow_uri_prefix", "field": "oldPolicyUri", "prefix": "policy://security/"},
{"type": "allow_uri_prefix", "field": "newPolicyUri", "prefix": "policy://security/"}
]
}
}
}
Guardrail 동작 방식
tools/call을 받으면:- role 허용 tool인지 확인 (server-level)
- tool별 guardrail 규칙 적용
- limit 상한 조정(자동 sanitize)
- 기간 상한 조정
- 금칙 패턴 탐지 시 deny
- 최종 sanitized args로 실제 handler 실행
운영 팁
- “deny”만 하면 사용자 경험이 나빠지니, 가능한 경우
review/sanitize로 자동 수정 후 실행하는 패턴이 실용적입니다.- 다만 보안 고위험(예: 삭제/변경)은 무조건 deny 또는 별도 강한 승인 경로로 분리하세요.
“동적으로 툴/리소스를 추가/수정”하는 운영 구조
Admin API로 CRUD
POST /admin/tools새로운 tool 정의 추가PUT /admin/tools/{name}수정POST /admin/resources리소스 추가PUT /admin/resources/{uri}수정POST /admin/roles역할 정책 변경POST /admin/guardrail가드레일 규칙 변경
MCP 서버 반영 방식(추천 2가지)
방식 A: 즉시 반영 (Registry 메모리 갱신)
- Admin API가 변경 시 Registry 업데이트
- 장점: 즉시 반영, 단순
- 주의: 동시성/롤백/검증 필요
방식 B: 버전 기반 반영 (config version + reload)
- DB에
config_version을 두고 - Admin API가 변경하면 version 증가
- MCP 서버는 주기적으로 version 체크 후 reload
- 장점: 안전, 롤백 용이(버전 되돌리기)
- 운영에 더 적합
로깅/감사(Audit) 설계
반드시 남겨야 할 필드
- request_id (NGINX에서 주입)
- client_dn / client_cn / role
- method (tools/call, resources/read 등)
- 대상 (tool name, resource uri)
- arguments (원문 저장은 민감할 수 있으니 해시+요약 권장)
- 결과 status (allow/deny/error)
- latency, backend call id
경보(탐지) 포인트 예시
- 짧은 시간에
resources/read대량 호출 - role 불일치 접근 시도(DEV가 security 정책 full 요청 등)
- guardrail deny가 급증
- 과도한 limit/timeRange 반복 요청
배포/운영 체크리스트
네트워크/배포
- MCP는 127.0.0.1에만 바인딩
- NGINX만 외부(또는 VPN) 노출
-
/admin은 별도 네트워크 경로/포트/서브도메인 분리 - 방화벽/보안그룹에서 MCP 포트 직접 접근 차단
인증/인가
- mTLS on, CA 관리 체계(발급/폐기/만료)
- 인증서 DN/OU 기반 role 매핑
- role별 tools/resources 허용 목록 최소화
Guardrail
- tool별 limit/timeRange 상한
- 위험 패턴 차단(민감 키워드, 과도한 wildcard 등)
- 변경/삭제 계열 tool은 별도 승인/분리(가능하면 MCP에 직접 노출 금지)
로깅/모니터링
- NGINX access log + MCP audit log 분리 수집
- deny/error 급증 알림
- rate limit hit 알림
빠른 테스트 방법
인증서로 접속 가능한지(개념)
WebSocket 클라이언트(예: wscat류)를 사용할 때 인증서 지정이 가능해야 합니다.
(툴마다 옵션이 달라서, 보통은 프록시 뒤에서 테스트용 클라이언트를 준비합니다.)
NGINX에서 mTLS 강제 확인
- 클라이언트 인증서 없이 접속 시 TLS handshake 단계에서 실패해야 정상
- access log에
$ssl_client_verify가SUCCESS로 찍히는지 확인
추천 “최소 안전” 표준 구성
- NGINX(mTLS) + rate limit + IP allowlist로 1차 차단
- MCP 서버는 동적 registry(DB/JSON) 기반으로 tools/resources 제공
- MCP 내부에 server-level RBAC + tool-level guardrail
- Admin API는 최고 보안 구역(별도 네트워크, mTLS+추가 인증, 변경 이력/승인)
- 전 구간 audit log를 남겨서 사후 추적/탐지 연계
728x90
그리드형(광고전용)
댓글