Rust, Postgres and openssl

I'm trying to make a connection to a cockroachdb database using the postgres crate.

I tried following the example code here: https://www.bookstack.cn/read/CockroachDB/d34f4af2f9885ead.md?wd=Rust
Which starts off:

`use postgres::{Connection, TlsMode};`
`use postgres::tls::openssl::OpenSsl;`
`use postgres::tls::openssl::openssl::ssl::{SslConnectorBuilder, SslMethod};`
`use postgres::tls::openssl::openssl::x509::X509_FILETYPE_PEM;`

This fails to build with "failed to resolve: could not find openssl in tls

Then I try following the example code here: https://docs.rs/postgres-openssl/0.2.0-rc.1/postgres_openssl/

Which fails to build with "unresolved import postgres_openssl::MakeTlsConnector"

I similarly failed to get anything to build using postgres_native_tls following the example here: https://docs.rs/postgres-native-tls/0.2.0-rc.1/postgres_native_tls/ Which failed to build with some similar unresolved errors.

Currently my Cargo.toml contains:

[dependencies]
openssl = "*"
postgres = "0.15.2"
postgres-openssl = "*"
postgres-native-tls = "*"
native-tls = "*"

I'm in a mess. After four hours of chasing documentation and finding nothing works and no clue anywhere I wonder if anyone has a working example.

1 Like

No but I have a similar experience with openssl in the context of trying to make normal TLS requests.
My server has a slightly older version of openssl (1.0.1), which means that I can not cross-compile for it with 1.1.1. Nor can I compile rust on the server directly because it can not even find its openssl, although it is there. I can not update it either, then there are problems with kernel incompatibility. This openssl is more bother than what it is worth. It all rather defeats the point of all those complex Rust compilations if at the end of the day you can not get a standalone binary out of it, I think.

It's day two of trying to make a secure connection with postgres/postgres_openssl.

Still not working.

I'm not sure how similar our problems may be Rustafarian, openssl does not seem to be the issue here, although I have yet to succeed in building any of my other Rust code that uses openssl on Windpws 10 for reasons that seem similar to yours.

Anyway today I find a golden nugget in the postgres-openssl source on github, an example! From the example I start to write code to make a connection, one line at a time...The openssl part seems to go OK but it does not build for "^^^^^^ could not find Client in `postgres"

// This from the example in the postgres_openssl source:
// https://github.com/sfackler/rust-postgres/blob/master/postgres-openssl/src/lib.rs

use openssl::ssl::{SslConnector, SslMethod};
use postgres_openssl::{MakeTlsConnector};

pub fn conn_secure () {
    println!("Making secure connection to cockroachdb");

    let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();

    builder.set_ca_file("/home/heater/certs/ca.crt").unwrap();

    let connector = MakeTlsConnector::new(builder.build());

    // This fails "^^^^^^ could not find `Client` in `postgres"
    let mut client = postgres::Client::connect(
        "host=xyz.com user=root sslmode=require",
        connector,
    ).unwrap();
}

This is nuts. A secure connection in node.js takes about 10 minutes to write. My Rust ergonomics are zero here.

The master branch of the rust-postgres repository does not correspond to the 0.15 version.

Thank you. That is good to know. Things look very different in the 0.15.2 tag.

I have now tried building the cockroachlabs secure example against a bunch of postgres crate releases, 15.2, 14 something, ...all the way down to 10 something. None of them work. Failing with

error[E0433]: failed to resolve: could not find `tls` in `postgres`

and the like,

The cockroachlabs example is here: https://www.cockroachlabs.com/docs/stable/build-a-rust-app-with-cockroachdb.html It says noting useful about when it was written or what versions it was written against. It does contain the warning:

// Warning! This API will be changing in the next version of these crates.

The upshot of all this is that I'm totally lost. Nothing works and there is no hint of documentation anywhere. Couple that with my being very new to Rust and it's hopeless.

I do have an insecure connection working and can make queries to my hearts content. So all I need is a dummies guide, a holding hand and a working example of how to make a secure connection.

[dependencies]
postgres = "0.15"
postgres-openssl = "0.1"
use postgres::{Connection, TlsMode};
use postgres_openssl::SslConnector;
use postgres_openssl::openssl::ssl::SslMethod;

fn main() {
    let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
    builder.set_ca_file("../test/server.crt").unwrap();
    let negotiator = OpenSsl::from(builder.build());
    let conn = Connection::connect(
        "postgres://ssl_user@localhost:5433/postgres",
        TlsMode::Require(&negotiator),
    ).unwrap();
    conn.execute("SELECT 1::VARCHAR", &[]).unwrap();
}

Ah thanks.

I feel we are getting very close. Sadly that fails to build with:

error[E0433]: failed to resolve: use of undeclared type or module `OpenSsl`
  --> src/sfackler_example.rs:10:22
   |
10 |     let negotiator = OpenSsl::from(builder.build());
   |                      ^^^^^^^ use of undeclared type or module `OpenSsl`

error[E0603]: struct `SslConnector` is private
 --> src/sfackler_example.rs:2:23
  |
2 | use postgres_openssl::SslConnector;
  |                       ^^^^^^^^^^^^

So I add "OpenSsl" to the use clause and end up with this:

use postgres::{Connection, TlsMode};
use postgres_openssl::{OpenSsl, SslConnector};
use postgres_openssl::openssl::ssl::SslMethod;

pub fn sfackler_example() {
    let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();

    builder.set_ca_file("../test/server.crt").unwrap();

    let negotiator = OpenSsl::from(builder.build());

    let conn = Connection::connect(
        "postgres://ssl_user@localhost:5433/postgres",
        TlsMode::Require(&negotiator),
    ).unwrap();

    conn.execute("SELECT 1::VARCHAR", &[]).unwrap();
}

Which fails with only:

error[E0603]: struct `SslConnector` is private
 --> src/sfackler_example.rs:2:32
  |
2 | use postgres_openssl::{OpenSsl,SslConnector};
  |                                ^^^^^^^^^^^^

Yay, this builds cleanly!

use postgres::{Connection, TlsMode};
use postgres_openssl::{OpenSsl};
use postgres_openssl::openssl::ssl::{SslMethod, SslConnector} ;

pub fn sfackler_example() {
    let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();

    builder.set_ca_file("/home/heater/certs/ca.crt").unwrap();

    let negotiator = OpenSsl::from(builder.build());

    let conn = Connection::connect(
        "postgres://root@xyz.com:26257",
        TlsMode::Require(&negotiator),
    ).unwrap();

    conn.execute("SELECT 1::VARCHAR", &[]).unwrap();
}

Thank you sfackler. Great stuff.

It even tries to connect....

Nearly, nearly there....

I need a key and cert to connect as root user. Like so:

use postgres::{Connection, TlsMode};
use postgres_openssl::{OpenSsl};
use postgres_openssl::openssl::ssl::{SslMethod, SslConnector};

pub fn sfackler_example() {
    let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();

    builder.set_ca_file("/home/heater/certs/ca.crt").unwrap();
    builder.set_certificate_chain_file("/home/heater/certs/client.root.crt").unwrap();
    builder.set_private_key_file("/home/heater/certs/client.root.key", X509_FILETYPE_PEM).unwrap();

    let negotiator = OpenSsl::from(builder.build());

    let conn = Connection::connect(
        "postgres://root@xyz.com:26257",
        TlsMode::Require(&negotiator),
    ).unwrap();

    conn.execute("SELECT 1::VARCHAR", &[]).unwrap();
}

But where to get that "X509_FILETYPE_PEM" from?

The path in the cockroachlabs example:

use postgres::tls::openssl::openssl::x509::X509_FILETYPE_PEM;

Does not work of course.

Neither google or docs.rs search or my randomly poking around can find any sign of "X509_FILETYPE_PEM" anywhere.

So close....

https://docs.rs/openssl/0.10.25/openssl/ssl/struct.SslFiletype.html#associatedconstant.PEM

use postgres_openssl::openssl::ssl::SslFiletype;

SslFiletype::PEM

The cockroach labs example is out of date.

BINGO! It works! Thanks so much. That has made my day.

Yes, way out of date, that's why I'm here. I have been discussing with the cockroachlabs guys :

For the record here is my complete working example of a secure connection to cockroachdb using the postgress crate:

use postgres::{Connection, TlsMode};
use postgres_openssl::{OpenSsl};
use postgres_openssl::openssl::ssl::{SslMethod, SslConnector};
use postgres_openssl::openssl::ssl::SslFiletype;

struct Person {
    id:   i64,
    name: String,
    data: Option<Vec<u8>>,
}

pub fn sfackler_example() {
    let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();

    builder.set_ca_file("/home/heater/certs/ca.crt").unwrap();
    builder.set_certificate_chain_file("/home/heater/certs/client.root.crt").unwrap();
    builder.set_private_key_file("/home/heater/certs/client.root.key", SslFiletype::PEM).unwrap();

    let negotiator = OpenSsl::from(builder.build());

    let conn = Connection::connect(
        "postgres://root@xyz.com:26257",
        TlsMode::Require(&negotiator),
    ).unwrap();

    conn.execute("USE defaultdb", &[]).unwrap();

    match conn.execute("CREATE TABLE person (
                    id              SERIAL PRIMARY KEY,
                    name            VARCHAR NOT NULL,
                    data            BYTEA
                  )", &[]) {
        Ok(_) => (),
        Err(e) => {
            // Will error out if table already exists.
            println!("{:?}", e);
        },
    }

    let me = Person {
        id: 1,
        name: "Heater".to_string(),
        data: None,
    };

    // NB: This does not insert the id.
    conn.execute("INSERT INTO person (name, data) VALUES ($1, $2)",
                 &[&me.name, &me.data]).unwrap();

    for row in &conn.query("SELECT id, name, data FROM person", &[]).unwrap() {
        let person = Person {
            id: row.get(0),
            name: row.get(1),
            data: row.get(2),
        };
        println!("Found person {}: {}", person.id, person.name);
    }
}

With toml file:

[dependencies]
postgres = "0.15"
postgres-openssl = "0.1"

Thanks again.

1 Like