3.8 KiB
3.8 KiB
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
accessclaim 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 viaRegistryTokenService:Authority:RequiredScopes).
Plan registry (authorization rules)
- The caller's plan is read from
stellaops:planclaim (fallback: configuredDefaultPlan). - Licence revocation uses
stellaops:licenseclaim and configuredRevokedLicenses. - 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). auddefaults to the requested registryservicevalue unlessSigning:Audienceis 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
- Docker/OCI client receives a
401from the registry with aWWW-Authenticate: Bearer realm=...,service=...,scope=repository:...challenge. - Client obtains an Authority token with the
registry.token.issuescope (and any required sender constraints for the deployment). - Client calls
GET /token?service=<service>&scope=repository:<repo>:<actions>on Registry Token Service. - Service validates:
serviceis present (and is allow-listed ifAllowedServicesis configured)- requested scopes parse correctly
- caller plan/licence claims authorize all requested repository actions
- Service returns a JSON response containing the signed registry token.
Denial paths:
400for malformed requests (servicemissing, invalidscopequery).403for authorization failures (plan/licence/policy denies).
Token shape (Docker registry compatible)
The issued JWT includes:
sub: subject derived fromnameidentifier/client_id/subclaimsservice: the requested registry serviceaccess: array of{ type, name, actions[] }entries (type isrepository)- Optional:
stellaops:licensepassthrough 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-listedservicevalues)Plans,DefaultPlan,RevokedLicenses
References
- Operations/runbook:
docs/modules/registry/operations/token-service.md