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

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 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