들어가며
전날 agentgateway 에 LangFlow MCP 등록을 마치고 나서 바로 다음 요구가 들어왔다.
"게이트웨이에 backend 로 붙은 MCP 들 중에서, 특정 도구 하나만 호출 못 하게 막고 싶다. 예를 들어 count-characters 같은 데모용 도구는 운영에서 차단."
agentgateway 는 이걸 위해 MCP Authorization 이라는 정책 레이어를 제공한다. 정책 언어는 Cedar (AWS 가 오픈소스화한 정책 언어). MCP 요청의 메타데이터 (mcp.tool.name, mcp.server.name, principal 등) 를 보고 allow / deny / forbid 한다.
처음 쓰면 deny rule 이 안 먹히는 함정이 있어서, 그 디버깅 과정과 운영용 allow-list 패턴까지 정리한다.
핵심 키워드: agentgateway, MCP Authorization, Cedar policy, Deny Rule, MCP 도구 차단, AI Agent 보안.
시스템 컨텍스트
- agentgateway 의 한 route 에 LangFlow MCP 가 backend 로 연결되어 있고, 도구 21개가 노출된 상태.
- 그 중 count-characters 라는 데모성 도구 1개를 차단하고 싶다.
- 운영 정책: "기본 deny + 명시적 허용" 원칙. PoC 는 deny rule 로 빠르게 막고, 운영은 allow-list 로 전환 예정.
LLM Client ─ tools/call name="count-characters" ─> agentgateway
│
▼
Cedar Policy 평가
│
┌──────────┴──────────┐
│ allow │ forbid
▼ ▼
backend 으로 forward 403 / forbidden 응답
문제 상황
agentgateway UI 의 MCP Authorization → Add Deny Rule 을 클릭해서 조건을 다음과 같이 줬다.
forbid (
principal,
action,
resource
) when {
mcp.tool.name == "count-characters"
};
Save. 그런데 LangFlow agent 에서 count-characters 가 여전히 호출 가능. agentgateway UI 의 Authorization 로그에도 deny 흔적이 없다.
증상 정리:
- 정책 저장은 됨 (재시작 후에도 정책 목록에 보임)
- 트래픽은 정상 통과 (deny 발동 안 함)
- 로그에 deny 도, allow 도 명시적으로 안 찍힘 → 정책 자체가 평가 대상에서 빠진 느낌
Cedar 정책 평가 모델 짧게 정리
문제를 이해하려면 Cedar 의 평가 흐름을 알아야 한다.
요청 = (principal, action, resource, context)
정책들 = [policy_1, policy_2, ...]
평가:
1. 매칭되는 forbid 가 하나라도 있으면 → deny (forbid 가 최우선)
2. 매칭되는 permit 가 하나라도 있으면 → allow
3. 그 외 → 기본 deny
중요: forbid 가 매칭되려면 when { ... } 안의 모든 attribute 가 요청 context 에서 참조 가능해야 한다. 참조 불가능한 attribute (서버가 안 채워준 항목) 가 하나라도 들어가 있으면 그 정책은 evaluation 자체에서 skip 된다 (Cedar 의 strict mode 동작).
이게 첫 번째 정책이 안 먹힌 진짜 이유였다.
시도한 것들
시도 1 — tool name 만 매칭 (실패)
첫 정책:
forbid (
principal,
action,
resource
) when {
mcp.tool.name == "count-characters"
};
이게 안 먹힌 이유를 좁히려면 agentgateway 가 정책 평가 시 어떤 attribute 를 채워서 넘기는지 알아야 한다. 문서를 보니 attribute 는 server scope 와 tool scope 가 분리되어 있고, scope 매칭이 안 된 attribute 는 미정의로 평가된다. 즉:
- request 가 어떤 server 에 가는 요청인지가 명확하지 않으면 (multiple backend 에 같은 tool name 이 있을 때 etc), mcp.tool.name 만으로는 어떤 도구를 지칭하는지 모호.
- 모호한 attribute 가 있는 정책은 conservative 하게 skip.
agentgateway 의 평가 정책 (보수적 default): "정책이 정확히 매칭되는 경우만 deny". 그래서 deny rule 이 silently 무시된 것.
시도 2 — server 이름까지 같이 매칭 (성공)
server scope 를 명시한 정책:
forbid (
principal,
action,
resource
) when {
mcp.server.name == "my-langflow-mcp" &&
mcp.tool.name == "count-characters"
};
이번에는 deny 발동. agent 호출은 다음 에러로 떨어진다:
MCP error: forbidden (policy denied: tool 'count-characters' on server 'my-langflow-mcp')
agentgateway UI 의 Authorization 로그에도 denied: count-characters 가 명시적으로 찍힌다. 정책이 평가됐다는 신호.
시도 3 — 여러 도구 한 번에 차단
in 연산자로 그룹 매칭.
forbid (
principal,
action,
resource
) when {
mcp.server.name == "my-langflow-mcp" &&
mcp.tool.name in [
"count-characters",
"echo-debug",
"internal-test"
]
};
in 은 list membership 검사. 깔끔하다.
시도 4 — 와일드카드성 매칭 (like)
내부 도구 prefix 가 일관되어 있다면 like 로:
forbid (
principal,
action,
resource
) when {
mcp.server.name == "my-langflow-mcp" &&
mcp.tool.name like "internal-*"
};
like 의 * 는 wildcard. SQL LIKE 의 % 와 동작이 비슷하다. 내부 prefix 규약을 잘 잡아두면 정책이 한 줄로 끝난다.
시도 5 — Allow-list 패턴 (운영 권장)
deny rule 보다 기본 deny + 화이트리스트 가 운영에 더 안전. 신규 도구가 추가될 때 자동으로 막혀서, 의도치 않은 expose 가 안 생긴다.
// 기본 정책: 명시 화이트리스트에 없으면 forbid
forbid (
principal,
action,
resource
) unless {
mcp.server.name == "my-langflow-mcp" &&
mcp.tool.name in [
"search",
"fetch-url",
"rag-query"
]
};
forbid ... unless 는 "조건이 참이 아니면 forbid". 위 정책은 "server 가 my-langflow-mcp 이면서 tool 이 화이트리스트에 있는 경우가 아니면 모두 forbid".
신규 도구를 expose 하려면 화이트리스트에 추가하는 PR 이 필요. 자동 차단의 핵심 이점.
시도 6 — Principal 별 차등 정책
운영자 / 일반 사용자별로 다른 정책이 필요해지면 principal attribute 활용.
// 운영자만 admin tool 호출 가능
forbid (
principal,
action,
resource
) when {
mcp.tool.name like "admin-*" &&
!(principal in Group::"operators")
};
principal 의 group 정보는 agentgateway 가 인증 단계에서 채워준다 (OIDC claim 기반). 어떤 attribute 가 채워져 있는지 정책 디버깅 화면에서 확인 가능.
시도 7 — 정책 syntax 검증 (배포 전)
Cedar 정책은 런타임 평가 전에 syntax 검증을 통과해야 한다. 운영 파이프라인에서는 CI 에 dry-run 단계를 추가.
# Cedar CLI 로 syntax 검증
cedar check-policy --policy ./policies/tenant-a.cedar
# 실제 평가 simulation
cedar authorize \
--policies ./policies/tenant-a.cedar \
--entities ./entities.json \
--principal 'User::"alice"' \
--action 'Action::"call"' \
--resource 'Tool::"count-characters"'
CI 가 syntax 에러 / 평가 simulation 결과를 PR comment 로 남기면, 정책 변경에 따른 의도 차이를 미리 잡을 수 있다.
원인 정리
처음 안 먹힌 이유: scope 매칭 부족. agentgateway 는 정책 평가 시 자기가 routing 한 server/route 컨텍스트와 정책의 attribute 가 일관되게 매칭되어야 적용한다. tool name 만으로는 모호한 경우 보수적으로 skip 한다.
해결: server scope 까지 명시.
해결 — 최종 권장 정책
// === my-langflow-mcp 의 화이트리스트 ===
forbid (
principal,
action,
resource
) unless {
mcp.server.name == "my-langflow-mcp" &&
mcp.tool.name in [
"search",
"fetch-url",
"rag-query"
]
};
추가 운영 절차:
- 신규 도구 expose → 화이트리스트에 추가하는 PR.
- CI 에서 Cedar syntax 검증 + 시뮬레이션 테스트.
- 배포 후 agentgateway 의 Authorization 로그에서 expected / unexpected deny 확인.
- 정기적으로 화이트리스트와 실제 expose 된 도구 목록을 diff (drift detection).
검증 절차
# 1. 차단된 도구 호출 → forbidden 응답
curl -sS -X POST https://gateway.example.com/agentgateway/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{
"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"count-characters","arguments":{"text":"abc"}}
}' | jq .
# 기대 응답:
# {"jsonrpc":"2.0","id":1,"error":{"code":-32603,"message":"forbidden"}}
# 2. 허용된 도구 호출 → 정상 응답
curl -sS -X POST https://gateway.example.com/agentgateway/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{
"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"search","arguments":{"query":"hello"}}
}' | jq .
# 3. agentgateway 로그에서 deny / allow 흔적 확인
docker logs agentgateway 2>&1 | grep -i "authz"
디버깅 체크리스트 (정책이 안 먹힐 때)
- agentgateway 로그에 deny / allow 가 찍히는지 먼저 확인. 안 찍히면 정책 자체가 evaluation 에서 skip 된 상태.
- skip 됐다면 정책의 attribute 가 요청 컨텍스트에 모두 채워져 있는지. server scope 부족이 가장 흔한 원인.
- attribute 이름 오타 (mcp.tool.name 을 mcp.toolname 으로) → 실제로 자주 발생.
- Cedar syntax 자체는 OK 인데 매칭 안 되는 경우 → cedar CLI 로 시뮬레이션.
- forbid ... unless 와 forbid ... when 의 의미 정반대. 한 번씩 꼭 헷갈린다.
배운 점
- MCP 도구 차단은 server scope 까지 명시해야 안전하게 동작한다. tool name 만으로는 모호해서 정책이 silently skip 될 수 있다.
- Deny rule 보다 allow-list 가 운영에 안전. 신규 도구 자동 차단으로 사고 감소. forbid ... unless 패턴이 핵심.
- agentgateway 의 Authorization 로그는 정책이 평가됐는지 자체를 확인하는 1번 도구. deny 흔적이 없으면 룰 자체가 매칭 안 된 것이지, 룰이 약해서 통과한 게 아니다.
- Cedar 의 in, like 연산자로 그룹 매칭이 가능. 운영 정책은 거의 항상 group 으로 쓰게 된다. prefix 규약을 잘 잡아두자 (internal-*, admin-* 등).
- 정책은 CI 에서 syntax + 시뮬레이션 검증. 런타임에 발견하면 늦다.
- forbid ... unless 와 forbid ... when 의 의미가 정반대. 한 번 헷갈리면 보안 사고. 작성 후 시뮬레이션으로 확인 필수.
다음 단계
- route 별로 정책을 분리. 다음 날 다룰 멀티 MCP 라우팅에서는 tenant 별 정책 파일이 갈라진다.
- principal 인증 강화. OIDC claim 기반 group 매핑.
- Audit 로그 별도 sink. Authorization 로그를 SIEM 으로 보내서 deny 패턴 모니터링.
참고
- Cedar policy language: https://www.cedarpolicy.com/
- Cedar 문법 정리: https://docs.cedarpolicy.com/policies/syntax-policy.html
- agentgateway 의 MCP Authorization 문서: https://agentgateway.dev/docs/mcp/authz/
- MCP spec - tool metadata: https://spec.modelcontextprotocol.io/specification/