Skip to content

Commit

Permalink
attempt to detect improper TLS config
Browse files Browse the repository at this point in the history
when an io.ErrUnexpectedEOF is returned when reading
a correlation ID, attempt to detect if the messages
from the broker was a TLS Alert Message and decorate
the returned error
  • Loading branch information
rhansen2 committed Feb 7, 2022
1 parent 54559ab commit 47c588f
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
44 changes: 44 additions & 0 deletions protocol/response.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package protocol

import (
"crypto/tls"
"encoding/binary"
"errors"
"fmt"
"io"
)
Expand Down Expand Up @@ -35,6 +38,25 @@ func ReadResponse(r io.Reader, apiKey ApiKey, apiVersion int16) (correlationID i

d.remain = int(size)
correlationID = d.readInt32()
if err = d.err; err != nil {
if errors.Is(err, io.ErrUnexpectedEOF) {
// If a Writer/Reader is configured without TLS and connects
// to a broker expecting TLS the only message we return to the
// caller is io.ErrUnexpetedEOF which is opaque. This section
// tries to determine if that's what has happened.
// We first deconstruct the initial 4 bytes of the message
// from the size which was read earlier.
// Next, we examine those bytes to see if they looks like a TLS
// error message. If they do we wrap the io.ErrUnexpectedEOF
// with some context.
if looksLikeUnexpectedTLS(size) {
err = fmt.Errorf("%w: broker appears to be expecting TLS", io.ErrUnexpectedEOF)
}
return
}
err = dontExpectEOF(err)
return
}

res := &t.responses[apiVersion-minVersion]

Expand Down Expand Up @@ -109,3 +131,25 @@ func WriteResponse(w io.Writer, apiVersion int16, correlationID int32, msg Messa

return err
}

const (
tlsAlertByte byte = 0x15
)

// looksLikeUnexpectedTLS returns true if the size passed in resemble
// the TLS alert message that is returned to a client which sends
// an invalid ClientHello message.
func looksLikeUnexpectedTLS(size int32) bool {
var sizeBytes [4]byte
binary.BigEndian.PutUint32(sizeBytes[:], uint32(size))

if sizeBytes[0] != tlsAlertByte {
return false
}
version := int(sizeBytes[1])<<8 | int(sizeBytes[2])

if version <= tls.VersionTLS13 && version >= tls.VersionTLS10 {
return true
}
return false
}
28 changes: 28 additions & 0 deletions protocol/response_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package protocol

import (
"bytes"
"errors"
"io"
"strings"
"testing"
)

func TestReadResponseUnexpectedTLSDetection(t *testing.T) {
var buf bytes.Buffer

buf.Write([]byte{tlsAlertByte, 0x03, 0x03, 10, 0, 0, 0})

correlationID, _, err := ReadResponse(&buf, ApiVersions, 0)
if !errors.Is(err, io.ErrUnexpectedEOF) {
t.Fatalf("expected an io.ErrUnexpectedEOF from ReadResponse got %v", err)
}

if !strings.Contains(err.Error(), "broker appears to be expecting TLS") {
t.Fatalf("expected error messae to contain %s got %s", "broker appears to be expecting TLS", err.Error())
}

if correlationID != 0 {
t.Fatalf("expected correlationID of 0 got %d", correlationID)
}
}

0 comments on commit 47c588f

Please sign in to comment.