mirror of
https://github.com/mcloh/Enterprise-MCP-Service-Bus.git
synced 2026-11-05 22:14:11 +00:00
M0 e M1 revisados e fixes implementados
This commit is contained in:
@@ -1509,8 +1509,8 @@ backlog:
|
||||
| Marco | Tarefas | Critério de "pronto" |
|
||||
|---|---|---|
|
||||
| **M0 — Walking skeleton** | EP-00-T01, EP-00-T02, EP-00-T04, EP-06-T02, EP-05-T01, EP-08-T01 | Cliente MCP → Gateway → (roteamento estático) → MCP Server de exemplo (Sales) → resposta, com `trace_id` correlacionado visível em log/coletor OTel. Sem enforcement real ainda. |
|
||||
| **M1 — Segurança núcleo (Fase 1 do README)** | EP-01 completo, EP-03 completo, EP-04 completo, EP-05-T02/T03/T04/T06/T07/T08, EP-02-T01/T02/T03/T05, EP-06-T01/T03/T04, EP-08-T02/T03/T04 | `tools/list` filtrado por entitlement real (Keycloak+OPA), `tools/call` reautorizado, cache privado, fail-closed, anti-bypass, auditoria completa. Cobre RF-01 a RF-09 e Fases 0–2 do roadmap do README. |
|
||||
| **M2 — Governança e downstream** | EP-02-T04, EP-04-T04, EP-07 completo, EP-14 completo | Publishing pipeline com lifecycle completo, outbound identity sem passthrough, aprovação humana funcionando fail-closed. Cobre RF-10, RF-11, Fase 3 parcial. |
|
||||
| **M1 — Segurança núcleo (Fase 1 do README) ✅ concluído (2026-09-11)** | EP-01 completo, EP-03 completo, EP-04 completo, EP-05-T02/T03/T04/T06/T07/T08, EP-02-T01/T02/T03/T05 (via M2), EP-06-T01/T03/T04, EP-08-T02/T03/T04 | `tools/list` filtrado por entitlement real (Keycloak+OPA), `tools/call` reautorizado, cache privado, fail-closed, anti-bypass, auditoria completa. Cobre RF-01 a RF-09 e Fases 0–2 do roadmap do README. **Adendo (2026-09-11, fechamento retroativo pós-M2)**: todos os itens acima que ainda restavam abertos após o M2 (EP-01-T04/T05, EP-05-T03/T07/T08, EP-06-T01/T03/T04, EP-08-T02/T03/T04) foram implementados nesta rodada — Fabric router real (multi-backend, EP-06-T01), segundo domínio Finance com backend real (EP-06-T03), adapter REST genérico standalone (EP-06-T04), taxonomia de auditoria IC/NOC/GRL + correlação decisionId/policyDecisionId/entitlementVersion (EP-08-T02/T03), stack local otel-collector+Jaeger (EP-08-T04), cache hint SEP-2549 (EP-05-T03 — ver limitação de wire-mode abaixo), segmentação de rede + gate de credencial nos domain servers (EP-05-T07), validação header/body Mcp-Method/Mcp-Name (EP-05-T08), lint de shared client (EP-01-T05), spike de mTLS documentado em `docs/adr/ADR-019-identity-extension.md` (EP-01-T04). **Limitação real descoberta e documentada**: SEP-2549 (`ttlMs`) é vocabulário exclusivo do protocolo 2026-07-28, que só existe no modo *stateless per-request* do SDK (`server/discover`, sem handshake `initialize`) — esta RI usa o handshake de sessão clássico em toda parte, então o campo nunca chega ao wire nesta arquitetura (o SDK descarta o campo na serialização para versões de protocolo anteriores); `cache_scope="private"` e o não-caching server-side já garantem a propriedade de segurança do Teste 5 independentemente disso. Ver `src/emcp_bus/gateway/cache.py`. **EP-00-T03 (pipeline de CI)**, apesar de não listado nesta linha, também foi implementado (`.github/workflows/ci.yml`). |
|
||||
| **M2 — Governança e downstream ✅ concluído (2026-09-11)** | EP-02 completo (T01-T05, incluindo T02/T03/T05 adiados do M1), EP-04-T04, EP-07 completo, EP-14 completo | Publishing pipeline com lifecycle completo, outbound identity sem passthrough, aprovação humana funcionando fail-closed — todos ligados ao Gateway real (EP-05) e testados com OPA real. Cobre RF-10, RF-11, Fase 3 parcial. Detalhes e limitações em `RI/README.md`. **Adendo (2026-09-11, mesmo dia — Docker e Java passaram a estar disponíveis no ambiente)**: `docker-compose.yml` validado com daemon Docker real (build/up/fail-closed/trace cross-container), e **EP-01-T01 (Keycloak) implementado e validado com Keycloak 26.7.3 real** (`deploy/keycloak/realm-export.json`, perfil `identity` no compose, `tests/e2e/test_real_keycloak.py`) — item que a linha do M1 acima já citava como pré-requisito ("Keycloak+OPA") mas que só foi de fato implementado/testado agora. |
|
||||
| **M3 — Personalização e orquestração (Fase 4 do README)** | EP-09, EP-10, EP-11, EP-12, EP-13-T01..T04 | Perfil→entitlement→ofertas filtradas→NBA→dispatch→execução MCP funcionando ponta a ponta com LangGraph no Orchestrator e Langfuse no agente de exemplo. Cobre RF-12 a RF-17. |
|
||||
| **M4 — Hardening e entrega** | EP-13-T05, EP-13-T06 (could), EP-15 completo, EP-16 completo | Suíte de 10 testes de aceite (§45) e cenários adversariais (§46, incluindo o de EP-15-T03/ADR-023) passando contra a stack completa; **os dois modos de integração do ADR-024 demonstrados** — Modo A (EP-13-T05: Global Supervisor externo, dois backends/MCP Clients, handoff sem elevação de privilégio) e, se o tempo permitir, Modo B (EP-13-T06: nosso Service Orchestrator como único cérebro de NBA/NBO, mesma stack de segurança); ADRs documentados; README da RI publicado. |
|
||||
|
||||
|
||||
101
docs/adr/ADR-019-identity-extension.md
Normal file
101
docs/adr/ADR-019-identity-extension.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# ADR-019: MCP Client identity -- production extension point (mTLS / workload identity)
|
||||
|
||||
**Status:** Documented (spike), not implemented in the RI (EP-01-T04, `could`).
|
||||
**Related:** `docs/RI-PLANNING.md` ADR-019 entry (confirmed decision, 2026-09-11), README.md §19, RF-02, Axiom 2.
|
||||
|
||||
## Context
|
||||
|
||||
The RI's identity boundary (`src/emcp_bus/identity/authn.py`, EP-01-T03) is
|
||||
OAuth 2.1 `client_credentials` against Keycloak (EP-01-T01): every MCP Client
|
||||
authenticates with a client_id + secret, gets a short-lived JWT, and the
|
||||
Gateway validates that JWT's signature/issuer/audience/expiry against the
|
||||
IdP's JWKS before trusting the `azp` claim as the caller's identity.
|
||||
|
||||
This is a deliberate, confirmed decision for the RI (not a placeholder): OAuth
|
||||
2.1 satisfies every property README.md §19 actually requires of an identity
|
||||
mechanism -- short-lived, revocable, audience-bound, verifiable without a
|
||||
shared long-term secret in the request path. Real enterprise deployments
|
||||
commonly go further, using mTLS (mutual TLS client certificates) or a cloud
|
||||
provider's workload identity federation (e.g. SPIFFE/SPIRE, AWS IAM roles for
|
||||
service accounts, GCP Workload Identity, Azure Managed Identity) instead of --
|
||||
or in addition to -- a bearer JWT. This ADR documents how that extension
|
||||
plugs into the RI's existing seam, without requiring one, so a production
|
||||
fork isn't blocked on a redesign.
|
||||
|
||||
## Decision
|
||||
|
||||
Do **not** implement mTLS/workload identity in the RI. Document the
|
||||
extension point instead, so `EP-01-T03`'s contract is provably stable under
|
||||
it.
|
||||
|
||||
## The extension point
|
||||
|
||||
`TokenValidator.validate(token: str) -> AuthenticatedClient` is the *only*
|
||||
function in the RI that produces client identity for authorization purposes
|
||||
(`emcp_bus.gateway.server.on_list_tools`/`on_call_tool` both call
|
||||
`get_access_token()`, which is populated by the SDK's `TokenVerifier`
|
||||
protocol wrapping this same validator -- see
|
||||
`emcp_bus.identity.token_verifier.MCPTokenVerifier`). Everything downstream
|
||||
of it -- `EntitlementManager.resolve`, the PDP, the audit/correlation layer
|
||||
(EP-08) -- consumes only the resulting `AuthenticatedClient.client_id`. None
|
||||
of that code cares *how* the identity was established.
|
||||
|
||||
A production deployment adding mTLS or workload identity would:
|
||||
|
||||
1. Implement a second identity source honoring the same output contract:
|
||||
|
||||
```python
|
||||
class MTLSClientValidator:
|
||||
def validate(self, request: ...) -> AuthenticatedClient: ...
|
||||
```
|
||||
|
||||
fed from the terminating proxy/load balancer's verified client
|
||||
certificate (a `X-Client-Cert-CN` style header from a trusted mTLS
|
||||
terminator, or the ASGI `scope["extensions"]["tls"]` a compliant server
|
||||
exposes) instead of a JWT. `AuthenticatedClient.client_id` would be the
|
||||
certificate's Subject/SAN, mapped 1:1 to a `ClientRegistration.client_id`
|
||||
exactly the way a JWT's `azp` is today -- **no other model in the RI
|
||||
changes**: `ClientRegistration`, `ClientProfile`, `EntitlementManager`,
|
||||
the PDP contract, and the audit schema are all already identity-mechanism
|
||||
agnostic.
|
||||
|
||||
2. Provide an alternate `build_auth()`-equivalent in
|
||||
`emcp_bus/gateway/server.py` that wires the new validator in instead of
|
||||
`MCPTokenVerifier`, gated by deployment config -- the same shape as
|
||||
today's `build_auth()` returning `(None, None)` for the "no OIDC
|
||||
configured" mode. A hybrid deployment (mTLS for one class of backend
|
||||
service, OAuth for interactive agents) is possible by making the
|
||||
`TokenVerifier`/equivalent selection per-listener rather than
|
||||
per-process, which the SDK's `streamable_http_app(auth=..., token_verifier=...)`
|
||||
parameters already support per app instance.
|
||||
|
||||
3. Cloud workload identity (SPIFFE/SPIRE, IRSA, Workload Identity, Managed
|
||||
Identity) is a variant of the same shape: the "verify a credential the
|
||||
caller cannot forge, and extract a stable identifier from it" contract is
|
||||
identical, only the credential format and verification library differ.
|
||||
None of it requires touching `EntitlementManager`, the PDP, the Fabric
|
||||
router (EP-06-T01), or the audit/correlation layer (EP-08-T02/T03).
|
||||
|
||||
## What must not change
|
||||
|
||||
- **`AuthenticatedClient` shape** (`client_id`, `issuer`, `expires_at`):
|
||||
every downstream consumer types against this, not against a JWT-specific
|
||||
claim set.
|
||||
- **Fail-closed contract**: any identity mechanism's failure path must raise
|
||||
(an `AuthenticationError`-shaped exception) rather than fall back to an
|
||||
unauthenticated or agent-declared identity (README.md §5, Axiom 2) -- this
|
||||
is the actual security property EP-01-T03's acceptance criteria test, and
|
||||
it is mechanism-independent.
|
||||
- **No new privileged path for `clientInfo`**: whatever the identity
|
||||
mechanism, only a value verified independently of the MCP request body
|
||||
may ever populate `client_id`.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Confirmed for the RI: reduces EP-01's implementation effort without
|
||||
weakening any of README.md §19's desirable identity properties (all are
|
||||
satisfied by OAuth 2.1 `client_credentials` against Keycloak).
|
||||
- A production fork adding mTLS/workload identity changes exactly one
|
||||
module (`identity/`) and one wiring function (`build_auth`-equivalent);
|
||||
every other component in the RI (Entitlement Manager, PDP, Fabric,
|
||||
Registry, audit/correlation, approval) requires zero changes.
|
||||
Reference in New Issue
Block a user