docs(identity): 0pt workload identity — målbild, design och blockerare #502

Open
admin wants to merge 1 commits from docs/workload-identity-20261107 into main
+78
View File
@@ -0,0 +1,78 @@
# 0pt workload identity — målbild, nuläge och övergång
Status: **design klar, provisionering blockerad** (2026-11-07).
Ägare: CL0UD (identitet) / 0pt (konsument).
## 1. Nuläge
0pt autentiserar maskin-till-maskin med en **delad hemlighet**:
- `X-Internal-Token` mot `INTERNAL_TOKEN` (samma värde i engine och edge).
- Tenant-auktoritet kommer från serverkonfigurationen `INTERNAL_SERVICE_TENANTS`
(se `src/auth/internal-service-tenant.ts`), aldrig från en klientheader.
- Tom allowlist ⇒ ingen tenant-auktoritet ⇒ tenant-scopade skrivningar 403.
Det här är säkert men grovkornigt: en enda hemlighet representerar hela
engine-processen, och varje ny konsument kräver en manuell allowlist-post.
## 2. Målbild (CL0UD workload identity)
CL0UD äger redan maskineriet:
- `POST /account/service-accounts` skapar en service account (identitet).
- `POST /account/service-accounts/{id}/workload-token` mints en **kortlivad**
(10 min default, 1 h max) workload-token vars `capabilities` är en delmängd av
service accountens `platform.cl0ud_service_account_scopes.allowedCapabilities`.
- `@siax/platform-core` `auth/workload.ts` är verifieringsadaptern; publika
nycklar via JWKS.
- `packages/capability-registry/src/workload-identities.ts` deklarerar
arbetsbelastningsnamn och `aud:*`-audiences. Modulen är uttryckligen **inte**
provisionerad och **inte** inkopplad i utfärdande eller verifiering än.
För 0pt innebär det:
| Egenskap | Idag | Mål |
|---|---|---|
| Credential | delad `INTERNAL_TOKEN` | CL0UD-workload-token, `aud:0pt`, 10 min |
| Tenant | serverkonfigurerad allowlist | `tenant_id`-claim i verifierad token |
| Capabilities | route-lista i middleware | `capabilities`-claim (t.ex. `0pt.canonical.write`) |
| Rotation | manuell | automatisk (kortlivad token) |
| Spårbarhet | delad hemlighet | per-arbetsbelastning (`0pt-engine`, `site0-pipeline`) |
## 3. Integrationsdesign (när provisionering är möjlig)
1. **Verifiering före route-logik.** Middleware accepterar
`Authorization: Bearer <workload-token>` för `INTERNAL_SERVICE_ROUTES` om
signaturen verifieras mot CL0UD JWKS, `aud` innehåller `0pt` och `exp` är
giltig. Ingen klientheader får bidra till tenant.
2. **Tenant från claim, inte från allowlist.** `tenant_id` i den verifierade
tokenen sätts som `x-0pt-verified-tenant`; `INTERNAL_SERVICE_TENANTS`
används endast för legacy `X-Internal-Token`.
3. **Capabilities per route.** Route-metadata (`0pt.canonical.write`,
`0pt.quality.run`) jämförs med tokenens `capabilities`. Saknad capability ⇒
403, inte 401 (identiteten är giltig, behörigheten räcker inte).
4. **Dual-accept under övergång.** Båda credentialtyperna accepteras tills
engine och site0-pipeline mintar workload-tokens; därefter tas
`X-Internal-Token` bort i en egen release med negativt test.
5. **Negativa tester i samma svit som idag**
(`tests/auth/internal-token-tenant-scope.test.ts`): fel `aud` ⇒ 401, utgången
token ⇒ 401, tenant utanför claim ⇒ 403, saknad capability ⇒ 403.
## 4. Blockerare
- `/account/service-accounts` svarar **307 → /login** utan operator-session;
ingen giltig `SIAX_TOKEN` finns i Infisical-miljön.
- Zitadel admin-PAT (`/opt/siax-access/zitadel-admin-pat`) är invalid
(AUTH-7fs1e), så nya machine users kan inte mintas via det flödet.
- `workload-identities.ts` är deklaration-only: ingen identitet är
`provisioned: true`.
Åtgärd kräver en operatörsinloggning i CL0UD-konsolen (eller en ny giltig
admin-PAT) och är därför **EXTERNALLY_BLOCKED** från agentsidan.
## 5. Vad som inte ska göras
- Ingen egen JWT-signering i 0pt.
- Ingen parallell identitetsplattform.
- Ingen klientheader som bär tenant.
- Ingen långlivad delad hemlighet som "tillfällig" lösning utan slutdatum.