From 60b5991482ce5423e9f191a2c5d9352516617b8c Mon Sep 17 00:00:00 2001 From: moss Date: Sat, 14 Jan 2023 17:07:50 +1100 Subject: [PATCH 1/7] Add support for using TLS with PostgreSQL (#260) --- refinery_cli/Cargo.toml | 2 +- refinery_core/Cargo.toml | 2 ++ refinery_core/src/config.rs | 48 ++++++++++++++++++++++++++--- refinery_core/src/drivers/config.rs | 11 ++++++- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/refinery_cli/Cargo.toml b/refinery_cli/Cargo.toml index ae58a2ee..8712cd79 100644 --- a/refinery_cli/Cargo.toml +++ b/refinery_cli/Cargo.toml @@ -16,7 +16,7 @@ path = "src/main.rs" [features] default = ["mysql", "postgresql", "sqlite-bundled", "mssql"] -postgresql = ["refinery-core/postgres"] +postgresql = ["refinery-core/postgres", "refinery-core/postgres-openssl", "refinery-core/openssl"] mysql = ["refinery-core/mysql", "refinery-core/flate2"] sqlite = ["refinery-core/rusqlite"] sqlite-bundled = ["sqlite", "refinery-core/rusqlite-bundled"] diff --git a/refinery_core/Cargo.toml b/refinery_core/Cargo.toml index 11b46917..b3fe9cc1 100644 --- a/refinery_core/Cargo.toml +++ b/refinery_core/Cargo.toml @@ -32,6 +32,8 @@ walkdir = "2.3.1" # allow multiple versions of the same dependency if API is similar rusqlite = { version = ">= 0.23, <= 0.28", optional = true } postgres = { version = "0.19", optional = true } +postgres-openssl = { version = "0.5", optional = true } +openssl = { version = "0.10", optional = true } tokio-postgres = { version = "0.7", optional = true } mysql = { version = ">= 21.0.0, <= 23", optional = true, default-features = false} mysql_async = { version = ">= 0.28, <= 0.30", optional = true } diff --git a/refinery_core/src/config.rs b/refinery_core/src/config.rs index c1fac221..535cadf7 100644 --- a/refinery_core/src/config.rs +++ b/refinery_core/src/config.rs @@ -5,6 +5,7 @@ use std::convert::TryFrom; use std::fs; use std::path::{Path, PathBuf}; use std::str::FromStr; +use std::{borrow::Cow, collections::HashMap}; use url::Url; // refinery config file used by migrate_from_config if migration from a Config struct is preferred instead of using the macros @@ -34,6 +35,7 @@ impl Config { db_user: None, db_pass: None, db_name: None, + use_tls: false, #[cfg(feature = "tiberius-config")] trust_cert: false, }, @@ -138,6 +140,10 @@ impl Config { self.main.db_port.as_deref() } + pub fn use_tls(&self) -> bool { + self.main.use_tls + } + pub fn set_db_user(self, db_user: &str) -> Config { Config { main: Main { @@ -202,13 +208,12 @@ impl TryFrom for Config { } }; + let query_params = url + .query_pairs() + .collect::, Cow<'_, str>>>(); + cfg_if::cfg_if! { if #[cfg(feature = "tiberius-config")] { - use std::{borrow::Cow, collections::HashMap}; - let query_params = url - .query_pairs() - .collect::, Cow<'_, str>>>(); - let trust_cert = query_params. get("trust_cert") .unwrap_or(&Cow::Borrowed("false")) @@ -222,6 +227,21 @@ impl TryFrom for Config { } } + let use_tls = match query_params + .get("sslmode") + .unwrap_or(&Cow::Borrowed("disable")) + { + &Cow::Borrowed("disable") => Ok(false), + &Cow::Borrowed("require") => Ok(true), + _ => Err(()), + } + .map_err(|_| { + Error::new( + Kind::ConfigError("Invalid sslmode value, please use disable/require".into()), + None, + ) + })?; + Ok(Self { main: Main { db_type, @@ -237,6 +257,7 @@ impl TryFrom for Config { db_user: Some(url.username().to_string()), db_pass: url.password().map(|r| r.to_string()), db_name: Some(url.path().trim_start_matches('/').to_string()), + use_tls, #[cfg(feature = "tiberius-config")] trust_cert, }, @@ -268,6 +289,7 @@ struct Main { db_user: Option, db_pass: Option, db_name: Option, + use_tls: bool, #[cfg(feature = "tiberius-config")] #[serde(default)] trust_cert: bool, @@ -451,6 +473,22 @@ mod tests { ); } + #[test] + fn build_no_tls_conn_from_str() { + let config = + Config::from_str("postgres://root:1234@localhost:5432/refinery?sslmode=disable") + .unwrap(); + assert!(!config.use_tls()); + } + + #[test] + fn build_tls_conn_from_str() { + let config = + Config::from_str("postgres://root:1234@localhost:5432/refinery?sslmode=require") + .unwrap(); + assert!(config.use_tls()); + } + #[test] fn builds_db_env_var_failure() { std::env::set_var("DATABASE_URL", "this_is_not_a_url"); diff --git a/refinery_core/src/drivers/config.rs b/refinery_core/src/drivers/config.rs index 92a00582..6d619108 100644 --- a/refinery_core/src/drivers/config.rs +++ b/refinery_core/src/drivers/config.rs @@ -80,7 +80,16 @@ macro_rules! with_connection { cfg_if::cfg_if! { if #[cfg(feature = "postgres")] { let path = build_db_url("postgresql", &$config); - let conn = postgres::Client::connect(path.as_str(), postgres::NoTls).migration_err("could not connect to database", None)?; + + let conn; + if $config.use_tls() { + let builder = openssl::ssl::SslConnector::builder(openssl::ssl::SslMethod::tls()).unwrap(); + let connector = postgres_openssl::MakeTlsConnector::new(builder.build()); + conn = postgres::Client::connect(path.as_str(), connector).migration_err("could not connect to database", None)?; + } else { + conn = postgres::Client::connect(path.as_str(), postgres::NoTls).migration_err("could not connect to database", None)?; + } + $op(conn) } else { panic!("tried to migrate from config for a postgresql database, but feature postgres not enabled!"); From ec18eee514fcb691704c7773125fd573c67dfb74 Mon Sep 17 00:00:00 2001 From: moss Date: Sat, 14 Jan 2023 21:52:11 +1100 Subject: [PATCH 2/7] Make use_tls optional in config --- refinery_core/src/config.rs | 14 ++++++++------ refinery_core/src/drivers/config.rs | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/refinery_core/src/config.rs b/refinery_core/src/config.rs index 535cadf7..a5f4ccac 100644 --- a/refinery_core/src/config.rs +++ b/refinery_core/src/config.rs @@ -35,7 +35,7 @@ impl Config { db_user: None, db_pass: None, db_name: None, - use_tls: false, + use_tls: None, #[cfg(feature = "tiberius-config")] trust_cert: false, }, @@ -140,7 +140,7 @@ impl Config { self.main.db_port.as_deref() } - pub fn use_tls(&self) -> bool { + pub fn use_tls(&self) -> Option { self.main.use_tls } @@ -257,7 +257,7 @@ impl TryFrom for Config { db_user: Some(url.username().to_string()), db_pass: url.password().map(|r| r.to_string()), db_name: Some(url.path().trim_start_matches('/').to_string()), - use_tls, + use_tls: Some(use_tls), #[cfg(feature = "tiberius-config")] trust_cert, }, @@ -289,7 +289,7 @@ struct Main { db_user: Option, db_pass: Option, db_name: Option, - use_tls: bool, + use_tls: Option, #[cfg(feature = "tiberius-config")] #[serde(default)] trust_cert: bool, @@ -478,7 +478,8 @@ mod tests { let config = Config::from_str("postgres://root:1234@localhost:5432/refinery?sslmode=disable") .unwrap(); - assert!(!config.use_tls()); + assert!(config.use_tls().is_some()); + assert!(!config.use_tls().unwrap()); } #[test] @@ -486,7 +487,8 @@ mod tests { let config = Config::from_str("postgres://root:1234@localhost:5432/refinery?sslmode=require") .unwrap(); - assert!(config.use_tls()); + assert!(config.use_tls().is_some()); + assert!(config.use_tls().unwrap()); } #[test] diff --git a/refinery_core/src/drivers/config.rs b/refinery_core/src/drivers/config.rs index 6d619108..86675489 100644 --- a/refinery_core/src/drivers/config.rs +++ b/refinery_core/src/drivers/config.rs @@ -82,7 +82,7 @@ macro_rules! with_connection { let path = build_db_url("postgresql", &$config); let conn; - if $config.use_tls() { + if $config.use_tls().is_some() && $config.use_tls().unwrap() { let builder = openssl::ssl::SslConnector::builder(openssl::ssl::SslMethod::tls()).unwrap(); let connector = postgres_openssl::MakeTlsConnector::new(builder.build()); conn = postgres::Client::connect(path.as_str(), connector).migration_err("could not connect to database", None)?; From 7092735d14df3fcbb377f96653ae948c9bb99001 Mon Sep 17 00:00:00 2001 From: moss Date: Sat, 14 Jan 2023 22:03:05 +1100 Subject: [PATCH 3/7] Include openssl crates in refinery module --- refinery/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/refinery/Cargo.toml b/refinery/Cargo.toml index eba439d9..6f32d6aa 100644 --- a/refinery/Cargo.toml +++ b/refinery/Cargo.toml @@ -16,7 +16,7 @@ edition = "2018" default = [] rusqlite-bundled = ["refinery-core/rusqlite-bundled"] rusqlite = ["refinery-core/rusqlite"] -postgres = ["refinery-core/postgres"] +postgres = ["refinery-core/postgres", "refinery-core/postgres-openssl", "refinery-core/openssl"] mysql = ["refinery-core/mysql", "refinery-core/flate2"] tokio-postgres = ["refinery-core/tokio-postgres"] mysql_async = ["refinery-core/mysql_async"] From 8858a8d5044a771383826e42d399a67d3c714f01 Mon Sep 17 00:00:00 2001 From: HugoCasa Date: Wed, 13 Nov 2024 18:21:16 +0100 Subject: [PATCH 4/7] tls support for postgres using native-tls --- refinery/Cargo.toml | 4 ++-- refinery_cli/Cargo.toml | 2 +- refinery_core/Cargo.toml | 4 ++-- refinery_core/src/config.rs | 28 +++++++++++++--------------- refinery_core/src/drivers/config.rs | 4 ++-- 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/refinery/Cargo.toml b/refinery/Cargo.toml index 6f32d6aa..5d247583 100644 --- a/refinery/Cargo.toml +++ b/refinery/Cargo.toml @@ -16,9 +16,9 @@ edition = "2018" default = [] rusqlite-bundled = ["refinery-core/rusqlite-bundled"] rusqlite = ["refinery-core/rusqlite"] -postgres = ["refinery-core/postgres", "refinery-core/postgres-openssl", "refinery-core/openssl"] +postgres = ["refinery-core/postgres", "refinery-core/postgres-native-tls", "refinery-core/native-tls"] mysql = ["refinery-core/mysql", "refinery-core/flate2"] -tokio-postgres = ["refinery-core/tokio-postgres"] +tokio-postgres = ["refinery-core/tokio-postgres", "refinery-core/postgres-native-tls", "refinery-core/native-tls"] mysql_async = ["refinery-core/mysql_async"] tiberius = ["refinery-core/tiberius"] tiberius-config = ["refinery-core/tiberius", "refinery-core/tiberius-config"] diff --git a/refinery_cli/Cargo.toml b/refinery_cli/Cargo.toml index 8712cd79..ae309224 100644 --- a/refinery_cli/Cargo.toml +++ b/refinery_cli/Cargo.toml @@ -16,7 +16,7 @@ path = "src/main.rs" [features] default = ["mysql", "postgresql", "sqlite-bundled", "mssql"] -postgresql = ["refinery-core/postgres", "refinery-core/postgres-openssl", "refinery-core/openssl"] +postgresql = ["refinery-core/postgres", "refinery-core/postgres-native-tls", "refinery-core/native-tls"] mysql = ["refinery-core/mysql", "refinery-core/flate2"] sqlite = ["refinery-core/rusqlite"] sqlite-bundled = ["sqlite", "refinery-core/rusqlite-bundled"] diff --git a/refinery_core/Cargo.toml b/refinery_core/Cargo.toml index b3fe9cc1..a724fce5 100644 --- a/refinery_core/Cargo.toml +++ b/refinery_core/Cargo.toml @@ -32,8 +32,8 @@ walkdir = "2.3.1" # allow multiple versions of the same dependency if API is similar rusqlite = { version = ">= 0.23, <= 0.28", optional = true } postgres = { version = "0.19", optional = true } -postgres-openssl = { version = "0.5", optional = true } -openssl = { version = "0.10", optional = true } +native-tls = { version = "0.2", optional = true } +postgres-native-tls = { version = "0.5", optional = true} tokio-postgres = { version = "0.7", optional = true } mysql = { version = ">= 21.0.0, <= 23", optional = true, default-features = false} mysql_async = { version = ">= 0.28, <= 0.30", optional = true } diff --git a/refinery_core/src/config.rs b/refinery_core/src/config.rs index a5f4ccac..404fb32e 100644 --- a/refinery_core/src/config.rs +++ b/refinery_core/src/config.rs @@ -231,16 +231,15 @@ impl TryFrom for Config { .get("sslmode") .unwrap_or(&Cow::Borrowed("disable")) { - &Cow::Borrowed("disable") => Ok(false), - &Cow::Borrowed("require") => Ok(true), - _ => Err(()), - } - .map_err(|_| { - Error::new( - Kind::ConfigError("Invalid sslmode value, please use disable/require".into()), - None, - ) - })?; + &Cow::Borrowed("disable") => false, + &Cow::Borrowed("require") => true, + _ => { + return Err(Error::new( + Kind::ConfigError("Invalid sslmode value, please use disable/require".into()), + None, + )) + } + }; Ok(Self { main: Main { @@ -474,21 +473,20 @@ mod tests { } #[test] - fn build_no_tls_conn_from_str() { + fn builds_tls_from_str() { let config = Config::from_str("postgres://root:1234@localhost:5432/refinery?sslmode=disable") .unwrap(); assert!(config.use_tls().is_some()); assert!(!config.use_tls().unwrap()); - } - - #[test] - fn build_tls_conn_from_str() { let config = Config::from_str("postgres://root:1234@localhost:5432/refinery?sslmode=require") .unwrap(); assert!(config.use_tls().is_some()); assert!(config.use_tls().unwrap()); + let config = + Config::from_str("postgres://root:1234@localhost:5432/refinery?sslmode=invalidvalue"); + assert!(config.is_err()); } #[test] diff --git a/refinery_core/src/drivers/config.rs b/refinery_core/src/drivers/config.rs index 86675489..081d164c 100644 --- a/refinery_core/src/drivers/config.rs +++ b/refinery_core/src/drivers/config.rs @@ -83,8 +83,8 @@ macro_rules! with_connection { let conn; if $config.use_tls().is_some() && $config.use_tls().unwrap() { - let builder = openssl::ssl::SslConnector::builder(openssl::ssl::SslMethod::tls()).unwrap(); - let connector = postgres_openssl::MakeTlsConnector::new(builder.build()); + let connector = native_tls::TlsConnector::builder().build().unwrap(); + let connector = postgres_native_tls::MakeTlsConnector::new(connector); conn = postgres::Client::connect(path.as_str(), connector).migration_err("could not connect to database", None)?; } else { conn = postgres::Client::connect(path.as_str(), postgres::NoTls).migration_err("could not connect to database", None)?; From 9b74598326ac173f89daf27883cc36865ce39473 Mon Sep 17 00:00:00 2001 From: HugoCasa Date: Wed, 13 Nov 2024 18:42:59 +0100 Subject: [PATCH 5/7] use tls for tokio_postgres as well --- refinery_core/src/drivers/config.rs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/refinery_core/src/drivers/config.rs b/refinery_core/src/drivers/config.rs index a47e68f5..2b3b0f73 100644 --- a/refinery_core/src/drivers/config.rs +++ b/refinery_core/src/drivers/config.rs @@ -138,13 +138,25 @@ macro_rules! with_connection_async { cfg_if::cfg_if! { if #[cfg(feature = "tokio-postgres")] { let path = build_db_url("postgresql", $config); - let (client, connection ) = tokio_postgres::connect(path.as_str(), tokio_postgres::NoTls).await.migration_err("could not connect to database", None)?; - tokio::spawn(async move { - if let Err(e) = connection.await { - eprintln!("connection error: {}", e); - } - }); - $op(client).await + if $config.use_tls().is_some() && $config.use_tls().unwrap() { + let connector = native_tls::TlsConnector::builder().build().unwrap(); + let connector = postgres_native_tls::MakeTlsConnector::new(connector); + let (client, connection) = tokio_postgres::connect(path.as_str(), connector).await.migration_err("could not connect to database", None)?; + tokio::spawn(async move { + if let Err(e) = connection.await { + eprintln!("connection error: {}", e); + } + }); + $op(client).await + } else { + let (client, connection) = tokio_postgres::connect(path.as_str(), tokio_postgres::NoTls).await.migration_err("could not connect to database", None)?; + tokio::spawn(async move { + if let Err(e) = connection.await { + eprintln!("connection error: {}", e); + } + }); + $op(client).await + } } else { panic!("tried to migrate async from config for a postgresql database, but tokio-postgres was not enabled!"); } From 70b82847f7538e2c88aaecfda9137b50a263675d Mon Sep 17 00:00:00 2001 From: HugoCasa Date: Wed, 13 Nov 2024 18:43:55 +0100 Subject: [PATCH 6/7] nit --- refinery_core/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/refinery_core/src/config.rs b/refinery_core/src/config.rs index 343a4874..1a4f1c65 100644 --- a/refinery_core/src/config.rs +++ b/refinery_core/src/config.rs @@ -475,7 +475,7 @@ mod tests { } #[test] - fn builds_tls_from_str() { + fn builds_from_sslmode_str() { let config = Config::from_str("postgres://root:1234@localhost:5432/refinery?sslmode=disable") .unwrap(); From d0bd62e0abe15882112dc3ac1ea9052bd891e88d Mon Sep 17 00:00:00 2001 From: HugoCasa Date: Wed, 13 Nov 2024 18:50:58 +0100 Subject: [PATCH 7/7] nit --- refinery_core/src/drivers/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/refinery_core/src/drivers/config.rs b/refinery_core/src/drivers/config.rs index 2b3b0f73..9fdd0975 100644 --- a/refinery_core/src/drivers/config.rs +++ b/refinery_core/src/drivers/config.rs @@ -91,7 +91,7 @@ macro_rules! with_connection { let conn; if $config.use_tls().is_some() && $config.use_tls().unwrap() { - let connector = native_tls::TlsConnector::builder().build().unwrap(); + let connector = native_tls::TlsConnector::new().unwrap(); let connector = postgres_native_tls::MakeTlsConnector::new(connector); conn = postgres::Client::connect(path.as_str(), connector).migration_err("could not connect to database", None)?; } else { @@ -139,7 +139,7 @@ macro_rules! with_connection_async { if #[cfg(feature = "tokio-postgres")] { let path = build_db_url("postgresql", $config); if $config.use_tls().is_some() && $config.use_tls().unwrap() { - let connector = native_tls::TlsConnector::builder().build().unwrap(); + let connector = native_tls::TlsConnector::new().unwrap(); let connector = postgres_native_tls::MakeTlsConnector::new(connector); let (client, connection) = tokio_postgres::connect(path.as_str(), connector).await.migration_err("could not connect to database", None)?; tokio::spawn(async move {