workloadidentity

package
v0.13.0 Latest Latest
Warning

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

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

Documentation

Overview

Package workloadidentity implements OIDC workload identity tokens for sandbox containers, following the GitHub Actions OIDC pattern.

Trust Model

Each cluster is its own OIDC issuer with an independent signing key and JWKS endpoint. New clusters sign with RS256 (RSA), the universally supported default; clusters provisioned before that default keep their EdDSA key advertised in JWKS for verification while new tokens are signed with a freshly generated RS256 key. Miren Cloud is not in the trust path — it only contributes organization_id and cluster_id as claim metadata during registration. This per-cluster model means external verifiers (e.g., AWS IAM OIDC) must configure trust per cluster rather than once for all of Miren. A future central issuer could reduce that to one trust config scoped by claims, but would introduce a single point of compromise for all clusters.

Issuer URL (iss claim)

The issuer URL is the cluster's cryptographic identity anchor — it's baked into every token and pinned in external trust configurations. For cloud-registered clusters, this is the provisioned DNS hostname (e.g., https://cluster-abc.miren.systems). For bare-metal clusters without registration, it falls back to cfg.TLS.AdditionalNames[0], meaning the identity anchor is determined by config list order. This fallback is intentionally simple for v1; a more deliberate selection mechanism (e.g., explicit --issuer-url flag) may be warranted if bare-metal OIDC federation sees adoption.

A cluster with no hostname at all still gets an issuer, anchored at LocalIssuerURL. Identity is not conditional on being externally addressable: the cluster's own services authenticate to each other with these tokens and verify them in-process against the signing keys, which needs no DNS, no reachability, and no publicly trusted certificate. Only federation to an outside party needs those, and that is a property of the anchor rather than a mode the cluster is in.

Index

Constants

View Source
const (
	DefaultTTL = 1 * time.Hour
	MaxTTL     = 24 * time.Hour
	MinTTL     = 60 * time.Second

	// DefaultAudience is the audience stamped on a token whose caller did not
	// ask for a specific one. Callers guarding a particular service should
	// always request their own audience and verify it, so that a token minted
	// for one service cannot be replayed against another.
	DefaultAudience = "miren"
)
View Source
const LocalIssuerURL = "https://cluster.local"

LocalIssuerURL anchors a cluster that has no hostname to advertise.

It is deliberately a name the cluster already owns rather than a synthetic placeholder: cluster.local is how the cluster refers to itself internally, resolved to whatever is locally appropriate (the registry router on the coordinator, the coordinator's address on a runner). Nothing outside can resolve it at all, which is the honest signal that such a cluster cannot federate to an external party until it is given a real hostname.

Note that this is an identity anchor, not an endpoint. It is compared as a string when verifying, never fetched, so it does not matter that the resolution it names is set up later in startup or means different things in different processes.

Variables

This section is empty.

Functions

This section is empty.

Types

type IdentityType added in v0.13.0

type IdentityType string

IdentityType names the kind of principal a token represents.

The value travels as its own claim rather than being inferred from the subject. This lets verifiers authorize an identity class without reparsing the subject grammar. Tokens issued before this claim existed carry no identity_type, which reads as "not a system workload" and so fails closed for system-only resources.

It is a defined type so that switches over it are exhaustiveness-checked, the way rpc.AuthMethod already is. Note that this buys nothing at the trust boundary itself: a token arriving from a caller decodes into whatever string it contained, so verification still has to compare the value rather than assume it is one of the constants below.

const (
	IdentityTypeSandbox IdentityType = "sandbox"
	IdentityTypeSystem  IdentityType = "system"
)

type Issuer

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

func NewIssuer

func NewIssuer(cfg IssuerConfig) (*Issuer, error)

func (*Issuer) DiscoveryDocument

func (iss *Issuer) DiscoveryDocument() []byte

func (*Issuer) Hostname

func (iss *Issuer) Hostname() string

func (*Issuer) IssueSystemWorkloadToken added in v0.13.0

func (iss *Issuer) IssueSystemWorkloadToken(workload SystemWorkload, opts TokenOptions) (string, error)

IssueSystemWorkloadToken mints a token identifying a Miren-owned workload (the sandbox controller, a telemetry writer) rather than a customer workload.

System workload tokens let Miren's own code authenticate to cluster-internal services without a second credential system: they are minted by the same issuer, verified the same way, and inherit the short lifetimes that make revocation tractable. They are deliberately distinguishable from sandbox tokens by the identity_type claim, because the services they open are precisely the ones a sandbox must not reach.

Callers choose an audience scoped to the service they intend to reach, since one workload may call several services. The receiving service verifies that audience and the expected workload together so a token minted for one workload or service cannot be replayed against another.

func (*Issuer) IssueToken

func (iss *Issuer) IssueToken(app, sandboxID string) (string, error)

func (*Issuer) IssueTokenWithOptions

func (iss *Issuer) IssueTokenWithOptions(app, sandboxID string, opts TokenOptions) (string, error)

func (*Issuer) IssuerURL

func (iss *Issuer) IssuerURL() string

func (*Issuer) JWKSDocument

func (iss *Issuer) JWKSDocument() ([]byte, error)

func (*Issuer) PublicKey

func (iss *Issuer) PublicKey() any

func (*Issuer) VerifySystemWorkloadToken added in v0.13.0

func (iss *Issuer) VerifySystemWorkloadToken(tokenString, expectedAudience string, expectedWorkload SystemWorkload) (*WorkloadClaims, error)

VerifySystemWorkloadToken verifies a token and additionally requires that it identifies the expected Miren-owned system workload rather than a customer workload or a different system workload.

Services that only system workloads may reach should call this rather than VerifyToken, so neither the identity type nor workload authorization can be forgotten at a call site. Sandbox tokens are signed by the same key and will verify cleanly; these claims are the only thing separating them.

func (*Issuer) VerifyToken added in v0.13.0

func (iss *Issuer) VerifyToken(tokenString, expectedAudience string) (*WorkloadClaims, error)

VerifyToken checks a token minted by this issuer and returns its claims.

This is the in-process verification path, for callers that live in the same process as the signing key (the coordinator). It reads the public keys directly and performs no network I/O, so it neither depends on nor races the JWKS endpoint. Verifiers in other processes should fetch JWKS over the issuer's discovery document instead — see pkg/oidcauth's Validator.

expectedAudience is required. A token is only accepted if it names that audience, which is what stops a token minted for one service being replayed against another.

type IssuerConfig

type IssuerConfig struct {
	DataPath       string
	IssuerURL      string
	OrganizationID string
	ClusterID      string
}

type Subject added in v0.13.0

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

Subject is the structured identity rendered into a workload token's sub claim. Its segments are private so every subject the issuer accepts has passed through the delimiter validation in newSubject.

func (Subject) String added in v0.13.0

func (s Subject) String() string

String renders the subject as alternating colon-delimited labels and values.

type SystemWorkload added in v0.13.0

type SystemWorkload string

SystemWorkload names a Miren-owned workload that may receive an identity. System workloads are a closed set: adding one is a code change, not a runtime configuration operation.

const (
	SystemWorkloadSandboxController SystemWorkload = "sandboxcontroller"
	SystemWorkloadTelemetryWriter   SystemWorkload = "telemetrywriter"
	SystemWorkloadBuildKit          SystemWorkload = "buildkit"
)

func ParseSystemWorkload added in v0.13.0

func ParseSystemWorkload(value string) (SystemWorkload, error)

ParseSystemWorkload converts the string representation used on the wire into a known system workload.

type TokenIssuer

type TokenIssuer interface {
	IssueToken(app, sandboxID string) (string, error)
	IssueTokenWithOptions(app, sandboxID string, opts TokenOptions) (string, error)
	IssueSystemWorkloadToken(workload SystemWorkload, opts TokenOptions) (string, error)
	IssuerURL() string
}

TokenIssuer is the minting surface the sandbox controller depends on. The concrete *Issuer satisfies it directly (the coordinator holds the signing key). Distributed runners have no signing key, so they supply an implementation that proxies minting to the coordinator over RPC.

type TokenOptions

type TokenOptions struct {
	Audience []string
	TTL      time.Duration
}

type WorkloadClaims

type WorkloadClaims struct {
	jwt.RegisteredClaims
	OrganizationID string `json:"organization_id,omitempty"`
	ClusterID      string `json:"cluster_id,omitempty"`
	App            string `json:"app,omitempty"`
	// SandboxID deliberately keeps its unconditional encoding: it predates
	// system workload tokens and external verifiers may already federate on its
	// presence. System workload tokens therefore carry an empty sandbox_id
	// rather than omitting it.
	SandboxID      string         `json:"sandbox_id"`
	IdentityType   IdentityType   `json:"identity_type,omitempty"`
	SystemWorkload SystemWorkload `json:"system_workload,omitempty"`
}

Jump to

Keyboard shortcuts

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