Skip to content

Commit

Permalink
Add support for sslmode=require for diesel-async DB connections
Browse files Browse the repository at this point in the history
  • Loading branch information
sunaurus committed Jun 23, 2023
1 parent 63d3759 commit d7f54f0
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 19 deletions.
92 changes: 78 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ rand = "0.8.5"
opentelemetry = { version = "0.17.0", features = ["rt-tokio"] }
tracing-opentelemetry = { version = "0.17.4" }
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]
lemmy_api = { workspace = true }
Expand Down Expand Up @@ -140,3 +144,8 @@ opentelemetry-otlp = { version = "0.10.0", optional = true }
pict-rs = { version = "0.4.0-rc.3", optional = true }
tokio.workspace = true
actix-cors = "0.6.4"
rustls = { workspace = true }
futures-util = { workspace = true }
tokio-postgres = { workspace = true }
tokio-postgres-rustls = { workspace = true }

6 changes: 5 additions & 1 deletion crates/db_schema/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ async-trait = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = 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]
serial_test = { workspace = true }
Expand Down
64 changes: 60 additions & 4 deletions crates/db_schema/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use diesel::{
backend::Backend,
deserialize::FromSql,
pg::Pg,
result::{Error as DieselError, Error::QueryBuilderError},
result::{ConnectionError, ConnectionResult, Error as DieselError, Error::QueryBuilderError},
serialize::{Output, ToSql},
sql_types::Text,
PgConnection,
Expand All @@ -25,11 +25,21 @@ use diesel_async::{
},
};
use diesel_migrations::EmbeddedMigrations;
use futures_util::{future::BoxFuture, FutureExt};
use lemmy_utils::{error::LemmyError, settings::structs::Settings};
use once_cell::sync::Lazy;
use regex::Regex;
use std::{env, env::VarError, time::Duration};
use tracing::info;
use rustls::{
client::{ServerCertVerified, ServerCertVerifier},
ServerName,
};
use std::{
env,
env::VarError,
sync::Arc,
time::{Duration, SystemTime},
};
use tracing::{error, info};
use url::Url;

const FETCH_LIMIT_DEFAULT: i64 = 10;
Expand Down Expand Up @@ -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> {
let db_url = get_database_url(settings);
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)
.max_size(pool_size)
.wait_timeout(POOL_TIMEOUT)
Expand All @@ -153,6 +171,44 @@ async fn build_db_pool_settings_opt(settings: Option<&Settings>) -> Result<DbPoo
Ok(pool)
}

fn establish_connection(config: &str) -> BoxFuture<ConnectionResult<AsyncPgConnection>> {
let fut = async move {
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 fn run_migrations(db_url: &str) {
Expand Down

0 comments on commit d7f54f0

Please sign in to comment.