Files
git.stella-ops.org/docs/modules/registry/architecture.md
2025-12-24 12:38:14 +02:00

87 lines
3.8 KiB
Markdown

# Registry Token Service architecture
## Overview
Registry Token Service issues short-lived Docker registry bearer tokens for private or mirrored registries.
It is designed for offline/self-hosted operation and enforces plan/licence constraints before minting any registry token.
The service is intentionally small:
- One HTTP endpoint: `GET /token`
- Stateless authorization decisions based on (a) Authority-issued identity token claims and (b) local configuration
## Primary responsibilities
- Validate caller identity using Authority-issued tokens (deployment profile may use bearer-only, DPoP, and/or mTLS).
- Authorize requested registry scopes against a configured plan catalogue.
- Deny issuance for revoked licences.
- Mint a Docker-registry-compatible JWT with an `access` claim covering the permitted repository actions.
- Emit deterministic observability signals (metrics + structured logs) for audits and ops.
## Runtime components
**Minimal API host**
- Project: `src/Registry/StellaOps.Registry.TokenService`
- Endpoints:
- `GET /token` (authorized)
- `GET /healthz` (unauthenticated liveness)
**Auth integration**
- Resource server validation is configured from `RegistryTokenService:Authority`.
- Authorization policy name: `registry.token.issue`
- Required scopes default to `registry.token.issue` (configurable via `RegistryTokenService:Authority:RequiredScopes`).
**Plan registry (authorization rules)**
- The caller's plan is read from `stellaops:plan` claim (fallback: configured `DefaultPlan`).
- Licence revocation uses `stellaops:license` claim and configured `RevokedLicenses`.
- Plan rules match repositories by wildcard pattern (`*`) and validate requested actions (`pull`, `push`, etc.) as a subset of allowed actions.
**Token issuer**
- Tokens are signed with an RSA private key loaded from `RegistryTokenService:Signing:KeyPath` (PEM or PFX).
- `aud` defaults to the requested registry `service` value unless `Signing:Audience` is configured.
- Token lifetime is configured and bounded (1s..1h, default 5m).
**Observability**
- OpenTelemetry metrics:
- `registry_token_issued_total{plan=...}`
- `registry_token_rejected_total{reason=...}`
- Structured logs via Serilog request logging.
## Request flow
1. Docker/OCI client receives a `401` from the registry with a `WWW-Authenticate: Bearer realm=...,service=...,scope=repository:...` challenge.
2. Client obtains an Authority token with the `registry.token.issue` scope (and any required sender constraints for the deployment).
3. Client calls `GET /token?service=<service>&scope=repository:<repo>:<actions>` on Registry Token Service.
4. Service validates:
- `service` is present (and is allow-listed if `AllowedServices` is configured)
- requested scopes parse correctly
- caller plan/licence claims authorize all requested repository actions
5. Service returns a JSON response containing the signed registry token.
Denial paths:
- `400` for malformed requests (`service` missing, invalid `scope` query).
- `403` for authorization failures (plan/licence/policy denies).
## Token shape (Docker registry compatible)
The issued JWT includes:
- `sub`: subject derived from `nameidentifier`/`client_id`/`sub` claims
- `service`: the requested registry service
- `access`: array of `{ type, name, actions[] }` entries (type is `repository`)
- Optional: `stellaops:license` passthrough claim (for downstream correlation)
## Configuration
Configuration is loaded from:
- `etc/registry-token.yaml` (optional)
- environment variables prefixed with `REGISTRY_TOKEN_`
Key sections are defined by `RegistryTokenServiceOptions`:
- `Authority` (issuer/metadata, audiences, required scopes)
- `Signing` (issuer, key, lifetime, optional audience/kid)
- `Registry` (realm, allow-listed `service` values)
- `Plans`, `DefaultPlan`, `RevokedLicenses`
## References
- Operations/runbook: `docs/modules/registry/operations/token-service.md`