Add support for sslmode=require for diesel-async DB connections (#3189)

This commit is contained in:
Sander Saarend 2023-06-26 11:25:38 +03:00 committed by Felix Ableitner
parent d6b580a530
commit 6b28f8c616
4 changed files with 152 additions and 19 deletions

92
Cargo.lock generated
View file

@ -215,7 +215,7 @@ dependencies = [
"futures-util", "futures-util",
"mio", "mio",
"num_cpus", "num_cpus",
"socket2", "socket2 0.4.9",
"tokio", "tokio",
"tracing", "tracing",
] ]
@ -245,7 +245,7 @@ dependencies = [
"http", "http",
"log", "log",
"pin-project-lite", "pin-project-lite",
"tokio-rustls", "tokio-rustls 0.23.4",
"tokio-util 0.7.4", "tokio-util 0.7.4",
"webpki-roots", "webpki-roots",
] ]
@ -297,7 +297,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"smallvec", "smallvec",
"socket2", "socket2 0.4.9",
"time 0.3.15", "time 0.3.15",
"url", "url",
] ]
@ -496,7 +496,7 @@ dependencies = [
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rand 0.8.5", "rand 0.8.5",
"rustls", "rustls 0.20.7",
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
@ -2262,7 +2262,7 @@ dependencies = [
"httpdate", "httpdate",
"itoa", "itoa",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2 0.4.9",
"tokio", "tokio",
"tower-service", "tower-service",
"tracing", "tracing",
@ -2640,9 +2640,11 @@ dependencies = [
"diesel-derive-newtype", "diesel-derive-newtype",
"diesel_ltree", "diesel_ltree",
"diesel_migrations", "diesel_migrations",
"futures-util",
"lemmy_utils", "lemmy_utils",
"once_cell", "once_cell",
"regex", "regex",
"rustls 0.21.2",
"serde", "serde",
"serde_json", "serde_json",
"serde_with", "serde_with",
@ -2651,6 +2653,8 @@ dependencies = [
"strum", "strum",
"strum_macros", "strum_macros",
"tokio", "tokio",
"tokio-postgres",
"tokio-postgres-rustls",
"tracing", "tracing",
"ts-rs", "ts-rs",
"typed-builder", "typed-builder",
@ -2736,6 +2740,7 @@ dependencies = [
"diesel", "diesel",
"diesel-async", "diesel-async",
"doku", "doku",
"futures-util",
"lemmy_api", "lemmy_api",
"lemmy_api_common", "lemmy_api_common",
"lemmy_api_crud", "lemmy_api_crud",
@ -2749,9 +2754,12 @@ dependencies = [
"reqwest", "reqwest",
"reqwest-middleware", "reqwest-middleware",
"reqwest-tracing", "reqwest-tracing",
"rustls 0.21.2",
"serde", "serde",
"serde_json", "serde_json",
"tokio", "tokio",
"tokio-postgres",
"tokio-postgres-rustls",
"tracing", "tracing",
"tracing-actix-web 0.6.2", "tracing-actix-web 0.6.2",
"tracing-error", "tracing-error",
@ -2820,7 +2828,7 @@ dependencies = [
"nom 7.1.1", "nom 7.1.1",
"once_cell", "once_cell",
"quoted_printable", "quoted_printable",
"socket2", "socket2 0.4.9",
] ]
[[package]] [[package]]
@ -3932,11 +3940,11 @@ dependencies = [
[[package]] [[package]]
name = "postgres-protocol" name = "postgres-protocol"
version = "0.6.4" version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "878c6cbf956e03af9aa8204b407b9cbf47c072164800aa918c516cd4b056c50c" checksum = "78b7fa9f396f51dffd61546fd8573ee20592287996568e6175ceb0f8699ad75d"
dependencies = [ dependencies = [
"base64 0.13.1", "base64 0.21.2",
"byteorder", "byteorder",
"bytes", "bytes",
"fallible-iterator", "fallible-iterator",
@ -4495,6 +4503,28 @@ dependencies = [
"webpki", "webpki",
] ]
[[package]]
name = "rustls"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e32ca28af694bc1bbf399c33a516dbdf1c90090b8ab23c2bc24f834aa2247f5f"
dependencies = [
"log",
"ring",
"rustls-webpki",
"sct",
]
[[package]]
name = "rustls-webpki"
version = "0.100.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b"
dependencies = [
"ring",
"untrusted",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.9" version = "1.0.9"
@ -4859,6 +4889,16 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "socket2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877"
dependencies = [
"libc",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "spin" name = "spin"
version = "0.5.2" version = "0.5.2"
@ -5304,7 +5344,7 @@ dependencies = [
"parking_lot 0.12.1", "parking_lot 0.12.1",
"pin-project-lite", "pin-project-lite",
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2 0.4.9",
"tokio-macros", "tokio-macros",
"tracing", "tracing",
"windows-sys 0.48.0", "windows-sys 0.48.0",
@ -5343,9 +5383,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-postgres" name = "tokio-postgres"
version = "0.7.7" version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29a12c1b3e0704ae7dfc25562629798b29c72e6b1d0a681b6f29ab4ae5e7f7bf" checksum = "6e89f6234aa8fd43779746012fcf53603cdb91fdd8399aa0de868c2d56b6dde1"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"byteorder", "byteorder",
@ -5360,22 +5400,46 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"postgres-protocol", "postgres-protocol",
"postgres-types", "postgres-types",
"socket2", "socket2 0.5.3",
"tokio", "tokio",
"tokio-util 0.7.4", "tokio-util 0.7.4",
] ]
[[package]]
name = "tokio-postgres-rustls"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd5831152cb0d3f79ef5523b357319ba154795d64c7078b2daa95a803b54057f"
dependencies = [
"futures",
"ring",
"rustls 0.21.2",
"tokio",
"tokio-postgres",
"tokio-rustls 0.24.1",
]
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.23.4" version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
dependencies = [ dependencies = [
"rustls", "rustls 0.20.7",
"tokio", "tokio",
"webpki", "webpki",
] ]
[[package]]
name = "tokio-rustls"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"rustls 0.21.2",
"tokio",
]
[[package]] [[package]]
name = "tokio-stream" name = "tokio-stream"
version = "0.1.11" version = "0.1.11"

View file

@ -107,6 +107,10 @@ rand = "0.8.5"
opentelemetry = { version = "0.17.0", features = ["rt-tokio"] } opentelemetry = { version = "0.17.0", features = ["rt-tokio"] }
tracing-opentelemetry = { version = "0.17.4" } tracing-opentelemetry = { version = "0.17.4" }
ts-rs = { version = "6.2", features = ["serde-compat", "format", "chrono-impl"] } ts-rs = { version = "6.2", features = ["serde-compat", "format", "chrono-impl"] }
rustls = { version ="0.21.2", features = ["dangerous_configuration"]}
futures-util = "0.3.28"
tokio-postgres = "0.7.8"
tokio-postgres-rustls = "0.10.0"
[dependencies] [dependencies]
lemmy_api = { workspace = true } lemmy_api = { workspace = true }
@ -140,3 +144,8 @@ opentelemetry-otlp = { version = "0.10.0", optional = true }
pict-rs = { version = "0.4.0-rc.3", optional = true } pict-rs = { version = "0.4.0-rc.3", optional = true }
tokio.workspace = true tokio.workspace = true
actix-cors = "0.6.4" actix-cors = "0.6.4"
rustls = { workspace = true }
futures-util = { workspace = true }
tokio-postgres = { workspace = true }
tokio-postgres-rustls = { workspace = true }

View file

@ -44,6 +44,10 @@ tokio = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
deadpool = { version = "0.9.5", features = ["rt_tokio_1"], optional = true } deadpool = { version = "0.9.5", features = ["rt_tokio_1"], optional = true }
ts-rs = { workspace = true, optional = true } ts-rs = { workspace = true, optional = true }
rustls = { workspace = true }
futures-util = { workspace = true }
tokio-postgres = { workspace = true }
tokio-postgres-rustls = { workspace = true }
[dev-dependencies] [dev-dependencies]
serial_test = { workspace = true } serial_test = { workspace = true }

View file

@ -12,7 +12,7 @@ use diesel::{
backend::Backend, backend::Backend,
deserialize::FromSql, deserialize::FromSql,
pg::Pg, pg::Pg,
result::{Error as DieselError, Error::QueryBuilderError}, result::{ConnectionError, ConnectionResult, Error as DieselError, Error::QueryBuilderError},
serialize::{Output, ToSql}, serialize::{Output, ToSql},
sql_types::Text, sql_types::Text,
PgConnection, PgConnection,
@ -25,11 +25,21 @@ use diesel_async::{
}, },
}; };
use diesel_migrations::EmbeddedMigrations; use diesel_migrations::EmbeddedMigrations;
use futures_util::{future::BoxFuture, FutureExt};
use lemmy_utils::{error::LemmyError, settings::structs::Settings}; use lemmy_utils::{error::LemmyError, settings::structs::Settings};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use std::{env, env::VarError, time::Duration}; use rustls::{
use tracing::info; client::{ServerCertVerified, ServerCertVerifier},
ServerName,
};
use std::{
env,
env::VarError,
sync::Arc,
time::{Duration, SystemTime},
};
use tracing::{error, info};
use url::Url; use url::Url;
const FETCH_LIMIT_DEFAULT: i64 = 10; const FETCH_LIMIT_DEFAULT: i64 = 10;
@ -136,7 +146,15 @@ pub fn diesel_option_overwrite_to_url_create(
async fn build_db_pool_settings_opt(settings: Option<&Settings>) -> Result<DbPool, LemmyError> { async fn build_db_pool_settings_opt(settings: Option<&Settings>) -> Result<DbPool, LemmyError> {
let db_url = get_database_url(settings); let db_url = get_database_url(settings);
let pool_size = settings.map(|s| s.database.pool_size).unwrap_or(5); let pool_size = settings.map(|s| s.database.pool_size).unwrap_or(5);
let manager = AsyncDieselConnectionManager::<AsyncPgConnection>::new(&db_url); // We only support TLS with sslmode=require currently
let tls_enabled = db_url.contains("sslmode=require");
let manager = if tls_enabled {
// diesel-async does not support any TLS connections out of the box, so we need to manually
// provide a setup function which handles creating the connection
AsyncDieselConnectionManager::<AsyncPgConnection>::new_with_setup(&db_url, establish_connection)
} else {
AsyncDieselConnectionManager::<AsyncPgConnection>::new(&db_url)
};
let pool = Pool::builder(manager) let pool = Pool::builder(manager)
.max_size(pool_size) .max_size(pool_size)
.wait_timeout(POOL_TIMEOUT) .wait_timeout(POOL_TIMEOUT)
@ -153,6 +171,44 @@ async fn build_db_pool_settings_opt(settings: Option<&Settings>) -> Result<DbPoo
Ok(pool) Ok(pool)
} }
fn establish_connection(config: &str) -> BoxFuture<ConnectionResult<AsyncPgConnection>> {
let fut = async {
let rustls_config = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_custom_certificate_verifier(Arc::new(NoCertVerifier {}))
.with_no_client_auth();
let tls = tokio_postgres_rustls::MakeRustlsConnect::new(rustls_config);
let (client, conn) = tokio_postgres::connect(config, tls)
.await
.map_err(|e| ConnectionError::BadConnection(e.to_string()))?;
tokio::spawn(async move {
if let Err(e) = conn.await {
error!("Database connection failed: {e}");
}
});
AsyncPgConnection::try_from(client).await
};
fut.boxed()
}
struct NoCertVerifier {}
impl ServerCertVerifier for NoCertVerifier {
fn verify_server_cert(
&self,
_end_entity: &rustls::Certificate,
_intermediates: &[rustls::Certificate],
_server_name: &ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp_response: &[u8],
_now: SystemTime,
) -> Result<ServerCertVerified, rustls::Error> {
// Will verify all (even invalid) certs without any checks (sslmode=require)
Ok(ServerCertVerified::assertion())
}
}
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!();
pub fn run_migrations(db_url: &str) { pub fn run_migrations(db_url: &str) {