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
- Variables
- func GenerateKey() (*ecdsa.PrivateKey, error)
- func NewCSR(key *ecdsa.PrivateKey) ([]byte, error)
- func RenewAt(notBefore, notAfter time.Time, rng *rand.Rand) time.Time
- func TLSConfig(cred *tls.Certificate, trustPEM []byte) (*tls.Config, error)
- type EnrollOptions
- type EnrollResult
- type Holder
- type RenewOptions
- type RenewResult
- type Renewer
- type Store
Constants ¶
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" )
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.
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 ¶
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).
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.
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 ¶
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) Leaf ¶
func (h *Holder) Leaf() *x509.Certificate
Leaf returns the current certificate.
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 ¶
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 ¶
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 ¶
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.