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
- type Config
- type JWK
- type JWKS
- type Key
- type KeyStore
- type Manager
- func (m *Manager) IssueToken(ctx context.Context, req TokenRequest) (string, error)
- func (m *Manager) JWKS(ctx context.Context) (JWKS, error)
- func (m *Manager) RemoveRetiringKey(ctx context.Context, kid string) error
- func (m *Manager) RotateKey(ctx context.Context, retireAfter time.Duration) (RotationResult, error)
- type RotationResult
- type TokenRequest
Constants ¶
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.
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 ¶
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 ¶
IssueToken mints a short-lived OIDC token for req, signed with ES256 using the current active key.
func (*Manager) JWKS ¶
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 ¶
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 ¶
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.