From c04d24cafa60e522842d5188587ab07af2082e9b Mon Sep 17 00:00:00 2001 From: Francis Lavoie Date: Mon, 13 Dec 2021 14:25:35 -0500 Subject: [PATCH] pki: Avoid provisioning the `local` CA when not necessary (#4463) * pki: Avoid provisioning the `local` CA when not necessary * pki: Refactor CA loading to keep the logic in the PKI app --- modules/caddypki/acmeserver/acmeserver.go | 6 +-- modules/caddypki/pki.go | 58 +++++++++++++++++++---- modules/caddytls/internalissuer.go | 15 +++--- 3 files changed, 59 insertions(+), 20 deletions(-) diff --git a/modules/caddypki/acmeserver/acmeserver.go b/modules/caddypki/acmeserver/acmeserver.go index 42d8cc49..388aa529 100644 --- a/modules/caddypki/acmeserver/acmeserver.go +++ b/modules/caddypki/acmeserver/acmeserver.go @@ -102,9 +102,9 @@ func (ash *Handler) Provision(ctx caddy.Context) error { return err } pkiApp := appModule.(*caddypki.PKI) - ca, ok := pkiApp.CAs[ash.CA] - if !ok { - return fmt.Errorf("no certificate authority configured with id: %s", ash.CA) + ca, err := pkiApp.GetCA(ash.CA, &ctx) + if err != nil { + return err } database, err := ash.openDatabase() diff --git a/modules/caddypki/pki.go b/modules/caddypki/pki.go index 012eaafd..f391fdaf 100644 --- a/modules/caddypki/pki.go +++ b/modules/caddypki/pki.go @@ -34,6 +34,8 @@ func init() { type PKI struct { // The certificate authorities to manage. Each CA is keyed by an // ID that is used to uniquely identify it from other CAs. + // At runtime, the GetCA() method should be used instead to ensure + // the default CA is provisioned if it hadn't already been. // The default CA ID is "local". CAs map[string]*CA `json:"certificate_authorities,omitempty"` @@ -54,16 +56,6 @@ func (p *PKI) Provision(ctx caddy.Context) error { p.ctx = ctx p.log = ctx.Logger(p) - // if this app is initialized at all, ensure there's at - // least a default CA that can be used: the standard CA - // which is used implicitly for signing local-use certs - if p.CAs == nil { - p.CAs = make(map[string]*CA) - } - if _, ok := p.CAs[DefaultCAID]; !ok { - p.CAs[DefaultCAID] = new(CA) - } - for caID, ca := range p.CAs { err := ca.Provision(ctx, caID, p.log) if err != nil { @@ -71,9 +63,29 @@ func (p *PKI) Provision(ctx caddy.Context) error { } } + // if this app is initialized at all, ensure there's at + // least a default CA that can be used: the standard CA + // which is used implicitly for signing local-use certs + if len(p.CAs) == 0 { + err := p.ProvisionDefaultCA(ctx) + if err != nil { + return fmt.Errorf("provisioning CA '%s': %v", DefaultCAID, err) + } + } + return nil } +// ProvisionDefaultCA sets up the default CA. +func (p *PKI) ProvisionDefaultCA(ctx caddy.Context) error { + if p.CAs == nil { + p.CAs = make(map[string]*CA) + } + + p.CAs[DefaultCAID] = new(CA) + return p.CAs[DefaultCAID].Provision(ctx, DefaultCAID, p.log) +} + // Start starts the PKI app. func (p *PKI) Start() error { // install roots to trust store, if not disabled @@ -107,6 +119,32 @@ func (p *PKI) Stop() error { return nil } +// GetCA retrieves a CA by ID. If the ID is the default +// CA ID, and it hasn't been provisioned yet, it will +// be provisioned. The context must be passed if this +// is called from a module's Provision(). +func (p *PKI) GetCA(id string, ctx *caddy.Context) (*CA, error) { + ca, ok := p.CAs[id] + if !ok { + // for anything other than the default CA ID, error out if it wasn't configured + if id != DefaultCAID { + return nil, fmt.Errorf("no certificate authority configured with id: %s", id) + } + + // for the default CA ID, provision it, because we want it to "just work" + if ctx == nil { + return nil, fmt.Errorf("cannot provision default CA without the context") + } + err := p.ProvisionDefaultCA(*ctx) + if err != nil { + return nil, fmt.Errorf("failed to provision default CA: %s", err) + } + ca = p.CAs[id] + } + + return ca, nil +} + // Interface guards var ( _ caddy.Provisioner = (*PKI)(nil) diff --git a/modules/caddytls/internalissuer.go b/modules/caddytls/internalissuer.go index 7a25f6d3..d9b6117d 100644 --- a/modules/caddytls/internalissuer.go +++ b/modules/caddytls/internalissuer.go @@ -19,7 +19,6 @@ import ( "context" "crypto/x509" "encoding/pem" - "fmt" "time" "github.com/caddyserver/caddy/v2" @@ -68,18 +67,20 @@ func (InternalIssuer) CaddyModule() caddy.ModuleInfo { func (iss *InternalIssuer) Provision(ctx caddy.Context) error { iss.logger = ctx.Logger(iss) + // set some defaults + if iss.CA == "" { + iss.CA = caddypki.DefaultCAID + } + // get a reference to the configured CA appModule, err := ctx.App("pki") if err != nil { return err } pkiApp := appModule.(*caddypki.PKI) - if iss.CA == "" { - iss.CA = caddypki.DefaultCAID - } - ca, ok := pkiApp.CAs[iss.CA] - if !ok { - return fmt.Errorf("no certificate authority configured with id: %s", iss.CA) + ca, err := pkiApp.GetCA(iss.CA, &ctx) + if err != nil { + return err } iss.ca = ca