pki

package
v0.15.0 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

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

DefaultAgentCertDuration is the lifetime of host certificates minted for agent-managed hosts that auto-rotate via the poll loop (ADR 0004). 30 days strikes a balance between rotation overhead and revocation latency.

View Source
const DefaultMobileCertDuration = 365 * 24 * time.Hour

DefaultMobileCertDuration is the lifetime of host certificates minted for Mobile Nebula clients (kind=mobile). Mobile rotation requires the operator to re-download a bundle and the user to re-import it manually, so a longer lifetime reduces operator burden. The signer clamps to remaining CA validity (see signer.go), so practical lifetime is min(365d, CA_remaining). Revocation via the blocklist remains the immediate security control.

Variables

View Source
var ErrMasterRequired = errors.New("master keystore is required")

Functions

func MintAndStoreCA

func MintAndStoreCA(ctx context.Context, s MintStore, master *keystore.Master, logger *slog.Logger, req MintRequest) (*models.CA, bool, error)

MintAndStoreCA creates a new CA for the given operator and persists it to the store. If SkipIfActive is set and the operator already has an active CA, the existing one is returned with minted=false. Returns (ca, true, nil) on successful creation, (existing, false, nil) if skipped due to existing CA, or (nil, false, err) on failure.

func RotateAndStoreCA

func RotateAndStoreCA(
	ctx context.Context,
	s store.Store,
	master *keystore.Master,
	logger *slog.Logger,
	oldCA *models.CA,
) (*models.CA, error)

RotateAndStoreCA creates a new CA that succeeds the given oldCA and persists it to the store. It implements CA rotation: minting a new certificate with the same lifetime as its predecessor, and establishing the predecessor link for trust bundle management.

The rotation is idempotent: if an active successor already exists for oldCA, it is returned without creating a duplicate. This handles cases where rotation is retried after partial failures.

func ShouldRenew

func ShouldRenew(notBefore, notAfter time.Time) bool

ShouldRenew returns true if the certificate should be renewed as of now. Renewal is needed when remaining TTL is less than 20% of total duration.

func ShouldRenewAt added in v0.3.7

func ShouldRenewAt(notBefore, notAfter, now time.Time) bool

ShouldRenewAt is ShouldRenew evaluated at an explicit instant, so callers holding an injectable clock (and tests advancing simulated time) get a deterministic decision instead of an implicit time.Now().

Types

type Blocklist

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

Blocklist tracks blocked certificate fingerprints.

func NewBlocklist

func NewBlocklist() *Blocklist

NewBlocklist creates an empty blocklist.

func (*Blocklist) Add

func (b *Blocklist) Add(fingerprint string)

Add adds a fingerprint to the blocklist.

func (*Blocklist) Contains

func (b *Blocklist) Contains(fingerprint string) bool

Contains checks if a fingerprint is in the blocklist.

func (*Blocklist) List

func (b *Blocklist) List() []string

List returns all fingerprints in the blocklist, sorted.

func (*Blocklist) Remove

func (b *Blocklist) Remove(fingerprint string)

Remove removes a fingerprint from the blocklist.

type CAManager

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

CAManager holds a loaded CA certificate and its signing key in memory.

func LoadCAFromMaterial

func LoadCAFromMaterial(certPEM []byte, rawKey ed25519.PrivateKey) (*CAManager, error)

LoadCAFromMaterial builds a CAManager from a PEM-encoded CA certificate and the raw ed25519 private key. Used by the DB-backed multi-CA path where key material is decrypted on demand via the keystore package.

func NewCA

func NewCA(name string, duration time.Duration) (*CAManager, error)

NewCA creates a new Curve25519 CA with the given name and duration.

func (*CAManager) CACert

func (m *CAManager) CACert() cert.Certificate

CACert returns the CA certificate.

func (*CAManager) CACertFingerprint

func (m *CAManager) CACertFingerprint() (string, error)

CACertFingerprint returns the SHA256 fingerprint of the CA certificate.

func (*CAManager) CACertPEM

func (m *CAManager) CACertPEM() ([]byte, error)

CACertPEM returns the PEM-encoded CA certificate.

func (*CAManager) RawKey

func (m *CAManager) RawKey() ed25519.PrivateKey

RawKey returns the in-memory ed25519 private key. Used by the migration path to re-encrypt a legacy file-based CA under the master key. Treat the returned slice as sensitive and zeroise once done.

func (*CAManager) Sign

func (m *CAManager) Sign(req SignRequest) (cert.Certificate, error)

Sign creates and signs a host certificate using the CA.

func (*CAManager) Wipe

func (m *CAManager) Wipe()

Wipe overwrites the in-memory plaintext signing key with zeros so it no longer lingers on the Go heap waiting for GC. Callers MUST defer this immediately after LoadByID / NewCA, per the keystore package's "zeroise the plaintext as soon as it is no longer needed" contract. After Wipe(), any subsequent Sign() will produce invalid signatures. Closes GHSA-8h84-fhqq-q58v.

Nil-safe so `defer caMgr.Wipe()` placed before the error check is also safe — load failures return nil and the defer becomes a no-op.

type CAResolver

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

CAResolver loads CAs from the database, decrypts their private key material with the master keystore, and returns a CAManager bound to the requested CA. The unwrapped key material lives only inside the returned manager; CAResolver does NOT cache anything across calls so a disable / rotation takes effect immediately.

func NewCAResolver

func NewCAResolver(store CAStore, master *keystore.Master) *CAResolver

NewCAResolver builds a resolver. The master keystore must be the same one used when CAs were created/imported.

func (*CAResolver) LoadByID

func (r *CAResolver) LoadByID(ctx context.Context, caID string) (*CAManager, error)

LoadByID returns a CAManager for the CA identified by caID. The caller is responsible for not retaining the manager beyond the signing scope.

type CAStore

type CAStore interface {
	GetCA(ctx context.Context, id string) (*models.CA, error)
}

CAStore is the subset of the application store interface required by CAResolver. Kept narrow so this package can depend only on what it uses.

type HostCertInfo

type HostCertInfo struct {
	HostID    string
	NotBefore time.Time
	NotAfter  time.Time
}

HostCertInfo holds certificate timing for renewal checks.

func FindHostsForRenewal

func FindHostsForRenewal(hosts []HostCertInfo) []HostCertInfo

FindHostsForRenewal filters hosts that need certificate renewal.

type MintRequest

type MintRequest struct {
	// Operator is the owner of the CA to be minted.
	Operator *models.Operator
	// Name is the user-facing name for the CA (e.g. "alice-default").
	Name string
	// Duration is the validity period for the cert (e.g. 10 * 365 * 24 * time.Hour).
	Duration time.Duration
	// SkipIfActive, when true, causes the function to return an existing active CA
	// for the operator instead of minting a new one (idempotent behavior).
	SkipIfActive bool
}

MintRequest describes parameters for MintAndStoreCA.

type MintStore

type MintStore interface {
	// CreateCA persists a new CA record to the store.
	CreateCA(ctx context.Context, c *models.CA) error
	// ListCAsByOwner returns all CAs owned by the given operator.
	ListCAsByOwner(ctx context.Context, ownerID string) ([]*models.CA, error)
	// AddAuditEntry records an action in the audit log (best-effort).
	AddAuditEntry(ctx context.Context, actor, action, resource, details string) error
}

MintStore is the narrow interface for store operations needed by MintAndStoreCA. It isolates pki from a concrete dependency on store.Store, enabling better testing and layering.

type Rotation

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

Rotation manages a CA rotation: old CA + new CA in parallel.

func NewRotation

func NewRotation(oldCA *CAManager, newName string, duration time.Duration) (*Rotation, error)

NewRotation creates a new CA and sets up a rotation from the old CA.

func (*Rotation) NewCA

func (r *Rotation) NewCA() *CAManager

NewCA returns the new CA.

func (*Rotation) OldCA

func (r *Rotation) OldCA() *CAManager

OldCA returns the old CA.

func (*Rotation) TrustBundle

func (r *Rotation) TrustBundle() ([]byte, error)

TrustBundle returns PEM bytes containing both CA certificates. Agents should trust both during the transition period.

type SignRequest

type SignRequest struct {
	Name      string
	PublicKey []byte // X25519 public key
	Networks  []netip.Prefix
	// UnsafeNetworks are the non-overlay prefixes this host is authorized to
	// route for. Nebula enforces routing on the certificate, not on config: a
	// gateway whose cert omits the prefix silently refuses to route it, and
	// both ends drop the traffic in Firewall.Drop before any rule is consulted
	// (the local address is not in routableNetworks). Empty for ordinary hosts.
	UnsafeNetworks []netip.Prefix
	Groups         []string
	Duration       time.Duration
	// Now sets the certificate's NotBefore (and the CA-expiry check instant).
	// Zero means time.Now(). Callers holding an injectable clock pass it so
	// re-signs under simulated time produce distinct, correctly-dated certs.
	Now time.Time
}

SignRequest contains the parameters for signing a host certificate.

Jump to

Keyboard shortcuts

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