@marcel91200 / caddy Mirror · read-only
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 11, 01:00 UTC

master
go 479 lines 15.1 KB
Raw
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 caddypki
16
17 import (
18 "crypto"
19 "crypto/x509"
20 "encoding/json"
21 "errors"
22 "fmt"
23 "io/fs"
24 "path"
25 "sync"
26 "time"
27
28 "github.com/caddyserver/certmagic"
29 "github.com/smallstep/certificates/authority"
30 "github.com/smallstep/certificates/db"
31 "github.com/smallstep/truststore"
32 "go.uber.org/zap"
33
34 "github.com/caddyserver/caddy/v2"
35 )
36
37 // CA describes a certificate authority, which consists of
38 // root/signing certificates and various settings pertaining
39 // to the issuance of certificates and trusting them.
40 type CA struct {
41 // The user-facing name of the certificate authority.
42 Name string `json:"name,omitempty"`
43
44 // The name to put in the CommonName field of the
45 // root certificate.
46 RootCommonName string `json:"root_common_name,omitempty"`
47
48 // The name to put in the CommonName field of the
49 // intermediate certificates.
50 IntermediateCommonName string `json:"intermediate_common_name,omitempty"`
51
52 // The lifetime for the intermediate certificates
53 IntermediateLifetime caddy.Duration `json:"intermediate_lifetime,omitempty"`
54
55 // Whether Caddy will attempt to install the CA's root
56 // into the system trust store, as well as into Java
57 // and Mozilla Firefox trust stores. Default: true.
58 InstallTrust *bool `json:"install_trust,omitempty"`
59
60 // The root certificate to use; if null, one will be generated.
61 Root *KeyPair `json:"root,omitempty"`
62
63 // The intermediate (signing) certificate; if null, one will be generated.
64 Intermediate *KeyPair `json:"intermediate,omitempty"`
65
66 // How often to check if intermediate (and root, when applicable) certificates need renewal.
67 // Default: 10m.
68 MaintenanceInterval caddy.Duration `json:"maintenance_interval,omitempty"`
69
70 // The fraction of certificate lifetime (0.0–1.0) after which renewal is attempted.
71 // For example, 0.2 means renew when 20% of the lifetime remains (e.g. ~73 days for a 1-year cert).
72 // Default: 0.2.
73 RenewalWindowRatio float64 `json:"renewal_window_ratio,omitempty"`
74
75 // Optionally configure a separate storage module associated with this
76 // issuer, instead of using Caddy's global/default-configured storage.
77 // This can be useful if you want to keep your signing keys in a
78 // separate location from your leaf certificates.
79 StorageRaw json.RawMessage `json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"`
80
81 // The unique config-facing ID of the certificate authority.
82 // Since the ID is set in JSON config via object key, this
83 // field is exported only for purposes of config generation
84 // and module provisioning.
85 ID string `json:"-"`
86
87 storage certmagic.Storage
88 root *x509.Certificate
89 interChain []*x509.Certificate
90 interKey crypto.Signer
91 mu *sync.RWMutex
92
93 rootCertPath string // mainly used for logging purposes if trusting
94 log *zap.Logger
95 ctx caddy.Context
96 }
97
98 // Provision sets up the CA.
99 func (ca *CA) Provision(ctx caddy.Context, id string, log *zap.Logger) error {
100 ca.mu = new(sync.RWMutex)
101 ca.log = log.Named("ca." + id)
102 ca.ctx = ctx
103
104 if id == "" {
105 return fmt.Errorf("CA ID is required (use 'local' for the default CA)")
106 }
107 ca.mu.Lock()
108 ca.ID = id
109 ca.mu.Unlock()
110
111 if ca.StorageRaw != nil {
112 val, err := ctx.LoadModule(ca, "StorageRaw")
113 if err != nil {
114 return fmt.Errorf("loading storage module: %v", err)
115 }
116 cmStorage, err := val.(caddy.StorageConverter).CertMagicStorage()
117 if err != nil {
118 return fmt.Errorf("creating storage configuration: %v", err)
119 }
120 ca.storage = cmStorage
121 }
122 if ca.storage == nil {
123 ca.storage = ctx.Storage()
124 }
125
126 if ca.Name == "" {
127 ca.Name = defaultCAName
128 }
129 if ca.RootCommonName == "" {
130 ca.RootCommonName = defaultRootCommonName
131 }
132 if ca.IntermediateCommonName == "" {
133 ca.IntermediateCommonName = defaultIntermediateCommonName
134 }
135 if ca.IntermediateLifetime == 0 {
136 ca.IntermediateLifetime = caddy.Duration(defaultIntermediateLifetime)
137 }
138 if ca.MaintenanceInterval == 0 {
139 ca.MaintenanceInterval = caddy.Duration(defaultMaintenanceInterval)
140 }
141 if ca.RenewalWindowRatio <= 0 || ca.RenewalWindowRatio > 1 {
142 ca.RenewalWindowRatio = defaultRenewalWindowRatio
143 }
144
145 // load the certs and key that will be used for signing
146 var rootCert *x509.Certificate
147 var rootCertChain, interCertChain []*x509.Certificate
148 var rootKey, interKey crypto.Signer
149 var err error
150 if ca.Root != nil {
151 if ca.Root.Format == "" || ca.Root.Format == "pem_file" {
152 ca.rootCertPath = ca.Root.Certificate
153 }
154 rootCertChain, rootKey, err = ca.Root.Load()
155 if err != nil {
156 return err
157 }
158 rootCert = rootCertChain[0]
159 } else {
160 ca.rootCertPath = "storage:" + ca.storageKeyRootCert()
161 rootCert, rootKey, err = ca.loadOrGenRoot()
162 }
163 if err != nil {
164 return err
165 }
166
167 if ca.Intermediate != nil {
168 interCertChain, interKey, err = ca.Intermediate.Load()
169 } else {
170 actualRootLifetime := time.Until(rootCert.NotAfter)
171 if time.Duration(ca.IntermediateLifetime) >= actualRootLifetime {
172 return fmt.Errorf("intermediate certificate lifetime must be less than actual root certificate lifetime (%s)", actualRootLifetime)
173 }
174
175 interCertChain, interKey, err = ca.loadOrGenIntermediate(rootCert, rootKey)
176 }
177 if err != nil {
178 return err
179 }
180
181 ca.mu.Lock()
182 ca.root, ca.interChain, ca.interKey = rootCert, interCertChain, interKey
183 ca.mu.Unlock()
184
185 return nil
186 }
187
188 // RootCertificate returns the CA's root certificate (public key).
189 func (ca CA) RootCertificate() *x509.Certificate {
190 ca.mu.RLock()
191 defer ca.mu.RUnlock()
192 return ca.root
193 }
194
195 // RootKey returns the CA's root private key. Since the root key is
196 // not cached in memory long-term, it needs to be loaded from storage,
197 // which could yield an error.
198 func (ca CA) RootKey() (crypto.Signer, error) {
199 _, rootKey, err := ca.loadOrGenRoot()
200 return rootKey, err
201 }
202
203 // IntermediateCertificateChain returns the CA's intermediate
204 // certificate chain.
205 func (ca CA) IntermediateCertificateChain() []*x509.Certificate {
206 ca.mu.RLock()
207 defer ca.mu.RUnlock()
208 return ca.interChain
209 }
210
211 // IntermediateKey returns the CA's intermediate private key.
212 func (ca CA) IntermediateKey() crypto.Signer {
213 ca.mu.RLock()
214 defer ca.mu.RUnlock()
215 return ca.interKey
216 }
217
218 // NewAuthority returns a new Smallstep-powered signing authority for this CA.
219 // Note that we receive *CA (a pointer) in this method to ensure the closure within it, which
220 // executes at a later time, always has the only copy of the CA so it can access the latest,
221 // renewed certificates since NewAuthority was called. See #4517 and #4669.
222 func (ca *CA) NewAuthority(authorityConfig AuthorityConfig) (*authority.Authority, error) {
223 // get the root certificate and the issuer cert+key
224 rootCert := ca.RootCertificate()
225
226 // set up the signer; cert/key which signs the leaf certs
227 var signerOption authority.Option
228 if authorityConfig.SignWithRoot {
229 // if we're signing with root, we can just pass the
230 // cert/key directly, since it's unlikely to expire
231 // while Caddy is running (long lifetime)
232 var issuerCert *x509.Certificate
233 var issuerKey crypto.Signer
234 issuerCert = rootCert
235 var err error
236 issuerKey, err = ca.RootKey()
237 if err != nil {
238 return nil, fmt.Errorf("loading signing key: %v", err)
239 }
240 signerOption = authority.WithX509Signer(issuerCert, issuerKey)
241 } else {
242 // if we're signing with intermediate, we need to make
243 // sure it's always fresh, because the intermediate may
244 // renew while Caddy is running (medium lifetime)
245 signerOption = authority.WithX509SignerFunc(func() ([]*x509.Certificate, crypto.Signer, error) {
246 issuerChain := ca.IntermediateCertificateChain()
247 issuerCert := issuerChain[0]
248 issuerKey := ca.IntermediateKey()
249 ca.log.Debug("using intermediate signer",
250 zap.String("serial", issuerCert.SerialNumber.String()),
251 zap.String("not_before", issuerCert.NotBefore.String()),
252 zap.String("not_after", issuerCert.NotAfter.String()))
253 return issuerChain, issuerKey, nil
254 })
255 }
256
257 opts := []authority.Option{
258 authority.WithConfig(&authority.Config{
259 AuthorityConfig: authorityConfig.AuthConfig,
260 }),
261 signerOption,
262 authority.WithX509RootCerts(rootCert),
263 }
264
265 // Add a database if we have one
266 if authorityConfig.DB != nil {
267 opts = append(opts, authority.WithDatabase(*authorityConfig.DB))
268 }
269 auth, err := authority.NewEmbedded(opts...)
270 if err != nil {
271 return nil, fmt.Errorf("initializing certificate authority: %v", err)
272 }
273
274 return auth, nil
275 }
276
277 func (ca CA) loadOrGenRoot() (rootCert *x509.Certificate, rootKey crypto.Signer, err error) {
278 if ca.Root != nil {
279 rootChain, rootSigner, err := ca.Root.Load()
280 if err != nil {
281 return nil, nil, err
282 }
283 return rootChain[0], rootSigner, nil
284 }
285 rootCertPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyRootCert())
286 if err != nil {
287 if !errors.Is(err, fs.ErrNotExist) {
288 return nil, nil, fmt.Errorf("loading root cert: %v", err)
289 }
290
291 // TODO: should we require that all or none of the assets are required before overwriting anything?
292 rootCert, rootKey, err = ca.genRoot()
293 if err != nil {
294 return nil, nil, fmt.Errorf("generating root: %v", err)
295 }
296 }
297
298 if rootCert == nil {
299 rootCert, err = pemDecodeCertificate(rootCertPEM)
300 if err != nil {
301 return nil, nil, fmt.Errorf("parsing root certificate PEM: %v", err)
302 }
303 }
304 if rootKey == nil {
305 rootKeyPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyRootKey())
306 if err != nil {
307 return nil, nil, fmt.Errorf("loading root key: %v", err)
308 }
309 rootKey, err = certmagic.PEMDecodePrivateKey(rootKeyPEM)
310 if err != nil {
311 return nil, nil, fmt.Errorf("decoding root key: %v", err)
312 }
313 }
314
315 return rootCert, rootKey, nil
316 }
317
318 func (ca CA) genRoot() (rootCert *x509.Certificate, rootKey crypto.Signer, err error) {
319 repl := ca.newReplacer()
320
321 rootCert, rootKey, err = generateRoot(repl.ReplaceAll(ca.RootCommonName, ""))
322 if err != nil {
323 return nil, nil, fmt.Errorf("generating CA root: %v", err)
324 }
325 rootCertPEM, err := pemEncodeCert(rootCert.Raw)
326 if err != nil {
327 return nil, nil, fmt.Errorf("encoding root certificate: %v", err)
328 }
329 err = ca.storage.Store(ca.ctx, ca.storageKeyRootCert(), rootCertPEM)
330 if err != nil {
331 return nil, nil, fmt.Errorf("saving root certificate: %v", err)
332 }
333 rootKeyPEM, err := certmagic.PEMEncodePrivateKey(rootKey)
334 if err != nil {
335 return nil, nil, fmt.Errorf("encoding root key: %v", err)
336 }
337 err = ca.storage.Store(ca.ctx, ca.storageKeyRootKey(), rootKeyPEM)
338 if err != nil {
339 return nil, nil, fmt.Errorf("saving root key: %v", err)
340 }
341
342 return rootCert, rootKey, nil
343 }
344
345 func (ca CA) loadOrGenIntermediate(rootCert *x509.Certificate, rootKey crypto.Signer) (interCertChain []*x509.Certificate, interKey crypto.Signer, err error) {
346 var interCert *x509.Certificate
347 interCertPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyIntermediateCert())
348 if err != nil {
349 if !errors.Is(err, fs.ErrNotExist) {
350 return nil, nil, fmt.Errorf("loading intermediate cert: %v", err)
351 }
352
353 // TODO: should we require that all or none of the assets are required before overwriting anything?
354 interCert, interKey, err = ca.genIntermediate(rootCert, rootKey)
355 if err != nil {
356 return nil, nil, fmt.Errorf("generating new intermediate cert: %v", err)
357 }
358
359 interCertChain = append(interCertChain, interCert)
360 }
361
362 if len(interCertChain) == 0 {
363 interCertChain, err = pemDecodeCertificateChain(interCertPEM)
364 if err != nil {
365 return nil, nil, fmt.Errorf("decoding intermediate certificate PEM: %v", err)
366 }
367 }
368
369 if interKey == nil {
370 interKeyPEM, err := ca.storage.Load(ca.ctx, ca.storageKeyIntermediateKey())
371 if err != nil {
372 return nil, nil, fmt.Errorf("loading intermediate key: %v", err)
373 }
374 interKey, err = certmagic.PEMDecodePrivateKey(interKeyPEM)
375 if err != nil {
376 return nil, nil, fmt.Errorf("decoding intermediate key: %v", err)
377 }
378 }
379
380 return interCertChain, interKey, nil
381 }
382
383 func (ca CA) genIntermediate(rootCert *x509.Certificate, rootKey crypto.Signer) (interCert *x509.Certificate, interKey crypto.Signer, err error) {
384 repl := ca.newReplacer()
385
386 interCert, interKey, err = generateIntermediate(repl.ReplaceAll(ca.IntermediateCommonName, ""), rootCert, rootKey, time.Duration(ca.IntermediateLifetime))
387 if err != nil {
388 return nil, nil, fmt.Errorf("generating CA intermediate: %v", err)
389 }
390 interCertPEM, err := pemEncodeCert(interCert.Raw)
391 if err != nil {
392 return nil, nil, fmt.Errorf("encoding intermediate certificate: %v", err)
393 }
394 err = ca.storage.Store(ca.ctx, ca.storageKeyIntermediateCert(), interCertPEM)
395 if err != nil {
396 return nil, nil, fmt.Errorf("saving intermediate certificate: %v", err)
397 }
398 interKeyPEM, err := certmagic.PEMEncodePrivateKey(interKey)
399 if err != nil {
400 return nil, nil, fmt.Errorf("encoding intermediate key: %v", err)
401 }
402 err = ca.storage.Store(ca.ctx, ca.storageKeyIntermediateKey(), interKeyPEM)
403 if err != nil {
404 return nil, nil, fmt.Errorf("saving intermediate key: %v", err)
405 }
406
407 return interCert, interKey, nil
408 }
409
410 func (ca CA) storageKeyCAPrefix() string {
411 return path.Join("pki", "authorities", certmagic.StorageKeys.Safe(ca.ID))
412 }
413
414 func (ca CA) storageKeyRootCert() string {
415 return path.Join(ca.storageKeyCAPrefix(), "root.crt")
416 }
417
418 func (ca CA) storageKeyRootKey() string {
419 return path.Join(ca.storageKeyCAPrefix(), "root.key")
420 }
421
422 func (ca CA) storageKeyIntermediateCert() string {
423 return path.Join(ca.storageKeyCAPrefix(), "intermediate.crt")
424 }
425
426 func (ca CA) storageKeyIntermediateKey() string {
427 return path.Join(ca.storageKeyCAPrefix(), "intermediate.key")
428 }
429
430 func (ca CA) newReplacer() *caddy.Replacer {
431 repl := caddy.NewReplacer()
432 repl.Set("pki.ca.name", ca.Name)
433 return repl
434 }
435
436 // installRoot installs this CA's root certificate into the
437 // local trust store(s) if it is not already trusted. The CA
438 // must already be provisioned.
439 func (ca CA) installRoot() error {
440 // avoid password prompt if already trusted
441 if trusted(ca.root) {
442 ca.log.Info("root certificate is already trusted by system",
443 zap.String("path", ca.rootCertPath))
444 return nil
445 }
446
447 ca.log.Warn("installing root certificate (you might be prompted for password)",
448 zap.String("path", ca.rootCertPath))
449
450 return truststore.Install(ca.root,
451 truststore.WithDebug(),
452 truststore.WithFirefox(),
453 truststore.WithJava(),
454 )
455 }
456
457 // AuthorityConfig is used to help a CA configure
458 // the underlying signing authority.
459 type AuthorityConfig struct {
460 SignWithRoot bool
461
462 // TODO: should we just embed the underlying authority.Config struct type?
463 DB *db.AuthDB
464 AuthConfig *authority.AuthConfig
465 }
466
467 const (
468 // DefaultCAID is the default CA ID.
469 DefaultCAID = "local"
470
471 defaultCAName = "Caddy Local Authority"
472 defaultRootCommonName = "{pki.ca.name} - {time.now.year} ECC Root"
473 defaultIntermediateCommonName = "{pki.ca.name} - ECC Intermediate"
474
475 defaultRootLifetime = 24 * time.Hour * 30 * 12 * 10
476 defaultIntermediateLifetime = 24 * time.Hour * 7
477 defaultMaintenanceInterval = 10 * time.Minute
478 defaultRenewalWindowRatio = 0.2
479 )