enroll

package
v0.1.0 Latest Latest
Warning

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

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

Documentation

Overview

Package enroll is the enrollment domain: provisioning tokens and the exchange of a token plus a certificate signing request for a workload identity and credential, and the renewal of that credential by an already identified workload (ADR-0016).

A provisioning token is the only credential an installer needs. It is scoped to a label set, expires, is revocable, and may enroll many workloads within its scope. The control plane stores its hash, which is what a presented token is looked up by, and a short listing hint. The agent's private key never leaves its host; there is no trust-on-first-use window (ADR-0004).

Signing goes through ca.Authority; persistence goes through Store. This package knows nothing about how either is implemented.

Index

Constants

View Source
const DefaultLeafTTL = 24 * time.Hour

DefaultLeafTTL is the lifetime of an issued workload credential when the operator sets none. Short lifetimes replace revocation machinery: a credential that is not renewed simply stops working (ADR-0016).

View Source
const DefaultTokenTTL = 30 * 24 * time.Hour

DefaultTokenTTL is the lifetime of a token when the operator sets none.

View Source
const TokenPrefix = "iw_"

TokenPrefix marks a provisioning token in transit. A fixed prefix on an otherwise random secret lets secret scanners recognize a leaked token without any knowledge of the deployment that minted it.

Variables

View Source
var (
	ErrTokenMalformed = errors.New("enroll: provisioning token is malformed")
	ErrTokenUnknown   = errors.New("enroll: provisioning token is not recognized")
	ErrTokenExpired   = errors.New("enroll: provisioning token has expired")
	ErrTokenRevoked   = errors.New("enroll: provisioning token has been revoked")
)

Errors a token check can produce. They are distinct so an operator sees exactly why an enrollment failed; none of them reveals whether a token with a different value would have succeeded.

View Source
var ErrWorkloadUnknown = errors.New("enroll: workload is not registered")

ErrWorkloadUnknown is returned when a credential names a workload the registry does not hold.

Functions

func HashEqual

func HashEqual(a, b []byte) bool

HashEqual compares two token hashes in constant time.

func HashToken

func HashToken(plaintext string) ([]byte, error)

HashToken validates the shape of a plaintext token and returns its SHA-256 digest. Shape validation happens before hashing so a malformed value is reported as malformed rather than as unknown. The digest is what is looked up; a store never sees the plaintext.

func ListingHint

func ListingHint(plaintext string) string

ListingHint is the part of a plaintext token kept for listing: the fixed prefix and the first characters of the random body.

func NewToken

func NewToken() (plaintext string, hash []byte, err error)

NewToken draws a fresh token. The plaintext is returned once, for display to the operator; the hash is what gets stored.

Types

type Credential

type Credential struct {
	CertificatePEM []byte
	BundlePEM      []byte
	Serial         string
	ExpiresAt      time.Time
}

Credential is what an enrollment or renewal hands back.

type Label

type Label struct {
	Key   string
	Value string
}

Label is one key/value pair assigned to a workload.

type Result

type Result struct {
	Workload   Workload
	Credential Credential
}

Result is the outcome of a successful enrollment.

type Service

type Service struct {
	Store     Store
	Authority ca.Authority
	// LeafTTL is the lifetime of issued credentials; DefaultLeafTTL if zero.
	LeafTTL time.Duration
	// Now is the clock; time.Now if nil.
	Now func() time.Time
}

Service performs enrollment and renewal.

func (*Service) Enroll

func (s *Service) Enroll(ctx context.Context, tokenPlaintext string, csrPEM []byte, hostname string) (*Result, error)

Enroll exchanges a provisioning token and a CSR for an identity and a credential. The order is deliberate: the token is checked before the CSR is even parsed, so an unauthenticated caller learns nothing about what the signing boundary accepts.

func (*Service) ListTokens

func (s *Service) ListTokens(ctx context.Context) ([]Token, error)

ListTokens returns every provisioning token, newest first: hashes and metadata, never a plaintext.

func (*Service) MintToken

func (s *Service) MintToken(ctx context.Context, name string, labels []Label, ttl time.Duration) (plaintext string, tok Token, err error)

MintToken creates a token scoped to labels with the given lifetime (DefaultTokenTTL if zero) and returns the plaintext exactly once.

func (*Service) Renew

func (s *Service) Renew(ctx context.Context, id identity.WorkloadID, csrPEM []byte) (*Credential, error)

Renew reissues the credential for id, which the caller has already derived from a verified connection credential. The same identity is bound; the CSR contributes only its (possibly new) public key.

func (*Service) RevokeToken

func (s *Service) RevokeToken(ctx context.Context, id uuid.UUID) error

RevokeToken revokes a token as of now. Future enrollments with it are refused; workloads it already enrolled are untouched (ADR-0016).

type Store

type Store interface {
	// CreateToken persists a minted token (hash only).
	CreateToken(ctx context.Context, t Token) error
	// FindTokenByHash returns ErrTokenUnknown when no token has that hash.
	FindTokenByHash(ctx context.Context, hash []byte) (*Token, error)
	// ListTokens returns every token, newest first.
	ListTokens(ctx context.Context) ([]Token, error)
	// RevokeToken marks a token revoked at instant now. It returns
	// ErrTokenUnknown for an unknown id and ErrTokenRevoked if it already was.
	RevokeToken(ctx context.Context, id uuid.UUID, now time.Time) error

	// CreateWorkload persists a newly enrolled workload with its labels and
	// records the use against its token, atomically.
	CreateWorkload(ctx context.Context, w Workload, now time.Time) error
	// GetWorkload returns ErrWorkloadUnknown when the id is not registered.
	GetWorkload(ctx context.Context, id identity.WorkloadID) (*Workload, error)
	// RecordRenewal stores the serial and expiry of a renewed credential. It
	// returns ErrWorkloadUnknown when the id is not registered.
	RecordRenewal(ctx context.Context, id identity.WorkloadID, serial string, expiresAt, now time.Time) error
}

Store is the persistence the enrollment domain needs. Every method is implemented with hand-written SQL in internal/store (ADR-0006).

type Token

type Token struct {
	ID         uuid.UUID
	Hash       []byte
	Prefix     *string
	Name       string
	Labels     []Label
	CreatedAt  time.Time
	ExpiresAt  time.Time
	RevokedAt  *time.Time
	UseCount   int64
	LastUsedAt *time.Time
}

Token is the stored form of a provisioning token. It never carries the plaintext: Hash is what is looked up, and Prefix, the listing hint, is what is listed. Prefix is nil for a token minted before hints were kept, since none can be recovered from a digest.

func (*Token) Check

func (t *Token) Check(now time.Time) error

Check reports whether the token may enroll a workload at instant now. Revocation is checked before expiry so that a revoked token reports as revoked even after it would have expired anyway; the operator's action is the more specific fact.

type Workload

type Workload struct {
	ID                  identity.WorkloadID
	TokenID             uuid.UUID
	Hostname            string
	Labels              []Label
	EnrolledAt          time.Time
	CredentialSerial    string
	CredentialExpiresAt time.Time
	LastRenewedAt       *time.Time
}

Workload is a registry row: the identity granted at enrollment plus the attributes that live beside it rather than in the credential.

Directories

Path Synopsis
Package enrolltest holds test doubles for the enrollment domain.
Package enrolltest holds test doubles for the enrollment domain.

Jump to

Keyboard shortcuts

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