From 2c915e2f3da33aab19a6aef0a393fce8df32631f Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Tue, 8 Jul 2025 17:06:48 +0100 Subject: [PATCH] support mtls --- proxy/src/binary/proxy.rs | 4 ++++ proxy/src/config.rs | 1 + proxy/src/tls/server_config.rs | 29 ++++++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/proxy/src/binary/proxy.rs b/proxy/src/binary/proxy.rs index 5abf237738..6d24cbc932 100644 --- a/proxy/src/binary/proxy.rs +++ b/proxy/src/binary/proxy.rs @@ -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, + /// path to mTLS certs for client postgres connections + #[clap(long)] + mtls_certs: Option, /// 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, )?), diff --git a/proxy/src/config.rs b/proxy/src/config.rs index 04080efcca..8cd7594dcb 100644 --- a/proxy/src/config.rs +++ b/proxy/src/config.rs @@ -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, ) }) diff --git a/proxy/src/tls/server_config.rs b/proxy/src/tls/server_config.rs index 66c53b3aff..954c5797e1 100644 --- a/proxy/src/tls/server_config.rs +++ b/proxy/src/tls/server_config.rs @@ -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 { // 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()];