#[cfg(feature = "tls")]
use std::sync::Arc;
#[cfg(feature = "tls")]
use rustls::ClientConfig;
#[cfg(feature = "tls")]
use rustls::sign::{CertifiedKey, SingleCertAndKey};
#[cfg(feature = "tls")]
use rustls_pemfile::{certs, pkcs8_private_keys};
#[cfg(feature = "tls")]
use rustls_pki_types::PrivateKeyDer;
#[cfg(feature = "tls")]
use tokio_postgres_rustls::MakeRustlsConnect;
#[cfg(feature = "tls")]
use crate::{config::Config, error::Error};
#[cfg(feature = "tls")]
pub(crate) fn connector(cfg: &Config) -> Result<Option<MakeRustlsConnect>, Error> {
let root_store = if let Some(ca_file) = cfg.ssl_ca_file.as_ref() {
let cert_file = std::fs::File::open(ca_file)?;
let mut buf = std::io::BufReader::new(cert_file);
let mut root_store = rustls::RootCertStore::empty();
for cert in rustls_pemfile::certs(&mut buf) {
root_store
.add(cert.expect("CA cert should be valid PEM"))
.expect("CA cert should be valid X.509");
}
root_store
} else {
rustls::RootCertStore {
roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
}
};
let (Some(cert_file), Some(key_file)) = (cfg.ssl_cert_file.as_ref(), cfg.ssl_key_file.as_ref())
else {
return Ok(None);
};
let cert_file = std::fs::File::open(cert_file)?;
let mut buf = std::io::BufReader::new(cert_file);
let mut client_chain = vec![];
for item in certs(&mut buf) {
client_chain.push(item.expect("getting cert should not fail"));
}
let key_file = std::fs::File::open(key_file)?;
let mut buf = std::io::BufReader::new(key_file);
let mut keys = pkcs8_private_keys(&mut buf);
let client_key_der = PrivateKeyDer::from(
keys.next()
.expect("getting key should not fail")
.expect("getting key should not fail"),
);
let builder = ClientConfig::builder().with_root_certificates(root_store);
let provider = builder.crypto_provider();
let certified_key = CertifiedKey::from_der(client_chain, client_key_der, provider)
.expect("creating certified key should not fail");
let resolver: SingleCertAndKey = SingleCertAndKey::from(Arc::new(certified_key));
let tls_config = builder.with_client_cert_resolver(Arc::new(resolver));
Ok(Some(MakeRustlsConnect::new(tls_config)))
}