mirror of
https://github.com/caddyserver/caddy.git
synced 2026-02-09 01:59:21 +08:00
acmeserver: Support custom CAs from Caddyfile
The HTTP Caddyfile adapter can now configure the PKI app, and the acme_server directive can now be used to specify a custom CA used for issuing certificates. More customization options can follow later as needed.
This commit is contained in:
@@ -16,23 +16,58 @@ package acmeserver
|
||||
|
||||
import (
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
||||
"github.com/caddyserver/caddy/v2/modules/caddypki"
|
||||
)
|
||||
|
||||
func init() {
|
||||
httpcaddyfile.RegisterHandlerDirective("acme_server", parseACMEServer)
|
||||
httpcaddyfile.RegisterDirective("acme_server", parseACMEServer)
|
||||
}
|
||||
|
||||
// parseACMEServer sets up an ACME server handler from Caddyfile tokens.
|
||||
//
|
||||
// acme_server [<matcher>]
|
||||
// acme_server [<matcher>] {
|
||||
// ca <id>
|
||||
// }
|
||||
//
|
||||
func parseACMEServer(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
|
||||
var as Handler
|
||||
func parseACMEServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) {
|
||||
if !h.Next() {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
|
||||
matcherSet, err := h.ExtractMatcherSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var acmeServer Handler
|
||||
var ca *caddypki.CA
|
||||
|
||||
for h.Next() {
|
||||
if h.NextArg() {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
for h.NextBlock(0) {
|
||||
switch h.Val() {
|
||||
case "ca":
|
||||
if !h.AllArgs(&acmeServer.CA) {
|
||||
return nil, h.ArgErr()
|
||||
}
|
||||
if ca == nil {
|
||||
ca = new(caddypki.CA)
|
||||
}
|
||||
ca.ID = acmeServer.CA
|
||||
}
|
||||
}
|
||||
}
|
||||
return as, nil
|
||||
|
||||
configVals := h.NewRoute(matcherSet, acmeServer)
|
||||
|
||||
if ca == nil {
|
||||
return configVals, nil
|
||||
}
|
||||
|
||||
return append(configVals, httpcaddyfile.ConfigValue{
|
||||
Class: "pki.ca",
|
||||
Value: ca,
|
||||
}), nil
|
||||
}
|
||||
|
||||
@@ -63,7 +63,12 @@ type CA struct {
|
||||
// separate location from your leaf certificates.
|
||||
StorageRaw json.RawMessage `json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"`
|
||||
|
||||
id string
|
||||
// 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 interface{} // TODO: should we just store these as crypto.Signer?
|
||||
@@ -82,7 +87,7 @@ func (ca *CA) Provision(ctx caddy.Context, id string, log *zap.Logger) error {
|
||||
return fmt.Errorf("CA ID is required (use 'local' for the default CA)")
|
||||
}
|
||||
ca.mu.Lock()
|
||||
ca.id = id
|
||||
ca.ID = id
|
||||
ca.mu.Unlock()
|
||||
|
||||
if ca.StorageRaw != nil {
|
||||
@@ -142,11 +147,6 @@ func (ca *CA) Provision(ctx caddy.Context, id string, log *zap.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ID returns the CA's ID, as given by the user in the config.
|
||||
func (ca CA) ID() string {
|
||||
return ca.id
|
||||
}
|
||||
|
||||
// RootCertificate returns the CA's root certificate (public key).
|
||||
func (ca CA) RootCertificate() *x509.Certificate {
|
||||
ca.mu.RLock()
|
||||
@@ -338,7 +338,7 @@ func (ca CA) genIntermediate(rootCert *x509.Certificate, rootKey interface{}) (i
|
||||
}
|
||||
|
||||
func (ca CA) storageKeyCAPrefix() string {
|
||||
return path.Join("pki", "authorities", certmagic.StorageKeys.Safe(ca.id))
|
||||
return path.Join("pki", "authorities", certmagic.StorageKeys.Safe(ca.ID))
|
||||
}
|
||||
func (ca CA) storageKeyRootCert() string {
|
||||
return path.Join(ca.storageKeyCAPrefix(), "root.crt")
|
||||
|
||||
@@ -50,7 +50,7 @@ func (p *PKI) renewCerts() {
|
||||
if err != nil {
|
||||
p.log.Error("renewing intermediate certificates",
|
||||
zap.Error(err),
|
||||
zap.String("ca", ca.id))
|
||||
zap.String("ca", ca.ID))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func (p *PKI) renewCertsForCA(ca *CA) error {
|
||||
ca.mu.Lock()
|
||||
defer ca.mu.Unlock()
|
||||
|
||||
log := p.log.With(zap.String("ca", ca.id))
|
||||
log := p.log.With(zap.String("ca", ca.ID))
|
||||
|
||||
// only maintain the root if it's not manually provided in the config
|
||||
if ca.Root == nil {
|
||||
|
||||
@@ -49,10 +49,14 @@ 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
|
||||
if len(p.CAs) == 0 {
|
||||
p.CAs = map[string]*CA{DefaultCAID: new(CA)}
|
||||
// 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 {
|
||||
|
||||
Reference in New Issue
Block a user