diff --git a/storage_controller/src/persistence.rs b/storage_controller/src/persistence.rs index 45f3108d6b..c4e5b39589 100644 --- a/storage_controller/src/persistence.rs +++ b/storage_controller/src/persistence.rs @@ -27,7 +27,7 @@ use pageserver_api::shard::ShardConfigError; use pageserver_api::shard::ShardIdentity; use pageserver_api::shard::ShardStripeSize; use pageserver_api::shard::{ShardCount, ShardNumber, TenantShardId}; -use rustls::client::danger::ServerCertVerifier; +use rustls::client::danger::{ServerCertVerified, ServerCertVerifier}; use rustls::client::WebPkiServerVerifier; use rustls::crypto::ring; use scoped_futures::ScopedBoxFuture; @@ -194,6 +194,8 @@ impl Persistence { timeout: Duration, ) -> Result<(), diesel::ConnectionError> { let started_at = Instant::now(); + log_postgres_connstr_info(database_url) + .map_err(|e| diesel::ConnectionError::InvalidConnectionUrl(e.to_string()))?; loop { match establish_connection_rustls(database_url).await { Ok(_) => { @@ -1281,6 +1283,51 @@ pub(crate) fn load_certs() -> anyhow::Result> { Ok(Arc::new(store)) } +#[derive(Debug)] +/// A verifier that accepts all certificates (but logs an error still) +struct AcceptAll(Arc); +impl ServerCertVerifier for AcceptAll { + fn verify_server_cert( + &self, + end_entity: &rustls::pki_types::CertificateDer<'_>, + intermediates: &[rustls::pki_types::CertificateDer<'_>], + server_name: &rustls::pki_types::ServerName<'_>, + ocsp_response: &[u8], + now: rustls::pki_types::UnixTime, + ) -> Result { + let r = + self.0 + .verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now); + if let Err(err) = r { + tracing::info!( + ?server_name, + "ignoring db connection TLS validation error: {err:?}" + ); + return Ok(ServerCertVerified::assertion()); + } + r + } + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &rustls::pki_types::CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + self.0.verify_tls12_signature(message, cert, dss) + } + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &rustls::pki_types::CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + self.0.verify_tls13_signature(message, cert, dss) + } + fn supported_verify_schemes(&self) -> Vec { + self.0.supported_verify_schemes() + } +} + /// Loads the root certificates and constructs a client config suitable for connecting. /// This function is blocking. fn client_config_with_root_certs() -> anyhow::Result { @@ -1290,76 +1337,12 @@ fn client_config_with_root_certs() -> anyhow::Result { .expect("ring should support the default protocol versions"); static DO_CERT_CHECKS: std::sync::OnceLock = std::sync::OnceLock::new(); let do_cert_checks = - DO_CERT_CHECKS.get_or_init(|| std::env::var("STORCON_CERT_CHECKS").is_ok()); + DO_CERT_CHECKS.get_or_init(|| std::env::var("STORCON_DB_CERT_CHECKS").is_ok()); Ok(if *do_cert_checks { client_config .with_root_certificates(load_certs()?) .with_no_client_auth() } else { - use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified}; - #[derive(Debug)] - struct AcceptAll(Arc); - impl ServerCertVerifier for AcceptAll { - fn verify_server_cert( - &self, - end_entity: &rustls::pki_types::CertificateDer<'_>, - intermediates: &[rustls::pki_types::CertificateDer<'_>], - server_name: &rustls::pki_types::ServerName<'_>, - ocsp_response: &[u8], - now: rustls::pki_types::UnixTime, - ) -> Result { - let r = self.0.verify_server_cert( - end_entity, - intermediates, - server_name, - ocsp_response, - now, - ); - if let Err(err) = r { - tracing::info!( - ?server_name, - "ignoring db connection TLS validation error: {err:?}" - ); - return Ok(ServerCertVerified::assertion()); - } - r - } - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &rustls::pki_types::CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, - ) -> Result - { - let r = self.0.verify_tls12_signature(message, cert, dss); - if let Err(err) = r { - tracing::info!( - "ignoring db connection 1.2 signature TLS validation error: {err:?}" - ); - return Ok(HandshakeSignatureValid::assertion()); - } - r - } - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &rustls::pki_types::CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, - ) -> Result - { - let r = self.0.verify_tls13_signature(message, cert, dss); - if let Err(err) = r { - tracing::info!( - "ignoring db connection 1.3 signature TLS validation error: {err:?}" - ); - return Ok(HandshakeSignatureValid::assertion()); - } - r - } - fn supported_verify_schemes(&self) -> Vec { - self.0.supported_verify_schemes() - } - } let verifier = AcceptAll( WebPkiServerVerifier::builder_with_provider( load_certs()?, @@ -1389,6 +1372,29 @@ fn establish_connection_rustls(config: &str) -> BoxFuture().unwrap(); + assert!(format!("{has_pw_cfg:?}").contains("specialuser")); + // Ensure that the password is not leaked by the debug impl + assert!(!format!("{has_pw_cfg:?}").contains("NOT ALLOWED TAG")); +} + +fn log_postgres_connstr_info(config_str: &str) -> anyhow::Result<()> { + let config = config_str + .parse::() + .map_err(|_e| anyhow::anyhow!("Couldn't parse config str"))?; + // We use debug formatting here, and use a unit test to ensure that we don't leak the password. + // To make extra sure the test gets ran, run it every time the function is called + // (this is rather cold code, we can afford it). + #[cfg(not(test))] + test_config_debug_censors_password(); + tracing::info!("database connection config: {config:?}"); + Ok(()) +} + /// Parts of [`crate::tenant_shard::TenantShard`] that are stored durably #[derive( QueryableByName, Queryable, Selectable, Insertable, Serialize, Deserialize, Clone, Eq, PartialEq,