diff options
Diffstat (limited to 'vendor/golang.org/x/crypto/acme/autocert/autocert.go')
-rw-r--r-- | vendor/golang.org/x/crypto/acme/autocert/autocert.go | 75 |
1 files changed, 52 insertions, 23 deletions
diff --git a/vendor/golang.org/x/crypto/acme/autocert/autocert.go b/vendor/golang.org/x/crypto/acme/autocert/autocert.go index f50c88e..b101020 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/autocert.go +++ b/vendor/golang.org/x/crypto/acme/autocert/autocert.go @@ -33,6 +33,12 @@ import ( "golang.org/x/crypto/acme" ) +// createCertRetryAfter is how much time to wait before removing a failed state +// entry due to an unsuccessful createCert call. +// This is a variable instead of a const for testing. +// TODO: Consider making it configurable or an exp backoff? +var createCertRetryAfter = time.Minute + // pseudoRand is safe for concurrent use. var pseudoRand *lockedMathRand @@ -77,20 +83,10 @@ func defaultHostPolicy(context.Context, string) error { // It obtains and refreshes certificates automatically, // as well as providing them to a TLS server via tls.Config. // -// A simple usage example: -// -// m := autocert.Manager{ -// Prompt: autocert.AcceptTOS, -// HostPolicy: autocert.HostWhitelist("example.org"), -// } -// s := &http.Server{ -// Addr: ":https", -// TLSConfig: &tls.Config{GetCertificate: m.GetCertificate}, -// } -// s.ListenAndServeTLS("", "") -// -// To preserve issued certificates and improve overall performance, -// use a cache implementation of Cache. For instance, DirCache. +// You must specify a cache implementation, such as DirCache, +// to reuse obtained certificates across program restarts. +// Otherwise your server is very likely to exceed the certificate +// issuer's request rate limits. type Manager struct { // Prompt specifies a callback function to conditionally accept a CA's Terms of Service (TOS). // The registration may require the caller to agree to the CA's TOS. @@ -124,7 +120,7 @@ type Manager struct { // RenewBefore optionally specifies how early certificates should // be renewed before they expire. // - // If zero, they're renewed 1 week before expiration. + // If zero, they're renewed 30 days before expiration. RenewBefore time.Duration // Client is used to perform low-level operations, such as account registration @@ -174,10 +170,20 @@ type Manager struct { // The error is propagated back to the caller of GetCertificate and is user-visible. // This does not affect cached certs. See HostPolicy field description for more details. func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { + if m.Prompt == nil { + return nil, errors.New("acme/autocert: Manager.Prompt not set") + } + name := hello.ServerName if name == "" { return nil, errors.New("acme/autocert: missing server name") } + if !strings.Contains(strings.Trim(name, "."), ".") { + return nil, errors.New("acme/autocert: server name component count invalid") + } + if strings.ContainsAny(name, `/\`) { + return nil, errors.New("acme/autocert: server name contains invalid character") + } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() @@ -252,6 +258,7 @@ func (m *Manager) cert(ctx context.Context, name string) (*tls.Certificate, erro } // cacheGet always returns a valid certificate, or an error otherwise. +// If a cached certficate exists but is not valid, ErrCacheMiss is returned. func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate, error) { if m.Cache == nil { return nil, ErrCacheMiss @@ -264,7 +271,7 @@ func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate // private priv, pub := pem.Decode(data) if priv == nil || !strings.Contains(priv.Type, "PRIVATE") { - return nil, errors.New("acme/autocert: no private key found in cache") + return nil, ErrCacheMiss } privKey, err := parsePrivateKey(priv.Bytes) if err != nil { @@ -282,13 +289,14 @@ func (m *Manager) cacheGet(ctx context.Context, domain string) (*tls.Certificate pubDER = append(pubDER, b.Bytes) } if len(pub) > 0 { - return nil, errors.New("acme/autocert: invalid public key") + // Leftover content not consumed by pem.Decode. Corrupt. Ignore. + return nil, ErrCacheMiss } // verify and create TLS cert leaf, err := validCert(domain, pubDER, privKey) if err != nil { - return nil, err + return nil, ErrCacheMiss } tlscert := &tls.Certificate{ Certificate: pubDER, @@ -369,6 +377,23 @@ func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certifica der, leaf, err := m.authorizedCert(ctx, state.key, domain) if err != nil { + // Remove the failed state after some time, + // making the manager call createCert again on the following TLS hello. + time.AfterFunc(createCertRetryAfter, func() { + defer testDidRemoveState(domain) + m.stateMu.Lock() + defer m.stateMu.Unlock() + // Verify the state hasn't changed and it's still invalid + // before deleting. + s, ok := m.state[domain] + if !ok { + return + } + if _, err := validCert(domain, s.cert, s.key); err == nil { + return + } + delete(m.state, domain) + }) return nil, err } state.cert = der @@ -417,7 +442,6 @@ func (m *Manager) certState(domain string) (*certState, error) { // authorizedCert starts domain ownership verification process and requests a new cert upon success. // The key argument is the certificate private key. func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) { - // TODO: make m.verify retry or retry m.verify calls here if err := m.verify(ctx, domain); err != nil { return nil, nil, err } @@ -643,10 +667,10 @@ func (m *Manager) hostPolicy() HostPolicy { } func (m *Manager) renewBefore() time.Duration { - if m.RenewBefore > maxRandRenew { + if m.RenewBefore > renewJitter { return m.RenewBefore } - return 7 * 24 * time.Hour // 1 week + return 720 * time.Hour // 30 days } // certState is ready when its mutex is unlocked for reading. @@ -788,5 +812,10 @@ func (r *lockedMathRand) int63n(max int64) int64 { return n } -// for easier testing -var timeNow = time.Now +// For easier testing. +var ( + timeNow = time.Now + + // Called when a state is removed. + testDidRemoveState = func(domain string) {} +) |