Help us shape the future of Go! Take the 2026 Go Developer Survey

cioidc

package
v0.5.8 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package cioidc verifies the OIDC tokens CI providers mint for their jobs (GitHub Actions, GitLab CI, Google Cloud Build), so a pipeline deploys without a stored credential (ADR 0092, #1882).

The operator lists the issuers it trusts. Each entry names the one tenant its tokens act in, and ordered rules that map the token's claims to a role of that tenant. The tenant is never read from a claim: a provider's claims are written by whoever owns the repository. A verified token whose claims match no rule is refused.

Index

Constants

View Source
const (
	// ProviderGitHub is GitHub Actions.
	ProviderGitHub = "github"
	// ProviderGitLab is GitLab CI, on gitlab.com or a self-managed instance.
	ProviderGitLab = "gitlab"
	// ProviderCloudBuild is Google Cloud Build, whose jobs sign in as a
	// service account with Google's ID tokens.
	ProviderCloudBuild = "cloudbuild"
	// ProviderOther is any other OIDC provider; its rules may pin any claim.
	ProviderOther = "other"
)

The providers an entry may name. The provider decides which claims a rule must pin (identityClaims) and which pipeline `dexaflow ci init` writes.

View Source
const DefaultMaxLifetime = time.Hour

DefaultMaxLifetime bounds exp - iat when an entry sets no MaxLifetime. CI tokens live minutes (five on GitHub Actions, an hour on Cloud Build), so an hour refuses a long-lived token minted elsewhere for the same audience.

View Source
const MaxLifetimeCeiling = 24 * time.Hour

MaxLifetimeCeiling is the longest MaxLifetime an entry may set.

Variables

View Source
var (
	// ErrUnknownIssuer is a token whose iss and aud name no configured entry.
	ErrUnknownIssuer = errors.New("cioidc: no trusted issuer for this token")
	// ErrInvalidToken is a token the entry's provider did not mint for us, now:
	// a bad signature or algorithm, an issuer or audience that does not match,
	// or a missing, expired, post-dated or too long validity, or no subject.
	ErrInvalidToken = errors.New("cioidc: invalid token")
	// ErrNoMatchingRule is a valid token whose claims match no rule of its
	// entry.
	ErrNoMatchingRule = errors.New("cioidc: no rule matches the token's claims")
	// ErrKeysUnavailable is a token that could not be checked because the
	// provider's discovery document or keys could not be downloaded. It says
	// nothing about the token, so the API answers 503.
	ErrKeysUnavailable = errors.New("cioidc: signing keys unavailable")
)

Verification failures. Each is fail-closed.

Functions

func Validate

func Validate(issuers []Issuer) error

Validate checks a list of entries, naming the first bad field as issuers[i].field. An empty list is valid: CI OIDC is off.

Types

type Error

type Error struct {
	// Issuer and Tenant are the entry's, empty for ErrUnknownIssuer.
	Issuer, Tenant string
	// Subject is the token's sub when it was read.
	Subject string
	// Err is the cause.
	Err error
}

Error is a refusal, naming the entry when the token named one, for the metric and the log. It wraps one of the Err values.

func (*Error) Error

func (e *Error) Error() string

Error returns the cause's message.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the cause.

type Identity

type Identity struct {
	// Issuer is the name of the entry that verified the token.
	Issuer string
	// Tenant is the entry's tenant.
	Tenant string
	// Subject is the token's sub, which names the job (on GitHub, the
	// repository and the environment or ref).
	Subject string
	// Role and Scopes are those of the rule that matched.
	Role   string
	Scopes []string
	// Fingerprint identifies the entry's configuration (Verifier.Fingerprint).
	Fingerprint string
}

Identity is what a verified token is allowed to be.

type Issuer

type Issuer struct {
	// Name identifies the entry in logs, metrics, sessions and `ci init`.
	Name string `json:"name" yaml:"name"`
	// Provider is one of the Provider constants.
	Provider string `json:"provider" yaml:"provider"`
	// Issuer is the exact iss of the tokens.
	Issuer string `json:"issuer" yaml:"issuer"`
	// JWKSURL is where the keys are; empty discovers it from Issuer.
	JWKSURL string `json:"jwks_url,omitempty" yaml:"jwks_url"`
	// Audience is the aud the tokens must carry.
	Audience string `json:"audience" yaml:"audience"`
	// Tenant is the only tenant the entry's tokens act in.
	Tenant string `json:"tenant" yaml:"tenant"`
	// MaxLifetime caps exp - iat; zero means DefaultMaxLifetime.
	MaxLifetime time.Duration `json:"-" yaml:"-"`
	// Rules are tried in order; the first that matches decides.
	Rules []Rule `json:"rules" yaml:"rules"`
}

Issuer is one trusted CI issuer.

type Rule

type Rule struct {
	// Claims maps a claim name to a pattern.
	Claims map[string]string `json:"claims" yaml:"claims"`
	// Role is the role of the entry's tenant the principal gets.
	Role string `json:"role" yaml:"role"`
	// Scopes narrow the principal (ADR 0067); empty means dexaflow:deploy.
	Scopes []string `json:"scopes,omitempty" yaml:"scopes"`
}

Rule maps claims to a role. It matches a token when every claim it names is a string claim of the token that matches its pattern (path.Match).

type Verifier

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

Verifier checks CI tokens against the configured issuers.

func New

func New(issuers []Issuer) *Verifier

New builds a Verifier for issuers, which Validate accepted. It makes no network call: discovery documents and keys are fetched on first use and cached, so a provider outage cannot block boot.

func (*Verifier) Advertised

func (v *Verifier) Advertised(tenant string) []domain.CIOIDCIssuer

Advertised returns the entries of tenant as GET /api/v2/deployDefaults lists them for `dexaflow ci init`: name, provider, issuer and audience.

func (*Verifier) Fingerprint

func (v *Verifier) Fingerprint(name string) (string, bool)

Fingerprint returns the fingerprint of the entry called name, and false when no entry has that name.

func (*Verifier) Verify

func (v *Verifier) Verify(ctx context.Context, raw string) (*Identity, error)

Verify checks raw against the entry its iss and aud name and returns what the first matching rule allows. A refusal is an *Error wrapping ErrUnknownIssuer, ErrInvalidToken, ErrNoMatchingRule or ErrKeysUnavailable.

Jump to

Keyboard shortcuts

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