Repository navigation
pgmoon never sends TLS SNI to PostgreSQL, making Kong unusable behind SNI-routing TCP proxies #14937
Replies: 1 comment 1 reply
|
Reproduced, and your reading of the cause is right. I put a listener in front that answers the PostgreSQL A plain TLS sniffer is not enough for this, incidentally, and it is the same reason The line is return self.sock:sslhandshake(false, nil, self.config.ssl_verify)The second argument to the cosocket I also confirmed the fix is the obvious one. Mounting a pgmoon patched to Upstream already has both halves open, worth subscribing to rather than re-filing: leafo/pgmoon#153 for the report and leafo/pgmoon#154 for a patch that adds an SNI test and fixes the hostname handling. There is no Kong-side workaround through configuration. One detail to keep in mind when testing: SNI carries hostnames only, never IP literals. |
Uh oh!
There was an error while loading. Please reload this page.
Summary
Kong cannot connect to a PostgreSQL database that sits behind a TCP proxy which routes by TLS SNI (Traefik, Neon, and similar). The bundled
kong-pgmoondriver passesnilas theserver_nameargument to the OpenResty cosocketsslhandshake(), so Kong's ClientHello contains no SNI extension. The proxy cannot match a route, closes the connection, and Kong reports:psqlagainst the exact same endpoint, from the exact same pod, connects without any problem. No Kong configuration option changes this behaviour.Environment
3.6.0role: control_planev3.7.4withtls.passthrough: trueandHostSNI(<hostname>)kong-pgmoon == 1.16.2(perkong-latest.rockspec)Current behaviour
Kong configuration (this is the documented equivalent of
sslmode=require+ no certificate verification):Every migration and every control-plane start fails with
handshake failed.Expected behaviour
Kong connects, the same way
psql "sslmode=require host=postgres.example.com port=30432 ..."does from the same network namespace.Root cause
kong-pgmoonv1.16.2,pgmoon/init.lua, lines 928-950:Same code in the MoonScript source,
pgmoon/init.moon:907:The OpenResty cosocket signature is:
The second argument is the SNI. It is hardcoded to
nil— it does not come fromself.config, and there is no option that feeds it. Kong always takes the"nginx"branch, so this path is unconditional.It has never been implemented, in any version
Every tag of the
Kong/pgmoonfork:Current HEADs are unchanged:
Kong/pgmoon, branchkong-dev, commit6e8a52b(2026-04-20) —nilat lines 1039 and 1048leafo/pgmoon, master, commit8667edd(2026-06-09) —nilat line 1113git log --all -i --grep="sni" --grep="server_name" --grep="servername"returns zero commits in either repository.To be clear, I don't think this is a regression — SNI for the PostgreSQL protocol simply predates nobody implementing it here. libpq itself only started sending SNI in PostgreSQL 14 (commit
5c55dc8b, which also added thesslsniopt-out). That is precisely whypsqlworks and Kong does not.Why this breaks specifically with Traefik
PostgreSQL does not do TLS-on-connect. The client opens in plaintext, sends an 8-byte
SSLRequest, the server answersS, and only then does the TLS handshake start. Traefik implements this: it peeks for the PostgreSQL STARTTLS message and, if it matches, acknowledges it and signals the client to begin the TLS handshake, so it can read the SNI from the ClientHello and route (pkg/server/router/tcp/postgres.go).The bytes pgmoon sends are exactly the ones Traefik looks for:
So Traefik correctly identifies pgmoon as a PostgreSQL client and replies
S(which pgmoon reads and compares againstMSG_TYPE_B.parameter_status = "S"). Then it waits for the ClientHello to extract the SNI — and pgmoon sends none. No router matches, Traefik closes the connection, and OpenResty surfaces it aspeer closed connection in SSL handshake.Note that
pg_ssl_verify=offcannot help here: this is a routing failure, and it happens before any certificate is ever presented.Reproduction
The cleanest repro needs no Kong at all —
openssl s_clientreproduces both sides. Run both from inside the Kong pod, against a PostgreSQL endpoint behind a TraefikIngressRouteTCPusingtls.passthrough: trueandHostSNI(<hostname>):The successful (SNI) handshake for reference:
19is expected and harmless here — it's the CloudNativePG self-signed CA, andpg_ssl_verify=offignores it.Impact
Any Kong deployment whose PostgreSQL sits behind an SNI-routing TCP proxy is affected, and there is no configuration workaround — the fix has to be topological (a dedicated entrypoint with
HostSNI(\*`)`, a direct LoadBalancer/Service, or bypassing the proxy). This is a well-known class of problem for PostgreSQL clients without SNI support:psqlworksThere is also an unanswered discussion with this exact error: #14675 (Kong 3.6.1, PostgreSQL 15,
psqlworking over SSL). The only reply suggests adding the certificate to Kong's trusted CAs, which does not apply whenpg_ssl_verify=off. I believe that report is this same issue.Proposed fix
Kong already does exactly this for other TLS cosockets. The
tcp-logplugin exposesconf.tls_sniand calls:So the capability exists in Kong; it just never reached the database connection. Options, roughly in order of preference:
self.config.hostasserver_name, with an opt-out — this is what libpq did in PG14 withsslsni(on by default). Lowest friction, matches what users already expect becausepsqlworks.pg_ssl_sni(andpg_ro_ssl_snifor the read-only connection), plumbed through to theserver_nameargument.The pgmoon change itself is one argument. Happy to open a PR against
Kong/pgmoonif maintainers indicate which of the above they'd accept.Questions
pg_ssl_sni) or as libpq-style default-on behaviour?Kong/pgmoonthe right place for the PR, or would you want it upstreamed toleafo/pgmoonfirst?All reactions