5cfb8a1이 .gitignore에 docs/를 넣고 68개 파일을 지웠다. 그런데
AgentBuilderContractExampleTest, ToolBundleContractExampleTest,
ArchitectureDocumentContractTest는 docs/ 아래 계약 예제와 architecture
문서를 입력으로 직접 읽는다. 그 결과 clean clone에서 테스트 10건이
입력을 찾지 못해 실패했다.
제외 범위를 원래 의도대로 좁힌다. 에이전트 산출물(AGENTS.md, .agents/,
.codex/, docs/superpowers/)은 계속 제외하고 저장소 문서는 추적한다.
문서는 삭제 직전 상태(3de052a)를 기준으로 복원하고, 그 위에 main 코드와
대조해 어긋난 부분을 고쳤다.
- ADR-0007을 Superseded로 바꾸고 ADR-0013을 새로 쓴다. route당 Tool
Service N개가 최종안이며, PortalToolRegistryClient가 이미 route별로
N개를 유지하고 있는데 ADR-0007은 "bundles는 항상 한 항목"을 Accepted
상태로 주장하고 있었다. ADR-0009 결정 4도 부분 대체한다.
- ADR-0008 파일 헤더가 Accepted였으나 ADR-0009가 이미 대체한 상태였다.
- 6078852의 endpoint 소유권 반전이 반영되지 않은 서술을 계약 v0.2,
bundle 설정 예제, Tool 적재 안내에서 고친다.
- Portal registry 계약 v0.1과 route key 규약을 새로 문서화한다. 둘 다
구현은 있는데 계약 문서가 없었다.
- MCP SDK 2.0.0 SBOM을 추가한다.
번호 주석: ADR-0011과 0012는 feature/mcp-integration이 Tool inputSchema
정책에 쓰고 있어 비워 둔다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5.2 KiB
ADR-0006 MCP Server는 인증·인가를 하지 않는다
이 ADR은 inbound 신뢰 경계를 다룬다. outbound 목적지는 ADR-0010 이후 Tool Service 매니페스트가 정하며, 도메인 허용목록도 egress NetworkPolicy도 없다. 아래 "책임 소재" 표의 NetworkPolicy 항목은 ingress 통제이므로 outbound 위험을 덮지 않는다.
배경
Tool 실행 권한은 Agent Builder가 Agent를 구성할 때 이미 확인하고 넘어온다. MCP Server는 Agent Builder가 지정한 Tool을 검증·실행하는 계층이므로 권한을 다시 판단할 근거가 없다.
문제는 MCP가 판단하지 않는 계층인 동시에 집행 지점이라는 데 있다. MCP는 이 배포에 속한 모든 Tool Service에 도달할 수 있는 유일한 경로다. 따라서 "MCP는 아무것도 하지 않는다"는 결정은, 그러면 누가 하는가를 함께 적지 않으면 세 계층이 서로 상대가 확인했다고 가정하는 공백을 만든다.
이 문서는 그 공백을 막기 위해 책임 소재를 명시한다.
결정
MCP Server는 인증(authentication)과 인가(authorization)를 수행하지 않는다.
- 요청자의 신원을 검증하지 않는다.
- Tool 실행 권한을 판단하지 않는다.
employee-no·virtual-employee-no를 복호화·검증·저장하지 않는다.- Authorization 헤더를 해석하지 않는다.
mcp.tool-client.forward-authorization이 켜져 있으면 값을 그대로 전달만 한다.
이에 따라 무검증 JWT decode 구현(JwtClaimExtractor, UnverifiedJwtClaimExtractor)을 삭제한다.
검증하지 않는 인증 코드는 없는 것보다 나쁘다. 이후 누군가 그 claim을 판단 근거로 쓸 여지를 남기고,
코드베이스에 인증이 처리되고 있다는 잘못된 인상을 준다.
책임 소재
| 책임 | 주체 | 근거 |
|---|---|---|
| 호출자가 Agent Builder인지 보장 | 플랫폼(NetworkPolicy) | Helm Chart의 templates/networkpolicy.yaml. 환경별 허용 namespace는 values-{env}.yaml의 global.agentBuilderNamespace |
| 사용자 인증, Tool 실행 권한 판단 | Agent Builder | Agent 구성 시점에 확인 |
| 사원 식별자 복호화와 업무 권한 집행 | Tool Service | KMS에서 발급받은 키 사용 |
| 요청 형식·schema 검증, 단일 Tool 실행 | MCP Server | ADR-0001 |
이 결정이 성립하기 위한 전제
전제 1 — 네트워크가 호출자를 고정한다.
MCP는 요청자를 확인하지 않으므로, /mcp에 도달할 수 있다는 것 자체가 곧 인가다.
NetworkPolicy로 Agent Builder namespace만 8080에 접근하도록 제한한다.
이 정책 없이 배포하면 클러스터 안의 어떤 Pod이든 Tool을 실행할 수 있다.
정책은 선택적 강화가 아니라 이 ADR의 성립 조건이다.
그래서 Helm Chart에 비활성화 스위치를 두지 않았고, HelmDeploymentContractTest가
조건부 렌더링이 들어오는 것까지 막는다. values 한 줄로 인가가 사라지지 않게 하기 위해서다.
전제 2 — 사원 식별자의 신뢰는 Tool Service가 확보한다.
employee-no·virtual-employee-no는 암호화되어 전달되고, 복호화 키는 사내 KMS에서 발급받는다.
MCP는 키를 갖지 않으므로 값을 읽을 수 없고, 따라서 위조 여부도 판별할 수 없다.
Tool 파트가 확인할 항목:
- 복호화는 KMS 키로만 가능하므로 기밀성은 확보된다. 위조 방지는 암호화 키의 배포 범위에 달려 있다. 암호화 키를 널리 배포하면 임의의 사원번호를 스스로 암호화해 넣을 수 있으므로, 키 배포 범위를 신뢰 경계와 맞춘다.
- 동일한 암호문의 재사용(replay)을 허용할지, 만료·nonce를 둘지 결정한다.
- 두 헤더가 모두 없는 요청의 처리 방침을 정한다. 현재 계약에서 두 값은 선택값이다.
전제 3 — 감사 추적은 세 계층의 로그를 합쳐야 완성된다.
MCP 로그에는 guid와 x-request-id만 남는다.
사원 식별자는 개인 식별자이므로 암호문이라도 기록하지 않는다.
따라서 "누가 조회했는가"는 MCP 로그만으로 답할 수 없다.
guid를 Agent Builder·MCP·Tool Service가 공통 상관 키로 사용해 세 로그를 연결한다.
규제 감사 요건이 확정되면 별도 durable sink를 설계한다.
영향
- MCP에 인증 코드를 추가하지 않는다. 필요가 생기면 이 ADR을 대체하는 새 ADR을 먼저 쓴다.
- NetworkPolicy는 배포 필수 구성요소다. 누락은 설정 실수가 아니라 보안 결함으로 다룬다.
- 인증 모델이 token 기반으로 바뀌면 이 결정과 전제 1을 함께 재검토한다.
forward-authorization설정은 유지한다. 검증이 아니라 통과 전달이며, Tool Service가 자체 인증을 도입할 때 필요한 연결점이다.