ca

package
v1.6.10 Latest Latest
Warning

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

Go to latest
Published: Jan 28, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// DefaultValidity is the default certificate lifetime (10 years)
	DefaultValidity = 10 * 365 * 24 * time.Hour

	// DefaultBackdateDuration moves NotBefore into the past for clock skew tolerance (10 minutes)
	DefaultBackdateDuration = 10 * time.Minute
)

Default CA configuration values

View Source
const (
	// CAKeyBucketName is the NATS KV bucket for CA private keys
	CAKeyBucketName = "neuwerk-ca-keys"
)

KV bucket and key names for CA storage

Variables

This section is empty.

Functions

func DecryptPrivateKey

func DecryptPrivateKey(encryptedPEM []byte, password []byte) (*rsa.PrivateKey, error)

DecryptPrivateKey decrypts a PKCS#8 encrypted RSA private key The password parameter should match the one used in EncryptPrivateKey Returns error if PEM format is invalid or decryption fails

func DiscoverInterfaceIPs

func DiscoverInterfaceIPs() ([]net.IP, error)

DiscoverInterfaceIPs enumerates all network interface IP addresses. Returns deduplicated list including localhost (127.0.0.1, ::1).

func DistributeCertificateViaJetStream

func DistributeCertificateViaJetStream(kv KeyValueStore, nodeID string, certType CertificateType, certPEM []byte) (uint64, error)

DistributeCertificateViaJetStream stores certificate in JetStream KV for cluster-wide access. Key format: "neuwerk.certs.{nodeID}.{certType}" Example: "neuwerk.certs.node-1.nats", "neuwerk.certs.node-1.api-server"

Only certificates are stored (NOT private keys - security requirement). Certificates are public data and can be safely distributed. Returns revision number and error.

func EncryptPrivateKey

func EncryptPrivateKey(privateKey *rsa.PrivateKey, password []byte) ([]byte, error)

EncryptPrivateKey encrypts an RSA private key using PKCS#8 with AES-256-GCM The password parameter receives output from token.DeriveCAEncryptionKey(). PKCS#8 library runs PBKDF2 internally - this provides layered security:

  • Outer layer: Argon2id (token derivation)
  • Inner layer: PBKDF2 (PKCS#8 encryption)

Returns PEM-encoded "ENCRYPTED PRIVATE KEY" block

func EnsureCertDir

func EnsureCertDir(dir string) error

EnsureCertDir creates the certificate directory if it doesn't exist. Idempotent - safe to call multiple times. Directory is created with 0755 permissions (rwxr-xr-x).

func GenerateAllCSRs

func GenerateAllCSRs(customSANs []string) (map[CertificateType]*CSRResult, error)

GenerateAllCSRs generates all 3 CSRs (NATS, API Server, API Client) in parallel. Returns map of CertificateType to CSRResult.

func ReadFileBytes added in v1.6.1

func ReadFileBytes(path string) ([]byte, error)

ReadFileBytes reads a file and returns its contents as bytes. Helper function to avoid importing os in places that use this package.

func SignCSR

func SignCSR(csr *x509.CertificateRequest, certType CertificateType, opts SigningOptions) ([]byte, error)

SignCSR signs a CSR to create an X.509 certificate. The certificate inherits SANs from CSR (no modification by CA). Extended Key Usage is set based on certType.

Returns PEM-encoded certificate or error.

func ValidateCSR

func ValidateCSR(csrPEM []byte, minKeyBits int) (*x509.CertificateRequest, error)

ValidateCSR validates CSR structure, signature, and key strength. Returns parsed *x509.CertificateRequest if valid, error otherwise.

Validation checks:

  • PEM format: Type must be "CERTIFICATE REQUEST"
  • Signature: CheckSignature() proves private key possession
  • Key strength: RSA >= minKeyBits (4096), ECDSA >= P-384, Ed25519 accepted
  • Subject: CommonName must be non-empty
  • SANs: At least one DNS name or IP address required

func WriteAllCAs

func WriteAllCAs(certDir string, cas []RootCA) error

WriteAllCAs persists all 3 CA certificates to filesystem. Called by bootstrap manager after CA generation completes. Parameters:

  • certDir: Base directory for certificate storage
  • cas: Slice of RootCA containing PEM-encoded CA certificates

func WriteAllCertificates

func WriteAllCertificates(certDir string, certs map[CertificateType][]byte, keys map[CertificateType][]byte, kv KeyValueStore, nodeID string) error

WriteAllCertificates persists all 3 certificate types to filesystem and JetStream KV. Writes certificates atomically to prevent partial writes on crash. Distributes certificates to JetStream KV for cluster-wide availability.

Parameters:

  • certDir: Base directory for certificate storage (e.g., "/var/lib/neuwerk/certs")
  • certs: Map of CertificateType to PEM-encoded certificate
  • keys: Map of CertificateType to PEM-encoded private key
  • kv: JetStream KeyValue bucket (may be nil for single-node deployments)
  • nodeID: Node identifier for JetStream key naming (hostname)

If any write fails, returns error immediately (fail-fast).

func WriteCertificateAtomic

func WriteCertificateAtomic(certPath, keyPath string, certPEM, keyPEM []byte) error

WriteCertificateAtomic writes certificate and key files atomically using temp file + rename. Private keys are written with 0600 permissions (owner-only read/write). Certificates are written with 0644 permissions (world-readable).

Atomicity:

  • Creates temp files (certPath.tmp, keyPath.tmp)
  • Writes content to temp files
  • Renames temp files to final paths (atomic on POSIX)
  • Cleans up temp files if any step fails

This prevents partial writes if the process crashes mid-operation.

Types

type CAKeyStorage added in v1.6.1

type CAKeyStorage interface {
	// StoreCAKeys stores encrypted CA private keys to persistent storage.
	// Keys are encrypted with AES-256-GCM using the bootstrap token.
	StoreCAKeys(ctx context.Context, keys map[CertificateType][]byte) error

	// LoadCAKeys retrieves and decrypts CA private keys from storage.
	// Returns an error if keys are not found or decryption fails.
	LoadCAKeys(ctx context.Context) (map[CertificateType][]byte, error)

	// HasCAKeys checks if CA keys exist in storage without loading them.
	HasCAKeys(ctx context.Context) (bool, error)
}

CAKeyStorage defines the interface for storing and retrieving CA private keys. This enables late joiners to sign certificates even when the original bootstrap leader is no longer available.

type CAOptions

type CAOptions struct {
	// Validity is the certificate lifetime (default: 10 years)
	Validity time.Duration

	// BackdateDuration moves NotBefore into the past for clock skew tolerance (default: 10 minutes)
	BackdateDuration time.Duration

	// EncryptionKey is derived from bootstrap token via DeriveCAEncryptionKey()
	// Must be 32 bytes for AES-256-GCM encryption
	EncryptionKey []byte
}

CAOptions configures certificate authority generation

func DefaultCAOptions

func DefaultCAOptions(encryptionKey []byte) CAOptions

DefaultCAOptions returns default CA generation options The encryptionKey should be derived from bootstrap token via token.DeriveCAEncryptionKey()

type CAType

type CAType int

CAType identifies the purpose of a certificate authority

const (
	// CATypeNATS signs certificates for NATS cluster mTLS
	CATypeNATS CAType = iota
	// CATypeAPIServer signs certificates for API server HTTPS
	CATypeAPIServer
	// CATypeAPIClient signs certificates for API client authentication
	CATypeAPIClient
)

func (CAType) String

func (t CAType) String() string

String returns human-readable CA type name

type CRLManager

type CRLManager interface {
	GenerateInitialCRL(ctx context.Context, caType CAType) ([]byte, error)
	RefreshCRL(ctx context.Context, caType CAType) ([]byte, error)
}

CRLManager defines interface for CRL generation (avoids import cycle with crl package)

type CRLStore

type CRLStore interface {
	StoreCRL(ctx context.Context, caType CAType, crlBytes []byte) error
	FetchCRL(ctx context.Context, caType CAType) ([]byte, error)
	IncrementCRLNumber(ctx context.Context, caType CAType) (int64, error)
}

CRLStore defines interface for CRL persistence (avoids import cycle with crl package)

type CSROptions

type CSROptions struct {
	// CertType identifies the certificate purpose
	CertType CertificateType
	// CommonName is the certificate subject CN (hostname or generic name)
	CommonName string
	// CustomSANs are additional SANs from --san CLI flag
	CustomSANs []string
	// KeySize is the RSA key size (default 4096)
	KeySize int
}

CSROptions configures CSR generation

type CSRResult

type CSRResult struct {
	// CertType identifies the certificate purpose
	CertType CertificateType
	// CSRPEM is PEM-encoded certificate signing request
	CSRPEM []byte
	// PrivateKeyPEM is PEM-encoded private key (PKCS#8 format)
	PrivateKeyPEM []byte
	// PublicKey is the public key from the generated key pair
	PublicKey interface{}
}

CSRResult contains generated CSR and private key

func GenerateCSR

func GenerateCSR(opts CSROptions) (*CSRResult, error)

GenerateCSR generates a single CSR with key pair. Auto-discovers IPs via DiscoverInterfaceIPs() and includes hostname, localhost in SANs.

type CertPaths

type CertPaths struct {
	// BaseDir is the root directory for certificates (e.g., "/var/lib/neuwerk/certs")
	BaseDir string

	// NATS cluster mTLS certificate and key
	NATSCert string
	NATSKey  string

	// API server HTTPS certificate and key
	APIServerCert string
	APIServerKey  string

	// API client authentication certificate and key
	APIClientCert string
	APIClientKey  string

	// CA certificates for verification
	ClusterCA   string // NATS cluster CA (e.g., /var/lib/neuwerk/certs/nats-ca.crt)
	APIServerCA string // API server CA (e.g., /var/lib/neuwerk/certs/api-server-ca.crt)
	APIClientCA string // API client CA (e.g., /var/lib/neuwerk/certs/api-client-ca.crt)
}

CertPaths holds standard paths for certificate files.

func DefaultCertPaths

func DefaultCertPaths(baseDir string) CertPaths

DefaultCertPaths builds standard certificate paths from base directory. Example: baseDir="/var/lib/neuwerk/certs" → NATSCert="/var/lib/neuwerk/certs/nats-server.crt"

type CertificateType

type CertificateType int

CertificateType identifies the purpose of a certificate

const (
	// CertTypeNATS is for NATS cluster mTLS
	CertTypeNATS CertificateType = iota
	// CertTypeAPIServer is for API server HTTPS
	CertTypeAPIServer
	// CertTypeAPIClient is for API client authentication
	CertTypeAPIClient
)

func (CertificateType) String

func (t CertificateType) String() string

String returns human-readable certificate type name

type DistributeCARequest

type DistributeCARequest struct {
	CAs             []RootCA  // All three CAs (NATS, API Server, API Client)
	EncryptedJWTKey []byte    // Encrypted JWT signing key (encrypted with bootstrap token)
	Timestamp       time.Time // For logging/debugging
}

DistributeCARequest is sent by the leader to distribute CAs to all followers

type DistributeCAResponse

type DistributeCAResponse struct {
	Success bool
	Error   string // Empty if success
}

DistributeCAResponse is the response from followers after receiving CAs

type KeyValueStore

type KeyValueStore interface {
	Put(key string, value []byte) (uint64, error)
}

KeyValueStore is an interface for JetStream KV operations (enables testing).

type Manager

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

Manager orchestrates CA generation and distribution across the cluster. It coordinates the bootstrap leader's CA generation and distribution to followers via the PSK-encrypted Raft transport.

func NewManager

func NewManager(bootstrapToken token.SecureToken, transport Transport, logger logr.Logger) (*Manager, error)

NewManager creates a new CA manager instance. The transport callback is set to receive CAs from leader during distribution. For single-node deployments, transport may be nil (no distribution needed). The crlManager is optional and will be set later via SetCRLManager.

func NewManagerFromKeys added in v1.6.1

func NewManagerFromKeys(keys map[CertificateType][]byte, certDir string) (*Manager, error)

NewManagerFromKeys creates a CA manager initialized with CA private keys loaded from storage. This is used by late joiner support to create a manager that can sign certificates using CA keys loaded from NATS KV storage.

The keys map contains encrypted private keys indexed by CertificateType. The certDir is used to load CA certificates from disk for signing.

func (*Manager) GenerateAndDistribute

func (m *Manager) GenerateAndDistribute(ctx context.Context, peers []string) error

GenerateAndDistribute generates all three CAs and distributes them to peers. Called by the bootstrap leader after achieving leadership stability.

Steps:

  1. Derive encryption key from bootstrap token
  2. Generate all three CAs (NATS, API Server, API Client)
  3. Store CAs locally
  4. Distribute to all peers in parallel
  5. Log distribution results (failures are logged but don't block)

func (*Manager) GetCAPrivateKeys added in v1.6.1

func (m *Manager) GetCAPrivateKeys() map[CertificateType][]byte

GetCAPrivateKeys returns the CA private keys (encrypted) for storage. Used by controller to store CA keys in NATS KV for late joiner support. Returns a map of CertificateType to encrypted private key PEM bytes.

func (*Manager) GetCAs

func (m *Manager) GetCAs() []RootCA

GetCAs returns the generated or received CAs (thread-safe). Returns empty slice if CAs haven't been generated or received yet.

func (*Manager) GetCRLManager

func (m *Manager) GetCRLManager() CRLManager

GetCRLManager returns the CRL manager for downstream use

func (*Manager) GetEncryptedJWTKey

func (m *Manager) GetEncryptedJWTKey() []byte

GetEncryptedJWTKey returns the encrypted JWT signing key received from leader. Called by bootstrap manager to save the JWT key after CA distribution.

func (*Manager) HandleCertificateRequest

func (m *Manager) HandleCertificateRequest(nodeID string, csrMap map[CertificateType][]byte, isFollowerRequest bool) (map[CertificateType][]byte, error)

HandleCertificateRequest validates and signs CSRs from a follower node. Called by the bootstrap leader when handling RequestCertificate RPC.

Steps:

  1. Validate CAs are available (generated by leader)
  2. Derive encryption key from bootstrap token
  3. Validate each CSR (signature, key strength, SANs)
  4. Sign each CSR with matching CA (NATS CSR → NATS CA, etc.)
  5. Return map of signed certificates or error

Returns error if:

  • CAs not yet generated
  • CSR validation fails (weak key, invalid signature, etc.)
  • Certificate signing fails

The isFollowerRequest parameter indicates whether this is a follower CSR submission (via RPC) that should be counted for the WaitForAllCSRs tracking. Leader's own certificate signing should pass false to avoid being counted.

func (*Manager) SetCRLManager

func (m *Manager) SetCRLManager(crlManager CRLManager)

SetCRLManager sets the CRL manager for initial CRL generation Called after CA manager creation to avoid circular dependency

func (*Manager) SetEncryptedJWTKey

func (m *Manager) SetEncryptedJWTKey(encryptedKey []byte)

SetEncryptedJWTKey sets the encrypted JWT signing key to be distributed with CAs. Called by bootstrap manager after generating the JWT key.

func (*Manager) WaitForAllCSRs

func (m *Manager) WaitForAllCSRs(ctx context.Context) error

WaitForAllCSRs blocks until all expected peers have submitted their CSRs. Called by the bootstrap leader after CA distribution to ensure all followers complete certificate generation before the leader shuts down the Raft transport. Returns error if context is cancelled or times out.

type NATSCAKeyStorage added in v1.6.1

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

NATSCAKeyStorage implements CAKeyStorage using NATS JetStream KV. CA private keys are encrypted with AES-256-GCM before storage.

func NewNATSCAKeyStorage added in v1.6.1

func NewNATSCAKeyStorage(js jetstream.JetStream, encryptionKey []byte) (*NATSCAKeyStorage, error)

NewNATSCAKeyStorage creates a new NATS-backed CA key storage. The encryptionKey must be 32 bytes (derived from bootstrap token via DeriveCAEncryptionKey).

func (*NATSCAKeyStorage) HasCAKeys added in v1.6.1

func (s *NATSCAKeyStorage) HasCAKeys(ctx context.Context) (bool, error)

HasCAKeys checks if all CA keys exist in storage without loading them.

func (*NATSCAKeyStorage) LoadCAKeys added in v1.6.1

func (s *NATSCAKeyStorage) LoadCAKeys(ctx context.Context) (map[CertificateType][]byte, error)

LoadCAKeys retrieves and decrypts CA private keys from NATS KV.

func (*NATSCAKeyStorage) StoreCAKeys added in v1.6.1

func (s *NATSCAKeyStorage) StoreCAKeys(ctx context.Context, keys map[CertificateType][]byte) error

StoreCAKeys stores encrypted CA private keys to NATS KV. Each key is encrypted separately with AES-256-GCM.

type RootCA

type RootCA struct {
	// Type identifies the CA's purpose
	Type CAType

	// CertificatePEM is PEM-encoded X.509 certificate
	CertificatePEM []byte

	// EncryptedKeyPEM is PEM-encoded PKCS#8 encrypted private key
	EncryptedKeyPEM []byte

	// NotBefore is the certificate validity start time
	NotBefore time.Time

	// NotAfter is the certificate validity end time
	NotAfter time.Time

	// SerialNumber is the certificate serial number
	SerialNumber *big.Int
}

RootCA represents a generated certificate authority

func GenerateRootCA

func GenerateRootCA(caType CAType, opts CAOptions) (*RootCA, error)

GenerateRootCA generates a self-signed root certificate authority The CA private key is encrypted with AES-256-GCM before being returned

Parameters:

  • caType: identifies the CA's purpose (NATS, API Server, or API Client)
  • opts: configuration options (validity period, backdate duration, encryption key)

Returns:

  • *RootCA containing PEM-encoded certificate and encrypted private key
  • error if key generation, certificate creation, or encryption fails

type SigningOptions

type SigningOptions struct {
	// CA is the root CA to sign with
	CA *RootCA
	// EncryptionKey for decrypting CA private key (from token.DeriveCAEncryptionKey)
	EncryptionKey []byte
	// Validity is certificate lifetime (default: 90 days)
	Validity time.Duration
	// BackdateDuration moves NotBefore into past for clock skew tolerance (default: 10 minutes)
	BackdateDuration time.Duration
}

SigningOptions configures certificate signing

func DefaultSigningOptions

func DefaultSigningOptions(ca *RootCA, encryptionKey []byte) SigningOptions

DefaultSigningOptions returns default signing configuration

type Transport

type Transport interface {
	SendCustomRPC(rpcType uint8, target string, req interface{}, resp interface{}) error
	SetCAReceiverCallback(callback func(cas []RootCA, encryptedJWTKey []byte) error)
}

Transport interface for CA distribution (avoids import cycle with raft package).

Jump to

Keyboard shortcuts

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