Go API Documentation

github.com/caddyserver/caddy/v2/modules/caddypki

No package summary is available.

Package

Files: 7. Third party imports: 7. Imports from organisation: 1. Tests: 0. Benchmarks: 0.

Constants

const (
	// DefaultCAID is the default CA ID.
	DefaultCAID	= "local"

	defaultCAName			= "Caddy Local Authority"
	defaultRootCommonName		= "{pki.ca.name} - {time.now.year} ECC Root"
	defaultIntermediateCommonName	= "{pki.ca.name} - ECC Intermediate"

	defaultRootLifetime		= 24 * time.Hour * 30 * 12 * 10
	defaultIntermediateLifetime	= 24 * time.Hour * 7
)
// adminPKIEndpointBase is the base admin endpoint under which all PKI admin endpoints exist.
const adminPKIEndpointBase = "/pki/"
const renewalWindowRatio = 0.2	// TODO: make configurable

Vars

var (
	_	caddy.AdminRouter	= (*adminAPI)(nil)
	_	caddy.Provisioner	= (*adminAPI)(nil)
)

Types

AuthorityConfig

AuthorityConfig is used to help a CA configure the underlying signing authority.

type AuthorityConfig struct {
	SignWithRoot	bool

	// TODO: should we just embed the underlying authority.Config struct type?
	DB		*db.AuthDB
	AuthConfig	*authority.AuthConfig
}

CA

CA describes a certificate authority, which consists of root/signing certificates and various settings pertaining to the issuance of certificates and trusting them.

type CA struct {
	// The user-facing name of the certificate authority.
	Name	string	`json:"name,omitempty"`

	// The name to put in the CommonName field of the
	// root certificate.
	RootCommonName	string	`json:"root_common_name,omitempty"`

	// The name to put in the CommonName field of the
	// intermediate certificates.
	IntermediateCommonName	string	`json:"intermediate_common_name,omitempty"`

	// The lifetime for the intermediate certificates
	IntermediateLifetime	caddy.Duration	`json:"intermediate_lifetime,omitempty"`

	// Whether Caddy will attempt to install the CA's root
	// into the system trust store, as well as into Java
	// and Mozilla Firefox trust stores. Default: true.
	InstallTrust	*bool	`json:"install_trust,omitempty"`

	// The root certificate to use; if null, one will be generated.
	Root	*KeyPair	`json:"root,omitempty"`

	// The intermediate (signing) certificate; if null, one will be generated.
	Intermediate	*KeyPair	`json:"intermediate,omitempty"`

	// Optionally configure a separate storage module associated with this
	// issuer, instead of using Caddy's global/default-configured storage.
	// This can be useful if you want to keep your signing keys in a
	// separate location from your leaf certificates.
	StorageRaw	json.RawMessage	`json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"`

	// The unique config-facing ID of the certificate authority.
	// Since the ID is set in JSON config via object key, this
	// field is exported only for purposes of config generation
	// and module provisioning.
	ID	string	`json:"-"`

	storage		certmagic.Storage
	root, inter	*x509.Certificate
	interKey	any	// TODO: should we just store these as crypto.Signer?
	mu		*sync.RWMutex

	rootCertPath	string	// mainly used for logging purposes if trusting
	log		*zap.Logger
	ctx		caddy.Context
}

KeyPair

KeyPair represents a public-private key pair, where the public key is also called a certificate.

type KeyPair struct {
	// The certificate. By default, this should be the path to
	// a PEM file unless format is something else.
	Certificate	string	`json:"certificate,omitempty"`

	// The private key. By default, this should be the path to
	// a PEM file unless format is something else.
	PrivateKey	string	`json:"private_key,omitempty"`

	// The format in which the certificate and private
	// key are provided. Default: pem_file
	Format	string	`json:"format,omitempty"`
}

PKI

PKI provides Public Key Infrastructure facilities for Caddy.

This app can define certificate authorities (CAs) which are capable of signing certificates. Other modules can be configured to use the CAs defined by this app for issuing certificates or getting key information needed for establishing trust.

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"`

	ctx	caddy.Context
	log	*zap.Logger
}

adminAPI

adminAPI is a module that serves PKI endpoints to retrieve information about the CAs being managed by Caddy.

type adminAPI struct {
	ctx	caddy.Context
	log	*zap.Logger
	pkiApp	*PKI
}

caInfo

caInfo is the response structure for the CA info API endpoint.

type caInfo struct {
	ID			string	`json:"id"`
	Name			string	`json:"name"`
	RootCN			string	`json:"root_common_name"`
	IntermediateCN		string	`json:"intermediate_common_name"`
	RootCert		string	`json:"root_certificate"`
	IntermediateCert	string	`json:"intermediate_certificate"`
}

Functions

func (*CA) NewAuthority

NewAuthority returns a new Smallstep-powered signing authority for this CA. Note that we receive *CA (a pointer) in this method to ensure the closure within it, which executes at a later time, always has the only copy of the CA so it can access the latest, renewed certificates since NewAuthority was called. See #4517 and #4669.

func (ca *CA) NewAuthority(authorityConfig AuthorityConfig) (*authority.Authority, error) {
	// get the root certificate and the issuer cert+key
	rootCert := ca.RootCertificate()

	// set up the signer; cert/key which signs the leaf certs
	var signerOption authority.Option
	if authorityConfig.SignWithRoot {
		// if we're signing with root, we can just pass the
		// cert/key directly, since it's unlikely to expire
		// while Caddy is running (long lifetime)
		var issuerCert *x509.Certificate
		var issuerKey any
		issuerCert = rootCert
		var err error
		issuerKey, err = ca.RootKey()
		if err != nil {
			return nil, fmt.Errorf("loading signing key: %v", err)
		}
		signerOption = authority.WithX509Signer(issuerCert, issuerKey.(crypto.Signer))
	} else {
		// if we're signing with intermediate, we need to make
		// sure it's always fresh, because the intermediate may
		// renew while Caddy is running (medium lifetime)
		signerOption = authority.WithX509SignerFunc(func() ([]*x509.Certificate, crypto.Signer, error) {
			issuerCert := ca.IntermediateCertificate()
			issuerKey := ca.IntermediateKey().(crypto.Signer)
			ca.log.Debug("using intermediate signer",
				zap.String("serial", issuerCert.SerialNumber.String()),
				zap.String("not_before", issuerCert.NotBefore.String()),
				zap.String("not_after", issuerCert.NotAfter.String()))
			return []*x509.Certificate{issuerCert}, issuerKey, nil
		})
	}

	opts := []authority.Option{
		authority.WithConfig(&authority.Config{
			AuthorityConfig: authorityConfig.AuthConfig,
		}),
		signerOption,
		authority.WithX509RootCerts(rootCert),
	}

	// Add a database if we have one
	if authorityConfig.DB != nil {
		opts = append(opts, authority.WithDatabase(*authorityConfig.DB))
	}
	auth, err := authority.NewEmbedded(opts...)
	if err != nil {
		return nil, fmt.Errorf("initializing certificate authority: %v", err)
	}

	return auth, nil
}

Cognitive complexity: 14, Cyclomatic complexity: 5

Uses: authority.Config, authority.NewEmbedded, authority.Option, authority.WithConfig, authority.WithDatabase, authority.WithX509RootCerts, authority.WithX509Signer, authority.WithX509SignerFunc, crypto.Signer, fmt.Errorf, x509.Certificate, zap.String.

func (*PKI) GetCA

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.

func (p *PKI) GetCA(ctx caddy.Context, id string) (*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"
		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
}

Cognitive complexity: 6, Cyclomatic complexity: 4

Uses: fmt.Errorf.

func (*PKI) ProvisionDefaultCA

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)
}

Cognitive complexity: 2, Cyclomatic complexity: 2

func (*PKI) Start

Start starts the PKI app.

func (p *PKI) Start() error {
	// install roots to trust store, if not disabled
	for _, ca := range p.CAs {
		if ca.InstallTrust != nil && !*ca.InstallTrust {
			ca.log.Info("root certificate trust store installation disabled; unconfigured clients may show warnings",
				zap.String("path", ca.rootCertPath))
			continue
		}

		if err := ca.installRoot(); err != nil {
			// could be some system dependencies that are missing;
			// shouldn't totally prevent startup, but we should log it
			ca.log.Error("failed to install root certificate",
				zap.Error(err),
				zap.String("certificate_file", ca.rootCertPath))
		}
	}

	// see if root/intermediates need renewal...
	p.renewCerts()

	// ...and keep them renewed
	go p.maintenance()

	return nil
}

Cognitive complexity: 7, Cyclomatic complexity: 5

Uses: zap.Error, zap.String.

func (*PKI) Stop

Stop stops the PKI app.

func (p *PKI) Stop() error {
	return nil
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (*adminAPI) Provision

Provision sets up the adminAPI module.

func (a *adminAPI) Provision(ctx caddy.Context) error {
	a.ctx = ctx
	a.log = ctx.Logger(a)	// TODO: passing in 'a' is a hack until the admin API is officially extensible (see #5032)

	// Avoid initializing PKI if it wasn't configured.
	// We intentionally ignore the error since it's not
	// fatal if the PKI app is not explicitly configured.
	pkiApp, err := ctx.AppIfConfigured("pki")
	if err == nil {
		a.pkiApp = pkiApp.(*PKI)
	}

	return nil
}

Cognitive complexity: 2, Cyclomatic complexity: 2

func (*adminAPI) Routes

Routes returns the admin routes for the PKI app.

func (a *adminAPI) Routes() []caddy.AdminRoute {
	return []caddy.AdminRoute{
		{
			Pattern:	adminPKIEndpointBase,
			Handler:	caddy.AdminHandlerFunc(a.handleAPIEndpoints),
		},
	}
}

Cognitive complexity: 2, Cyclomatic complexity: 1

func (CA) IntermediateCertificate

IntermediateCertificate returns the CA's intermediate certificate (public key).

func (ca CA) IntermediateCertificate() *x509.Certificate {
	ca.mu.RLock()
	defer ca.mu.RUnlock()
	return ca.inter
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (CA) IntermediateKey

IntermediateKey returns the CA's intermediate private key.

func (ca CA) IntermediateKey() any {
	ca.mu.RLock()
	defer ca.mu.RUnlock()
	return ca.interKey
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (CA) RootCertificate

RootCertificate returns the CA's root certificate (public key).

func (ca CA) RootCertificate() *x509.Certificate {
	ca.mu.RLock()
	defer ca.mu.RUnlock()
	return ca.root
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (CA) RootKey

RootKey returns the CA's root private key. Since the root key is not cached in memory long-term, it needs to be loaded from storage, which could yield an error.

func (ca CA) RootKey() (any, error) {
	_, rootKey, err := ca.loadOrGenRoot()
	return rootKey, err
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (KeyPair) Load

Load loads the certificate and key.

func (kp KeyPair) Load() (*x509.Certificate, crypto.Signer, error) {
	switch kp.Format {
	case "", "pem_file":
		certData, err := os.ReadFile(kp.Certificate)
		if err != nil {
			return nil, nil, err
		}
		cert, err := pemDecodeSingleCert(certData)
		if err != nil {
			return nil, nil, err
		}

		var key crypto.Signer
		if kp.PrivateKey != "" {
			keyData, err := os.ReadFile(kp.PrivateKey)
			if err != nil {
				return nil, nil, err
			}
			key, err = certmagic.PEMDecodePrivateKey(keyData)
			if err != nil {
				return nil, nil, err
			}
		}

		return cert, key, nil

	default:
		return nil, nil, fmt.Errorf("unsupported format: %s", kp.Format)
	}
}

Cognitive complexity: 13, Cyclomatic complexity: 8

Uses: certmagic.PEMDecodePrivateKey, crypto.Signer, fmt.Errorf, os.ReadFile.

func (adminAPI) CaddyModule

CaddyModule returns the Caddy module information.

func (adminAPI) CaddyModule() caddy.ModuleInfo {
	return caddy.ModuleInfo{
		ID:	"admin.api.pki",
		New:	func() caddy.Module { return new(adminAPI) },
	}
}

Cognitive complexity: 2, Cyclomatic complexity: 1

Private functions

func cmdTrust

cmdTrust (fl caddycmd.Flags) (int, error)
References: caddycmd.DetermineAdminAPIAddress, fmt.Errorf, path.Join.

func cmdUntrust

cmdUntrust (fl caddycmd.Flags) (int, error)
References: caddycmd.DetermineAdminAPIAddress, fmt.Errorf, os.Stat, truststore.Uninstall, truststore.UninstallFile, truststore.WithDebug, truststore.WithFirefox, truststore.WithJava.

func generateIntermediate

generateIntermediate (commonName string, rootCrt *x509.Certificate, rootKey crypto.Signer, lifetime time.Duration) (*x509.Certificate, crypto.Signer, error)
References: x509util.CreateCertificate, x509util.DefaultIntermediateTemplate.

func generateRoot

generateRoot (commonName string) (*x509.Certificate, crypto.Signer, error)
References: x509util.CreateCertificate, x509util.DefaultRootTemplate.

func init

init ()

func needsRenewal

needsRenewal (cert *x509.Certificate) bool
References: time.Duration, time.Now.

func newCert

newCert (commonName,templateName string, lifetime time.Duration) (*x509.Certificate, crypto.Signer, error)
References: keyutil.GenerateDefaultSigner, time.Now, time.Second, x509util.CreateCertificateRequest, x509util.CreateTemplateData, x509util.NewCertificate, x509util.WithTemplate.

func pemDecodeSingleCert

pemDecodeSingleCert (pemDER []byte) (*x509.Certificate, error)
References: fmt.Errorf, pem.Decode, x509.ParseCertificate.

func pemEncode

pemEncode (blockType string, b []byte) ([]byte, error)
References: bytes.Buffer, pem.Block, pem.Encode.

func pemEncodeCert

pemEncodeCert (der []byte) ([]byte, error)

func rootAndIntermediatePEM

rootAndIntermediatePEM (ca *CA) ([]byte, error)

func rootCertFromAdmin

rootCertFromAdmin makes the API request to fetch the root certificate for the named CA via admin API.

rootCertFromAdmin (adminAddr string, caID string) (*x509.Certificate, error)
References: caddycmd.AdminAPIRequest, fmt.Errorf, http.Header, http.MethodGet, json.NewDecoder, path.Join, pem.Decode, x509.ParseCertificate.

func trusted

trusted (cert *x509.Certificate) bool
References: x509.VerifyOptions.

func maintenance

maintenance ()
References: debug.Stack, log.Printf, time.Minute, time.NewTicker.

func renewCerts

renewCerts ()
References: zap.Error, zap.String.

func renewCertsForCA

renewCertsForCA (ca *CA) error
References: fmt.Errorf, time.Until, zap.Duration, zap.String, zap.Time.

func getCAFromAPIRequestPath

getCAFromAPIRequestPath (r *http.Request) (*CA, error)
References: fmt.Errorf, http.StatusBadRequest, http.StatusInternalServerError, http.StatusNotFound, strings.Split.

func handleAPIEndpoints

handleAPIEndpoints routes API requests within adminPKIEndpointBase.

handleAPIEndpoints (w http.ResponseWriter, r *http.Request) error
References: fmt.Errorf, http.StatusNotFound, strings.Split, strings.TrimPrefix.

func handleCACerts

handleCACerts returns the certificate chain for a particular CA by its ID. If the CA ID is the default, then the CA will be provisioned if it has not already been. Other CA IDs will return an error if they have not been previously provisioned.

handleCACerts (w http.ResponseWriter, r *http.Request) error
References: fmt.Errorf, http.MethodGet, http.StatusInternalServerError, http.StatusMethodNotAllowed.

func handleCAInfo

handleCAInfo returns information about a particular CA by its ID. If the CA ID is the default, then the CA will be provisioned if it has not already been. Other CA IDs will return an error if they have not been previously provisioned.

handleCAInfo (w http.ResponseWriter, r *http.Request) error
References: fmt.Errorf, http.MethodGet, http.StatusInternalServerError, http.StatusMethodNotAllowed, json.Marshal.

func genIntermediate

genIntermediate (rootCert *x509.Certificate, rootKey crypto.Signer) (*x509.Certificate, crypto.Signer, error)
References: certmagic.PEMEncodePrivateKey, fmt.Errorf, time.Duration.

func genRoot

genRoot () (*x509.Certificate, crypto.Signer, error)
References: certmagic.PEMEncodePrivateKey, fmt.Errorf.

func installRoot

installRoot installs this CA's root certificate into the local trust store(s) if it is not already trusted. The CA must already be provisioned.

installRoot () error
References: truststore.Install, truststore.WithDebug, truststore.WithFirefox, truststore.WithJava, zap.String.

func loadOrGenIntermediate

loadOrGenIntermediate (rootCert *x509.Certificate, rootKey crypto.Signer) (*x509.Certificate, crypto.Signer, error)
References: certmagic.PEMDecodePrivateKey, errors.Is, fmt.Errorf, fs.ErrNotExist.

func loadOrGenRoot

loadOrGenRoot () (*x509.Certificate, crypto.Signer, error)
References: certmagic.PEMDecodePrivateKey, errors.Is, fmt.Errorf, fs.ErrNotExist.

func newReplacer

newReplacer () *caddy.Replacer

func storageKeyCAPrefix

storageKeyCAPrefix () string
References: certmagic.StorageKeys, path.Join.

func storageKeyIntermediateCert

storageKeyIntermediateCert () string
References: path.Join.

func storageKeyIntermediateKey

storageKeyIntermediateKey () string
References: path.Join.

func storageKeyRootCert

storageKeyRootCert () string
References: path.Join.

func storageKeyRootKey

storageKeyRootKey () string
References: path.Join.