@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 115 lines 3.09 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/x509"
19 "fmt"
20 "log"
21 "runtime/debug"
22 "time"
23
24 "go.uber.org/zap"
25 )
26
27 func (p *PKI) maintenanceForCA(ca *CA) {
28 defer func() {
29 if err := recover(); err != nil {
30 log.Printf("[PANIC] PKI maintenance for CA %s: %v\n%s", ca.ID, err, debug.Stack())
31 }
32 }()
33
34 interval := time.Duration(ca.MaintenanceInterval)
35 if interval <= 0 {
36 interval = defaultMaintenanceInterval
37 }
38 ticker := time.NewTicker(interval)
39 defer ticker.Stop()
40
41 for {
42 select {
43 case <-ticker.C:
44 _ = p.renewCertsForCA(ca)
45 case <-p.ctx.Done():
46 return
47 }
48 }
49 }
50
51 func (p *PKI) renewCerts() {
52 for _, ca := range p.CAs {
53 err := p.renewCertsForCA(ca)
54 if err != nil {
55 p.log.Error("renewing intermediate certificates",
56 zap.Error(err),
57 zap.String("ca", ca.ID))
58 }
59 }
60 }
61
62 func (p *PKI) renewCertsForCA(ca *CA) error {
63 ca.mu.Lock()
64 defer ca.mu.Unlock()
65
66 log := p.log.With(zap.String("ca", ca.ID))
67
68 // only maintain the root if it's not manually provided in the config
69 if ca.Root == nil {
70 if ca.needsRenewal(ca.root) {
71 // TODO: implement root renewal (use same key)
72 log.Warn("root certificate expiring soon (FIXME: ROOT RENEWAL NOT YET IMPLEMENTED)",
73 zap.Duration("time_remaining", time.Until(ca.interChain[0].NotAfter)),
74 )
75 }
76 }
77
78 // only maintain the intermediate if it's not manually provided in the config
79 if ca.Intermediate == nil {
80 if ca.needsRenewal(ca.interChain[0]) {
81 log.Info("intermediate expires soon; renewing",
82 zap.Duration("time_remaining", time.Until(ca.interChain[0].NotAfter)),
83 )
84
85 rootCert, rootKey, err := ca.loadOrGenRoot()
86 if err != nil {
87 return fmt.Errorf("loading root key: %v", err)
88 }
89 interCert, interKey, err := ca.genIntermediate(rootCert, rootKey)
90 if err != nil {
91 return fmt.Errorf("generating new certificate: %v", err)
92 }
93 ca.interChain, ca.interKey = []*x509.Certificate{interCert}, interKey
94
95 log.Info("renewed intermediate",
96 zap.Time("new_expiration", ca.interChain[0].NotAfter),
97 )
98 }
99 }
100
101 return nil
102 }
103
104 // needsRenewal reports whether the certificate is within its renewal window
105 // (i.e. the fraction of lifetime remaining is less than or equal to RenewalWindowRatio).
106 func (ca *CA) needsRenewal(cert *x509.Certificate) bool {
107 ratio := ca.RenewalWindowRatio
108 if ratio <= 0 {
109 ratio = defaultRenewalWindowRatio
110 }
111 lifetime := cert.NotAfter.Sub(cert.NotBefore)
112 renewalWindow := time.Duration(float64(lifetime) * ratio)
113 renewalWindowStart := cert.NotAfter.Add(-renewalWindow)
114 return time.Now().After(renewalWindowStart)
115 }