Skip to content

Commit 17f44a4

Browse files
authored
Merge pull request #9192 from Turbo87/fix-tls
Use `postgres-native-tls` to connect to the database with TLS
2 parents e0bb004 + 692f85e commit 17f44a4

File tree

8 files changed

+97
-8
lines changed

8 files changed

+97
-8
lines changed

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,13 @@ tikv-jemallocator = { version = "=0.6.0", features = ['unprefixed_malloc_on_supp
9191
lettre = { version = "=0.11.7", default-features = false, features = ["file-transport", "smtp-transport", "native-tls", "hostname", "builder"] }
9292
minijinja = "=2.1.0"
9393
mockall = "=0.13.0"
94+
native-tls = "=0.2.12"
9495
oauth2 = "=4.4.2"
9596
object_store = { version = "=0.10.2", features = ["aws"] }
9697
p256 = "=0.13.2"
9798
parking_lot = "=0.12.3"
9899
paste = "=1.0.15"
100+
postgres-native-tls = "=0.5.0"
99101
prometheus = { version = "=0.13.4", default-features = false }
100102
quick-xml = "=0.36.1"
101103
rand = "=0.8.5"
@@ -113,6 +115,7 @@ tar = "=0.4.41"
113115
tempfile = "=3.10.1"
114116
thiserror = "=1.0.63"
115117
tokio = { version = "=1.39.2", features = ["net", "signal", "io-std", "io-util", "rt-multi-thread", "macros", "process"]}
118+
tokio-postgres = "=0.7.11"
116119
toml = "=0.8.15"
117120
tower = "=0.4.13"
118121
tower-http = { version = "=0.5.2", features = ["add-extension", "fs", "catch-panic", "timeout", "compression-full"] }

src/app.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Application-wide components in a struct accessible from each request
22
33
use crate::config;
4-
use crate::db::{connection_url, ConnectionConfig};
4+
use crate::db::{connection_url, make_manager_config, ConnectionConfig};
55
use std::ops::Deref;
66
use std::sync::Arc;
77

@@ -88,7 +88,8 @@ impl App {
8888
};
8989

9090
let url = connection_url(&config.db, config.db.primary.url.expose_secret());
91-
let manager = AsyncDieselConnectionManager::<AsyncPgConnection>::new(url);
91+
let manager_config = make_manager_config(config.db.enforce_tls);
92+
let manager = AsyncDieselConnectionManager::new_with_config(url, manager_config);
9293

9394
DeadpoolPool::builder(manager)
9495
.runtime(Runtime::Tokio1)
@@ -108,7 +109,8 @@ impl App {
108109
};
109110

110111
let url = connection_url(&config.db, pool_config.url.expose_secret());
111-
let manager = AsyncDieselConnectionManager::<AsyncPgConnection>::new(url);
112+
let manager_config = make_manager_config(config.db.enforce_tls);
113+
let manager = AsyncDieselConnectionManager::new_with_config(url, manager_config);
112114

113115
let pool = DeadpoolPool::builder(manager)
114116
.runtime(Runtime::Tokio1)

src/bin/background-worker.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extern crate tracing;
1515

1616
use anyhow::Context;
1717
use crates_io::cloudfront::CloudFront;
18+
use crates_io::db::make_manager_config;
1819
use crates_io::fastly::Fastly;
1920
use crates_io::storage::Storage;
2021
use crates_io::team_repo::TeamRepoImpl;
@@ -27,7 +28,6 @@ use crates_io_index::RepositoryConfig;
2728
use crates_io_worker::Runner;
2829
use diesel_async::pooled_connection::deadpool::Pool;
2930
use diesel_async::pooled_connection::AsyncDieselConnectionManager;
30-
use diesel_async::AsyncPgConnection;
3131
use reqwest::Client;
3232
use secrecy::ExposeSecret;
3333
use std::sync::Arc;
@@ -82,7 +82,8 @@ fn main() -> anyhow::Result<()> {
8282
let fastly = Fastly::from_environment(client.clone());
8383
let team_repo = TeamRepoImpl::default();
8484

85-
let manager = AsyncDieselConnectionManager::<AsyncPgConnection>::new(db_url);
85+
let manager_config = make_manager_config(config.db.enforce_tls);
86+
let manager = AsyncDieselConnectionManager::new_with_config(db_url, manager_config);
8687
let deadpool = Pool::builder(manager).max_size(10).build().unwrap();
8788

8889
let environment = Environment::builder()

src/certs/crunchy.pem

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBozCCAUqgAwIBAgIJAMDLUd0ypWHdMAoGCCqGSM49BAMDMCUxIzAhBgNVBAMM
3+
GnJqamxwMmxnbWJjanZvamV4YjN3NndsNTVlMB4XDTI0MDYwNTA2MjcyOVoXDTQ0
4+
MDUzMTA2MjcyOVowJTEjMCEGA1UEAwwacmpqbHAybGdtYmNqdm9qZXhiM3c2d2w1
5+
NWUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4J3uDAfsWOQFD6XAwpPWOvviY
6+
kCPqyJ37OGMOhA70zvQKOnxTmrKu2p7lsyVrnbCtD4Ve11CouI4iDPeVmK/wo2Mw
7+
YTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUy5m8
8+
qXuAIReA7KFV1fKaHLPZo14wHwYDVR0jBBgwFoAUy5m8qXuAIReA7KFV1fKaHLPZ
9+
o14wCgYIKoZIzj0EAwMDRwAwRAIgQfpsO+B96Xse+ushnl+0Abx2tx0F5ac+K0L/
10+
x4uyKP4CIBaCSz+Oa/rG30W2F0VVtJN8guKFvnCMy7Gg/XCGGx8l
11+
-----END CERTIFICATE-----

src/certs/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
//! Certificates from <https://letsencrypt.org/certificates/>.
2-
1+
/// Certificate from <https://letsencrypt.org/certificates/>.
32
pub const ISRG_ROOT_X1: &[u8] = include_bytes!("./isrg-root-x1.pem");
3+
4+
/// Certificate from <https://letsencrypt.org/certificates/>.
45
pub const ISRG_ROOT_X2: &[u8] = include_bytes!("./isrg-root-x2.pem");
6+
7+
/// crates.io team certificate from <https://crunchybridge.com/>.
8+
pub const CRUNCHY: &[u8] = include_bytes!("./crunchy.pem");

src/db.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
use crate::certs::CRUNCHY;
12
use diesel::{Connection, ConnectionResult, PgConnection, QueryResult};
23
use diesel_async::pooled_connection::deadpool::{Hook, HookError};
4+
use diesel_async::pooled_connection::ManagerConfig;
35
use diesel_async::{AsyncPgConnection, RunQueryDsl};
6+
use native_tls::{Certificate, TlsConnector};
7+
use postgres_native_tls::MakeTlsConnector;
48
use secrecy::ExposeSecret;
59
use std::time::Duration;
610
use url::Url;
@@ -43,6 +47,53 @@ fn maybe_append_url_param(url: &mut Url, key: &str, value: &str) {
4347
}
4448
}
4549

50+
/// Create a new [ManagerConfig] for the database connection pool, which can
51+
/// be used with [diesel_async::pooled_connection::AsyncDieselConnectionManager::new_with_config()].
52+
pub fn make_manager_config(enforce_tls: bool) -> ManagerConfig<AsyncPgConnection> {
53+
let mut manager_config = ManagerConfig::default();
54+
manager_config.custom_setup =
55+
Box::new(move |url| Box::pin(establish_async_connection(url, enforce_tls)));
56+
manager_config
57+
}
58+
59+
/// Establish a new database connection with the given URL.
60+
///
61+
/// Adapted from <https://github.com/weiznich/diesel_async/blob/v0.5.0/examples/postgres/pooled-with-rustls/src/main.rs>.
62+
async fn establish_async_connection(
63+
url: &str,
64+
enforce_tls: bool,
65+
) -> ConnectionResult<AsyncPgConnection> {
66+
use diesel::ConnectionError::BadConnection;
67+
68+
let cert = Certificate::from_pem(CRUNCHY).map_err(|err| BadConnection(err.to_string()))?;
69+
70+
let connector = TlsConnector::builder()
71+
.add_root_certificate(cert)
72+
// On OSX the native TLS stack is complaining about the long validity
73+
// period of the certificate, so if locally we don't enforce TLS
74+
// connections, we also don't enforce the validity of the certificate.
75+
//
76+
// Similarly, on CI the native TLS stack is complaining about the
77+
// certificate being self-signed. On CI we are connecting to a locally
78+
// running database, so we also don't need to enforce the validity of
79+
// the certificate either.
80+
.danger_accept_invalid_certs(!enforce_tls)
81+
.build()
82+
.map_err(|err| BadConnection(err.to_string()))?;
83+
84+
let connector = MakeTlsConnector::new(connector);
85+
let result = tokio_postgres::connect(url, connector).await;
86+
let (client, conn) = result.map_err(|err| BadConnection(err.to_string()))?;
87+
88+
tokio::spawn(async move {
89+
if let Err(e) = conn.await {
90+
eprintln!("Database connection: {e}");
91+
}
92+
});
93+
94+
AsyncPgConnection::try_from(client).await
95+
}
96+
4697
#[derive(Debug, Clone, Copy)]
4798
pub struct ConnectionConfig {
4899
pub statement_timeout: Duration,

src/util/errors.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ impl From<EmailError> for BoxedAppError {
172172
}
173173

174174
impl From<diesel_async::pooled_connection::deadpool::PoolError> for BoxedAppError {
175-
fn from(_err: diesel_async::pooled_connection::deadpool::PoolError) -> BoxedAppError {
175+
fn from(err: diesel_async::pooled_connection::deadpool::PoolError) -> BoxedAppError {
176+
error!("Database pool error: {err}");
176177
service_unavailable()
177178
}
178179
}

0 commit comments

Comments
 (0)