FED-M3: mTLS handshake + list/get + scope enforcement #462

Open
opened 2026-04-19 22:02:04 +00:00 by jason.woltje · 0 comments
Owner

Epic: Federation v1 — see docs/federation/PRD.md and docs/federation/MILESTONES.md.

Goal

Two federated gateways exchange real data over mTLS with scope intersecting native RBAC. This is the critical trust boundary.

Scope

  • FederationClient (outbound): picks cert from federation_peers, mTLS call
  • FederationServer (inbound): NestJS guard validates client cert, extracts grantId + subjectUserId, loads grant
  • Scope enforcement pipeline:
    1. Resource allowlist / excluded-list check
    2. Native RBAC evaluation as the subjectUserId
    3. Scope filter intersection (include_teams, include_personal)
    4. max_rows_per_query cap
  • Verbs: list, get, capabilities
  • Gateway query layer: source: "local" | "federated:<host>" | "all"; fan-out + merge for "all"
  • Federation E2E harness (tools/federation-harness/): docker-compose.two-gateways.yml, seed script, assertion helpers

Deliverables

  • apps/gateway/src/federation/client/federation-client.service.ts
  • apps/gateway/src/federation/server/federation-auth.guard.ts
  • apps/gateway/src/federation/server/scope.service.ts
  • apps/gateway/src/federation/server/verbs/{list,get,capabilities}.controller.ts
  • apps/gateway/src/federation/client/query-source.service.ts (fan-out/merge)
  • tools/federation-harness/ (compose + seed + test helpers)
  • packages/typesfederation.dto.ts

Acceptance Tests

  • A→B list tasks returns subjectUser's tasks intersected with scope
  • A→B list tasks with include_teams: [T1] excludes T2 tasks the user owns
  • A→B get credential <id> returns 403 when credentials in excluded_resources
  • Client with cert for grant X cannot query subjectUser of grant Y (cross-user isolation)
  • Cert signed by untrusted CA rejected at TLS layer (no NestJS handler reached)
  • Malformed SAN OIDs → 401; cert valid but grant revoked in DB → 403
  • max_rows_per_query caps response; further results paginated
  • source: "all" fan-out merges local + federated results, each tagged with _source
  • Federation responses never persist: DB row count unchanged after list round-trip
  • Scope cannot grant more than native RBAC: user without access to team T still gets [] even if scope allows T

Dependencies

Blocked by FED-M2.

Estimated budget

~40K tokens — largest milestone, most complex logic

Risk notes

Critical trust boundary. Code review must focus on scope-enforcement bypass and cert-SAN-spoofing paths. Every 403/401 path needs a test.

**Epic:** Federation v1 — see `docs/federation/PRD.md` and `docs/federation/MILESTONES.md`. ## Goal Two federated gateways exchange real data over mTLS with scope intersecting native RBAC. This is the critical trust boundary. ## Scope - `FederationClient` (outbound): picks cert from `federation_peers`, mTLS call - `FederationServer` (inbound): NestJS guard validates client cert, extracts `grantId` + `subjectUserId`, loads grant - Scope enforcement pipeline: 1. Resource allowlist / excluded-list check 2. Native RBAC evaluation as the `subjectUserId` 3. Scope filter intersection (`include_teams`, `include_personal`) 4. `max_rows_per_query` cap - Verbs: `list`, `get`, `capabilities` - Gateway query layer: `source: "local" | "federated:<host>" | "all"`; fan-out + merge for `"all"` - **Federation E2E harness** (`tools/federation-harness/`): docker-compose.two-gateways.yml, seed script, assertion helpers ## Deliverables - `apps/gateway/src/federation/client/federation-client.service.ts` - `apps/gateway/src/federation/server/federation-auth.guard.ts` - `apps/gateway/src/federation/server/scope.service.ts` - `apps/gateway/src/federation/server/verbs/{list,get,capabilities}.controller.ts` - `apps/gateway/src/federation/client/query-source.service.ts` (fan-out/merge) - `tools/federation-harness/` (compose + seed + test helpers) - `packages/types` — `federation.dto.ts` ## Acceptance Tests - [ ] A→B `list tasks` returns subjectUser's tasks intersected with scope - [ ] A→B `list tasks` with `include_teams: [T1]` excludes T2 tasks the user owns - [ ] A→B `get credential <id>` returns 403 when `credentials` in `excluded_resources` - [ ] Client with cert for grant X cannot query subjectUser of grant Y (cross-user isolation) - [ ] Cert signed by untrusted CA rejected at TLS layer (no NestJS handler reached) - [ ] Malformed SAN OIDs → 401; cert valid but grant revoked in DB → 403 - [ ] `max_rows_per_query` caps response; further results paginated - [ ] `source: "all"` fan-out merges local + federated results, each tagged with `_source` - [ ] Federation responses never persist: DB row count unchanged after `list` round-trip - [ ] Scope cannot grant more than native RBAC: user without access to team T still gets [] even if scope allows T ## Dependencies Blocked by **FED-M2**. ## Estimated budget ~40K tokens — largest milestone, most complex logic ## Risk notes Critical trust boundary. Code review must focus on scope-enforcement bypass and cert-SAN-spoofing paths. Every 403/401 path needs a test.
jason.woltje added this to the Federation v1 milestone 2026-04-19 22:02:04 +00:00
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#462