1 // Copyright 2016 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 5 package autocert
6 7 import (
8 "context"
9 "crypto"
10 "sync"
11 "time"
12 )
13 14 // domainRenewal tracks the state used by the periodic timers
15 // renewing a single domain's cert.
16 type domainRenewal struct {
17 m *Manager
18 ck certKey
19 key crypto.Signer
20 21 timerMu sync.Mutex
22 timer *time.Timer
23 timerClose chan struct{} // if non-nil, renew closes this channel (and nils out the timer fields) instead of running
24 }
25 26 // start starts a cert renewal timer at the time
27 // defined by the certificate expiration time exp.
28 //
29 // If the timer is already started, calling start is a noop.
30 func (dr *domainRenewal) start(notBefore, notAfter time.Time) {
31 dr.timerMu.Lock()
32 defer dr.timerMu.Unlock()
33 if dr.timer != nil {
34 return
35 }
36 dr.timer = time.AfterFunc(dr.next(notBefore, notAfter), dr.renew)
37 }
38 39 // stop stops the cert renewal timer and waits for any in-flight calls to renew
40 // to complete. If the timer is already stopped, calling stop is a noop.
41 func (dr *domainRenewal) stop() {
42 dr.timerMu.Lock()
43 defer dr.timerMu.Unlock()
44 for {
45 if dr.timer == nil {
46 return
47 }
48 if dr.timer.Stop() {
49 dr.timer = nil
50 return
51 } else {
52 // dr.timer fired, and we acquired dr.timerMu before the renew callback did.
53 // (We know this because otherwise the renew callback would have reset dr.timer!)
54 timerClose := make(chan struct{})
55 dr.timerClose = timerClose
56 dr.timerMu.Unlock()
57 <-timerClose
58 dr.timerMu.Lock()
59 }
60 }
61 }
62 63 // renew is called periodically by a timer.
64 // The first renew call is kicked off by dr.start.
65 func (dr *domainRenewal) renew() {
66 dr.timerMu.Lock()
67 defer dr.timerMu.Unlock()
68 if dr.timerClose != nil {
69 close(dr.timerClose)
70 dr.timer, dr.timerClose = nil, nil
71 return
72 }
73 74 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
75 defer cancel()
76 // TODO: rotate dr.key at some point?
77 next, err := dr.do(ctx)
78 if err != nil {
79 next = time.Hour / 2
80 next += time.Duration(pseudoRand.int63n(int64(next)))
81 }
82 testDidRenewLoop(next, err)
83 dr.timer = time.AfterFunc(next, dr.renew)
84 }
85 86 // updateState locks and replaces the relevant Manager.state item with the given
87 // state. It additionally updates dr.key with the given state's key.
88 func (dr *domainRenewal) updateState(state *certState) {
89 dr.m.stateMu.Lock()
90 defer dr.m.stateMu.Unlock()
91 dr.key = state.key
92 dr.m.state[dr.ck] = state
93 }
94 95 // do is similar to Manager.createCert but it doesn't lock a Manager.state item.
96 // Instead, it requests a new certificate independently and, upon success,
97 // replaces dr.m.state item with a new one and updates cache for the given domain.
98 //
99 // It may lock and update the Manager.state if the expiration date of the currently
100 // cached cert is far enough in the future.
101 //
102 // The returned value is a time interval after which the renewal should occur again.
103 func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) {
104 // a race is likely unavoidable in a distributed environment
105 // but we try nonetheless
106 if tlscert, err := dr.m.cacheGet(ctx, dr.ck); err == nil {
107 next := dr.next(tlscert.Leaf.NotBefore, tlscert.Leaf.NotAfter)
108 if next > 0 {
109 signer, ok := tlscert.PrivateKey.(crypto.Signer)
110 if ok {
111 state := &certState{
112 key: signer,
113 cert: tlscert.Certificate,
114 leaf: tlscert.Leaf,
115 }
116 dr.updateState(state)
117 return next, nil
118 }
119 }
120 }
121 122 der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.ck)
123 if err != nil {
124 return 0, err
125 }
126 state := &certState{
127 key: dr.key,
128 cert: der,
129 leaf: leaf,
130 }
131 tlscert, err := state.tlscert()
132 if err != nil {
133 return 0, err
134 }
135 if err := dr.m.cachePut(ctx, dr.ck, tlscert); err != nil {
136 return 0, err
137 }
138 dr.updateState(state)
139 return dr.next(leaf.NotBefore, leaf.NotAfter), nil
140 }
141 142 // next returns the wait time before the next renewal should start.
143 // If manager.RenewBefore is set, it uses that capped at 30 days,
144 // otherwise it uses a default of 1/3 of the cert lifetime.
145 // It builds in a jitter of 10% of the renew threshold, capped at 1 hour.
146 func (dr *domainRenewal) next(notBefore, notAfter time.Time) time.Duration {
147 threshold := min(notAfter.Sub(notBefore)/3, 30*24*time.Hour)
148 if dr.m.RenewBefore > 0 {
149 threshold = min(dr.m.RenewBefore, 30*24*time.Hour)
150 }
151 maxJitter := min(threshold/10, time.Hour)
152 jitter := pseudoRand.int63n(int64(maxJitter))
153 renewAt := notAfter.Add(-(threshold - time.Duration(jitter)))
154 renewWait := renewAt.Sub(dr.m.now())
155 return max(0, renewWait)
156 }
157 158 var testDidRenewLoop = func(next time.Duration, err error) {}
159