Add timeout to ACME-TLS/1 challenge handshake
Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
This commit is contained in:
parent
47d7094dfb
commit
e9f3089e90
2 changed files with 70 additions and 1 deletions
|
|
@ -3,6 +3,7 @@ package tcp
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -206,7 +207,17 @@ func (r *Router) acmeTLSALPNHandler() tcp.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
return tcp.HandlerFunc(func(conn tcp.WriteCloser) {
|
return tcp.HandlerFunc(func(conn tcp.WriteCloser) {
|
||||||
_ = tls.Server(conn, r.httpsTLSConfig).Handshake()
|
tlsConn := tls.Server(conn, r.httpsTLSConfig)
|
||||||
|
defer tlsConn.Close()
|
||||||
|
|
||||||
|
// This avoids stale connections when validating the ACME challenge,
|
||||||
|
// as we expect a validation request to complete in a short period of time.
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||||
|
log.FromContext(ctx).WithError(err).Debug("Error during ACME-TLS/1 handshake")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -679,6 +679,64 @@ func Test_Routing(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_Router_acmeTLSALPNHandlerTimeout(t *testing.T) {
|
||||||
|
router, err := NewRouter()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
router.httpsTLSConfig = &tls.Config{}
|
||||||
|
|
||||||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
acceptCh := make(chan struct{}, 1)
|
||||||
|
go func() {
|
||||||
|
close(acceptCh)
|
||||||
|
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
router.acmeTLSALPNHandler().
|
||||||
|
ServeTCP(conn.(*net.TCPConn))
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-acceptCh
|
||||||
|
|
||||||
|
conn, err := net.DialTimeout("tcp", listener.Addr().String(), 2*time.Second)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// This is a minimal truncated Client Hello message
|
||||||
|
// to simulate a hanging connection during TLS handshake.
|
||||||
|
clientHello := []byte{
|
||||||
|
// TLS Record Header
|
||||||
|
0x16, // Content Type: Handshake
|
||||||
|
0x03, 0x01, // Version: TLS 1.0 (for compatibility)
|
||||||
|
0x00, 0x50, // Length: 80 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = conn.Write(clientHello)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
// This will return an EOF as the acmeTLSALPNHandler will close the connection
|
||||||
|
// after a timeout during the TLS handshake.
|
||||||
|
b := make([]byte, 256)
|
||||||
|
_, err = conn.Read(b)
|
||||||
|
|
||||||
|
errCh <- err
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-errCh:
|
||||||
|
assert.ErrorIs(t, err, io.EOF)
|
||||||
|
|
||||||
|
case <-time.After(3 * time.Second):
|
||||||
|
t.Fatal("Error: Timeout waiting for acmeTLSALPNHandler to close the connection")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// routerTCPCatchAll configures a TCP CatchAll No TLS - HostSNI(`*`) router.
|
// routerTCPCatchAll configures a TCP CatchAll No TLS - HostSNI(`*`) router.
|
||||||
func routerTCPCatchAll(conf *runtime.Configuration) {
|
func routerTCPCatchAll(conf *runtime.Configuration) {
|
||||||
conf.TCPRouters["tcp-catchall"] = &runtime.TCPRouterInfo{
|
conf.TCPRouters["tcp-catchall"] = &runtime.TCPRouterInfo{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue