Commit a8734e1d authored by Sergio Gimenez's avatar Sergio Gimenez Committed by Dimitrios Gogos
Browse files

docs: align FM implementation-and-delivery EWBI auth to ADR-0038

Section P.3 rewritten from mTLS to OAuth2 Client-Credentials: inbound
JWKS/JWT validation (fed-mgmt scope, Keycloak federation realm) replaces the
cert-thumbprint gate; outbound token acquisition + cache per partner via httpx.
mTLS moves to a new deferred P.3.1 (sender-constrained tokens, RFC 8705) once
Nginx performs real mTLS termination. Boundary and threat tables updated.
parent 3a04fcbb
Loading
Loading
Loading
Loading
+36 −9
Changes for fm/implementation-and-delivery.md: 36 added lines, 9 removed lines.
Original line number Diff line number Diff line
@@ -248,7 +248,7 @@ class RoutingResolver:

| Module | FM Usage |
|---|---|
| `auth/` | `require_role("operator")` on internal admin routes. EWBI routes use mTLS instead of JWT. |
| `auth/` | `require_role("operator")` on internal admin routes. EWBI routes use OAuth2 Client-Credentials + JWT against the `federation` Keycloak realm (ADR-0038), not mTLS. |
| `db/` | SQLAlchemy async session management for `fm_db`. PostgreSQL connection pooling. |
| `events/` | DataBus publish/subscribe helpers. `CommandEnvelopeV1` and per-subject payload schemas for `command.srm.*` publishing. `SrmOperationCompletedV1` / `SrmOperationStatusV1` for consumption. |
| `health/` | `/healthz` and `/readyz` endpoint registration. Readiness checks PostgreSQL, DataBus. |
@@ -256,16 +256,43 @@ class RoutingResolver:
| `models/` | Shared domain models. |
| `testing/` | Test fixtures — fake tokens, mock DataBus, shared helpers. |

### P.3 mTLS Implementation
### P.3 EWBI Authorization Implementation (ADR-0038)

FM extracts the inbound client certificate in FastAPI middleware (from the Nginx-forwarded header) and uses `httpx` for outbound mTLS:
FM authorizes E/WBI requests with OAuth2 Client-Credentials + signed JWT, against the platform's centralized Keycloak (`federation` realm). FastAPI middleware validates inbound Bearer tokens; `httpx` fetches and caches outbound tokens per partner.

**Inbound (middleware):** FastAPI middleware extracts the client certificate from the TLS connection. In production, Nginx performs mTLS termination and forwards the client certificate in a header (`X-SSL-Client-Cert` or `X-Client-Certificate-Thumbprint`). FM middleware extracts and validates the thumbprint against `partner_ops`.
**Inbound (middleware):** FastAPI middleware reads the `Authorization: Bearer` header, validates the JWT signature against Keycloak's JWKS endpoint (cached, refreshed on `kid` miss), and checks issuer, expiry, and the `fed-mgmt` scope. The validated token's client identity resolves to a partner via `partner_ops`.

**Outbound:** FM creates `httpx.AsyncClient` instances with client certificate and key configured per partner OP:
**Outbound:** FM requests a token from the partner's Keycloak realm using our registered client credentials for that partner, caches it until near-expiry, and attaches it as a Bearer header:

```python
async def create_partner_client(partner: PartnerOP) -> httpx.AsyncClient:
async def get_partner_token(partner: PartnerOP, http: httpx.AsyncClient) -> str:
    cached = _token_cache.get(partner.id)
    if cached and not cached.is_near_expiry():
        return cached.access_token
    response = await http.post(
        partner.oauth2_token_url,
        data={
            "grant_type": "client_credentials",
            "client_id": partner.our_client_id,
            "client_secret": partner.our_client_secret,
            "scope": "fed-mgmt",
        },
    )
    token = TokenCacheEntry.from_response(response.json())
    _token_cache[partner.id] = token
    return token.access_token
```

### P.3.1 Future: mTLS Defense-in-Depth (deferred, ADR-0038)

Not built yet, and not required for a request to be authorized today. Once Nginx performs real mTLS termination (ADR-0016's original assumption), this layers back in as sender-constrained tokens (RFC 8705), binding a partner's JWT to their TLS client certificate:

**Inbound (middleware, future):** FastAPI middleware extracts the client certificate from the TLS connection. Nginx performs mTLS termination and forwards the client certificate in a header (`X-SSL-Client-Cert` or `X-Client-Certificate-Thumbprint`). FM middleware validates the thumbprint against `partner_ops.cert_thumbprint` (column retained for this purpose; not part of the authorization decision until this lands).

**Outbound (future):** FM creates `httpx.AsyncClient` instances with client certificate and key configured per partner OP:

```python
async def create_partner_mtls_client(partner: PartnerOP) -> httpx.AsyncClient:
    return httpx.AsyncClient(
        base_url=partner.base_url,
        cert=(partner.our_client_cert_path, partner.our_client_key_path),
@@ -282,10 +309,10 @@ async def create_partner_client(partner: PartnerOP) -> httpx.AsyncClient:

| Boundary | Mechanism | Owner |
|---|---|---|
| Partner OP → Nginx → FM (EWBI) | mTLS (mutual TLS) with pre-exchanged certificates | FM |
| Partner OP → Nginx → FM (EWBI) | OAuth 2.0 Client-Credentials + signed JWT (Keycloak realm `federation`, `fed-mgmt` scope) — ADR-0038. mTLS deferred defense-in-depth, not required today. | FM |
| Operator → FM (internal admin) | OAuth 2.0 Bearer JWT (Keycloak realm `oop`, `operator` role) | FM via oop-commons |
| FM → SRM (internal HTTP) | Cluster network trust initially; service JWT when available | Platform deployment |
| FM → Partner OP (outbound EWBI) | mTLS with our client certificate | FM |
| FM → Partner OP (outbound EWBI) | OAuth 2.0 Client-Credentials — FM as client, per-partner credentials (ADR-0038). mTLS with our client certificate deferred. | FM |

### Q.2 Data Protection

@@ -315,7 +342,7 @@ async def create_partner_client(partner: PartnerOP) -> httpx.AsyncClient:

| Threat | Mitigation |
|---|---|
| Unauthorized partner impersonation | mTLS with certificate thumbprint validation |
| Unauthorized partner impersonation | OAuth2 Client-Credentials + signed JWT validation (`fed-mgmt` scope, Keycloak `federation` realm); mTLS certificate thumbprint validation layers in later as sender-binding (ADR-0038) |
| DDoS from partner or internet | Nginx IP whitelisting + per-partner rate limiting |
| Topology leakage | Mandatory topology-hiding middleware (applying the `TopologyHider` rules) + automated security tests |
| Internal header injection via EWBI | FM strips all `X-Internal-*` headers from inbound requests before processing |