From c7dd3553946261b6863814018f2801f4f1d0465c Mon Sep 17 00:00:00 2001 From: Colleen Murphy Date: Fri, 22 Oct 2021 15:54:20 -0700 Subject: [PATCH] Skip closing an initializing connection Without this change, if a cert is updated (e.g. to add CNs) while the listener is in the middle of Accept()ing a new connection, the connection gets dropped, we'll see a message like this in the server logs: http: TLS handshake error from 127.0.0.1:51232: write tcp 127.0.7.1:8443->127.0.0.1:51232: use of closed network connection and the client (like a browser) won't necessarily reconnect. This change modifies the GetCertificate routine in the listener's tls.Config to keep track of the state of the incoming connections and only close connections that have completed GetCertificate and therefore are finished with their TLS handshake, so that only old established connections are closed. --- listener.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/listener.go b/listener.go index bd310ee..7ba32fb 100644 --- a/listener.go +++ b/listener.go @@ -275,8 +275,9 @@ func (l *listener) wrap(conn net.Conn) net.Conn { type closeWrapper struct { net.Conn - id int - l *listener + id int + l *listener + ready bool } func (c *closeWrapper) close() error { @@ -291,13 +292,14 @@ func (c *closeWrapper) Close() error { } func (l *listener) getCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { + newConn := hello.Conn if hello.ServerName != "" { if err := l.updateCert(hello.ServerName); err != nil { return nil, err } } - return l.loadCert() + return l.loadCert(newConn.(*closeWrapper)) } func (l *listener) updateCert(cn ...string) error { @@ -339,7 +341,7 @@ func (l *listener) updateCert(cn ...string) error { return nil } -func (l *listener) loadCert() (*tls.Certificate, error) { +func (l *listener) loadCert(currentConn *closeWrapper) (*tls.Certificate, error) { l.RLock() defer l.RUnlock() @@ -373,8 +375,13 @@ func (l *listener) loadCert() (*tls.Certificate, error) { if l.conns != nil && l.cert != nil { l.connLock.Lock() for _, conn := range l.conns { + // Don't close a connection that's in the middle of completing a TLS handshake + if !conn.ready { + continue + } _ = conn.close() } + l.conns[currentConn.id].ready = true l.connLock.Unlock() }