oidc

package
v0.8.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package oidc mints short-lived, signed OIDC tokens for pipeline jobs that opt in with `oidc: {audience: ...}`, and publishes the matching JWKS so AWS IAM, GCP workload identity federation, and Vault's JWT auth method can verify them without a long-lived credential ever touching a job. Self-contained: it never imports internal/secrets or internal/pipeline, only the narrow KeyStore interface a caller satisfies (internal/secrets.Manager does, structurally), the same consumer-defined-boundary shape the rest of this codebase uses for DockerPinger and friends.

The signing key is ECDSA P-256 (ES256), generated once and persisted through the caller's KeyStore, deliberately not the envelope encryption master key (internal/secrets): that key is an age Hybrid key meant for wrapping data encryption keys, not for producing a signature an external verifier can check against a published public key. This is a dedicated key for this one purpose.

Index

Constants

View Source
const DefaultKeyRetireGrace = 24 * time.Hour

DefaultKeyRetireGrace is how long a rotated-out key stays published in the JWKS when RotateKey's retireAfter is zero: long enough that every token minted under it (DefaultTTL-lived) expires on its own, and that a verifier caching the JWKS document (AWS, GCP, and Vault all do, for hours) has re-fetched it at least once before the key disappears.

View Source
const DefaultTTL = 10 * time.Minute

DefaultTTL is how long a minted token is valid for when Config.TTL is unset: short enough that a leaked token (a job's own logs, however unlikely given masking) is useless soon after the job finishes.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	KeyStore KeyStore
	// IssuerURL is the "iss" claim every minted token carries, and the
	// URL a verifier (AWS, GCP, Vault) is told to fetch this instance's
	// JWKS from. Must be a real, reachable HTTPS URL for federation to
	// actually work; Manager itself does not validate that, since it has
	// no way to confirm reachability from inside the process that mints
	// the tokens.
	IssuerURL string
	TTL       time.Duration
	// KeyRetireGrace is the default RotateKey uses when its own
	// retireAfter is zero. DefaultKeyRetireGrace when this is also zero.
	KeyRetireGrace time.Duration
	Now            func() time.Time
}

Config configures a Manager.

type JWK

type JWK struct {
	Kty string `json:"kty"`
	Crv string `json:"crv"`
	X   string `json:"x"`
	Y   string `json:"y"`
	Kid string `json:"kid"`
	Use string `json:"use"`
	Alg string `json:"alg"`
}

JWK is one entry in a JWKS document: the public half of an EC signing key, RFC 7518 §6.2's field names.

type JWKS

type JWKS struct {
	Keys []JWK `json:"keys"`
}

JWKS is a JSON Web Key Set: RFC 7517's document shape.

type Key

type Key struct {
	// contains filtered or unexported fields
}

Key is one ES256 signing key, identified by KID.

type KeyStore

type KeyStore interface {
	Exists(ctx context.Context, serviceName, envKey string) (bool, error)
	SetValue(ctx context.Context, serviceName, envKey, plaintext string) error
	Resolve(ctx context.Context, serviceName, envKey string) (string, error)
}

KeyStore is the narrow surface Manager needs to persist the signing key: internal/secrets.Manager satisfies this structurally, the same consumer-defined-interface shape internal/api.GitHubAppSecrets already establishes for that package's own private key.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager mints OIDC tokens and serves the matching JWKS, backed by one active signing key plus zero or more retiring keys, all cached in memory for the life of the process and persisted through KeyStore.

func NewManager

func NewManager(cfg Config) (*Manager, error)

NewManager builds a Manager. cfg.KeyStore and cfg.IssuerURL are required; NewManager returns an error rather than a Manager that fails on first use, so a misconfiguration is caught at startup.

func (*Manager) IssueToken

func (m *Manager) IssueToken(ctx context.Context, req TokenRequest) (string, error)

IssueToken mints a short-lived OIDC token for req, signed with ES256 using the current active key.

func (*Manager) JWKS

func (m *Manager) JWKS(ctx context.Context) (JWKS, error)

JWKS returns the active key's public JWK plus every retiring key whose grace period has not yet passed, so a token minted moments before a rotation still verifies.

func (*Manager) RemoveRetiringKey

func (m *Manager) RemoveRetiringKey(ctx context.Context, kid string) error

RemoveRetiringKey immediately drops a retiring key from the JWKS before its grace period would otherwise elapse, for an operator who knows a specific key is compromised and cannot wait out the normal propagation window. Not wired to a CLI or UI control yet: RotateKey's automatic, time-based removal is the path every rotation actually needs, and an emergency override is a deliberate follow-up, not part of this pass.

func (*Manager) RotateKey

func (m *Manager) RotateKey(ctx context.Context, retireAfter time.Duration) (RotationResult, error)

RotateKey generates a fresh signing key and makes it active immediately. The previous active key moves to the retiring set and stays published in the JWKS, so tokens it already signed keep verifying, until retireAfter elapses (DefaultKeyRetireGrace when retireAfter is zero). Nothing removes a retiring key before that: removal is the deadline passing, checked live by JWKS, not a separate step an operator can forget.

type RotationResult

type RotationResult struct {
	OldKID   string
	NewKID   string
	RetireAt time.Time
	// RetiringCount is how many keys are now published in the JWKS
	// purely for verification (no longer used to sign anything new).
	RetiringCount int
}

RotationResult is the outcome of one RotateKey call.

type TokenRequest

type TokenRequest struct {
	// Audience is the OIDC "aud" claim: the cloud provider's expected
	// value (e.g. "sts.amazonaws.com" for AWS, a GCP workload identity
	// pool audience URL, or a Vault role's bound_audiences entry).
	Audience string
	// Subject is the OIDC "sub" claim, the identity a trust policy
	// matches against. The caller builds it (pipeline jobs use
	// "repo:<app>:ref:<ref>:job:<job_key>"): this package stays
	// pipeline-agnostic, the same "translation is the caller's job"
	// reasoning internal/docker.ContainerSpec's own doc comments give.
	Subject string
	Repo    string
	Ref     string
	// PipelineID identifies the pipeline definition this run came from.
	PipelineID string
}

TokenRequest is what a pipeline job's `oidc: {audience: ...}` config asks Manager.IssueToken to mint.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL