Skip to content

Commit 5a0f4df

Browse files
committed
feat: multiple client CA.
Updates the policy server to allow loading multiple CA to validate the certificate used by client in a mTLS scenario. Signed-off-by: José Guilherme Vanz <jguilhermevanz@suse.com>
1 parent f8b201b commit 5a0f4df

File tree

4 files changed

+196
-75
lines changed

4 files changed

+196
-75
lines changed

src/cli.rs

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ pub(crate) fn build_cli() -> Command {
9595

9696
Arg::new("client-ca-file")
9797
.long("client-ca-file")
98+
.value_delimiter(',')
9899
.value_name("CLIENT_CA_FILE")
99100
.env("KUBEWARDEN_CLIENT_CA_FILE")
100101
.help("Path to an CA certificate file that issued the client certificate. Required to enable mTLS"),

src/config.rs

+13-7
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub struct Config {
5454
pub struct TlsConfig {
5555
pub cert_file: String,
5656
pub key_file: String,
57-
pub client_ca_file: Option<String>,
57+
pub client_ca_file: Vec<PathBuf>,
5858
}
5959

6060
impl Config {
@@ -192,14 +192,20 @@ fn readiness_probe_bind_address(matches: &clap::ArgMatches) -> Result<SocketAddr
192192
fn build_tls_config(matches: &clap::ArgMatches) -> Result<Option<TlsConfig>> {
193193
let cert_file = matches.get_one::<String>("cert-file").cloned();
194194
let key_file = matches.get_one::<String>("key-file").cloned();
195-
let client_ca_file = matches.get_one::<String>("client-ca-file").cloned();
195+
let client_ca_file = matches.get_many::<String>("client-ca-file");
196196

197197
match (cert_file, key_file, &client_ca_file) {
198-
(Some(cert_file), Some(key_file), _) => Ok(Some(TlsConfig {
199-
cert_file,
200-
key_file,
201-
client_ca_file,
202-
})),
198+
(Some(cert_file), Some(key_file), _) => {
199+
let client_ca_file = client_ca_file
200+
.unwrap_or_default()
201+
.map(PathBuf::from)
202+
.collect();
203+
Ok(Some(TlsConfig {
204+
cert_file,
205+
key_file,
206+
client_ca_file,
207+
}))
208+
}
203209
// No TLS configuration provided
204210
(None, None, None) => Ok(None),
205211
// Client CA certificate provided without server certificate and key

src/lib.rs

+30-22
Original file line numberDiff line numberDiff line change
@@ -332,11 +332,19 @@ async fn build_tls_server_config(tls_config: &TlsConfig) -> Result<rustls::Serve
332332
let key = PrivateKeyDer::try_from(key_vec.pop().unwrap())
333333
.map_err(|e| anyhow!("Cannot parse server key: {e}"))?;
334334

335-
if let Some(client_ca_file) = tls_config.client_ca_file.clone() {
335+
if tls_config.client_ca_file.is_empty() {
336+
return Ok(ServerConfig::builder()
337+
.with_no_client_auth()
338+
.with_single_cert(cert, key)?);
339+
}
340+
341+
let mut store = RootCertStore::empty();
342+
343+
//mTLS enabled
344+
for client_ca_file in tls_config.client_ca_file.clone() {
336345
// we have the client CA. Therefore, we should enable mTLS.
337346
let client_ca_reader = &mut BufReader::new(File::open(client_ca_file)?);
338347

339-
let mut store = RootCertStore::empty();
340348
let client_ca_certs: Vec<_> = rustls_pemfile::certs(client_ca_reader)
341349
.filter_map(|it| {
342350
if let Err(ref e) = it {
@@ -351,15 +359,10 @@ async fn build_tls_server_config(tls_config: &TlsConfig) -> Result<rustls::Serve
351359
client_ca_certs_ignored = cert_ignored,
352360
"Loaded client CA certificates"
353361
);
354-
let client_verifier = WebPkiClientVerifier::builder(Arc::new(store)).build()?;
355-
356-
return Ok(ServerConfig::builder()
357-
.with_client_cert_verifier(client_verifier)
358-
.with_single_cert(cert, key)?);
359362
}
360-
363+
let client_verifier = WebPkiClientVerifier::builder(Arc::new(store)).build()?;
361364
Ok(ServerConfig::builder()
362-
.with_no_client_auth()
365+
.with_client_cert_verifier(client_verifier)
363366
.with_single_cert(cert, key)?)
364367
}
365368

@@ -385,11 +388,12 @@ async fn create_tls_config_and_watch_certificate_changes(
385388
) -> Result<RustlsConfig> {
386389
use ::tracing::error;
387390

388-
let config = build_tls_server_config(&tls_config).await?;
389-
390-
let rust_config = RustlsConfig::from_config(Arc::new(config));
391+
// Build initial TLS configuration
392+
let initial_config = build_tls_server_config(&tls_config).await?;
393+
let rust_config = RustlsConfig::from_config(Arc::new(initial_config));
391394
let reloadable_rust_config = rust_config.clone();
392395

396+
// Init inotify to watch for changes in the certificate files
393397
let inotify =
394398
inotify::Inotify::init().map_err(|e| anyhow!("Cannot initialize inotify: {e}"))?;
395399
let cert_watch = inotify
@@ -404,15 +408,18 @@ async fn create_tls_config_and_watch_certificate_changes(
404408
.add(tls_config.key_file.clone(), inotify::WatchMask::CLOSE_WRITE)
405409
.map_err(|e| anyhow!("Cannot watch key file: {e}"))?;
406410

407-
let mut client_cert_watch = None;
408-
if let Some(ref client_ca_file) = tls_config.client_ca_file {
409-
client_cert_watch = Some(
411+
let client_cert_watches = tls_config
412+
.client_ca_file
413+
.clone()
414+
.into_iter()
415+
.map(|path| {
410416
inotify
411417
.watches()
412-
.add(client_ca_file, inotify::WatchMask::CLOSE_WRITE)
413-
.map_err(|e| anyhow!("Cannot watch client certificate file: {e}"))?,
414-
);
415-
}
418+
.add(path, inotify::WatchMask::CLOSE_WRITE)
419+
.map_err(|e| anyhow!("Cannot watch client certificate file: {e}"))
420+
.unwrap()
421+
})
422+
.collect::<Vec<_>>();
416423

417424
let buffer = [0; 1024];
418425
let stream = inotify
@@ -442,7 +449,8 @@ async fn create_tls_config_and_watch_certificate_changes(
442449
info!("TLS key file has been modified");
443450
key_changed = true;
444451
}
445-
if let Some(ref client_cert_watch) = client_cert_watch {
452+
453+
for client_cert_watch in client_cert_watches.iter() {
446454
if event.wd == *client_cert_watch {
447455
info!("TLS client certificate file has been modified");
448456
client_cert_changed = true;
@@ -454,11 +462,12 @@ async fn create_tls_config_and_watch_certificate_changes(
454462
if (key_changed && cert_changed)
455463
|| (client_cert_changed && (key_changed == cert_changed))
456464
{
457-
info!("reloading TLS certificates");
465+
info!("Reloading TLS certificates");
458466

459467
cert_changed = false;
460468
key_changed = false;
461469
client_cert_changed = false;
470+
462471
let server_config = build_tls_server_config(&tls_config).await;
463472
if let Err(e) = server_config {
464473
error!("Failed to reload TLS certificate: {}", e);
@@ -468,7 +477,6 @@ async fn create_tls_config_and_watch_certificate_changes(
468477
}
469478
}
470479
});
471-
472480
Ok(rust_config)
473481
}
474482

0 commit comments

Comments
 (0)