summaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/crypto/acme/autocert
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/crypto/acme/autocert')
-rw-r--r--vendor/golang.org/x/crypto/acme/autocert/autocert.go126
-rw-r--r--vendor/golang.org/x/crypto/acme/autocert/renewal.go35
2 files changed, 60 insertions, 101 deletions
diff --git a/vendor/golang.org/x/crypto/acme/autocert/autocert.go b/vendor/golang.org/x/crypto/acme/autocert/autocert.go
index c7fbc54c..1858184e 100644
--- a/vendor/golang.org/x/crypto/acme/autocert/autocert.go
+++ b/vendor/golang.org/x/crypto/acme/autocert/autocert.go
@@ -47,6 +47,8 @@ var createCertRetryAfter = time.Minute
// pseudoRand is safe for concurrent use.
var pseudoRand *lockedMathRand
+var errPreRFC = errors.New("autocert: ACME server doesn't support RFC 8555")
+
func init() {
src := mathrand.NewSource(time.Now().UnixNano())
pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
@@ -456,7 +458,7 @@ func (m *Manager) cert(ctx context.Context, ck certKey) (*tls.Certificate, error
leaf: cert.Leaf,
}
m.state[ck] = s
- go m.renew(ck, s.key, s.leaf.NotAfter)
+ go m.startRenew(ck, s.key, s.leaf.NotAfter)
return cert, nil
}
@@ -582,8 +584,9 @@ func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate,
if err != nil {
// Remove the failed state after some time,
// making the manager call createCert again on the following TLS hello.
+ didRemove := testDidRemoveState // The lifetime of this timer is untracked, so copy mutable local state to avoid races.
time.AfterFunc(createCertRetryAfter, func() {
- defer testDidRemoveState(ck)
+ defer didRemove(ck)
m.stateMu.Lock()
defer m.stateMu.Unlock()
// Verify the state hasn't changed and it's still invalid
@@ -601,7 +604,7 @@ func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate,
}
state.cert = der
state.leaf = leaf
- go m.renew(ck, state.key, state.leaf.NotAfter)
+ go m.startRenew(ck, state.key, state.leaf.NotAfter)
return state.tlscert()
}
@@ -658,99 +661,24 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck cert
if err != nil {
return nil, nil, err
}
+ if dir.OrderURL == "" {
+ return nil, nil, errPreRFC
+ }
- var chain [][]byte
- switch {
- // Pre-RFC legacy CA.
- case dir.OrderURL == "":
- if err := m.verify(ctx, client, ck.domain); err != nil {
- return nil, nil, err
- }
- der, _, err := client.CreateCert(ctx, csr, 0, true)
- if err != nil {
- return nil, nil, err
- }
- chain = der
- // RFC 8555 compliant CA.
- default:
- o, err := m.verifyRFC(ctx, client, ck.domain)
- if err != nil {
- return nil, nil, err
- }
- der, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
- if err != nil {
- return nil, nil, err
- }
- chain = der
+ o, err := m.verifyRFC(ctx, client, ck.domain)
+ if err != nil {
+ return nil, nil, err
}
- leaf, err = validCert(ck, chain, key, m.now())
+ chain, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
if err != nil {
return nil, nil, err
}
- return chain, leaf, nil
-}
-
-// verify runs the identifier (domain) pre-authorization flow for legacy CAs
-// using each applicable ACME challenge type.
-func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
- // Remove all hanging authorizations to reduce rate limit quotas
- // after we're done.
- var authzURLs []string
- defer func() {
- go m.deactivatePendingAuthz(authzURLs)
- }()
-
- // errs accumulates challenge failure errors, printed if all fail
- errs := make(map[*acme.Challenge]error)
- challengeTypes := m.supportedChallengeTypes()
- var nextTyp int // challengeType index of the next challenge type to try
- for {
- // Start domain authorization and get the challenge.
- authz, err := client.Authorize(ctx, domain)
- if err != nil {
- return err
- }
- authzURLs = append(authzURLs, authz.URI)
- // No point in accepting challenges if the authorization status
- // is in a final state.
- switch authz.Status {
- case acme.StatusValid:
- return nil // already authorized
- case acme.StatusInvalid:
- return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
- }
-
- // Pick the next preferred challenge.
- var chal *acme.Challenge
- for chal == nil && nextTyp < len(challengeTypes) {
- chal = pickChallenge(challengeTypes[nextTyp], authz.Challenges)
- nextTyp++
- }
- if chal == nil {
- errorMsg := fmt.Sprintf("acme/autocert: unable to authorize %q", domain)
- for chal, err := range errs {
- errorMsg += fmt.Sprintf("; challenge %q failed with error: %v", chal.Type, err)
- }
- return errors.New(errorMsg)
- }
- cleanup, err := m.fulfill(ctx, client, chal, domain)
- if err != nil {
- errs[chal] = err
- continue
- }
- defer cleanup()
- if _, err := client.Accept(ctx, chal); err != nil {
- errs[chal] = err
- continue
- }
- // A challenge is fulfilled and accepted: wait for the CA to validate.
- if _, err := client.WaitAuthorization(ctx, authz.URI); err != nil {
- errs[chal] = err
- continue
- }
- return nil
+ leaf, err = validCert(ck, chain, key, m.now())
+ if err != nil {
+ return nil, nil, err
}
+ return chain, leaf, nil
}
// verifyRFC runs the identifier (domain) order-based authorization flow for RFC compliant CAs
@@ -966,7 +894,7 @@ func httpTokenCacheKey(tokenPath string) string {
return path.Base(tokenPath) + "+http-01"
}
-// renew starts a cert renewal timer loop, one per domain.
+// startRenew starts a cert renewal timer loop, one per domain.
//
// The loop is scheduled in two cases:
// - a cert was fetched from cache for the first time (wasn't in m.state)
@@ -974,7 +902,7 @@ func httpTokenCacheKey(tokenPath string) string {
//
// The key argument is a certificate private key.
// The exp argument is the cert expiration time (NotAfter).
-func (m *Manager) renew(ck certKey, key crypto.Signer, exp time.Time) {
+func (m *Manager) startRenew(ck certKey, key crypto.Signer, exp time.Time) {
m.renewalMu.Lock()
defer m.renewalMu.Unlock()
if m.renewal[ck] != nil {
@@ -1200,6 +1128,10 @@ func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf
if err := leaf.VerifyHostname(ck.domain); err != nil {
return nil, err
}
+ // renew certificates revoked by Let's Encrypt in January 2022
+ if isRevokedLetsEncrypt(leaf) {
+ return nil, errors.New("acme/autocert: certificate was probably revoked by Let's Encrypt")
+ }
// ensure the leaf corresponds to the private key and matches the certKey type
switch pub := leaf.PublicKey.(type) {
case *rsa.PublicKey:
@@ -1230,6 +1162,18 @@ func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf
return leaf, nil
}
+// https://community.letsencrypt.org/t/2022-01-25-issue-with-tls-alpn-01-validation-method/170450
+var letsEncryptFixDeployTime = time.Date(2022, time.January, 26, 00, 48, 0, 0, time.UTC)
+
+// isRevokedLetsEncrypt returns whether the certificate is likely to be part of
+// a batch of certificates revoked by Let's Encrypt in January 2022. This check
+// can be safely removed from May 2022.
+func isRevokedLetsEncrypt(cert *x509.Certificate) bool {
+ O := cert.Issuer.Organization
+ return len(O) == 1 && O[0] == "Let's Encrypt" &&
+ cert.NotBefore.Before(letsEncryptFixDeployTime)
+}
+
type lockedMathRand struct {
sync.Mutex
rnd *mathrand.Rand
diff --git a/vendor/golang.org/x/crypto/acme/autocert/renewal.go b/vendor/golang.org/x/crypto/acme/autocert/renewal.go
index 665f870d..0df7da78 100644
--- a/vendor/golang.org/x/crypto/acme/autocert/renewal.go
+++ b/vendor/golang.org/x/crypto/acme/autocert/renewal.go
@@ -21,8 +21,9 @@ type domainRenewal struct {
ck certKey
key crypto.Signer
- timerMu sync.Mutex
- timer *time.Timer
+ timerMu sync.Mutex
+ timer *time.Timer
+ timerClose chan struct{} // if non-nil, renew closes this channel (and nils out the timer fields) instead of running
}
// start starts a cert renewal timer at the time
@@ -38,16 +39,28 @@ func (dr *domainRenewal) start(exp time.Time) {
dr.timer = time.AfterFunc(dr.next(exp), dr.renew)
}
-// stop stops the cert renewal timer.
-// If the timer is already stopped, calling stop is a noop.
+// stop stops the cert renewal timer and waits for any in-flight calls to renew
+// to complete. If the timer is already stopped, calling stop is a noop.
func (dr *domainRenewal) stop() {
dr.timerMu.Lock()
defer dr.timerMu.Unlock()
- if dr.timer == nil {
- return
+ for {
+ if dr.timer == nil {
+ return
+ }
+ if dr.timer.Stop() {
+ dr.timer = nil
+ return
+ } else {
+ // dr.timer fired, and we acquired dr.timerMu before the renew callback did.
+ // (We know this because otherwise the renew callback would have reset dr.timer!)
+ timerClose := make(chan struct{})
+ dr.timerClose = timerClose
+ dr.timerMu.Unlock()
+ <-timerClose
+ dr.timerMu.Lock()
+ }
}
- dr.timer.Stop()
- dr.timer = nil
}
// renew is called periodically by a timer.
@@ -55,7 +68,9 @@ func (dr *domainRenewal) stop() {
func (dr *domainRenewal) renew() {
dr.timerMu.Lock()
defer dr.timerMu.Unlock()
- if dr.timer == nil {
+ if dr.timerClose != nil {
+ close(dr.timerClose)
+ dr.timer, dr.timerClose = nil, nil
return
}
@@ -67,8 +82,8 @@ func (dr *domainRenewal) renew() {
next = renewJitter / 2
next += time.Duration(pseudoRand.int63n(int64(next)))
}
- dr.timer = time.AfterFunc(next, dr.renew)
testDidRenewLoop(next, err)
+ dr.timer = time.AfterFunc(next, dr.renew)
}
// updateState locks and replaces the relevant Manager.state item with the given