From 9a30cac7b087e98c3004e13f76e6ccffb2537f2f Mon Sep 17 00:00:00 2001 From: Valentin Tolmer Date: Tue, 11 Apr 2023 13:22:07 +0200 Subject: [PATCH] healthcheck: check that the server's certificate is the one in the config --- server/Cargo.toml | 5 ++- server/src/infra/healthcheck.rs | 63 +++++++++++++++++++++++++-------- server/src/infra/ldap_server.rs | 32 +++++++++-------- 3 files changed, 71 insertions(+), 29 deletions(-) diff --git a/server/Cargo.toml b/server/Cargo.toml index c069131..4d9bca4 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -31,7 +31,6 @@ lber = "0.4.1" ldap3_proto = ">=0.3.1" log = "*" orion = "0.17" -rustls = "0.20" rustls-pemfile = "1" serde = "*" serde_bytes = "0.11" @@ -115,5 +114,9 @@ version = "0.11" default-features = false features = ["rustls-tls-webpki-roots"] +[dependencies.rustls] +version = "0.20" +features = ["dangerous_configuration"] + [dev-dependencies] mockall = "0.11" diff --git a/server/src/infra/healthcheck.rs b/server/src/infra/healthcheck.rs index 40089a0..55f00c6 100644 --- a/server/src/infra/healthcheck.rs +++ b/server/src/infra/healthcheck.rs @@ -1,4 +1,4 @@ -use crate::infra::configuration::LdapsOptions; +use crate::infra::{configuration::LdapsOptions, ldap_server::read_certificates}; use anyhow::{anyhow, bail, ensure, Context, Result}; use futures_util::SinkExt; use ldap3_proto::{ @@ -65,6 +65,7 @@ where invalid_answer ); info!("Success"); + resp.close().await?; Ok(()) } @@ -85,15 +86,44 @@ fn get_root_certificates() -> rustls::RootCertStore { root_store } -fn get_tls_connector() -> Result { - use rustls::ClientConfig; - let client_config = std::sync::Arc::new( - ClientConfig::builder() - .with_safe_defaults() - .with_root_certificates(get_root_certificates()) - .with_no_client_auth(), - ); - Ok(client_config.into()) +fn get_tls_connector(ldaps_options: &LdapsOptions) -> Result { + let mut client_config = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(get_root_certificates()) + .with_no_client_auth(); + let (certs, _private_key) = read_certificates(ldaps_options)?; + // Check that the server cert is the one in the config file. + struct CertificateVerifier { + certificate: rustls::Certificate, + certificate_path: String, + } + impl rustls::client::ServerCertVerifier for CertificateVerifier { + fn verify_server_cert( + &self, + end_entity: &rustls::Certificate, + _intermediates: &[rustls::Certificate], + _server_name: &rustls::ServerName, + _scts: &mut dyn Iterator, + _ocsp_response: &[u8], + _now: std::time::SystemTime, + ) -> std::result::Result { + if end_entity != &self.certificate { + return Err(rustls::Error::InvalidCertificateData(format!( + "Server certificate doesn't match the one in the config file {}", + &self.certificate_path + ))); + } + Ok(rustls::client::ServerCertVerified::assertion()) + } + } + let mut dangerous_config = rustls::client::DangerousClientConfig { + cfg: &mut client_config, + }; + dangerous_config.set_certificate_verifier(std::sync::Arc::new(CertificateVerifier { + certificate: certs.first().expect("empty certificate chain").clone(), + certificate_path: ldaps_options.cert_file.clone(), + })); + Ok(std::sync::Arc::new(client_config).into()) } #[instrument(skip_all, level = "info", err)] @@ -102,15 +132,20 @@ pub async fn check_ldaps(ldaps_options: &LdapsOptions) -> Result<()> { info!("LDAPS not enabled"); return Ok(()); }; - let tls_connector = get_tls_connector()?; + let tls_connector = + get_tls_connector(ldaps_options).context("while preparing the tls connection")?; let url = format!("localhost:{}", ldaps_options.port); check_ldap_endpoint( tls_connector .connect( - rustls::ServerName::try_from(url.as_str())?, - TcpStream::connect(&url).await?, + rustls::ServerName::try_from("localhost") + .context("while parsing the server name")?, + TcpStream::connect(&url) + .await + .context("while connecting TCP")?, ) - .await?, + .await + .context("while connecting TLS")?, ) .await } diff --git a/server/src/infra/ldap_server.rs b/server/src/infra/ldap_server.rs index 82f7b49..8aa1b90 100644 --- a/server/src/infra/ldap_server.rs +++ b/server/src/infra/ldap_server.rs @@ -4,7 +4,8 @@ use crate::{ opaque_handler::OpaqueHandler, }, infra::{ - access_control::AccessControlledBackendHandler, configuration::Configuration, + access_control::AccessControlledBackendHandler, + configuration::{Configuration, LdapsOptions}, ldap_handler::LdapHandler, }, }; @@ -126,20 +127,22 @@ fn read_private_key(key_file: &str) -> Result { .map(rustls::PrivateKey) } -fn get_tls_acceptor(config: &Configuration) -> Result { - use rustls::{Certificate, ServerConfig}; - use rustls_pemfile::certs; +pub fn read_certificates( + ldaps_options: &LdapsOptions, +) -> Result<(Vec, rustls::PrivateKey)> { use std::{fs::File, io::BufReader}; - // Load TLS key and cert files - let certs = certs(&mut BufReader::new(File::open( - &config.ldaps_options.cert_file, - )?))? - .into_iter() - .map(Certificate) - .collect::>(); - let private_key = read_private_key(&config.ldaps_options.key_file)?; + let certs = rustls_pemfile::certs(&mut BufReader::new(File::open(&ldaps_options.cert_file)?))? + .into_iter() + .map(rustls::Certificate) + .collect::>(); + let private_key = read_private_key(&ldaps_options.key_file)?; + Ok((certs, private_key)) +} + +fn get_tls_acceptor(ldaps_options: &LdapsOptions) -> Result { + let (certs, private_key) = read_certificates(ldaps_options)?; let server_config = std::sync::Arc::new( - ServerConfig::builder() + rustls::ServerConfig::builder() .with_safe_defaults() .with_no_client_auth() .with_single_cert(certs, private_key)?, @@ -190,7 +193,8 @@ where if config.ldaps_options.enabled { let tls_context = ( context_for_tls, - get_tls_acceptor(config).context("while setting up the SSL certificate")?, + get_tls_acceptor(&config.ldaps_options) + .context("while setting up the SSL certificate")?, ); let tls_binder = move || { let tls_context = tls_context.clone();