From 01bb95b9d8f7f4551ad8c35638656b3cadc7498e Mon Sep 17 00:00:00 2001 From: Leo Nash Date: Sat, 29 Nov 2025 01:50:31 +0000 Subject: [PATCH 1/2] Fix visibility errors in `postgres_store` tests --- rust/impls/src/migrations.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/impls/src/migrations.rs b/rust/impls/src/migrations.rs index bab951b..02d3f54 100644 --- a/rust/impls/src/migrations.rs +++ b/rust/impls/src/migrations.rs @@ -5,7 +5,7 @@ pub(crate) const MIGRATION_LOG_COLUMN: &str = "upgrade_from"; pub(crate) const CHECK_DB_STMT: &str = "SELECT 1 FROM pg_database WHERE datname = $1"; pub(crate) const INIT_DB_CMD: &str = "CREATE DATABASE"; #[cfg(test)] -const DROP_DB_CMD: &str = "DROP DATABASE"; +pub(crate) const DROP_DB_CMD: &str = "DROP DATABASE"; pub(crate) const GET_VERSION_STMT: &str = "SELECT db_version FROM vss_db_version;"; pub(crate) const UPDATE_VERSION_STMT: &str = "UPDATE vss_db_version SET db_version=$1;"; pub(crate) const LOG_MIGRATION_STMT: &str = "INSERT INTO vss_db_upgrades VALUES($1);"; @@ -36,4 +36,4 @@ pub(crate) const MIGRATIONS: &[&str] = &[ );", ]; #[cfg(test)] -const DUMMY_MIGRATION: &str = "SELECT 1 WHERE FALSE;"; +pub(crate) const DUMMY_MIGRATION: &str = "SELECT 1 WHERE FALSE;"; From cfd075a6ad97a5978d3170eb3d72cd11a8d63e34 Mon Sep 17 00:00:00 2001 From: Leo Nash Date: Sat, 29 Nov 2025 01:45:34 +0000 Subject: [PATCH 2/2] Add the option to make TLS connections to the database Also add the option to specify an additional root certificate for the client to trust. --- rust/Cargo.lock | 153 +++++++++++++++++++++++++++++ rust/impls/Cargo.toml | 2 + rust/impls/src/postgres_store.rs | 149 ++++++++++++++++++++-------- rust/server/src/main.rs | 51 ++++++++-- rust/server/src/util/config.rs | 6 ++ rust/server/vss-server-config.toml | 2 + 6 files changed, 313 insertions(+), 50 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 4cd71d8..6c37e11 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -310,6 +310,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -649,6 +664,8 @@ dependencies = [ "bb8-postgres", "bytes", "chrono", + "native-tls", + "postgres-native-tls", "tokio", "tokio-postgres", ] @@ -810,6 +827,23 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -862,6 +896,50 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -967,12 +1045,30 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "portable-atomic" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +[[package]] +name = "postgres-native-tls" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac73153d92e4bde922bd6f1dfba7f1ab8132266c031153b55e20a1521cd36d49" +dependencies = [ + "native-tls", + "tokio", + "tokio-native-tls", + "tokio-postgres", +] + [[package]] name = "postgres-protocol" version = "0.6.9" @@ -1324,6 +1420,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1340,6 +1445,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.228" @@ -1662,6 +1790,16 @@ dependencies = [ "syn 2.0.108", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-postgres" version = "0.7.15" @@ -1826,6 +1964,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -2101,6 +2245,15 @@ dependencies = [ "windows-targets 0.53.5", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.48.5" diff --git a/rust/impls/Cargo.toml b/rust/impls/Cargo.toml index 37e3c47..505bda1 100644 --- a/rust/impls/Cargo.toml +++ b/rust/impls/Cargo.toml @@ -11,6 +11,8 @@ tokio-postgres = { version = "0.7.12", features = ["with-chrono-0_4"] } bb8-postgres = "0.7" bytes = "1.4.0" tokio = { version = "1.38.0", default-features = false } +native-tls = { version = "0.2.14", default-features = false } +postgres-native-tls = { version = "0.5.2", default-features = false, features = ["runtime"] } [dev-dependencies] tokio = { version = "1.38.0", default-features = false, features = ["rt-multi-thread", "macros"] } diff --git a/rust/impls/src/postgres_store.rs b/rust/impls/src/postgres_store.rs index 1e951df..4424a00 100644 --- a/rust/impls/src/postgres_store.rs +++ b/rust/impls/src/postgres_store.rs @@ -11,10 +11,14 @@ use bb8_postgres::bb8::Pool; use bb8_postgres::PostgresConnectionManager; use bytes::Bytes; use chrono::Utc; +use native_tls::TlsConnector; +use postgres_native_tls::MakeTlsConnector; use std::cmp::min; -use std::io; -use std::io::{Error, ErrorKind}; -use tokio_postgres::{error, NoTls, Transaction}; +use std::io::{self, Error, ErrorKind}; +use tokio_postgres::tls::{MakeTlsConnect, TlsConnect}; +use tokio_postgres::{error, Client, NoTls, Socket, Transaction}; + +pub use native_tls::Certificate; pub(crate) struct VssDbRecord { pub(crate) user_token: String, @@ -44,13 +48,31 @@ pub const LIST_KEY_VERSIONS_MAX_PAGE_SIZE: i32 = 100; pub const MAX_PUT_REQUEST_ITEM_COUNT: usize = 1000; /// A [PostgreSQL](https://www.postgresql.org/) based backend implementation for VSS. -pub struct PostgresBackendImpl { - pool: Pool>, +pub struct PostgresBackend +where + T: MakeTlsConnect + Clone + Send + Sync + 'static, + >::Stream: Send + Sync, + >::TlsConnect: Send, + <>::TlsConnect as TlsConnect>::Future: Send, +{ + pool: Pool>, } -async fn initialize_vss_database(postgres_endpoint: &str, db_name: &str) -> Result<(), Error> { - let postgres_dsn = format!("{}/{}", postgres_endpoint, "postgres"); - let (client, connection) = tokio_postgres::connect(&postgres_dsn, NoTls) +/// A postgres backend with plaintext connections to the database +pub type PostgresPlaintextBackend = PostgresBackend; + +/// A postgres backend with TLS connections to the database +pub type PostgresTlsBackend = PostgresBackend; + +async fn make_postgres_db_connection(postgres_endpoint: &str, tls: T) -> Result +where + T: MakeTlsConnect + Clone + Send + Sync + 'static, + T::Stream: Send + Sync, + T::TlsConnect: Send, + <>::TlsConnect as TlsConnect>::Future: Send, +{ + let dsn = format!("{}/{}", postgres_endpoint, "postgres"); + let (client, connection) = tokio_postgres::connect(&dsn, tls) .await .map_err(|e| Error::new(ErrorKind::Other, format!("Connection error: {}", e)))?; // Connection must be driven on a separate task, and will resolve when the client is dropped @@ -59,6 +81,19 @@ async fn initialize_vss_database(postgres_endpoint: &str, db_name: &str) -> Resu eprintln!("Connection error: {}", e); } }); + Ok(client) +} + +async fn initialize_vss_database( + postgres_endpoint: &str, db_name: &str, tls: T, +) -> Result<(), Error> +where + T: MakeTlsConnect + Clone + Send + Sync + 'static, + T::Stream: Send + Sync, + T::TlsConnect: Send, + <>::TlsConnect as TlsConnect>::Future: Send, +{ + let client = make_postgres_db_connection(&postgres_endpoint, tls).await?; let num_rows = client.execute(CHECK_DB_STMT, &[&db_name]).await.map_err(|e| { Error::new( @@ -66,7 +101,6 @@ async fn initialize_vss_database(postgres_endpoint: &str, db_name: &str) -> Resu format!("Failed to check presence of database {}: {}", db_name, e), ) })?; - if num_rows == 0 { let stmt = format!("{} {};", INIT_DB_CMD, db_name); client.execute(&stmt, &[]).await.map_err(|e| { @@ -79,17 +113,14 @@ async fn initialize_vss_database(postgres_endpoint: &str, db_name: &str) -> Resu } #[cfg(test)] -async fn drop_database(postgres_endpoint: &str, db_name: &str) -> Result<(), Error> { - let postgres_dsn = format!("{}/{}", postgres_endpoint, "postgres"); - let (client, connection) = tokio_postgres::connect(&postgres_dsn, NoTls) - .await - .map_err(|e| Error::new(ErrorKind::Other, format!("Connection error: {}", e)))?; - // Connection must be driven on a separate task, and will resolve when the client is dropped - tokio::spawn(async move { - if let Err(e) = connection.await { - eprintln!("Connection error: {}", e); - } - }); +async fn drop_database(postgres_endpoint: &str, db_name: &str, tls: T) -> Result<(), Error> +where + T: MakeTlsConnect + Clone + Send + Sync + 'static, + T::Stream: Send + Sync, + T::TlsConnect: Send, + <>::TlsConnect as TlsConnect>::Future: Send, +{ + let client = make_postgres_db_connection(&postgres_endpoint, tls).await?; let drop_database_statement = format!("{} {};", DROP_DB_CMD, db_name); let num_rows = client.execute(&drop_database_statement, &[]).await.map_err(|e| { @@ -100,14 +131,42 @@ async fn drop_database(postgres_endpoint: &str, db_name: &str) -> Result<(), Err Ok(()) } -impl PostgresBackendImpl { - /// Constructs a [`PostgresBackendImpl`] using `dsn` for PostgreSQL connection information. +impl PostgresPlaintextBackend { + /// Constructs a [`PostgresPlaintextBackend`] using `postgres_endpoint` for PostgreSQL connection information. pub async fn new(postgres_endpoint: &str, db_name: &str) -> Result { - initialize_vss_database(postgres_endpoint, db_name).await?; + PostgresBackend::new_internal(postgres_endpoint, db_name, NoTls).await + } +} + +impl PostgresTlsBackend { + /// Constructs a [`PostgresTlsBackend`] using `postgres_endpoint` for PostgreSQL connection information. + pub async fn new( + postgres_endpoint: &str, db_name: &str, additional_certificate: Option, + ) -> Result { + let mut builder = TlsConnector::builder(); + if let Some(cert) = additional_certificate { + builder.add_root_certificate(cert); + } + let connector = builder.build().map_err(|e| { + Error::new(ErrorKind::Other, format!("Error building tls connector: {}", e)) + })?; + PostgresBackend::new_internal(postgres_endpoint, db_name, MakeTlsConnector::new(connector)) + .await + } +} +impl PostgresBackend +where + T: MakeTlsConnect + Clone + Send + Sync + 'static, + T::Stream: Send + Sync, + T::TlsConnect: Send, + <>::TlsConnect as TlsConnect>::Future: Send, +{ + async fn new_internal(postgres_endpoint: &str, db_name: &str, tls: T) -> Result { + initialize_vss_database(postgres_endpoint, db_name, tls.clone()).await?; let vss_dsn = format!("{}/{}", postgres_endpoint, db_name); let manager = - PostgresConnectionManager::new_from_stringlike(vss_dsn, NoTls).map_err(|e| { + PostgresConnectionManager::new_from_stringlike(vss_dsn, tls).map_err(|e| { Error::new( ErrorKind::Other, format!("Failed to create PostgresConnectionManager: {}", e), @@ -121,7 +180,7 @@ impl PostgresBackendImpl { .build(manager) .await .map_err(|e| Error::new(ErrorKind::Other, format!("Failed to build Pool: {}", e)))?; - let postgres_backend = PostgresBackendImpl { pool }; + let postgres_backend = PostgresBackend { pool }; #[cfg(not(test))] postgres_backend.migrate_vss_database(MIGRATIONS).await?; @@ -376,7 +435,13 @@ impl PostgresBackendImpl { } #[async_trait] -impl KvStore for PostgresBackendImpl { +impl KvStore for PostgresBackend +where + T: MakeTlsConnect + Clone + Send + Sync + 'static, + T::Stream: Send + Sync, + T::TlsConnect: Send, + >::Future: Send, +{ async fn get( &self, user_token: String, request: GetObjectRequest, ) -> Result { @@ -578,9 +643,10 @@ impl KvStore for PostgresBackendImpl { #[cfg(test)] mod tests { use super::{drop_database, DUMMY_MIGRATION, MIGRATIONS}; - use crate::postgres_store::PostgresBackendImpl; + use crate::postgres_store::PostgresPlaintextBackend; use api::define_kv_store_tests; use tokio::sync::OnceCell; + use tokio_postgres::NoTls; const POSTGRES_ENDPOINT: &str = "postgresql://postgres:postgres@localhost:5432"; const MIGRATIONS_START: usize = 0; @@ -588,18 +654,19 @@ mod tests { static START: OnceCell<()> = OnceCell::const_new(); - define_kv_store_tests!(PostgresKvStoreTest, PostgresBackendImpl, { + define_kv_store_tests!(PostgresKvStoreTest, PostgresPlaintextBackend, { let db_name = "postgres_kv_store_tests"; START .get_or_init(|| async { - let _ = drop_database(POSTGRES_ENDPOINT, db_name).await; - let store = PostgresBackendImpl::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); + let _ = drop_database(POSTGRES_ENDPOINT, db_name, NoTls).await; + let store = + PostgresPlaintextBackend::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); let (start, end) = store.migrate_vss_database(MIGRATIONS).await.unwrap(); assert_eq!(start, MIGRATIONS_START); assert_eq!(end, MIGRATIONS_END); }) .await; - let store = PostgresBackendImpl::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); + let store = PostgresPlaintextBackend::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); let (start, end) = store.migrate_vss_database(MIGRATIONS).await.unwrap(); assert_eq!(start, MIGRATIONS_END); assert_eq!(end, MIGRATIONS_END); @@ -612,17 +679,17 @@ mod tests { #[should_panic(expected = "We do not allow downgrades")] async fn panic_on_downgrade() { let db_name = "panic_on_downgrade_test"; - let _ = drop_database(POSTGRES_ENDPOINT, db_name).await; + let _ = drop_database(POSTGRES_ENDPOINT, db_name, NoTls).await; { let mut migrations = MIGRATIONS.to_vec(); migrations.push(DUMMY_MIGRATION); - let store = PostgresBackendImpl::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); + let store = PostgresPlaintextBackend::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); let (start, end) = store.migrate_vss_database(&migrations).await.unwrap(); assert_eq!(start, MIGRATIONS_START); assert_eq!(end, MIGRATIONS_END + 1); }; { - let store = PostgresBackendImpl::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); + let store = PostgresPlaintextBackend::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); let _ = store.migrate_vss_database(MIGRATIONS).await.unwrap(); }; } @@ -630,9 +697,9 @@ mod tests { #[tokio::test] async fn new_migrations_increments_upgrades() { let db_name = "new_migrations_increments_upgrades_test"; - let _ = drop_database(POSTGRES_ENDPOINT, db_name).await; + let _ = drop_database(POSTGRES_ENDPOINT, db_name, NoTls).await; { - let store = PostgresBackendImpl::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); + let store = PostgresPlaintextBackend::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); let (start, end) = store.migrate_vss_database(MIGRATIONS).await.unwrap(); assert_eq!(start, MIGRATIONS_START); assert_eq!(end, MIGRATIONS_END); @@ -640,7 +707,7 @@ mod tests { assert_eq!(store.get_schema_version().await, MIGRATIONS_END); }; { - let store = PostgresBackendImpl::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); + let store = PostgresPlaintextBackend::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); let (start, end) = store.migrate_vss_database(MIGRATIONS).await.unwrap(); assert_eq!(start, MIGRATIONS_END); assert_eq!(end, MIGRATIONS_END); @@ -651,7 +718,7 @@ mod tests { let mut migrations = MIGRATIONS.to_vec(); migrations.push(DUMMY_MIGRATION); { - let store = PostgresBackendImpl::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); + let store = PostgresPlaintextBackend::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); let (start, end) = store.migrate_vss_database(&migrations).await.unwrap(); assert_eq!(start, MIGRATIONS_END); assert_eq!(end, MIGRATIONS_END + 1); @@ -662,7 +729,7 @@ mod tests { migrations.push(DUMMY_MIGRATION); migrations.push(DUMMY_MIGRATION); { - let store = PostgresBackendImpl::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); + let store = PostgresPlaintextBackend::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); let (start, end) = store.migrate_vss_database(&migrations).await.unwrap(); assert_eq!(start, MIGRATIONS_END + 1); assert_eq!(end, MIGRATIONS_END + 3); @@ -674,13 +741,13 @@ mod tests { }; { - let store = PostgresBackendImpl::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); + let store = PostgresPlaintextBackend::new(POSTGRES_ENDPOINT, db_name).await.unwrap(); let list = store.get_upgrades_list().await; assert_eq!(list, [MIGRATIONS_START, MIGRATIONS_END, MIGRATIONS_END + 1]); let version = store.get_schema_version().await; assert_eq!(version, MIGRATIONS_END + 3); } - drop_database(POSTGRES_ENDPOINT, db_name).await.unwrap(); + drop_database(POSTGRES_ENDPOINT, db_name, NoTls).await.unwrap(); } } diff --git a/rust/server/src/main.rs b/rust/server/src/main.rs index 5a78be6..38fdccd 100644 --- a/rust/server/src/main.rs +++ b/rust/server/src/main.rs @@ -20,7 +20,7 @@ use hyper_util::rt::TokioIo; use crate::vss_service::VssService; use api::auth::{Authorizer, NoopAuthorizer}; use api::kv_store::KvStore; -use impls::postgres_store::PostgresBackendImpl; +use impls::postgres_store::{Certificate, PostgresPlaintextBackend, PostgresTlsBackend}; use std::sync::Arc; pub(crate) mod util; @@ -66,15 +66,48 @@ fn main() { std::process::exit(-1); }, }; - let authorizer = Arc::new(NoopAuthorizer {}); - let postgresql_config = config.postgresql_config.expect("PostgreSQLConfig must be defined in config file."); + let authorizer: Arc = Arc::new(NoopAuthorizer {}); + let postgresql_config = + config.postgresql_config.expect("PostgreSQLConfig must be defined in config file."); let endpoint = postgresql_config.to_postgresql_endpoint(); let db_name = postgresql_config.database; - let store = Arc::new( - PostgresBackendImpl::new(&endpoint, &db_name) - .await - .unwrap(), - ); + let store: Arc = if let Some(tls_config) = postgresql_config.tls { + let additional_certificate = tls_config.ca_file.map(|file| { + let certificate = match std::fs::read(&file) { + Ok(cert) => cert, + Err(e) => { + println!("Failed to read certificate file: {}", e); + std::process::exit(-1); + }, + }; + match Certificate::from_pem(&certificate) { + Ok(cert) => cert, + Err(e) => { + println!("Failed to parse certificate file: {}", e); + std::process::exit(-1); + }, + } + }); + let postgres_tls_backend = + match PostgresTlsBackend::new(&endpoint, &db_name, additional_certificate).await { + Ok(backend) => backend, + Err(e) => { + println!("Failed to start postgres tls backend: {}", e); + std::process::exit(-1); + }, + }; + Arc::new(postgres_tls_backend) + } else { + let postgres_plaintext_backend = + match PostgresPlaintextBackend::new(&endpoint, &db_name).await { + Ok(backend) => backend, + Err(e) => { + println!("Failed to start postgres plaintext backend: {}", e); + std::process::exit(-1); + }, + }; + Arc::new(postgres_plaintext_backend) + }; println!("Connected to PostgreSQL backend with DSN: {}/{}", endpoint, db_name); let rest_svc_listener = TcpListener::bind(&addr).await.expect("Failed to bind listening port"); @@ -85,7 +118,7 @@ fn main() { match res { Ok((stream, _)) => { let io_stream = TokioIo::new(stream); - let vss_service = VssService::new(Arc::clone(&store) as Arc, Arc::clone(&authorizer) as Arc); + let vss_service = VssService::new(Arc::clone(&store), Arc::clone(&authorizer)); runtime.spawn(async move { if let Err(err) = http1::Builder::new().serve_connection(io_stream, vss_service).await { eprintln!("Failed to serve connection: {}", err); diff --git a/rust/server/src/util/config.rs b/rust/server/src/util/config.rs index cf70daf..801d1bd 100644 --- a/rust/server/src/util/config.rs +++ b/rust/server/src/util/config.rs @@ -19,6 +19,12 @@ pub(crate) struct PostgreSQLConfig { pub(crate) host: String, pub(crate) port: u16, pub(crate) database: String, + pub(crate) tls: Option, +} + +#[derive(Deserialize)] +pub(crate) struct TlsConfig { + pub(crate) ca_file: Option, } impl PostgreSQLConfig { diff --git a/rust/server/vss-server-config.toml b/rust/server/vss-server-config.toml index 8a013b5..8c3d9c0 100644 --- a/rust/server/vss-server-config.toml +++ b/rust/server/vss-server-config.toml @@ -8,3 +8,5 @@ password = "postgres" # Optional in TOML, can be overridden by env var `VSS_POS host = "localhost" port = 5432 database = "postgres" +# tls = { } # Uncomment to make TLS connections to the postgres database using your machine's PKI +# tls = { ca_file = "ca.pem" } # Uncomment to make TLS connections to the postgres database with an additional root certificate