credential

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: 20 Imported by: 0

Documentation

Overview

Package credential is the agent's side of enrollment and renewal: it generates the workload keypair, builds certificate signing requests, calls the control plane, and keeps the resulting credential in a state directory the daemon reads (ADR-0016).

The private key is generated here and never leaves the host. The control plane sees only a CSR, and the CSR contributes only its public key; identity is granted by the control plane.

Index

Constants

View Source
const (
	// KeyFile holds the workload private key (PKCS#8 PEM).
	KeyFile = "workload.key"
	// CertFile holds the current workload certificate (PEM).
	CertFile = "workload.crt"
	// BundleFile holds the authority bundle the control plane is verified with.
	BundleFile = "ca-bundle.crt"
)
View Source
const RenewFraction = 2.0 / 3.0

RenewFraction is the point in a credential's lifetime at which the daemon renews: when less than one third remains.

View Source
const RenewJitterFraction = 1.0 / 12.0

RenewJitterFraction is the width of the jitter window before the renewal point, as a fraction of lifetime, so that a fleet enrolled together does not renew together.

Variables

View Source
var ErrCredentialExpired = errors.New("credential: expired; re-enrollment with a new provisioning token is required")

ErrCredentialExpired is returned by the renewer when the credential is already past its expiry: the workload can no longer authenticate, so it cannot renew, and only a new enrollment with a fresh provisioning token restores it. The daemon reports this and does not attempt enrollment itself (ADR-0016).

View Source
var ErrNotEnrolled = errors.New("credential: not enrolled")

ErrNotEnrolled is returned by Load when the state directory holds no credential.

Functions

func GenerateKey

func GenerateKey() (*ecdsa.PrivateKey, error)

GenerateKey creates the workload keypair.

func NewCSR

func NewCSR(key *ecdsa.PrivateKey) ([]byte, error)

NewCSR builds a PEM-encoded PKCS#10 request over key. The request names nothing: no subject, no SANs. The control plane ignores those fields by rule, and an empty request makes that rule visible from the agent side.

func RenewAt

func RenewAt(notBefore, notAfter time.Time, rng *rand.Rand) time.Time

RenewAt returns when a credential valid from notBefore to notAfter should be renewed: two thirds of the way through its lifetime, pulled earlier by a random amount up to RenewJitterFraction of the lifetime.

func TLSConfig

func TLSConfig(cred *tls.Certificate, trustPEM []byte) (*tls.Config, error)

TLSConfig builds the agent's client configuration: the workload credential (nil for enrollment, which has none yet) and the PEM trust anchor for the control plane (empty means the host's trust store).

Types

type EnrollOptions

type EnrollOptions struct {
	// Server is the control-plane address, host:port.
	Server string
	// Token is the provisioning token plaintext.
	Token string
	// BootstrapCA is the PEM trust anchor for the control plane, delivered
	// out of band together with the token. At enrollment the agent holds
	// nothing that could verify the control plane: no credential and no
	// bundle. The token authenticates the agent to the server; this anchor
	// authenticates the server to the agent. Without it the token would be
	// sent to whatever answers at Server. Empty means the host's system
	// trust store, which is right when the control plane presents a
	// certificate the host already trusts.
	BootstrapCA []byte
	// Facts are descriptive host facts sent alongside; they never influence
	// identity or authorization.
	Facts *innerwallv1.HostFacts
}

EnrollOptions configure one enrollment call.

type EnrollResult

type EnrollResult struct {
	WorkloadID     identity.WorkloadID
	CertificatePEM []byte
	BundlePEM      []byte
	Labels         []*innerwallv1.Label
}

EnrollResult is what the control plane granted.

func Enroll

func Enroll(ctx context.Context, key *ecdsa.PrivateKey, opts EnrollOptions) (*EnrollResult, error)

Enroll generates nothing and persists nothing: it exchanges the token and a CSR over key for a credential and returns it for the caller to store.

type Holder

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

Holder is the credential a running daemon presents, swappable in place. Connections opened while it holds one certificate keep using it; the client TLS configuration it builds asks the holder for the certificate at each handshake, so a renewal takes effect on the next connection without dropping the current stream.

func LoadHolder

func LoadHolder(store Store) (*Holder, error)

LoadHolder reads the stored credential into a holder.

func (*Holder) Current

func (h *Holder) Current() (tls.Certificate, []byte)

Current returns the current credential and trust bundle.

func (*Holder) Key

func (h *Holder) Key() *ecdsa.PrivateKey

Key returns the workload private key.

func (*Holder) Leaf

func (h *Holder) Leaf() *x509.Certificate

Leaf returns the current certificate.

func (*Holder) Replace

func (h *Holder) Replace(certPEM, bundlePEM []byte) error

Replace installs a renewed certificate, on disk first and then in memory, so a crash between the two leaves the newer one on disk.

func (*Holder) TLSConfig

func (h *Holder) TLSConfig() (*tls.Config, error)

TLSConfig builds a client configuration that presents whatever certificate the holder has at handshake time and trusts the bundle it has now.

type RenewOptions

type RenewOptions struct {
	Server string
	// Credential is the current workload credential, presented over mTLS.
	Credential tls.Certificate
	// Trust is the authority bundle stored at enrollment.
	Trust []byte
}

RenewOptions configure one renewal call.

type RenewResult

type RenewResult struct {
	WorkloadID     identity.WorkloadID
	CertificatePEM []byte
	BundlePEM      []byte
}

RenewResult is a renewed credential.

func Renew

func Renew(ctx context.Context, key *ecdsa.PrivateKey, opts RenewOptions) (*RenewResult, error)

Renew asks for a fresh certificate over the current key, authenticated by the current credential. The control plane binds the same identity; the caller checks nothing about which workload because it cannot choose.

type Renewer

type Renewer struct {
	Holder *Holder
	// Server is the control-plane address for RenewCredential.
	Server string
	Log    *slog.Logger
	// RetryBase and RetryCap bound the backoff after a failed renewal;
	// one minute and one hour when zero.
	RetryBase time.Duration
	RetryCap  time.Duration
	// Now is the clock; time.Now if nil.
	Now func() time.Time
	// After is the timer; time.After if nil. Tests substitute it to run
	// the schedule without waiting.
	After func(d time.Duration) <-chan time.Time
	// Renew performs one renewal; the package's Renew when nil. Tests
	// substitute it.
	Renew func(ctx context.Context, key *ecdsa.PrivateKey, opts RenewOptions) (*RenewResult, error)
	// Renewed, when set, is called after each successful renewal.
	Renewed func(leaf *x509.Certificate)
	// contains filtered or unexported fields
}

Renewer renews the holder's credential on a timer.

func (*Renewer) LastError

func (r *Renewer) LastError() string

LastError returns the reason the most recent renewal attempt failed, or empty when the last attempt succeeded or none has been due. The daemon carries it on every heartbeat so the control plane sees a workload heading for expiry before it gets there.

func (*Renewer) Run

func (r *Renewer) Run(ctx context.Context) error

Run renews until ctx ends. It returns ErrCredentialExpired when the credential expires before a renewal succeeds; the caller decides what that means for the process. Failed attempts retry with jittered exponential backoff and are logged at error level every time, because each one brings the workload closer to needing re-enrollment.

type Store

type Store struct {
	Dir string
}

Store is the on-disk credential state of one agent.

func (Store) Load

func (s Store) Load() (cred tls.Certificate, bundlePEM []byte, err error)

Load reads the stored credential and bundle.

func (Store) LoadKey

func (s Store) LoadKey() (*ecdsa.PrivateKey, error)

LoadKey reads only the private key.

func (Store) ReplaceCertificate

func (s Store) ReplaceCertificate(certPEM, bundlePEM []byte) error

ReplaceCertificate swaps in a renewed certificate (and refreshed bundle) atomically: a reader sees either the old file or the new one, never a partial write. The key is untouched.

func (Store) Save

func (s Store) Save(key *ecdsa.PrivateKey, certPEM, bundlePEM []byte) error

Save writes a freshly enrolled credential: key, certificate, and bundle. The directory is created with mode 0700 and every file with mode 0600.

Jump to

Keyboard shortcuts

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