DEV Community

Frits Hoogland for YugabyteDB

Posted on

Use PostgreSQL SSL connection in rust with self-signed certificates

This blogpost is about using the rust language to create a connection to PostgreSQL and YugabyteDB, which is wire-compatible with postgres so therefore applies too. This is actually extremely simple:

unencrypted simple postgres connection

Add the necessary crate to Cargo.toml:

postgres = "0.19.2"
Enter fullscreen mode Exit fullscreen mode

And perform the connection in main.rs:

use postgres::{Client, NoTls};

fn main() {
    // no SSL/TLS
    let mut connection = Client::connect("host=192.168.66.201 port=5432 user=postgres password=postgres", NoTls).expect("failed to create notls postgres connection");
    let result = connection.query_one("select 10", &[]).expect("failed to execute select 10 to postgres");
    let value: i32 = result.get(0);
    println!("result of query_one call: {}", value);
}
Enter fullscreen mode Exit fullscreen mode

Move the postgres crate into the scope for the Client and NoTls methods, create a connection, and execute a query. I used query_one(), which executes a query that should return a single row.

encrypted/TLS simple postgres connection

However, it gets more interesting with SSL. If you want to use a TLS connection with postgres in rust, there are two options: openssl and native_tls. The reason I included 'self signed certificates' in the title is: so far, it seems the native_tls crate does not allow self-signed certificates. This seems to cause some people to state that you can't use rust, postgres and TLS connections with self-signed certificates. This is not true.

Using openssl you can. Does that make openssl less secure? No: openssl also does not allow self-signed certificates to be used by default. However, it allows you to disable the verification for certificate authorities, so unofficial (self-signed) certificate authority certificates can be used. Of course that should not be done in an official implementation that is supposed to be secure. But it is perfectly fine to do so for a test or proof-of-concept setup so you can run with SSL/TLS connections without requiring to obtain officially signed certificates.

This is how that is done:
Cargo.toml:

postgres = "0.19.2"
openssl = "0.10.38"
postgres-openssl = "0.5.0"
Enter fullscreen mode Exit fullscreen mode

main.rs:

fn main() {
    let mut builder = SslConnector::builder(SslMethod::tls()).expect("unable to create sslconnector builder");
    builder.set_ca_file("/tmp/ca.cert").expect("unable to load ca.cert");
    builder.set_verify(SslVerifyMode::NONE);
    let connector = MakeTlsConnector::new(builder.build());

    let mut connection = Client::connect("host=192.168.66.201 port=5432 sslmode=require user=postgres password=postgres", connector).expect("failed to create tls postgres connection");
    let result = connection.query_one("select 10", &[]).expect("failed to execute select 10 to postgres");
    let value: i32 = result.get(0);
    println!("result of query_one call: {}", value);
}
Enter fullscreen mode Exit fullscreen mode

The first part builds an SSL TLS connector based on a custom created certificate authority certificate, and explicitly turns off the verification of the certificate authority certificate. This is what allows the self-signed certificate to be used.

The second part is identical to the first example, with the exception that TLS specification of the connection is changed from NoTls to the TLS connector.

Discussion (0)