fix: decline SSL negotiation in plaintext mode
All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 5m7s
Test and Release PawSQL / test (push) Successful in 44s
Test and Release PawSQL / release (push) Successful in 10s

This commit is contained in:
2026-09-15 22:28:16 -04:00
parent e67698f3c2
commit cae9419cd5
4 changed files with 125 additions and 2 deletions

View File

@@ -257,6 +257,79 @@ func TestServerRoutesPlaintextConnectionsByDatabase(t *testing.T) {
}
}
func TestServerDeclinesSSLNegotiationInPlaintextMode(t *testing.T) {
backendListener := listen(t)
defer backendListener.Close()
backendDatabase := make(chan string, 1)
go func() {
connection, err := backendListener.Accept()
if err != nil {
return
}
defer connection.Close()
startup, err := pgwire.ReadStartupMessage(connection)
if err != nil {
return
}
backendDatabase <- startup.Database
_, _ = connection.Write([]byte("backend"))
}()
staticResolver, err := router.NewStaticResolver([]config.DatabaseConfig{{
Name: "analytics",
Upstream: backendListener.Addr().String(),
}})
if err != nil {
t.Fatal(err)
}
routingServer, err := New(nil, staticResolver, nil)
if err != nil {
t.Fatal(err)
}
listener := listen(t)
defer func() {
_ = routingServer.Shutdown()
routingServer.Wait()
_ = listener.Close()
}()
go func() { _ = routingServer.Serve(listener) }()
connection, err := net.Dial("tcp", listener.Addr().String())
if err != nil {
t.Fatal(err)
}
defer connection.Close()
request := pgwire.SSLRequest()
if _, err := connection.Write(request[:]); err != nil {
t.Fatal(err)
}
response := make([]byte, 1)
if _, err := io.ReadFull(connection, response); err != nil {
t.Fatal(err)
}
if response[0] != 'N' {
t.Fatalf("SSL response = %q, want N", response)
}
if _, err := connection.Write(startupMessage("analytics")); err != nil {
t.Fatal(err)
}
response = make([]byte, len("backend"))
if _, err := io.ReadFull(connection, response); err != nil {
t.Fatal(err)
}
if string(response) != "backend" {
t.Fatalf("proxied response = %q", response)
}
select {
case database := <-backendDatabase:
if database != "analytics" {
t.Errorf("backend database = %q, want analytics", database)
}
case <-time.After(time.Second):
t.Fatal("backend did not receive the StartupMessage after SSL decline")
}
}
func startupMessage(database string) []byte {
body := make([]byte, 4)
binary.BigEndian.PutUint32(body, 196608)