blob: ffe4af2a5ce2f5e1a6832d81c7cb4eddb6e5ba75 [file] [log] [blame]
Alex Vaghin33b41822016-08-22 21:27:50 +02001// 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
5package autocert
6
7import (
Brad Fitzpatrick88915cc2017-04-02 13:18:05 -07008 "context"
Filippo Valsorda198e4372022-01-28 13:06:27 -05009 "crypto"
Alex Vaghin33b41822016-08-22 21:27:50 +020010 "crypto/ecdsa"
Alex Vaghin33b41822016-08-22 21:27:50 +020011 "testing"
12 "time"
13
Alex Vaghindfc9fd32016-08-31 22:02:02 +020014 "golang.org/x/crypto/acme"
Filippo Valsorda198e4372022-01-28 13:06:27 -050015 "golang.org/x/crypto/acme/autocert/internal/acmetest"
Alex Vaghin33b41822016-08-22 21:27:50 +020016)
17
18func TestRenewalNext(t *testing.T) {
19 now := time.Now()
Adam Langleyff745d02018-08-08 12:05:25 -070020 man := &Manager{
21 RenewBefore: 7 * 24 * time.Hour,
22 nowFunc: func() time.Time { return now },
23 }
Alex Vaghin05d11b22016-09-03 09:54:54 +020024 defer man.stopRenew()
Alex Vaghin33b41822016-08-22 21:27:50 +020025 tt := []struct {
26 expiry time.Time
27 min, max time.Duration
28 }{
Alex Vaghin0242f072017-03-22 10:33:23 +010029 {now.Add(90 * 24 * time.Hour), 83*24*time.Hour - renewJitter, 83 * 24 * time.Hour},
Alex Vaghin33b41822016-08-22 21:27:50 +020030 {now.Add(time.Hour), 0, 1},
31 {now, 0, 1},
32 {now.Add(-time.Hour), 0, 1},
33 }
34
35 dr := &domainRenewal{m: man}
36 for i, test := range tt {
37 next := dr.next(test.expiry)
38 if next < test.min || test.max < next {
39 t.Errorf("%d: next = %v; want between %v and %v", i, next, test.min, test.max)
40 }
41 }
42}
43
44func TestRenewFromCache(t *testing.T) {
Filippo Valsorda198e4372022-01-28 13:06:27 -050045 man := testManager(t)
46 man.RenewBefore = 24 * time.Hour
Alex Vaghin33b41822016-08-22 21:27:50 +020047
Filippo Valsorda198e4372022-01-28 13:06:27 -050048 ca := acmetest.NewCAServer(t).Start()
49 ca.ResolveGetCertificate(exampleDomain, man.GetCertificate)
Alex Vaghin33b41822016-08-22 21:27:50 +020050
Filippo Valsorda198e4372022-01-28 13:06:27 -050051 man.Client = &acme.Client{
52 DirectoryURL: ca.URL(),
Alex Vaghin33b41822016-08-22 21:27:50 +020053 }
Alex Vaghin33b41822016-08-22 21:27:50 +020054
55 // cache an almost expired cert
56 now := time.Now()
Filippo Valsorda198e4372022-01-28 13:06:27 -050057 c := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
58 if err := man.cachePut(context.Background(), exampleCertKey, c); err != nil {
Alex Vaghin33b41822016-08-22 21:27:50 +020059 t.Fatal(err)
60 }
61
HowJmay2f5cac02021-05-12 12:31:47 +000062 // verify the renewal happened
Alex Vaghin33b41822016-08-22 21:27:50 +020063 defer func() {
Bryan C. Millsf4118a52022-02-09 11:50:56 -050064 // Stop the timers that read and execute testDidRenewLoop before restoring it.
65 // Otherwise the timer callback may race with the deferred write.
66 man.stopRenew()
Alex Vaghin33b41822016-08-22 21:27:50 +020067 testDidRenewLoop = func(next time.Duration, err error) {}
68 }()
Bryan C. Millsf4118a52022-02-09 11:50:56 -050069 renewed := make(chan bool, 1)
Alex Vaghin33b41822016-08-22 21:27:50 +020070 testDidRenewLoop = func(next time.Duration, err error) {
Bryan C. Millsf4118a52022-02-09 11:50:56 -050071 defer func() {
72 select {
73 case renewed <- true:
74 default:
75 // The renewal timer uses a random backoff. If the first renewal fails for
76 // some reason, we could end up with multiple calls here before the test
77 // stops the timer.
78 }
79 }()
80
Alex Vaghin33b41822016-08-22 21:27:50 +020081 if err != nil {
82 t.Errorf("testDidRenewLoop: %v", err)
83 }
84 // Next should be about 90 days:
Filippo Valsorda198e4372022-01-28 13:06:27 -050085 // CaServer creates 90days expiry + account for man.RenewBefore.
Alex Vaghin33b41822016-08-22 21:27:50 +020086 // Previous expiration was within 1 min.
87 future := 88 * 24 * time.Hour
88 if next < future {
89 t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
90 }
91
92 // ensure the new cert is cached
93 after := time.Now().Add(future)
Filippo Valsorda8f8078c2018-06-05 20:30:02 -040094 tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
Alex Vaghin33b41822016-08-22 21:27:50 +020095 if err != nil {
Bryan C. Millsf4118a52022-02-09 11:50:56 -050096 t.Errorf("man.cacheGet: %v", err)
97 return
Alex Vaghin33b41822016-08-22 21:27:50 +020098 }
99 if !tlscert.Leaf.NotAfter.After(after) {
100 t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
101 }
102
103 // verify the old cert is also replaced in memory
104 man.stateMu.Lock()
105 defer man.stateMu.Unlock()
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400106 s := man.state[exampleCertKey]
Alex Vaghin33b41822016-08-22 21:27:50 +0200107 if s == nil {
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500108 t.Errorf("m.state[%q] is nil", exampleCertKey)
109 return
Alex Vaghin33b41822016-08-22 21:27:50 +0200110 }
111 tlscert, err = s.tlscert()
112 if err != nil {
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500113 t.Errorf("s.tlscert: %v", err)
114 return
Alex Vaghin33b41822016-08-22 21:27:50 +0200115 }
116 if !tlscert.Leaf.NotAfter.After(after) {
117 t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
118 }
119 }
120
121 // trigger renew
Alex Vaghin227b76d2019-09-10 22:06:19 -0400122 hello := clientHelloInfo(exampleDomain, algECDSA)
Alex Vaghin33b41822016-08-22 21:27:50 +0200123 if _, err := man.GetCertificate(hello); err != nil {
124 t.Fatal(err)
125 }
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500126 <-renewed
Alex Vaghin33b41822016-08-22 21:27:50 +0200127}
Brad Morganc3a3ad62018-01-25 12:32:49 -0800128
129func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
Filippo Valsorda198e4372022-01-28 13:06:27 -0500130 ca := acmetest.NewCAServer(t).Start()
131 man := testManager(t)
132 man.RenewBefore = 24 * time.Hour
133 man.Client = &acme.Client{
134 DirectoryURL: "invalid",
Brad Morganc3a3ad62018-01-25 12:32:49 -0800135 }
Brad Morganc3a3ad62018-01-25 12:32:49 -0800136
137 // cache a recently renewed cert with a different private key
Brad Morganc3a3ad62018-01-25 12:32:49 -0800138 now := time.Now()
Filippo Valsorda198e4372022-01-28 13:06:27 -0500139 newCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Hour*24*90))
140 if err := man.cachePut(context.Background(), exampleCertKey, newCert); err != nil {
Brad Morganc3a3ad62018-01-25 12:32:49 -0800141 t.Fatal(err)
142 }
Filippo Valsorda198e4372022-01-28 13:06:27 -0500143 newLeaf, err := validCert(exampleCertKey, newCert.Certificate, newCert.PrivateKey.(crypto.Signer), now)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800144 if err != nil {
145 t.Fatal(err)
146 }
Brad Morganc3a3ad62018-01-25 12:32:49 -0800147
148 // set internal state to an almost expired cert
Filippo Valsorda198e4372022-01-28 13:06:27 -0500149 oldCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
Filippo Valsorda78e79282018-06-05 20:32:59 -0400150 if err != nil {
151 t.Fatal(err)
152 }
Filippo Valsorda198e4372022-01-28 13:06:27 -0500153 oldLeaf, err := validCert(exampleCertKey, oldCert.Certificate, oldCert.PrivateKey.(crypto.Signer), now)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800154 if err != nil {
155 t.Fatal(err)
156 }
157 man.stateMu.Lock()
158 if man.state == nil {
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400159 man.state = make(map[certKey]*certState)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800160 }
161 s := &certState{
Filippo Valsorda198e4372022-01-28 13:06:27 -0500162 key: oldCert.PrivateKey.(crypto.Signer),
163 cert: oldCert.Certificate,
Brad Morganc3a3ad62018-01-25 12:32:49 -0800164 leaf: oldLeaf,
165 }
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400166 man.state[exampleCertKey] = s
Brad Morganc3a3ad62018-01-25 12:32:49 -0800167 man.stateMu.Unlock()
168
Filippo Valsorda198e4372022-01-28 13:06:27 -0500169 // verify the renewal accepted the newer cached cert
Brad Morganc3a3ad62018-01-25 12:32:49 -0800170 defer func() {
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500171 // Stop the timers that read and execute testDidRenewLoop before restoring it.
172 // Otherwise the timer callback may race with the deferred write.
173 man.stopRenew()
Brad Morganc3a3ad62018-01-25 12:32:49 -0800174 testDidRenewLoop = func(next time.Duration, err error) {}
175 }()
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500176 renewed := make(chan bool, 1)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800177 testDidRenewLoop = func(next time.Duration, err error) {
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500178 defer func() {
179 select {
180 case renewed <- true:
181 default:
182 // The renewal timer uses a random backoff. If the first renewal fails for
183 // some reason, we could end up with multiple calls here before the test
184 // stops the timer.
185 }
186 }()
187
Brad Morganc3a3ad62018-01-25 12:32:49 -0800188 if err != nil {
189 t.Errorf("testDidRenewLoop: %v", err)
190 }
191 // Next should be about 90 days
192 // Previous expiration was within 1 min.
193 future := 88 * 24 * time.Hour
194 if next < future {
195 t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
196 }
197
198 // ensure the cached cert was not modified
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400199 tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800200 if err != nil {
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500201 t.Errorf("man.cacheGet: %v", err)
202 return
Brad Morganc3a3ad62018-01-25 12:32:49 -0800203 }
204 if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
205 t.Errorf("cache leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
206 }
207
208 // verify the old cert is also replaced in memory
209 man.stateMu.Lock()
210 defer man.stateMu.Unlock()
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400211 s := man.state[exampleCertKey]
Brad Morganc3a3ad62018-01-25 12:32:49 -0800212 if s == nil {
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500213 t.Errorf("m.state[%q] is nil", exampleCertKey)
214 return
Brad Morganc3a3ad62018-01-25 12:32:49 -0800215 }
216 stateKey := s.key.Public().(*ecdsa.PublicKey)
Filippo Valsorda198e4372022-01-28 13:06:27 -0500217 if !stateKey.Equal(newLeaf.PublicKey) {
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500218 t.Error("state key was not updated from cache")
219 return
Brad Morganc3a3ad62018-01-25 12:32:49 -0800220 }
221 tlscert, err = s.tlscert()
222 if err != nil {
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500223 t.Errorf("s.tlscert: %v", err)
224 return
Brad Morganc3a3ad62018-01-25 12:32:49 -0800225 }
226 if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
227 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
228 }
Brad Morganc3a3ad62018-01-25 12:32:49 -0800229 }
230
231 // assert the expiring cert is returned from state
Alex Vaghin227b76d2019-09-10 22:06:19 -0400232 hello := clientHelloInfo(exampleDomain, algECDSA)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800233 tlscert, err := man.GetCertificate(hello)
234 if err != nil {
235 t.Fatal(err)
236 }
237 if !oldLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
238 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, oldLeaf.NotAfter)
239 }
240
241 // trigger renew
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500242 man.startRenew(exampleCertKey, s.key, s.leaf.NotAfter)
243 <-renewed
244 func() {
245 man.renewalMu.Lock()
246 defer man.renewalMu.Unlock()
Brad Morganc3a3ad62018-01-25 12:32:49 -0800247
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500248 // verify the private key is replaced in the renewal state
249 r := man.renewal[exampleCertKey]
250 if r == nil {
251 t.Errorf("m.renewal[%q] is nil", exampleCertKey)
252 return
Brad Morganc3a3ad62018-01-25 12:32:49 -0800253 }
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500254 renewalKey := r.key.Public().(*ecdsa.PublicKey)
255 if !renewalKey.Equal(newLeaf.PublicKey) {
256 t.Error("renewal private key was not updated from cache")
Brad Morganc3a3ad62018-01-25 12:32:49 -0800257 }
Bryan C. Millsf4118a52022-02-09 11:50:56 -0500258 }()
259
260 // assert the new cert is returned from state after renew
261 hello = clientHelloInfo(exampleDomain, algECDSA)
262 tlscert, err = man.GetCertificate(hello)
263 if err != nil {
264 t.Fatal(err)
265 }
266 if !newLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
267 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800268 }
269}