reload

package
v0.22.0 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: AGPL-3.0 Imports: 14 Imported by: 0

Documentation

Overview

Package reload provides hot-reloading of TLS keypairs that are rotated in place on disk (e.g. cert-manager rewriting a Kubernetes Secret mount). A long-lived process that loads its keypair once at startup keeps serving the stale leaf until it restarts; once that leaf expires, every TLS handshake fails. The primitives here — a validated Bundle, an atomic Store, an fsnotify-backed Watch, and a Reloader exposing tls.Config.GetCertificate — let a server pick up rotations without a restart.

This is the single implementation shared by the kube-controller upstream client cert, the tunnelproxy QUIC server cert, and the tunnel relay.

Index

Constants

View Source
const (
	SecretCertFile = "tls.crt"
	SecretKeyFile  = "tls.key"
	SecretCAFile   = "ca.crt"
)

Standard file names inside a Kubernetes TLS Secret mount.

Variables

View Source
var ErrNoCertificate = errors.New("reload: no certificate loaded")

ErrNoCertificate is returned by Reloader.GetCertificate before any bundle has been successfully loaded.

Functions

func Watch

func Watch(ctx context.Context, p Paths, store *Store, opts WatchOptions) error

Watch watches the directories holding p's files and atomically swaps the live bundle in store on every successful reload, until ctx is done. It also resyncs from disk periodically as a backstop against missed events.

Watch is best-effort: if no directory can be watched (e.g. a pod whose Secret isn't mounted) it logs once and returns nil rather than failing the caller — hot-reload is an enhancement, not a hard dependency.

Types

type Bundle

type Bundle struct {
	Cert        tls.Certificate
	RootCAs     *x509.CertPool // system pool plus the optional CA file
	Fingerprint string
	NotAfter    time.Time
}

Bundle is an immutable view of one validated keypair generation. It is published via Store as a whole-pointer swap; nothing inside is mutated after publication, so readers need no locks.

func BundleFromPEM

func BundleFromPEM(certPEM, keyPEM, caPEM []byte) (*Bundle, error)

BundleFromPEM validates the keypair, parses the leaf to extract its fingerprint + expiry, and builds the root pool. All validation happens before construction so a half-written input surfaces as an error rather than a partially-populated bundle.

func LoadBundle

func LoadBundle(p Paths) (*Bundle, error)

LoadBundle reads and validates the keypair (and optional CA) from disk. A missing CA file is tolerated (RootCAs falls back to the system pool); a missing or malformed cert/key surfaces as an error so a partial-write race is retried rather than published.

type Metrics

type Metrics interface {
	// ReloadAttempt records one reload attempt and whether it swapped in a
	// new bundle. Identical-content resyncs are no-ops and are not recorded.
	ReloadAttempt(success bool)
	// SetExpiry publishes the NotAfter of the live leaf.
	SetExpiry(t time.Time)
}

Metrics records reload outcomes and live-cert expiry. Implementations must be safe for concurrent use. A nil Metrics disables metric recording, which lets callers keep their own pre-existing series by passing an adapter instead of the default below.

func DefaultMetrics

func DefaultMetrics(component string) Metrics

DefaultMetrics records to the shared apoxy_tls_cert_* series, labelled by component. Multiple Reloaders may share the same component label.

type Paths

type Paths struct {
	Cert string
	Key  string
	CA   string // optional; empty means there is no CA file to read
}

Paths locates the cert, key, and optional CA files on disk.

func FromDir

func FromDir(dir string) Paths

FromDir returns the standard Kubernetes Secret-mount layout under dir (tls.crt / tls.key / ca.crt).

type Reloader

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

Reloader serves a TLS server certificate from disk and hot-reloads it when the backing files rotate (e.g. cert-manager rewriting a Kubernetes Secret). GetCertificate reads are lock-free atomic-pointer loads with no syscalls — a background watcher (Start) does all the disk work — so they stay cheap on the handshake-accept hot path.

Wire GetCertificate into tls.Config.GetCertificate and run Start in a goroutine (or add it to a controller-runtime manager as a Runnable).

func NewReloader

func NewReloader(p Paths, component string) (*Reloader, error)

NewReloader loads the initial bundle (retrying briefly past a torn-write race) so construction fails fast on a genuinely missing/malformed keypair, and returns a Reloader ready to serve it. component labels the metrics and logs; metrics default to the shared apoxy_tls_cert_* series.

func (*Reloader) Bundle

func (r *Reloader) Bundle() *Bundle

Bundle returns the live bundle (cert + root pool + expiry), or nil before the first load. Useful for client-side consumers that need RootCAs.

func (*Reloader) GetCertificate

func (r *Reloader) GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error)

GetCertificate implements tls.Config.GetCertificate, returning the live leaf from the atomic store with no disk access.

func (*Reloader) Start

func (r *Reloader) Start(ctx context.Context) error

Start runs the background watcher until ctx is done. Implements sigs.k8s.io/controller-runtime/pkg/manager.Runnable.

type Store

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

Store holds the live Bundle behind an atomic pointer so handshakes, the metrics emitter, and the watcher can all read/write without locks.

func NewStore

func NewStore() *Store

NewStore returns an empty Store.

func (*Store) Load

func (s *Store) Load() *Bundle

Load returns the current bundle, or nil if none has been stored.

func (*Store) Store

func (s *Store) Store(b *Bundle)

Store publishes b as the current bundle in a single atomic swap.

type WatchOptions

type WatchOptions struct {
	// OnSwap, if set, is called after each successful bundle swap.
	OnSwap func(*Bundle)
	// Metrics records reload outcomes; nil disables metric recording.
	Metrics Metrics
	// Component labels this watcher's log lines.
	Component string
}

WatchOptions configures a watcher run.

Jump to

Keyboard shortcuts

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