You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

191 lines
4.6 KiB

  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. package autocert
  5. import (
  6. "crypto/ecdsa"
  7. "crypto/elliptic"
  8. "crypto/rand"
  9. "crypto/tls"
  10. "crypto/x509"
  11. "encoding/base64"
  12. "fmt"
  13. "net/http"
  14. "net/http/httptest"
  15. "testing"
  16. "time"
  17. "golang.org/x/crypto/acme"
  18. )
  19. func TestRenewalNext(t *testing.T) {
  20. now := time.Now()
  21. timeNow = func() time.Time { return now }
  22. defer func() { timeNow = time.Now }()
  23. man := &Manager{RenewBefore: 7 * 24 * time.Hour}
  24. defer man.stopRenew()
  25. tt := []struct {
  26. expiry time.Time
  27. min, max time.Duration
  28. }{
  29. {now.Add(90 * 24 * time.Hour), 83*24*time.Hour - maxRandRenew, 83 * 24 * time.Hour},
  30. {now.Add(time.Hour), 0, 1},
  31. {now, 0, 1},
  32. {now.Add(-time.Hour), 0, 1},
  33. }
  34. dr := &domainRenewal{m: man}
  35. for i, test := range tt {
  36. next := dr.next(test.expiry)
  37. if next < test.min || test.max < next {
  38. t.Errorf("%d: next = %v; want between %v and %v", i, next, test.min, test.max)
  39. }
  40. }
  41. }
  42. func TestRenewFromCache(t *testing.T) {
  43. const domain = "example.org"
  44. // ACME CA server stub
  45. var ca *httptest.Server
  46. ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  47. w.Header().Set("replay-nonce", "nonce")
  48. if r.Method == "HEAD" {
  49. // a nonce request
  50. return
  51. }
  52. switch r.URL.Path {
  53. // discovery
  54. case "/":
  55. if err := discoTmpl.Execute(w, ca.URL); err != nil {
  56. t.Fatalf("discoTmpl: %v", err)
  57. }
  58. // client key registration
  59. case "/new-reg":
  60. w.Write([]byte("{}"))
  61. // domain authorization
  62. case "/new-authz":
  63. w.Header().Set("location", ca.URL+"/authz/1")
  64. w.WriteHeader(http.StatusCreated)
  65. w.Write([]byte(`{"status": "valid"}`))
  66. // cert request
  67. case "/new-cert":
  68. var req struct {
  69. CSR string `json:"csr"`
  70. }
  71. decodePayload(&req, r.Body)
  72. b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
  73. csr, err := x509.ParseCertificateRequest(b)
  74. if err != nil {
  75. t.Fatalf("new-cert: CSR: %v", err)
  76. }
  77. der, err := dummyCert(csr.PublicKey, domain)
  78. if err != nil {
  79. t.Fatalf("new-cert: dummyCert: %v", err)
  80. }
  81. chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL)
  82. w.Header().Set("link", chainUp)
  83. w.WriteHeader(http.StatusCreated)
  84. w.Write(der)
  85. // CA chain cert
  86. case "/ca-cert":
  87. der, err := dummyCert(nil, "ca")
  88. if err != nil {
  89. t.Fatalf("ca-cert: dummyCert: %v", err)
  90. }
  91. w.Write(der)
  92. default:
  93. t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
  94. }
  95. }))
  96. defer ca.Close()
  97. // use EC key to run faster on 386
  98. key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  99. if err != nil {
  100. t.Fatal(err)
  101. }
  102. man := &Manager{
  103. Prompt: AcceptTOS,
  104. Cache: newMemCache(),
  105. RenewBefore: 24 * time.Hour,
  106. Client: &acme.Client{
  107. Key: key,
  108. DirectoryURL: ca.URL,
  109. },
  110. }
  111. defer man.stopRenew()
  112. // cache an almost expired cert
  113. now := time.Now()
  114. cert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), domain)
  115. if err != nil {
  116. t.Fatal(err)
  117. }
  118. tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}}
  119. if err := man.cachePut(domain, tlscert); err != nil {
  120. t.Fatal(err)
  121. }
  122. // veriy the renewal happened
  123. defer func() {
  124. testDidRenewLoop = func(next time.Duration, err error) {}
  125. }()
  126. done := make(chan struct{})
  127. testDidRenewLoop = func(next time.Duration, err error) {
  128. defer close(done)
  129. if err != nil {
  130. t.Errorf("testDidRenewLoop: %v", err)
  131. }
  132. // Next should be about 90 days:
  133. // dummyCert creates 90days expiry + account for man.RenewBefore.
  134. // Previous expiration was within 1 min.
  135. future := 88 * 24 * time.Hour
  136. if next < future {
  137. t.Errorf("testDidRenewLoop: next = %v; want >= %v", next, future)
  138. }
  139. // ensure the new cert is cached
  140. after := time.Now().Add(future)
  141. tlscert, err := man.cacheGet(domain)
  142. if err != nil {
  143. t.Fatalf("man.cacheGet: %v", err)
  144. }
  145. if !tlscert.Leaf.NotAfter.After(after) {
  146. t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
  147. }
  148. // verify the old cert is also replaced in memory
  149. man.stateMu.Lock()
  150. defer man.stateMu.Unlock()
  151. s := man.state[domain]
  152. if s == nil {
  153. t.Fatalf("m.state[%q] is nil", domain)
  154. }
  155. tlscert, err = s.tlscert()
  156. if err != nil {
  157. t.Fatalf("s.tlscert: %v", err)
  158. }
  159. if !tlscert.Leaf.NotAfter.After(after) {
  160. t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
  161. }
  162. }
  163. // trigger renew
  164. hello := &tls.ClientHelloInfo{ServerName: domain}
  165. if _, err := man.GetCertificate(hello); err != nil {
  166. t.Fatal(err)
  167. }
  168. // wait for renew loop
  169. select {
  170. case <-time.After(10 * time.Second):
  171. t.Fatal("renew took too long to occur")
  172. case <-done:
  173. }
  174. }