From e18fafe4e63f0d1d2267e3acb6ef5fc11e6c99da Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Tue, 28 May 2024 03:19:59 +0900 Subject: [PATCH] wip: implemented reloader service with trait object for future support of acme --- rpxy-certs/Cargo.toml | 2 +- .../src/{source.rs => crypto_source.rs} | 0 rpxy-certs/src/lib.rs | 9 +- rpxy-certs/src/reloader_service.rs | 83 +++++++++++++++++++ .../src/{service.rs => server_crypto.rs} | 12 ++- 5 files changed, 100 insertions(+), 6 deletions(-) rename rpxy-certs/src/{source.rs => crypto_source.rs} (100%) create mode 100644 rpxy-certs/src/reloader_service.rs rename rpxy-certs/src/{service.rs => server_crypto.rs} (52%) diff --git a/rpxy-certs/Cargo.toml b/rpxy-certs/Cargo.toml index cd5bdfb..26febe4 100644 --- a/rpxy-certs/Cargo.toml +++ b/rpxy-certs/Cargo.toml @@ -18,7 +18,7 @@ tracing = { version = "0.1.40" } # anyhow = "1.0.86" derive_builder = { version = "0.20.0" } thiserror = { version = "1.0.61" } -# hot_reload = {version = "0.1.5"} +hot_reload = { version = "0.1.5" } async-trait = { version = "0.1.80" } # tokio-rustls = { version = "0.26.0", features = ["early-data"] } rustls = { version = "0.23.8", default-features = false, features = [ diff --git a/rpxy-certs/src/source.rs b/rpxy-certs/src/crypto_source.rs similarity index 100% rename from rpxy-certs/src/source.rs rename to rpxy-certs/src/crypto_source.rs diff --git a/rpxy-certs/src/lib.rs b/rpxy-certs/src/lib.rs index 75b107c..ebb4789 100644 --- a/rpxy-certs/src/lib.rs +++ b/rpxy-certs/src/lib.rs @@ -1,7 +1,8 @@ mod certs; +mod crypto_source; mod error; -mod service; -mod source; +mod reloader_service; +mod server_crypto; #[allow(unused_imports)] mod log { @@ -10,6 +11,6 @@ mod log { pub use crate::{ certs::SingleServerCertsKeys, - service::{ServerCrypto, ServerNameBytes, ServerNameCryptoMap}, - source::{CryptoFileSource, CryptoFileSourceBuilder, CryptoFileSourceBuilderError, CryptoSource}, + crypto_source::{CryptoFileSource, CryptoFileSourceBuilder, CryptoFileSourceBuilderError, CryptoSource}, + server_crypto::{ServerCrypto, ServerNameBytes, ServerNameCryptoMap}, }; diff --git a/rpxy-certs/src/reloader_service.rs b/rpxy-certs/src/reloader_service.rs new file mode 100644 index 0000000..06f5ad6 --- /dev/null +++ b/rpxy-certs/src/reloader_service.rs @@ -0,0 +1,83 @@ +use crate::{ + crypto_source::CryptoSource, + error::*, + log::*, + server_crypto::{ServerCryptoBase, ServerNameBytes}, +}; +use async_trait::async_trait; +use hot_reload::{Reload, ReloaderError}; +use rustc_hash::FxHashMap as HashMap; +use std::sync::Arc; + +/* ------------------------------------------------ */ +/// Boxed CryptoSource trait object with Send and Sync +/// TODO: support for not only `CryptoFileSource` but also other type of sources +type DynCryptoSource = dyn CryptoSource + Send + Sync + 'static; + +/// Reloader service for certificates and keys for TLS +pub struct CryptoReloader { + inner: HashMap>>, +} + +impl Extend<(ServerNameBytes, T)> for CryptoReloader +where + T: CryptoSource + Send + Sync + 'static, +{ + fn extend>(&mut self, iter: I) { + let iter = iter + .into_iter() + .map(|(k, v)| (k, Arc::new(Box::new(v) as Box))); + self.inner.extend(iter); + } +} + +#[async_trait] +impl Reload for CryptoReloader { + type Source = HashMap>>; + + async fn new(source: &Self::Source) -> Result> { + let mut inner = HashMap::default(); + inner.extend(source.clone()); + Ok(Self { inner }) + } + + async fn reload(&self) -> Result, ReloaderError> { + let mut server_crypto_base = ServerCryptoBase::default(); + + for (server_name_bytes, crypto_source) in self.inner.iter() { + let certs_keys = crypto_source.read().await.map_err(|e| { + error!("Failed to reload cert, key or ca cert: {e}"); + ReloaderError::::Reload("Failed to reload cert, key or ca cert") + })?; + server_crypto_base.inner.insert(server_name_bytes.clone(), certs_keys); + } + + Ok(Some(server_crypto_base)) + } +} +/* ------------------------------------------------ */ + +#[cfg(test)] +mod tests { + use super::*; + use crate::crypto_source::CryptoFileSourceBuilder; + + #[tokio::test] + async fn test_crypto_reloader() { + let tls_cert_path = "../example-certs/server.crt"; + let tls_cert_key_path = "../example-certs/server.key"; + let client_ca_cert_path = Some("../example-certs/client.ca.crt"); + + let mut crypto_reloader = CryptoReloader::new(&HashMap::default()).await.unwrap(); + let crypto_source = CryptoFileSourceBuilder::default() + .tls_cert_path(tls_cert_path) + .tls_cert_key_path(tls_cert_key_path) + .client_ca_cert_path(client_ca_cert_path) + .build() + .unwrap(); + crypto_reloader.extend(vec![(b"localhost".to_vec(), crypto_source)]); + + let server_crypto_base = crypto_reloader.reload().await.unwrap().unwrap(); + assert_eq!(server_crypto_base.inner.len(), 1); + } +} diff --git a/rpxy-certs/src/service.rs b/rpxy-certs/src/server_crypto.rs similarity index 52% rename from rpxy-certs/src/service.rs rename to rpxy-certs/src/server_crypto.rs index 31f24a1..4494397 100644 --- a/rpxy-certs/src/service.rs +++ b/rpxy-certs/src/server_crypto.rs @@ -1,8 +1,10 @@ +use crate::SingleServerCertsKeys; use rustc_hash::FxHashMap as HashMap; use rustls::ServerConfig; use std::sync::Arc; -/// ServerName in bytes type +/* ------------------------------------------------ */ +/// ServerName in bytes type (TODO: this may be changed to define `common` layer defining types of names. or should be independent?) pub type ServerNameBytes = Vec; /// ServerName (SNI) to ServerConfig map type pub type ServerNameCryptoMap = HashMap>; @@ -13,3 +15,11 @@ pub struct ServerCrypto { // // For TLS over TCP/HTTP2 and 1.1, map of SNI to server_crypto for all given servers pub inner_local_map: Arc, } + +/* ------------------------------------------------ */ +/// Reloader target for the certificate reloader service +#[derive(Debug, PartialEq, Eq, Clone, Default)] +pub struct ServerCryptoBase { + /// Map of server name to certs and keys + pub(super) inner: HashMap, +}