sfackler / rust-postgres

Native PostgreSQL driver for the Rust programming language
Apache License 2.0
3.42k stars 436 forks source link

TLS Support in Wasm Environment #1075

Closed kflansburg closed 10 months ago

kflansburg commented 10 months ago

While it may be possible to include a TLS library in Wasm, some environments such as Cloudflare Workers handle TLS origination for you. Today, this appears to not work because once the connection is established, tokio-postgres connects to the server in a way that suggests the connection is not encrypted and the server appears to reject the request.

This is a proposal solution here, where a library can mark a Stream as encrypted with TLS by implementing the TlsStream trait, and then use a PassthroughTls struct to indicate to tokio-postgres that it can you the stream as-is, but treat the connection as a TLS connection.

Note that I have not actually gotten this to work and don't want to merge yet, because my test server appears to be rejecting the TLS version being used by Workers.

I'm looking for a sanity check on this proposal, or would appreciate any other suggestions for how this could be achieved.

sfackler commented 10 months ago

TLS connections can't be opened in the normal way to Postgres servers - there's a small pre-TLS handshake: https://www.postgresql.org/docs/16/protocol-flow.html#PROTOCOL-FLOW-SSL

kflansburg commented 10 months ago

We do have support for Start TLS and know that this works with Node pg, however, I'm not sure where I should hook in to call the method to switch from plaintext to TLS, could that be done in the TlsConnect::connect method?

Unfortunately there is not yet a standard API for what I'm describing, however we have a Winter CG proposal for our JavaScript API which is exposed to Rust guests via wasm-bindgen.

sfackler commented 10 months ago

Ah gotcha. Once you have a stream that's finished the TLS handshake you should be able to hand it to https://docs.rs/tokio-postgres/latest/tokio_postgres/config/struct.Config.html#method.connect_raw with NoTls. I don't think the client needs to know the TLS bits have already completed, but I could be misremembering?

kflansburg commented 10 months ago

Ah gotcha. Once you have a stream that's finished the TLS handshake you should be able to hand it to https://docs.rs/tokio-postgres/latest/tokio_postgres/config/struct.Config.html#method.connect_raw with NoTls.

Unfortunately doing this appears to cause an error. There must be some information sent in the Postgres protocol that indicates that the connection is plaintext (even though it's not) and the server rejects that. Perhaps that rings a bell?

I've pushed another commit that calls our start_tls method and this indeed works! But I understand if you want to go a different direction with this.

Edit: Alternatively, is there some preamble I must pass before upgrading the connection?

sfackler commented 10 months ago

Interesting - I'll go poke around a bit and see what's going on there.

kflansburg commented 10 months ago

Interesting - I'll go poke around a bit and see what's going on there.

Thanks, for reference the error I see on the server is invalid length of startup packet, and the exception on the client (probably from our implementation) indicates that the TLS handshake fails.

sfackler commented 10 months ago

If the current PR is working, then I don't think we need to actually make any changes to tokio-postgres - you can just define the TlsConnect implementation in a separate cloudflare-specific crate like we have for e.g. https://docs.rs/postgres-openssl/latest/postgres_openssl/

kflansburg commented 10 months ago

Ah yes, that makes sense. Thanks!

kflansburg commented 10 months ago

@sfackler, apologies, I remember why I felt that I needed to contributed upstream. Implementing TlsConnect requires access to the type private::ForcePrivateApi which I think is intended to prevent other crates from implementing it. Is there a way around this?

sfackler commented 10 months ago

You don't need to override that method: https://github.com/sfackler/rust-postgres/blob/master/postgres-openssl/src/lib.rs#L142-L167