Skip to content

Commit 79dbfba

Browse files
maxl99strongjz
authored andcommitted
fix: tls-passthrough if ClientHello is fragmented
We need to read the entire length of the ClientHello packet in order to get the SNI field for proper tls passthrough.
1 parent 698960e commit 79dbfba

File tree

1 file changed

+31
-4
lines changed

1 file changed

+31
-4
lines changed

pkg/tcpproxy/tcp.go

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ type TCPProxy struct {
4040
Default *TCPServer
4141
}
4242

43+
var (
44+
tlsHeaderLength = 5
45+
tlsMaxMessageLength = 16384
46+
)
47+
4348
// Get returns the TCPServer to use for a given host.
4449
func (p *TCPProxy) Get(host string) *TCPServer {
4550
if p.ServerList == nil {
@@ -59,15 +64,37 @@ func (p *TCPProxy) Get(host string) *TCPServer {
5964
// and open a connection to the passthrough server.
6065
func (p *TCPProxy) Handle(conn net.Conn) {
6166
defer conn.Close()
62-
// See: https://www.ibm.com/docs/en/ztpf/1.1.0.15?topic=sessions-ssl-record-format
63-
data := make([]byte, 16384)
64-
65-
length, err := conn.Read(data)
67+
// It appears that the ClientHello must fit into *one* TLSPlaintext message:
68+
// When a client first connects to a server, it is REQUIRED to send the ClientHello as its first TLS message.
69+
// Source: https://datatracker.ietf.org/doc/html/rfc8446#section-4.1.2
70+
71+
// length: The length (in bytes) of the following TLSPlaintext.fragment. The length MUST NOT exceed 2^14 bytes.
72+
// An endpoint that receives a record that exceeds this length MUST terminate the connection with a "record_overflow" alert.
73+
// Source: https://datatracker.ietf.org/doc/html/rfc8446#section-5.1
74+
// bytes 0 : content type
75+
// bytes 1-2: legacy version
76+
// bytes 3-4: length
77+
// bytes 5+ : message
78+
// https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_record
79+
// Thus, we need to allocate 5 + 16384 bytes
80+
data := make([]byte, tlsHeaderLength+tlsMaxMessageLength)
81+
82+
length, err := io.ReadAtLeast(conn, data, tlsHeaderLength)
6683
if err != nil {
6784
klog.V(4).ErrorS(err, "Error reading data from the connection")
6885
return
6986
}
7087

88+
// otherwise, ReadAtLeast may produce an index out of bounds
89+
clientHelloLength := min(int(data[3])<<8+int(data[4]), tlsMaxMessageLength)
90+
91+
bLength, err := io.ReadAtLeast(conn, data[length:], tlsHeaderLength+clientHelloLength-length)
92+
if err != nil {
93+
klog.V(4).ErrorS(err, fmt.Sprintf("Error reading ClientHello of length %d from the connection", clientHelloLength))
94+
return
95+
}
96+
length += bLength
97+
7198
proxy := p.Default
7299
hostname, err := parser.GetHostname(data)
73100
if err == nil {

0 commit comments

Comments
 (0)