Documentation
¶
Overview ¶
Package certificate provides pluggable TLSBackend abstractions for certificate management in Kubernetes operators.
It supports three backends:
- selfmanaged: generates and rotates CA + leaf certificates using Go's crypto/x509, returning them as Kubernetes Secrets for SSA apply.
- byo (bring-your-own): references an existing user-managed Secret, emitting no child objects.
- certmanager (optional subpackage): emits cert-manager Issuer/Certificate CRs and relies on cert-manager to produce the Secret.
The selfmanaged backend supports ECDSA curve selection (P-256/P-384/P-521), IP SANs, an optional CA CRL, and a renewal window; the reusable rotation saga lives in pkg/controller/certificate/rotation.
Index ¶
- Constants
- Variables
- func DedupIPs(in []net.IP) []net.IP
- func DedupStrings(in []string) []string
- func GetValidCADays(spec TLSSpec) int
- func GetValidLeafDays(spec TLSSpec) int
- func GetValidRenewalDays(spec TLSSpec) int
- func RolloutAnnotation(shouldRollout bool, secret *corev1.Secret, currentHash string) (map[string]string, error)
- func SecretHash(secret *corev1.Secret) (string, error)
- func SecretHashAnnotation(secret *corev1.Secret) (map[string]string, error)
- func ShouldRollout(policy RolloutPolicy, sig *LayerSignals) bool
- type CertificateCustomizer
- type CertificateCustomizerFunc
- type CertificateSubject
- type ContentIgnoringBackend
- type LayerSignals
- type LeafChange
- type LeafChangeReason
- type LeafManager
- type NodeSetTLSBackend
- type RolloutPolicy
- type TLSBackend
- type TLSSpec
- func (s TLSSpec) EffectiveKeyAlgorithm() string
- func (s TLSSpec) EffectiveKeySize() (int, error)
- func (s TLSSpec) EffectiveKeyUsages() []string
- func (s TLSSpec) EffectiveUsages() []string
- func (s TLSSpec) LeafSubject() pkix.Name
- func (s TLSSpec) Organizations() []string
- func (s TLSSpec) ResolvedCASubject() pkix.Name
- func (s TLSSpec) ValidateContent() error
- type TLSSpecProvider
- type TLSSpecProviderFunc
Constants ¶
const ( // CurveP256 is the default ECDSA curve (secp256r1). CurveP256 = "P-256" // CurveP384 selects ECDSA P-384 (secp384r1). CurveP384 = "P-384" // CurveP521 selects ECDSA P-521 (secp521r1). CurveP521 = "P-521" // DefaultRenewalDays is the renewal window before NotAfter when renewal // is triggered, used when TLSSpec.RenewalDays is not set. DefaultRenewalDays = 30 // MaxRenewalDays is the largest accepted renewal window. Values above // this are clamped down to it. This prevents integer overflow when // computing `RenewalDays * 24 * time.Hour` (a time.Duration in int64 // nanoseconds overflows around 106_752 days, which would produce a // negative window and silently disable renewal — or a huge positive // window causing perpetual renewal). 100 years is well below the // overflow threshold and far exceeds any legitimate cert validity. MaxRenewalDays = 36500 // MaxValidityDays is the largest accepted certificate validity (leaf or // CA) in days. It mirrors MaxRenewalDays: `ValidityDays * 24 * time.Hour` // (a time.Duration in int64 nanoseconds) overflows around 106_752 days, // which would either fail certificate creation or wrap to a small negative // (already-expired) NotAfter and trigger a perpetual renewal loop. 100 // years is safely below the overflow threshold and exceeds any legitimate // certificate validity. MaxValidityDays = 36500 // KeyAlgorithmECDSA selects an ECDSA private key (P-256/P-384/P-521). KeyAlgorithmECDSA = "ECDSA" // KeyAlgorithmRSA selects an RSA private key. KeyAlgorithmRSA = "RSA" // DefaultECDSAKeySize is the default ECDSA key size in bits (P-256). DefaultECDSAKeySize = 256 // DefaultRSAKeySize is the default RSA key size in bits. DefaultRSAKeySize = 2048 // MaxRSAKeySize is the largest accepted RSA key size in bits. RSA key // generation is CPU- and memory-intensive and scales super-linearly with // the bit size. An unbounded KeySize would let a misconfiguration (or a // tampered CRD in a multi-tenant deployment) drive rsa.GenerateKey to // synthesize an enormous key and exhaust the controller's CPU/memory // (CWE-400/CWE-770). 8192 is a generous ceiling that covers even very // high-assurance deployments while keeping generation bounded. MaxRSAKeySize = 8192 // ExtKeyUsage (leaf) string vocabulary, mapped per backend. UsageServerAuth = "serverAuth" UsageClientAuth = "clientAuth" UsageCodeSigning = "codeSigning" UsageEmailProtection = "emailProtection" UsageTimestamping = "timestamping" UsageOCSPSigning = "ocspSigning" // KeyUsage string vocabulary, mapped per backend. KeyUsageDigitalSignature = "digitalSignature" KeyUsageContentCommitment = "contentCommitment" KeyUsageKeyEncipherment = "keyEncipherment" KeyUsageDataEncipherment = "dataEncipherment" KeyUsageKeyAgreement = "keyAgreement" KeyUsageCertSign = "certSign" KeyUsageCRLSign = "crlSign" )
const ( // AnnotationSecretHash is the annotation key used to store a stable // hash of the certificate Secret contents on pod templates. When the // Secret changes, the hash changes, triggering a natural rolling restart. AnnotationSecretHash = "operator-sdk-extra.webcenter.fr/certificate-hash" // AnnotationForceRegenerateAll triggers a full CA+leaf rotation + rollout. // Read as == "true" (the ignoreReconcile convention). Overridable via // rotation.WithForceRegenerateAllAnnotation. AnnotationForceRegenerateAll = "operator-sdk-extra.webcenter.fr/force-regenerate-tls" // AnnotationForceRegenerateLeaf triggers a leaf-only regen + rollout. AnnotationForceRegenerateLeaf = "operator-sdk-extra.webcenter.fr/force-regenerate-certificates" )
Variables ¶
var ErrInvalidKeySize = errors.Sentinel("invalid key size")
ErrInvalidKeySize is returned when TLSSpec.KeySize is invalid for the selected key algorithm (or disagrees with TLSSpec.Curve).
var ErrUnknownCurve = errors.Sentinel("unknown curve")
ErrUnknownCurve is returned when TLSSpec.Curve is set to a value other than CurveP256, CurveP384 or CurveP521.
var ErrUnknownUsage = errors.Sentinel("unknown usage")
ErrUnknownUsage is returned when TLSSpec.Usages or TLSSpec.KeyUsages contains an unknown usage string.
var ErrUnsupportedKeyAlgorithm = errors.Sentinel("unsupported key algorithm")
ErrUnsupportedKeyAlgorithm is returned when TLSSpec.KeyAlgorithm is set to an unsupported value. It is wrapped with context at each failing site so errors.Cause still finds it.
Functions ¶
func DedupIPs ¶ added in v3.0.6
DedupIPs returns a copy of in with duplicate IPs removed, preserving the order of first occurrence. Duplicates are detected by each IP's canonical String() form (so "2001:db8::1" and "2001:db8:0:0:0:0:0:1" dedupe).
func DedupStrings ¶ added in v3.0.6
DedupStrings returns a copy of in with duplicate entries removed, preserving the order of first occurrence.
func GetValidCADays ¶ added in v3.0.4
GetValidCADays returns the CA certificate validity in days, defaulting to 2× GetValidLeafDays when CAValidityDays <= 0. Values above MaxValidityDays are clamped down to it (see MaxValidityDays).
func GetValidLeafDays ¶ added in v3.0.4
GetValidLeafDays returns the leaf certificate validity in days, defaulting to 365 when LeafValidityDays <= 0. Values above MaxValidityDays are clamped down to it to prevent time.Duration overflow in downstream `LeafValidityDays * 24 * time.Hour` math (see MaxValidityDays).
func GetValidRenewalDays ¶ added in v3.0.4
GetValidRenewalDays returns the renewal window in days, defaulting to DefaultRenewalDays (30) when RenewalDays is not set or invalid (<= 0). Values above MaxRenewalDays are clamped to MaxRenewalDays to prevent time.Duration overflow in downstream `RenewalDays * 24 * time.Hour` math (see MaxRenewalDays).
func RolloutAnnotation ¶ added in v3.0.4
func RolloutAnnotation(shouldRollout bool, secret *corev1.Secret, currentHash string) (map[string]string, error)
RolloutAnnotation builds the pod-template annotation map entry.
- shouldRollout=true: fresh hash of secret (SecretHashAnnotation).
- shouldRollout=false and currentHash!="": keep currentHash (no restart).
- shouldRollout=false and currentHash=="" (first run): initialize hash.
func SecretHash ¶
SecretHash computes a stable SHA-256 hash of a Secret's data fields. This hash is used as a pod-template annotation so that any change to the certificate Secret forces a rolling restart of the consuming workload.
This decouples "certificate changed" from "must restart" without bespoke polling of StatefulSet.Status.CurrentReplicas.
func SecretHashAnnotation ¶
SecretHashAnnotation builds the annotation map entry for the certificate hash. The returned map is suitable for merging into a pod template's metadata.annotations.
func ShouldRollout ¶ added in v3.0.4
func ShouldRollout(policy RolloutPolicy, sig *LayerSignals) bool
ShouldRollout decides whether a pod rollout is needed for one layer's signals. Forced overrides everything, including RolloutNever.
Types ¶
type CertificateCustomizer ¶ added in v3.0.6
type CertificateCustomizer[T object.MultiPhaseObject] interface { CustomizeCertificate(o T, base TLSSpec) (TLSSpec, error) }
CertificateCustomizer lets an operator compute certificate content that cannot be declared statically (e.g., SANs/IPs from cluster state). It receives the reconciled object and the declared base TLSSpec and returns the final content. Return base unchanged to keep declared defaults. It has no ctx by design (matching TLSSpecProvider/NodeSpecProvider); capture a client in a closure if cluster reads are needed.
func DefaultCertificateCustomizer ¶ added in v3.0.6
func DefaultCertificateCustomizer[T object.MultiPhaseObject]() CertificateCustomizer[T]
DefaultCertificateCustomizer returns a customizer that only fills CommonName with the object name when empty, and is otherwise the identity. Opt-in: pass it explicitly via rotation.WithCertificateCustomizer (no implicit behavior change for existing operators).
type CertificateCustomizerFunc ¶ added in v3.0.6
type CertificateCustomizerFunc[T object.MultiPhaseObject] func(o T, base TLSSpec) (TLSSpec, error)
CertificateCustomizerFunc adapts a func to CertificateCustomizer.
func (CertificateCustomizerFunc[T]) CustomizeCertificate ¶ added in v3.0.6
func (f CertificateCustomizerFunc[T]) CustomizeCertificate(o T, base TLSSpec) (TLSSpec, error)
CustomizeCertificate implements CertificateCustomizer.
type CertificateSubject ¶ added in v3.0.6
type CertificateSubject struct {
Organizations []string `json:"organizations,omitempty"`
OrganizationalUnits []string `json:"organizationalUnits,omitempty"`
Countries []string `json:"countries,omitempty"`
Localities []string `json:"localities,omitempty"`
Provinces []string `json:"provinces,omitempty"`
StreetAddresses []string `json:"streetAddresses,omitempty"`
PostalCodes []string `json:"postalCodes,omitempty"`
SerialNumber string `json:"serialNumber,omitempty"`
}
CertificateSubject is the certificate RDN block beyond the legacy single CommonName/Organization fields. All fields are optional; empty = unset.
type ContentIgnoringBackend ¶ added in v3.0.6
type ContentIgnoringBackend interface {
// IgnoresCertificateContent reports whether TLSSpec content fields
// (subject, SANs, key algorithm/size, usages, validity) are ignored by
// this backend.
IgnoresCertificateContent() bool
}
ContentIgnoringBackend is an optional capability for backends that perform no certificate generation and therefore ignore TLSSpec content fields (e.g. the BYO backend, which only references an existing Secret). The rotation saga skips ValidateContent for such backends while still enforcing each backend's own structural validation (e.g. a non-empty SecretName).
type LayerSignals ¶ added in v3.0.4
type LayerSignals struct {
CARotated bool // a CA-saga started this cycle
LeafRegenerated bool // leaf regenerated (CA saga OR leaf-only)
LeafChange *LeafChange // drift/expiry detail (SAN/IP/node deltas)
Forced bool // a force annotation was honored this cycle
}
LayerSignals is the per-layer change-delta signal a TLS step publishes under data["tls.<phaseName>"] for the STS step to gate rollout.
type LeafChange ¶ added in v3.0.4
type LeafChange struct {
Reason LeafChangeReason
SANsAdded []string
SANsRemoved []string
IPsAdded []string
IPsRemoved []string
NodesAdded []string
NodesRemoved []string
}
LeafChange describes leaf drift vs spec at `now`. Delta slices are always populated (independent of Reason) so consumers can compute additive-only rollout decisions.
func (LeafChange) IsZero ¶ added in v3.0.4
func (c LeafChange) IsZero() bool
IsZero reports whether no regeneration is needed.
type LeafChangeReason ¶ added in v3.0.4
type LeafChangeReason string
LeafChangeReason is the dominant reason a leaf needs regeneration.
const ( LeafNone LeafChangeReason = "" LeafMissing LeafChangeReason = "Missing" LeafExpiring LeafChangeReason = "Expiring" LeafCNChanged LeafChangeReason = "CNChanged" LeafOrgChanged LeafChangeReason = "OrgChanged" LeafSubjectChanged LeafChangeReason = "SubjectChanged" LeafKeyChanged LeafChangeReason = "KeyChanged" LeafUsagesChanged LeafChangeReason = "UsagesChanged" LeafSANsChanged LeafChangeReason = "SANsChanged" LeafIPsChanged LeafChangeReason = "IPsChanged" LeafNodesChanged LeafChangeReason = "NodesChanged" LeafForceRegen LeafChangeReason = "Forced" )
type LeafManager ¶ added in v3.0.4
type LeafManager[T object.MultiPhaseObject] interface { // DesiredLeafWithCA re-issues the leaf signed by the CA in caSecret, // reusing that CA's key/cert (no new CA). The returned Secret's ca.crt // equals caSecret's ca.crt (single CA, no bundle). DesiredLeafWithCA(ctx context.Context, o T, spec TLSSpec, caSecret *corev1.Secret) (*corev1.Secret, error) // LeafNeedsChange reports whether the leaf Secret needs regeneration vs // spec at now. leafSecret may be nil (treated as LeafMissing). LeafNeedsChange(ctx context.Context, o T, leafSecret *corev1.Secret, spec TLSSpec, now time.Time) (LeafChange, error) }
LeafManager is the optional capability for saga backends that own their leaf Secret and can (a) regenerate it against an existing CA and (b) report drift. Implemented by selfmanaged (single-leaf) and selfmanaged/pernode. The rotation step type-asserts this; when absent (cert-manager/BYO) leaf-only regen is skipped and drift falls back to the full CA saga.
type NodeSetTLSBackend ¶ added in v3.0.4
type NodeSetTLSBackend[T object.MultiPhaseObject] interface { TLSBackend[T] // ExpectedNodeNames returns the node names that should have a certificate. ExpectedNodeNames(o T) ([]string, error) // NodeSecretKeys returns the Data-key suffixes for a node's cert and key // (e.g. ".crt", ".key"); a node's Data keys are name+certSuffix and // name+keySuffix. NodeSecretKeys() (certSuffix, keySuffix string) }
NodeSetTLSBackend marks per-node (multi-cert) saga backends. Its presence changes rotation data publishing (omit tlsSecret/leafCert).
type RolloutPolicy ¶ added in v3.0.4
type RolloutPolicy string
RolloutPolicy selects which change-delta signals trigger a rollout.
const ( RolloutAlways RolloutPolicy = "Always" RolloutOnCAChange RolloutPolicy = "OnCAChange" RolloutOnAdditive RolloutPolicy = "OnAdditive" // recommended default RolloutNever RolloutPolicy = "Never" )
type TLSBackend ¶
type TLSBackend[T object.MultiPhaseObject] interface { // DesiredObjects returns the child objects that the reconciler should // manage via SSA. These may be Secrets (self-managed) or cert-manager // CRs (cert-manager backend). DesiredObjects(ctx context.Context, o T, spec TLSSpec) ([]client.Object, error) // CertificateSecretName returns the name of the Secret that consumers // should mount or hash for rolling restart. CertificateSecretName(o T, spec TLSSpec) string // RequiresRotationSaga reports whether the multi-cycle CA-rotation // workflow is needed. Only the self-managed mutual-TLS backend returns // true; cert-manager and BYO backends return false. RequiresRotationSaga() bool }
TLSBackend defines the interface for pluggable certificate providers.
Each backend returns the child objects it wants the reconciler to manage via SSA, the name of the resulting certificate Secret, and whether a multi-cycle CA-rotation saga is needed.
- selfmanaged backend: returns Secret objects (CA + leaf), needs saga.
- cert-manager backend: returns Issuer/Certificate CRs, no saga needed.
- BYO backend: returns nothing, no saga needed.
type TLSSpec ¶
type TLSSpec struct {
// SecretName is the name of the Secret that will hold the certificate
// and key. Consumers mount this Secret or hash it for rollout.
SecretName string `json:"secretName,omitempty"`
// IssuerRef references an existing Issuer or ClusterIssuer for the
// cert-manager backend (existing-CA mode). When empty, a self-signed
// Issuer is created (dedicated-CA mode).
IssuerRef string `json:"issuerRef,omitempty"`
// CommonName is the CN for the generated certificate.
CommonName string `json:"commonName,omitempty"`
// DNSNames lists the SAN DNS names for the certificate.
DNSNames []string `json:"dnsNames,omitempty"`
// Organization is the O field for the certificate.
Organization string `json:"organization,omitempty"`
// LeafValidityDays is the leaf certificate validity in days.
// Defaults to 365 if not set (see GetValidLeafDays).
LeafValidityDays int `json:"leafValidityDays,omitempty"`
// CAValidityDays is the CA certificate validity in days.
// Defaults to 2× LeafValidityDays when <= 0 (see GetValidCADays).
CAValidityDays int `json:"caValidityDays,omitempty"`
// Curve selects the ECDSA curve. One of P-256 (default), P-384, P-521.
// Unknown values are rejected by ValidateContent. It drives the effective
// ECDSA key size (EffectiveKeySize) and therefore also cert-manager's
// privateKey.size.
Curve string `json:"curve,omitempty"`
// IPAddresses lists the SAN IPs for the generated certificate.
// Each entry must parse via net.ParseIP.
IPAddresses []string `json:"ipAddresses,omitempty"`
// RenewalDays is the window before expiry during which renewal is
// triggered. Defaults to 30 (see GetValidRenewalDays).
RenewalDays int `json:"renewalDays,omitempty"`
// GenerateCRL, when true, adds a ca.crl key (DER-encoded revocation
// list) to the CA Secret (selfmanaged only).
GenerateCRL bool `json:"generateCRL,omitempty"`
// Subject is the extended RDN block. When Subject.Organizations is empty,
// the legacy Organization field is used (backward compatible).
Subject CertificateSubject `json:"subject,omitempty"`
// KeyAlgorithm is ECDSA (default) or RSA.
KeyAlgorithm string `json:"keyAlgorithm,omitempty"`
// KeySize is the key size in bits. 0 = default per algorithm
// (ECDSA -> 256/P-256; RSA -> 2048). For ECDSA must be 256/384/521.
KeySize int `json:"keySize,omitempty"`
// Usages is the leaf ExtKeyUsage list (see Usage* constants).
// Empty = [serverAuth, clientAuth].
Usages []string `json:"usages,omitempty"`
// KeyUsages is the leaf KeyUsage list (see KeyUsage* constants).
// Empty = [digitalSignature, keyEncipherment].
KeyUsages []string `json:"keyUsages,omitempty"`
// CACommonName overrides the CA certificate CN. Default "<CommonName>-ca".
CACommonName string `json:"caCommonName,omitempty"`
// CASubject overrides the CA RDN block. Default = leaf subject with
// CN = CACommonName.
CASubject *CertificateSubject `json:"caSubject,omitempty"`
}
TLSSpec defines the desired (computed) TLS configuration for a component. Operators embed a SLIMMED version in their CRD spec and pass computed values to the library via a TLSSpecProvider. Backend selection is an operator decision (which backend type to construct), NOT spec data — the old SelfSigned/CertManager booleans are removed.
func (TLSSpec) EffectiveKeyAlgorithm ¶ added in v3.0.6
EffectiveKeyAlgorithm returns KeyAlgorithm or KeyAlgorithmECDSA (default).
func (TLSSpec) EffectiveKeySize ¶ added in v3.0.6
EffectiveKeySize resolves the key size: KeySize if > 0, else the size implied by Curve (P-256->256, P-384->384, P-521->521), else the algorithm default. Returns an error if KeySize and Curve disagree.
func (TLSSpec) EffectiveKeyUsages ¶ added in v3.0.6
EffectiveKeyUsages returns the leaf KeyUsage strings (defaults applied).
func (TLSSpec) EffectiveUsages ¶ added in v3.0.6
EffectiveUsages returns the leaf ExtKeyUsage strings (defaults applied).
func (TLSSpec) LeafSubject ¶ added in v3.0.6
LeafSubject returns the resolved leaf pkix.Name (CN + full RDN).
func (TLSSpec) Organizations ¶ added in v3.0.6
Organizations returns the effective leaf organizations: Subject.Organizations if non-empty, else []string{Organization} (dropping "").
func (TLSSpec) ResolvedCASubject ¶ added in v3.0.6
ResolvedCASubject returns the resolved CA pkix.Name. CN = CACommonName (default "<CommonName>-ca"); other RDN fields = CASubject if set, else leaf subject. It is a method rather than a field because the exported `CASubject *CertificateSubject` field already occupies that identifier.
func (TLSSpec) ValidateContent ¶ added in v3.0.6
ValidateContent validates all customizable content fields. Returns the first error. Called by backends and by the rotation saga before generation.
type TLSSpecProvider ¶ added in v3.0.4
type TLSSpecProvider[T object.MultiPhaseObject] interface { TLSSpec(o T) TLSSpec }
TLSSpecProvider supplies the computed TLSSpec for an object. The operator implements this (or uses TLSSpecProviderFunc) instead of passing a bare func to NewTLSStep.
type TLSSpecProviderFunc ¶ added in v3.0.4
type TLSSpecProviderFunc[T object.MultiPhaseObject] func(o T) TLSSpec
TLSSpecProviderFunc adapts a func(o T) TLSSpec to TLSSpecProvider.
func (TLSSpecProviderFunc[T]) TLSSpec ¶ added in v3.0.4
func (f TLSSpecProviderFunc[T]) TLSSpec(o T) TLSSpec
Directories
¶
| Path | Synopsis |
|---|---|
|
Package byo provides a TLSBackend that references an existing user-managed Secret.
|
Package byo provides a TLSBackend that references an existing user-managed Secret. |
|
Package certmanager provides a TLSBackend that emits cert-manager Issuer and Certificate custom resources.
|
Package certmanager provides a TLSBackend that emits cert-manager Issuer and Certificate custom resources. |
|
Package rotation provides a reusable multi-cycle TLS rotation saga step built on workflow.WorkflowStepReconcilerActionWithDiff.
|
Package rotation provides a reusable multi-cycle TLS rotation saga step built on workflow.WorkflowStepReconcilerActionWithDiff. |
|
Package selfmanaged provides a TLSBackend that generates and manages CA and leaf certificates using Go's crypto/x509 standard library.
|
Package selfmanaged provides a TLSBackend that generates and manages CA and leaf certificates using Go's crypto/x509 standard library. |
|
pernode
Package pernode provides a TLSBackend that keeps one certificate per node in a single Secret (multi-cert transport TLS, e.g.
|
Package pernode provides a TLSBackend that keeps one certificate per node in a single Secret (multi-cert transport TLS, e.g. |