pgx icon indicating copy to clipboard operation
pgx copied to clipboard

PasswordMessage passed to postgres instance returns 'insufficient data left in message'

Open joram opened this issue 1 year ago • 4 comments

Describe the bug When proxying messages from the psql cli to a postgres instance (using a Backend for the client and a Frontend for the DB, I can successfully pass most messages back and forth until it gets to the PasswordMessage. When I proxy that to the database, it returns the error: FATAL 08P01 insufficient data left in message

To Reproduce Steps to reproduce the behavior:

If possible, please provide runnable example such as:

package main

# the code has a channel of the messages going back and forth, so it can keep the sequence in order.
# I'd be happy to spin up an example codebase with a working copy of the code or something, but it feels too big to drop in a bug report

switch msg := unCast.(type) {

case *pgproto3.SSLRequest:
    _, err := p.clientConn.Write([]byte("N"))
    if err != nil {
	    return err
    }

case *pgproto3.StartupMessage:
    p.username = msg.Parameters["user"]
    p.databaseFrontend.Send(msg)
    p.databaseFrontend.Flush()

case *pgproto3.AuthenticationSASL:
    p.clientBackend.Send(msg)
    p.clientBackend.Flush()

case *pgproto3.PasswordMessage:
    p.databaseFrontend.Send(msg)
    p.databaseFrontend.Flush()

case *pgproto3.ErrorResponse:
    p.clientBackend.Send(msg)
    p.clientBackend.Flush()

}

Expected behavior I expect the message from the client to be received without error by the postgres database, and for the initialization handshake to continue.

Actual behavior

 msg c->db (*pgproto3.SSLRequest): &{}
 msg c->db (*pgproto3.StartupMessage): ....
msg db->c (*pgproto3.AuthenticationSASL): &{[SCRAM-SHA-256]}
 msg c->db (*pgproto3.PasswordMessage): &{SCRAM-SHA-256}
 msg db->c (*pgproto3.ErrorResponse): &{FATAL FATAL 08P01 insufficient data left in message   %!!(MISSING)s(int32=0) %!!(MISSING)s(int32=0)        pqformat.c %!!(MISSING)s(int32=531) pq_copymsgbytes map[]}

Version

  • Go: go version go1.23.2 linux/amd64
  • PostgreSQL: postgres:17 docker image
  • pgx: github.com/jackc/pgx/v5 v5.7.1

joram avatar Nov 16 '24 04:11 joram

I've proxied the PG protocol before, but I've never done it with SSL or SASL. I would suggest trying it without SSL and maybe with a simpler password type. I wonder if psql is trying to use channel binding or something else not supported by pgx.

jackc avatar Nov 16 '24 14:11 jackc

Good info, thanks.

I'm currently injecting a N to ssl requests, but I'll give a simpler auth method a try.

Interestingly im able to make it work by proxying net.Conn communication, then when I see a Q message, i wrap those connections with pgproto3 frontend/backend, and operate at a higher level. I was just hoping to keep it with the nice higher level library. :)

joram avatar Nov 16 '24 15:11 joram

Clarification

The problem occurs because pgproto3.Backend requires its AuthType to be explicitly set to correctly parse authentication-related messages. If this state isn't synchronized with the upstream server (frontend), the backend will fail to handle messages like AuthenticationCleartextPassword, AuthenticationMD5Password, AuthenticationSASL, etc.

Resolution

You have to manually do something like: backend.SetAuthType(frontend.GetAuthType()) while reading from the frontend when possible.

alash3al avatar Jan 08 '25 21:01 alash3al

Not the original poster but I can confirm that @alash3al 's solution works. When you receive authentication methods from the backend you are proxying, you have to call SetAuthType or the p message will be processed as an empty pgproto3.PasswordMessage struct and then if you forward that to the backend you will get this error.

This Github issue saved me hours!

andrewcurioso avatar Aug 08 '25 22:08 andrewcurioso