support mtls

This commit is contained in:
Conrad Ludgate
2025-07-08 17:06:48 +01:00
parent 2b22e0b069
commit 2c915e2f3d
3 changed files with 33 additions and 1 deletions

View File

@@ -134,6 +134,9 @@ struct ProxyCliArgs {
/// tls-key and tls-cert are for backwards compatibility, we can put all certs in one dir
#[clap(short = 'c', long, alias = "ssl-cert")]
tls_cert: Option<PathBuf>,
/// path to mTLS certs for client postgres connections
#[clap(long)]
mtls_certs: Option<PathBuf>,
/// Allow writing TLS session keys to the given file pointed to by the environment variable `SSLKEYLOGFILE`.
#[clap(long, alias = "allow-ssl-keylogfile")]
allow_tls_keylogfile: bool,
@@ -625,6 +628,7 @@ fn build_config(args: &ProxyCliArgs) -> anyhow::Result<&'static ProxyConfig> {
(Some(key_path), Some(cert_path)) => Some(config::configure_tls(
key_path,
cert_path,
args.mtls_certs.as_deref(),
args.certs_dir.as_deref(),
args.allow_tls_keylogfile,
)?),

View File

@@ -493,6 +493,7 @@ pub(crate) async fn refresh_config_inner(
tls_config.key_path.as_ref(),
tls_config.cert_path.as_ref(),
None,
None,
false,
)
})

View File

@@ -4,8 +4,10 @@ use std::sync::Arc;
use anyhow::{Context, bail};
use itertools::Itertools;
use rustls::RootCertStore;
use rustls::crypto::ring::{self, sign};
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use rustls::server::WebPkiClientVerifier;
use rustls::sign::CertifiedKey;
use x509_cert::der::{Reader, SliceReader};
@@ -24,12 +26,37 @@ pub struct TlsConfig {
pub fn configure_tls(
key_path: &Path,
cert_path: &Path,
mtls_certs_dir: Option<&Path>,
certs_dir: Option<&Path>,
allow_tls_keylogfile: bool,
) -> anyhow::Result<TlsConfig> {
// add default certificate
let mut cert_resolver = CertResolver::parse_new(key_path, cert_path)?;
let verifier = match mtls_certs_dir {
Some(dir) => {
let mut roots = RootCertStore::empty();
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_file() {
let cert_chain_bytes = std::fs::read(&path).context(format!(
"Failed to read TLS cert file at '{}.'",
path.display()
))?;
for cert in rustls_pemfile::certs(&mut &cert_chain_bytes[..]) {
roots.add(cert?)?;
}
}
}
WebPkiClientVerifier::builder(Arc::new(roots)).build()?
}
None => WebPkiClientVerifier::no_client_auth(),
};
// add extra certificates
if let Some(certs_dir) = certs_dir {
for entry in std::fs::read_dir(certs_dir)? {
@@ -55,7 +82,7 @@ pub fn configure_tls(
rustls::ServerConfig::builder_with_provider(Arc::new(ring::default_provider()))
.with_protocol_versions(&[&rustls::version::TLS13, &rustls::version::TLS12])
.context("ring should support TLS1.2 and TLS1.3")?
.with_no_client_auth()
.with_client_cert_verifier(verifier)
.with_cert_resolver(cert_resolver.clone());
config.alpn_protocols = vec![PG_ALPN_PROTOCOL.to_vec()];