-
-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to match on StartTLS for proxying Postgres? #187
Comments
Matching of PostgreSQL connections was recently merged in #186. Anything beyond matching and simple proxying/teeing will require custom handlers or matchers to be developed. My previous research indicates each database has its own packet structure, so each requires its own matching logic: I can't come up with a UX-friendly way to match arbitrary bytes |
@mohammed90 #186
The See: traefik/traefik#9929
How about: {
"match": [
{
"bytes": {
"values": [
{
"offset": 0,
"value": "0x0000000804d2162f"
},
{
"offset": 0,
"value": "0x0000000804d21630"
}
]
}
}
]
} The way that HAProxy does it is similar to that. |
I believe what's supposed to happen is that Caddy should send a "hello" / "accepted" style packet back, then the client will send SNI + ALPN like a normal TLS connection. Then caddy would reconstruct the StartTLS byte to the correct server, eat the response, and forward the true TLS connection on from that point. I'm digging in a little bit to try to figure out more... |
Looks like I was wrong. The packet is a postgres-specific postgres_prepare_tls_without_reconnect = function(host, port)
-- http://www.postgresql.org/docs/devel/static/protocol-message-formats.html
-- 80877103 is "SSLRequest" in v2 and v3 of Postgres protocol
local s, resp = comm.opencon(host, port, string.pack(">I4I4", 8, 80877103))
if not s then
return false, ("Failed to connect to Postgres server: %s"):format(resp)
end
-- v2 has "Y", v3 has "S"
if string.match(resp, "^[SY]") then
starttls_supported(host, port, true)
return true, s
elseif string.match(resp, "^N") then
starttls_supported(host, port, false)
return false, "Postgres server does not support SSL"
end
return false, "Unknown response from Postgres server"
end, So Caddy would have to respond with the byte Is there currently a way to modify that matcher to send the |
Verified: Terminal 1printf 'S' | nc -l localhost 54321 | hexyl Terminal 2psql 'postgres://postgres:postgres@localhost:54321/postgres?sslmode=require' Result (Terminal 1)
Comparing that to the output of
That very much appears that as soon as the character |
Thanks for digging into the SSL side of Postgres - it's certainly more complex and so I started by addressing the non-SSL side of things, for which I have #188 in progess, however it looks like there may be some hope from your comments. What is currently unclear to me is how Caddy L4 can match across multiple requests, I see some use of For other details on the Postgres protocol I have found a great resource in https://github.com/rueian/pgbroker |
@metafeather Awesome, thanks. We'll probably be making some improvements to the matching algorithm here soon: #192 -- just FYI. I don't know, maybe it won't affect your plugin (much). |
FYI: I've submitted a request to Postgres to add
I probably didn't submit it to the right place, but perhaps it will get the conversation started. |
Speak of the Zeitgeist! This discussed a little over a year ago, and the commits are beginning to land. |
Heyo! Postgres 17 has landed with standard TLS!! Now, I know many were looking forward for waiting the next 6 years for Debian and Ubuntu to include it but, for shame, I put together a script for so that anyone can compile it with fairly relocatable options (i.e. should work on Ubuntu 22 and 24, Debian Buster and Bookworm, etc, Alpine 3.18, 3.19, maybe even 3.20): See pinned issues at: https://github.com/bnnanet/postgresql-releases/issues And I made the binaries I've built with that process available on the same repo: https://github.com/bnnanet/postgresql-releases/releases/tag/REL_17_0 Due to a |
I died. 😆 Thanks AJ! |
Update
psql
will send00 00 00 08 04 d2 16 2f
(SSLRequest) and wait for eitherN
(no - plaintext)Y
(yes - version 2) (deprecated)S
(ssl - version 3)If
S
is sent, it will immediately begin a standard TLS connection.After TLS is terminated, of if
N
(plaintext) is sent, it will send00 00 xx xx 00 03 00 00
- where xx are Little-Endian length bytes (up to 4k at least) and 03 means "pg version 3", followed by the plaintext db connection information (username, dbname, application name, character encoding).If
sslmode=disable
in the client, it will skip the SSLRequest handshake and go straight to plain text, as ifN
had been sent, or TLS had been terminated.Original
The first packet from
psql
is00 00 00 08 04 d2 16 2f
, which I believe is related to StartTLS.openssl s_client -starttls postgres
Either way, it doesn't send any SNI or ALPN information until later on in the handshake.
Is there a way that I could route based on the first 8 bytes?
I almost got around this by using
sclient
for TLS and piping thepsql
connection through withsslmode=disable
, but I think my HTTP matcher is causing it to hang waiting for a\r\n
that never comes.The text was updated successfully, but these errors were encountered: