cioidc

package
v0.5.7 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 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