Add support for sslnegotiation=direct (PostgreSQL 17)#3688
Conversation
PostgreSQL 17 added the `sslnegotiation` connection parameter, which
allows clients to start the TLS handshake immediately after the TCP
connection ("direct" negotiation) instead of first sending an SSLRequest
packet and waiting for the server's S/N reply ("postgres" negotiation,
the default and prior behavior). Direct negotiation saves one network
round-trip and works with protocol-agnostic TLS tooling.
- connection.js: extract the TLS upgrade into upgradeToSSL(); in direct
mode upgrade the socket right after connect (skipping the SSLRequest
exchange) and advertise the `postgresql` ALPN protocol as the server
requires.
- client.js: forward sslNegotiation to the Connection and skip
requestSsl() in direct mode.
- connection-parameters.js: read sslnegotiation from config /
PGSSLNEGOTIATION, validate it is `postgres` or `direct`, require SSL to
be enabled for `direct`, and include it in the libpq connection string.
- pg-connection-string: parse the sslnegotiation query param and enable
SSL automatically when `direct` is requested without other SSL config.
- docs: document the new option.
- tests: cover connection-string parsing, connection-parameters
validation, and the direct-vs-traditional connection behavior
(no SSLRequest packet, ALPN set only for direct).
Closes brianc#3346
|
Hi @brianc @hjr3 👋 Thank you for maintaining this package! Just a friendly heads up on this PR. I need my app to support a Postgres instance that requires direct SSL negotiation, so this would be really helpful on my end. I’d really appreciate it if you could take a look when you have a chance. Happy to adjust the implementation if there’s a better direction for node-postgres. |
|
This is really nice & comprehensive. Also, doesn't look like you auto-generated the entire PR description w/ a wall of chatbot text which is a nice bonus! It'd be dope to have a version of postgres in CI that has direct ssl negotation enabled so we have an end to end test there, but historically testing ssl permutations has been difficult to automate, and I appreciate the nice unit test coverage. I'm good to ship this, ty! |
#3693 😄 |
Adds support for PostgreSQL 17's
sslnegotiationconnection parameter. The default,postgres, keeps the existing behavior: the driver sends anSSLRequestpacket and waits for the server'sS/Nreply before starting the TLS handshake. The newdirectvalue starts the TLS handshake immediately after the TCP connection is established, saving a network round-trip and allowing protocol-agnostic TLS tooling. As in libpq,directadvertises thepostgresqlALPN protocol during the handshake and requires SSL to be enabled.Usage mirrors the existing
ssloptions — via config object,PGSSLNEGOTIATION, or connection string:Closes #3346.
A few design notes:
directrequires SSL to be enabled (ConnectionParametersthrows otherwise), matching libpq's rule that direct negotiation is only allowed withsslmode=requireor higher so there's no silent plaintext fallback. In a connection string,sslnegotiation=directenables SSL automatically when no other SSL option is present.connection.jswas extracted intoupgradeToSSL()so both negotiation styles share it; only thedirectpath skips theSSLRequest/S-byte exchange and setsALPNProtocols.sslnegotiationis also added to the libpq connection string for the native (pg-native) path.ConnectionParametersvalidation, and the connection behavior itself (noSSLRequestpacket is written in direct mode, and ALPN is set only for direct). I followed pgjdbc/libpq for the parameter semantics.I've verified this against a personal PostgreSQL 17.5 instance configured to require direct SSL. With
sslnegotiation=direct, the client establishes TLS 1.3, negotiates thepostgresqlALPN protocol, and runs queries successfully;pg_stat_sslconfirms the connection is encrypted server-side. The same server rejects the traditionalsslnegotiation=postgreshandshake withECONNRESET, confirming the two negotiation styles take genuinely different code paths.