@marcel91200 / caddy Mirror · read-only / commits / bf50d701
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

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.

Matthew Holt committed Feb 2, 2021 at 17:23 UTC bf50d7010a26468791f4397c0f0c4f9a8ed1d6a2
7 files changed +111 -21
caddyconfig/httpcaddyfile/httptype.go
+10
@@ -28,6 +28,7 @@ import (
28 "github.com/caddyserver/caddy/v2/caddyconfig"
29 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
30 "github.com/caddyserver/caddy/v2/modules/caddyhttp"
31 + "github.com/caddyserver/caddy/v2/modules/caddypki"
32 "github.com/caddyserver/caddy/v2/modules/caddytls"
33 )
34
@@ -219,6 +220,12 @@ func (st ServerType) Setup(inputServerBlocks []caddyfile.ServerBlock,
220 return nil, warnings, err
221 }
222
223 + // then make the PKI app
224 + pkiApp, warnings, err := st.buildPKIApp(pairings, options, warnings)
225 + if err != nil {
226 + return nil, warnings, err
227 + }
228 +
229 // extract any custom logs, and enforce configured levels
230 var customLogs []namedCustomLog
231 var hasDefaultLog bool
@@ -259,6 +266,9 @@ func (st ServerType) Setup(inputServerBlocks []caddyfile.ServerBlock,
266 if !reflect.DeepEqual(tlsApp, &caddytls.TLS{CertificatesRaw: make(caddy.ModuleMap)}) {
267 cfg.AppsRaw["tls"] = caddyconfig.JSON(tlsApp, &warnings)
268 }
269 + if !reflect.DeepEqual(pkiApp, &caddypki.PKI{CAs: make(map[string]*caddypki.CA)}) {
270 + cfg.AppsRaw["pki"] = caddyconfig.JSON(pkiApp, &warnings)
271 + }
272 if storageCvtr, ok := options["storage"].(caddy.StorageConverter); ok {
273 cfg.StorageRaw = caddyconfig.JSONModuleObject(storageCvtr,
274 "module",
caddyconfig/httpcaddyfile/pkiapp.go new
+41
@@ -0,0 +1,41 @@
1 +// Copyright 2015 Matthew Holt and The Caddy Authors
2 +//
3 +// Licensed under the Apache License, Version 2.0 (the "License");
4 +// you may not use this file except in compliance with the License.
5 +// You may obtain a copy of the License at
6 +//
7 +// http://www.apache.org/licenses/LICENSE-2.0
8 +//
9 +// Unless required by applicable law or agreed to in writing, software
10 +// distributed under the License is distributed on an "AS IS" BASIS,
11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 +// See the License for the specific language governing permissions and
13 +// limitations under the License.
14 +
15 +package httpcaddyfile
16 +
17 +import (
18 + "github.com/caddyserver/caddy/v2/caddyconfig"
19 + "github.com/caddyserver/caddy/v2/modules/caddypki"
20 +)
21 +
22 +func (st ServerType) buildPKIApp(
23 + pairings []sbAddrAssociation,
24 + options map[string]interface{},
25 + warnings []caddyconfig.Warning,
26 +) (*caddypki.PKI, []caddyconfig.Warning, error) {
27 +
28 + pkiApp := &caddypki.PKI{CAs: make(map[string]*caddypki.CA)}
29 +
30 + for _, p := range pairings {
31 + for _, sblock := range p.serverBlocks {
32 + // find all the CAs that were defined and add them to the app config
33 + for _, caCfgValue := range sblock.pile["pki.ca"] {
34 + ca := caCfgValue.Value.(*caddypki.CA)
35 + pkiApp.CAs[ca.ID] = ca
36 + }
37 + }
38 + }
39 +
40 + return pkiApp, warnings, nil
41 +}
modules/caddypki/acmeserver/caddyfile.go
+41 -6
@@ -16,23 +16,58 @@ package acmeserver
16
17 import (
18 "github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
19 - "github.com/caddyserver/caddy/v2/modules/caddyhttp"
19 + "github.com/caddyserver/caddy/v2/modules/caddypki"
20 )
21
22 func init() {
23 - httpcaddyfile.RegisterHandlerDirective("acme_server", parseACMEServer)
23 + httpcaddyfile.RegisterDirective("acme_server", parseACMEServer)
24 }
25
26 // parseACMEServer sets up an ACME server handler from Caddyfile tokens.
27 //
28 -// acme_server [<matcher>]
28 +// acme_server [<matcher>] {
29 +// ca <id>
30 +// }
31 //
30 -func parseACMEServer(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
31 - var as Handler
32 +func parseACMEServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error) {
33 + if !h.Next() {
34 + return nil, h.ArgErr()
35 + }
36 +
37 + matcherSet, err := h.ExtractMatcherSet()
38 + if err != nil {
39 + return nil, err
40 + }
41 +
42 + var acmeServer Handler
43 + var ca *caddypki.CA
44 +
45 for h.Next() {
46 if h.NextArg() {
47 return nil, h.ArgErr()
48 }
49 + for h.NextBlock(0) {
50 + switch h.Val() {
51 + case "ca":
52 + if !h.AllArgs(&acmeServer.CA) {
53 + return nil, h.ArgErr()
54 + }
55 + if ca == nil {
56 + ca = new(caddypki.CA)
57 + }
58 + ca.ID = acmeServer.CA
59 + }
60 + }
61 }
37 - return as, nil
62 +
63 + configVals := h.NewRoute(matcherSet, acmeServer)
64 +
65 + if ca == nil {
66 + return configVals, nil
67 + }
68 +
69 + return append(configVals, httpcaddyfile.ConfigValue{
70 + Class: "pki.ca",
71 + Value: ca,
72 + }), nil
73 }
modules/caddypki/ca.go
+8 -8
@@ -63,7 +63,12 @@ type CA struct {
63 // separate location from your leaf certificates.
64 StorageRaw json.RawMessage `json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"`
65
66 - id string
66 + // The unique config-facing ID of the certificate authority.
67 + // Since the ID is set in JSON config via object key, this
68 + // field is exported only for purposes of config generation
69 + // and module provisioning.
70 + ID string `json:"-"`
71 +
72 storage certmagic.Storage
73 root, inter *x509.Certificate
74 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 {
87 return fmt.Errorf("CA ID is required (use 'local' for the default CA)")
88 }
89 ca.mu.Lock()
85 - ca.id = id
90 + ca.ID = id
91 ca.mu.Unlock()
92
93 if ca.StorageRaw != nil {
@@ -142,11 +147,6 @@ func (ca *CA) Provision(ctx caddy.Context, id string, log *zap.Logger) error {
147 return nil
148 }
149
145 -// ID returns the CA's ID, as given by the user in the config.
146 -func (ca CA) ID() string {
147 - return ca.id
148 -}
149 -
150 // RootCertificate returns the CA's root certificate (public key).
151 func (ca CA) RootCertificate() *x509.Certificate {
152 ca.mu.RLock()
@@ -338,7 +338,7 @@ func (ca CA) genIntermediate(rootCert *x509.Certificate, rootKey interface{}) (i
338 }
339
340 func (ca CA) storageKeyCAPrefix() string {
341 - return path.Join("pki", "authorities", certmagic.StorageKeys.Safe(ca.id))
341 + return path.Join("pki", "authorities", certmagic.StorageKeys.Safe(ca.ID))
342 }
343 func (ca CA) storageKeyRootCert() string {
344 return path.Join(ca.storageKeyCAPrefix(), "root.crt")
modules/caddypki/maintain.go
+2 -2
@@ -50,7 +50,7 @@ func (p *PKI) renewCerts() {
50 if err != nil {
51 p.log.Error("renewing intermediate certificates",
52 zap.Error(err),
53 - zap.String("ca", ca.id))
53 + zap.String("ca", ca.ID))
54 }
55 }
56 }
@@ -59,7 +59,7 @@ func (p *PKI) renewCertsForCA(ca *CA) error {
59 ca.mu.Lock()
60 defer ca.mu.Unlock()
61
62 - log := p.log.With(zap.String("ca", ca.id))
62 + log := p.log.With(zap.String("ca", ca.ID))
63
64 // only maintain the root if it's not manually provided in the config
65 if ca.Root == nil {
modules/caddypki/pki.go
+8 -4
@@ -49,10 +49,14 @@ func (p *PKI) Provision(ctx caddy.Context) error {
49 p.ctx = ctx
50 p.log = ctx.Logger(p)
51
52 - // if this app is initialized at all, ensure there's
53 - // at least a default CA that can be used
54 - if len(p.CAs) == 0 {
55 - p.CAs = map[string]*CA{DefaultCAID: new(CA)}
52 + // if this app is initialized at all, ensure there's at
53 + // least a default CA that can be used: the standard CA
54 + // which is used implicitly for signing local-use certs
55 + if p.CAs == nil {
56 + p.CAs = make(map[string]*CA)
57 + }
58 + if _, ok := p.CAs[DefaultCAID]; !ok {
59 + p.CAs[DefaultCAID] = new(CA)
60 }
61
62 for caID, ca := range p.CAs {
modules/caddytls/internalissuer.go
+1 -1
@@ -94,7 +94,7 @@ func (iss *InternalIssuer) Provision(ctx caddy.Context) error {
94 // IssuerKey returns the unique issuer key for the
95 // confgured CA endpoint.
96 func (iss InternalIssuer) IssuerKey() string {
97 - return iss.ca.ID()
97 + return iss.ca.ID
98 }
99
100 // Issue issues a certificate to satisfy the CSR.