blob: e5f48ffbb85ca65080dcd8b7ba70baf7f4511e2a [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() {
64 testDidRenewLoop = func(next time.Duration, err error) {}
65 }()
66 done := make(chan struct{})
67 testDidRenewLoop = func(next time.Duration, err error) {
68 defer close(done)
69 if err != nil {
70 t.Errorf("testDidRenewLoop: %v", err)
71 }
72 // Next should be about 90 days:
Filippo Valsorda198e4372022-01-28 13:06:27 -050073 // CaServer creates 90days expiry + account for man.RenewBefore.
Alex Vaghin33b41822016-08-22 21:27:50 +020074 // Previous expiration was within 1 min.
75 future := 88 * 24 * time.Hour
76 if next < future {
77 t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
78 }
79
80 // ensure the new cert is cached
81 after := time.Now().Add(future)
Filippo Valsorda8f8078c2018-06-05 20:30:02 -040082 tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
Alex Vaghin33b41822016-08-22 21:27:50 +020083 if err != nil {
84 t.Fatalf("man.cacheGet: %v", err)
85 }
86 if !tlscert.Leaf.NotAfter.After(after) {
87 t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
88 }
89
90 // verify the old cert is also replaced in memory
91 man.stateMu.Lock()
92 defer man.stateMu.Unlock()
Filippo Valsorda8f8078c2018-06-05 20:30:02 -040093 s := man.state[exampleCertKey]
Alex Vaghin33b41822016-08-22 21:27:50 +020094 if s == nil {
Filippo Valsorda8f8078c2018-06-05 20:30:02 -040095 t.Fatalf("m.state[%q] is nil", exampleCertKey)
Alex Vaghin33b41822016-08-22 21:27:50 +020096 }
97 tlscert, err = s.tlscert()
98 if err != nil {
99 t.Fatalf("s.tlscert: %v", err)
100 }
101 if !tlscert.Leaf.NotAfter.After(after) {
102 t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
103 }
104 }
105
106 // trigger renew
Alex Vaghin227b76d2019-09-10 22:06:19 -0400107 hello := clientHelloInfo(exampleDomain, algECDSA)
Alex Vaghin33b41822016-08-22 21:27:50 +0200108 if _, err := man.GetCertificate(hello); err != nil {
109 t.Fatal(err)
110 }
111
112 // wait for renew loop
113 select {
114 case <-time.After(10 * time.Second):
115 t.Fatal("renew took too long to occur")
116 case <-done:
117 }
118}
Brad Morganc3a3ad62018-01-25 12:32:49 -0800119
120func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
Filippo Valsorda198e4372022-01-28 13:06:27 -0500121 ca := acmetest.NewCAServer(t).Start()
122 man := testManager(t)
123 man.RenewBefore = 24 * time.Hour
124 man.Client = &acme.Client{
125 DirectoryURL: "invalid",
Brad Morganc3a3ad62018-01-25 12:32:49 -0800126 }
Brad Morganc3a3ad62018-01-25 12:32:49 -0800127
128 // cache a recently renewed cert with a different private key
Brad Morganc3a3ad62018-01-25 12:32:49 -0800129 now := time.Now()
Filippo Valsorda198e4372022-01-28 13:06:27 -0500130 newCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Hour*24*90))
131 if err := man.cachePut(context.Background(), exampleCertKey, newCert); err != nil {
Brad Morganc3a3ad62018-01-25 12:32:49 -0800132 t.Fatal(err)
133 }
Filippo Valsorda198e4372022-01-28 13:06:27 -0500134 newLeaf, err := validCert(exampleCertKey, newCert.Certificate, newCert.PrivateKey.(crypto.Signer), now)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800135 if err != nil {
136 t.Fatal(err)
137 }
Brad Morganc3a3ad62018-01-25 12:32:49 -0800138
139 // set internal state to an almost expired cert
Filippo Valsorda198e4372022-01-28 13:06:27 -0500140 oldCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
Filippo Valsorda78e79282018-06-05 20:32:59 -0400141 if err != nil {
142 t.Fatal(err)
143 }
Filippo Valsorda198e4372022-01-28 13:06:27 -0500144 oldLeaf, err := validCert(exampleCertKey, oldCert.Certificate, oldCert.PrivateKey.(crypto.Signer), now)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800145 if err != nil {
146 t.Fatal(err)
147 }
148 man.stateMu.Lock()
149 if man.state == nil {
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400150 man.state = make(map[certKey]*certState)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800151 }
152 s := &certState{
Filippo Valsorda198e4372022-01-28 13:06:27 -0500153 key: oldCert.PrivateKey.(crypto.Signer),
154 cert: oldCert.Certificate,
Brad Morganc3a3ad62018-01-25 12:32:49 -0800155 leaf: oldLeaf,
156 }
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400157 man.state[exampleCertKey] = s
Brad Morganc3a3ad62018-01-25 12:32:49 -0800158 man.stateMu.Unlock()
159
Filippo Valsorda198e4372022-01-28 13:06:27 -0500160 // verify the renewal accepted the newer cached cert
Brad Morganc3a3ad62018-01-25 12:32:49 -0800161 defer func() {
162 testDidRenewLoop = func(next time.Duration, err error) {}
163 }()
164 done := make(chan struct{})
165 testDidRenewLoop = func(next time.Duration, err error) {
166 defer close(done)
167 if err != nil {
168 t.Errorf("testDidRenewLoop: %v", err)
169 }
170 // Next should be about 90 days
171 // Previous expiration was within 1 min.
172 future := 88 * 24 * time.Hour
173 if next < future {
174 t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
175 }
176
177 // ensure the cached cert was not modified
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400178 tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800179 if err != nil {
180 t.Fatalf("man.cacheGet: %v", err)
181 }
182 if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
183 t.Errorf("cache leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
184 }
185
186 // verify the old cert is also replaced in memory
187 man.stateMu.Lock()
188 defer man.stateMu.Unlock()
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400189 s := man.state[exampleCertKey]
Brad Morganc3a3ad62018-01-25 12:32:49 -0800190 if s == nil {
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400191 t.Fatalf("m.state[%q] is nil", exampleCertKey)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800192 }
193 stateKey := s.key.Public().(*ecdsa.PublicKey)
Filippo Valsorda198e4372022-01-28 13:06:27 -0500194 if !stateKey.Equal(newLeaf.PublicKey) {
195 t.Fatal("state key was not updated from cache")
Brad Morganc3a3ad62018-01-25 12:32:49 -0800196 }
197 tlscert, err = s.tlscert()
198 if err != nil {
199 t.Fatalf("s.tlscert: %v", err)
200 }
201 if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
202 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
203 }
204
205 // verify the private key is replaced in the renewal state
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400206 r := man.renewal[exampleCertKey]
Brad Morganc3a3ad62018-01-25 12:32:49 -0800207 if r == nil {
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400208 t.Fatalf("m.renewal[%q] is nil", exampleCertKey)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800209 }
210 renewalKey := r.key.Public().(*ecdsa.PublicKey)
Filippo Valsorda198e4372022-01-28 13:06:27 -0500211 if !renewalKey.Equal(newLeaf.PublicKey) {
212 t.Fatal("renewal private key was not updated from cache")
Brad Morganc3a3ad62018-01-25 12:32:49 -0800213 }
214
215 }
216
217 // assert the expiring cert is returned from state
Alex Vaghin227b76d2019-09-10 22:06:19 -0400218 hello := clientHelloInfo(exampleDomain, algECDSA)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800219 tlscert, err := man.GetCertificate(hello)
220 if err != nil {
221 t.Fatal(err)
222 }
223 if !oldLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
224 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, oldLeaf.NotAfter)
225 }
226
227 // trigger renew
Filippo Valsorda8f8078c2018-06-05 20:30:02 -0400228 go man.renew(exampleCertKey, s.key, s.leaf.NotAfter)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800229
230 // wait for renew loop
231 select {
232 case <-time.After(10 * time.Second):
233 t.Fatal("renew took too long to occur")
234 case <-done:
235 // assert the new cert is returned from state after renew
Alex Vaghin227b76d2019-09-10 22:06:19 -0400236 hello := clientHelloInfo(exampleDomain, algECDSA)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800237 tlscert, err := man.GetCertificate(hello)
238 if err != nil {
239 t.Fatal(err)
240 }
Filippo Valsorda198e4372022-01-28 13:06:27 -0500241 if !newLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
242 t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
Brad Morganc3a3ad62018-01-25 12:32:49 -0800243 }
244 }
245}