docs consolidation
This commit is contained in:
@@ -1,11 +1,26 @@
|
||||
# Gateway Tenant Auth & ABAC Contract (Web V)
|
||||
|
||||
## Status
|
||||
- Final v1.0 (2025-12-01); aligns with Policy Guild checkpoint for Sprint 0216.
|
||||
- v1.1 (2025-12-24); updated for identity header hardening (Sprint 8100.0011.0002).
|
||||
- v1.0 (2025-12-01); aligns with Policy Guild checkpoint for Sprint 0216.
|
||||
|
||||
## Header Naming Migration
|
||||
As of Sprint 8100.0011.0002, the Gateway uses canonical `X-StellaOps-*` headers:
|
||||
- `X-StellaOps-Tenant`, `X-StellaOps-Project`, `X-StellaOps-Actor`, `X-StellaOps-Scopes`
|
||||
|
||||
Legacy `X-Stella-*` headers are emitted when `Gateway:Auth:EnableLegacyHeaders=true` (default) for backward compatibility. Clients should migrate to `X-StellaOps-*` headers.
|
||||
|
||||
## Security: Identity Header Hardening (v1.1)
|
||||
**CRITICAL:** As of Sprint 8100.0011.0002, the Gateway enforces a **strip-and-overwrite** policy for identity headers:
|
||||
1. All reserved identity headers (`X-StellaOps-*`, `X-Stella-*`, `sub`, `tid`, `scope`, `scp`, `cnf`) are **stripped** from incoming requests.
|
||||
2. Downstream identity headers are **overwritten** from validated JWT claims—clients cannot spoof identity.
|
||||
3. For anonymous requests, explicit `anonymous` identity is set to prevent ambiguity.
|
||||
|
||||
This replaces the legacy "set-if-missing" behavior which allowed header spoofing.
|
||||
|
||||
## Decisions (2025-12-01)
|
||||
- Proof-of-possession: DPoP is **optional** for Web V. If a `DPoP` header is present the gateway verifies it; interactive clients SHOULD send DPoP, service tokens MAY omit it. A cluster flag `Gateway:Auth:RequireDpopForInteractive` can make DPoP mandatory later without changing the contract.
|
||||
- Scope override header: `X-Stella-Scopes` is accepted only in pre-prod/offline bundles or when `Gateway:Auth:AllowScopeHeader=true`; otherwise the request is rejected with `ERR_SCOPE_HEADER_FORBIDDEN`.
|
||||
- Scope override header: `X-StellaOps-Scopes` (or legacy `X-Stella-Scopes`) is forbidden by default; accepted only when `Gateway:Auth:AllowScopeHeader=true` for offline/pre-prod bundles. Violation returns `ERR_SCOPE_HEADER_FORBIDDEN`.
|
||||
- ABAC overlay: evaluated on every tenant-scoped route after RBAC success; failures are hard denies (no fallback). Attribute sources are frozen for Web V as listed below to keep determism.
|
||||
|
||||
## Scope
|
||||
@@ -16,25 +31,38 @@
|
||||
## Header & Claim Inputs
|
||||
| Name | Required | Notes |
|
||||
| --- | --- | --- |
|
||||
| `Authorization: Bearer <jwt>` | Yes | RS256/ES256; claims: `iss`, `sub`, `aud`, `exp`, `iat`, `nbf`, `jti`, optional `scp` (space-delimited), `ten` (tenant). DPoP proof verified when `DPoP` header present. |
|
||||
| `Authorization: Bearer <jwt>` | Yes | RS256/ES256; claims: `iss`, `sub`, `aud`, `exp`, `iat`, `nbf`, `jti`, optional `scp`/`scope`, `stellaops:tenant`. DPoP proof verified when `DPoP` header present. |
|
||||
| `DPoP` | Cond. | Proof-of-possession JWS for interactive clients; validated against `htm`/`htu` and access token `jti`. Ignored for service tokens when absent. |
|
||||
| `X-Stella-Tenant` | Yes | Tenant slug/UUID; must equal `ten` claim when provided. Missing or mismatch → `ERR_TENANT_MISMATCH` (400). |
|
||||
| `X-Stella-Project` | Cond. | Required for project-scoped routes; otherwise optional. |
|
||||
| `X-Stella-Scopes` | Cond. | Only honored when `Gateway:Auth:AllowScopeHeader=true`; rejected with 403 otherwise. Value is space-delimited scopes. |
|
||||
| `X-Stella-Trace-Id` | Optional | If absent the gateway issues a ULID trace id and propagates downstream. |
|
||||
| `X-StellaOps-Trace-Id` | Optional | If absent the gateway issues a ULID trace id and propagates downstream. Legacy: `X-Stella-Trace-Id`. |
|
||||
| `X-Request-Id` | Optional | Echoed for idempotency diagnostics and response envelopes. |
|
||||
|
||||
### Reserved Identity Headers (Client-Provided Values Ignored)
|
||||
The following headers are **stripped** from client requests and **overwritten** from validated claims:
|
||||
| Header | Source Claim | Notes |
|
||||
| --- | --- | --- |
|
||||
| `X-StellaOps-Tenant` | `stellaops:tenant` (fallback: `tid`) | Tenant identifier from token claims. |
|
||||
| `X-StellaOps-Project` | `stellaops:project` | Project identifier (optional). |
|
||||
| `X-StellaOps-Actor` | `sub` | Subject/actor from token claims. |
|
||||
| `X-StellaOps-Scopes` | `scp` (individual) or `scope` (space-separated) | Scopes from token claims, sorted deterministically. |
|
||||
|
||||
Legacy `X-Stella-*` headers are emitted alongside canonical headers when `EnableLegacyHeaders=true`.
|
||||
|
||||
## Processing Rules
|
||||
1) Validate JWT signature against offline bundle trust roots; `aud` must be one of `stellaops-web` or `stellaops-gateway`; reject on `exp/nbf` drift > 60s.
|
||||
2) Resolve tenant: prefer `X-Stella-Tenant`, otherwise `ten` claim. Any mismatch → `ERR_TENANT_MISMATCH` (400).
|
||||
3) Resolve project: from `X-Stella-Project` when route is project-scoped; otherwise null.
|
||||
4) Build scope set: start from `scp` claim; if `X-Stella-Scopes` is allowed and present, replace the set with its value.
|
||||
5) RBAC: check required scopes per route (matrix below). Missing scope → `ERR_SCOPE_MISMATCH` (403).
|
||||
6) ABAC overlay:
|
||||
1) **Strip reserved headers:** Remove all reserved identity headers from the incoming request (see table above).
|
||||
2) **Validate JWT:** Verify signature against offline bundle trust roots; `aud` must be one of `stellaops-web` or `stellaops-gateway`; reject on `exp/nbf` drift > 60s.
|
||||
3) **Extract identity from claims:**
|
||||
- Tenant: `stellaops:tenant` claim, fallback to `tid` claim.
|
||||
- Project: `stellaops:project` claim (optional).
|
||||
- Actor: `sub` claim.
|
||||
- Scopes: `scp` claims (individual items) or `scope` claim (space-separated).
|
||||
4) **Write downstream headers:** Overwrite `X-StellaOps-*` headers from extracted claims. Legacy `X-Stella-*` headers written when enabled.
|
||||
5) **Anonymous handling:** If unauthenticated and `AllowAnonymous=true`, set explicit anonymous identity (`X-StellaOps-Actor: anonymous`, empty scopes).
|
||||
6) **RBAC:** Check required scopes per route (matrix below). Missing scope → `ERR_SCOPE_MISMATCH` (403).
|
||||
7) **ABAC overlay:**
|
||||
- Attributes: `subject`, `roles`, `org`, `tenant_id`, `project_id`, route vars (e.g., `finding_id`, `policy_id`), and request body keys explicitly listed in the route contract.
|
||||
- Order: RBAC allow → ABAC evaluate → deny overrides → allow.
|
||||
- Fail closed: on evaluation error or missing attributes return `ERR_ABAC_DENY` (403) with `reason` + `trace_id`.
|
||||
7) Determinism: tenant header is mandatory; anonymous/implicit tenants are not allowed. Error codes are stable and surfaced in the response envelope.
|
||||
8) **Determinism:** Identity headers are always overwritten from claims; error codes are stable and surfaced in the response envelope.
|
||||
|
||||
## Route Scope Matrix (Web V)
|
||||
- `/risk/*` → `risk:read` for GET, `risk:write` for POST/PUT; severity events additionally require `notify:emit`.
|
||||
|
||||
Reference in New Issue
Block a user