diff options
author | Niall Sheridan <nsheridan@gmail.com> | 2017-10-18 13:15:14 +0100 |
---|---|---|
committer | Niall Sheridan <niall@intercom.io> | 2017-10-18 13:25:46 +0100 |
commit | 7b320119ba532fd409ec7dade7ad02011c309599 (patch) | |
tree | a39860f35b55e6cc499f8f5bfa969138c5dd6b73 /vendor/golang.org/x/crypto/acme/autocert | |
parent | 7c99874c7a3e7a89716f3ee0cdf696532e35ae35 (diff) |
Update dependencies
Diffstat (limited to 'vendor/golang.org/x/crypto/acme/autocert')
4 files changed, 71 insertions, 34 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) {} +) diff --git a/vendor/golang.org/x/crypto/acme/autocert/cache.go b/vendor/golang.org/x/crypto/acme/autocert/cache.go index 9f3e9d1..61a5fd2 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/cache.go +++ b/vendor/golang.org/x/crypto/acme/autocert/cache.go @@ -77,12 +77,13 @@ func (d DirCache) Put(ctx context.Context, name string, data []byte) error { if tmp, err = d.writeTempFile(name, data); err != nil { return } - // prevent overwriting the file if the context was cancelled - if ctx.Err() != nil { - return // no need to set err + select { + case <-ctx.Done(): + // Don't overwrite the file if the context was canceled. + default: + newName := filepath.Join(string(d), name) + err = os.Rename(tmp, newName) } - name = filepath.Join(string(d), name) - err = os.Rename(tmp, name) }() select { case <-ctx.Done(): diff --git a/vendor/golang.org/x/crypto/acme/autocert/listener.go b/vendor/golang.org/x/crypto/acme/autocert/listener.go index d4c93d2..d744df0 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/listener.go +++ b/vendor/golang.org/x/crypto/acme/autocert/listener.go @@ -36,6 +36,9 @@ import ( // operating system-specific cache or temp directory. This may not // be suitable for servers spanning multiple machines. // +// The returned listener uses a *tls.Config that enables HTTP/2, and +// should only be used with servers that support HTTP/2. +// // The returned Listener also enables TCP keep-alives on the accepted // connections. The returned *tls.Conn are returned before their TLS // handshake has completed. @@ -58,6 +61,9 @@ func NewListener(domains ...string) net.Listener { // Listener listens on the standard TLS port (443) on all interfaces // and returns a net.Listener returning *tls.Conn connections. // +// The returned listener uses a *tls.Config that enables HTTP/2, and +// should only be used with servers that support HTTP/2. +// // The returned Listener also enables TCP keep-alives on the accepted // connections. The returned *tls.Conn are returned before their TLS // handshake has completed. @@ -68,7 +74,8 @@ func (m *Manager) Listener() net.Listener { ln := &listener{ m: m, conf: &tls.Config{ - GetCertificate: m.GetCertificate, // bonus: panic on nil m + GetCertificate: m.GetCertificate, // bonus: panic on nil m + NextProtos: []string{"h2", "http/1.1"}, // Enable HTTP/2 }, } ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443") diff --git a/vendor/golang.org/x/crypto/acme/autocert/renewal.go b/vendor/golang.org/x/crypto/acme/autocert/renewal.go index 14ac905..6c5da2b 100644 --- a/vendor/golang.org/x/crypto/acme/autocert/renewal.go +++ b/vendor/golang.org/x/crypto/acme/autocert/renewal.go @@ -11,8 +11,8 @@ import ( "time" ) -// maxRandRenew is a maximum deviation from Manager.RenewBefore. -const maxRandRenew = time.Hour +// renewJitter is the maximum deviation from Manager.RenewBefore. +const renewJitter = time.Hour // domainRenewal tracks the state used by the periodic timers // renewing a single domain's cert. @@ -64,7 +64,7 @@ func (dr *domainRenewal) renew() { // TODO: rotate dr.key at some point? next, err := dr.do(ctx) if err != nil { - next = maxRandRenew / 2 + next = renewJitter / 2 next += time.Duration(pseudoRand.int63n(int64(next))) } dr.timer = time.AfterFunc(next, dr.renew) @@ -84,7 +84,7 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) { // but we try nonetheless if tlscert, err := dr.m.cacheGet(ctx, dr.domain); err == nil { next := dr.next(tlscert.Leaf.NotAfter) - if next > dr.m.renewBefore()+maxRandRenew { + if next > dr.m.renewBefore()+renewJitter { return next, nil } } @@ -113,7 +113,7 @@ func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) { func (dr *domainRenewal) next(expiry time.Time) time.Duration { d := expiry.Sub(timeNow()) - dr.m.renewBefore() // add a bit of randomness to renew deadline - n := pseudoRand.int63n(int64(maxRandRenew)) + n := pseudoRand.int63n(int64(renewJitter)) d -= time.Duration(n) if d < 0 { return 0 |