From 3d3dfb34ed65496b74ed1626b55721044181f23d Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Sun, 18 Jun 2023 13:44:15 +0200 Subject: [PATCH] Got rid of Boxed acme types --- src/domain/acme.rs | 1 + src/domain/acme/manager.rs | 53 +++++++++++---------------------- src/domain/acme/resolver.rs | 44 +++++++++++++++++++++++++++ src/domain/acme/store.rs | 59 ++++++------------------------------- src/domain/manager.rs | 16 ++++------ src/main.rs | 19 +++++------- 6 files changed, 84 insertions(+), 108 deletions(-) create mode 100644 src/domain/acme/resolver.rs diff --git a/src/domain/acme.rs b/src/domain/acme.rs index 8b2e126..94150d1 100644 --- a/src/domain/acme.rs +++ b/src/domain/acme.rs @@ -1,4 +1,5 @@ pub mod manager; +pub mod resolver; pub mod store; mod private_key; diff --git a/src/domain/acme/manager.rs b/src/domain/acme/manager.rs index 353717c..f12336b 100644 --- a/src/domain/acme/manager.rs +++ b/src/domain/acme/manager.rs @@ -7,39 +7,24 @@ const LETS_ENCRYPT_URL: &str = "https://acme-v02.api.letsencrypt.org/directory"; pub type GetHttp01ChallengeKeyError = acme::store::GetHttp01ChallengeKeyError; -#[mockall::automock( - type SyncDomainFuture=future::Ready>; -)] -pub trait Manager { - type SyncDomainFuture<'mgr>: future::Future> - + Send - + Unpin - + 'mgr - where - Self: 'mgr; - - fn sync_domain(&self, domain: domain::Name) -> Self::SyncDomainFuture<'_>; - +#[mockall::automock] +pub trait Manager: Sync + Send { + fn sync_domain<'mgr>( + &'mgr self, + domain: domain::Name, + ) -> pin::Pin> + Send + 'mgr>>; fn get_http01_challenge_key(&self, token: &str) -> Result; } -pub trait BoxedManager: Manager + Send + Sync + Clone + 'static {} - -struct ManagerImpl -where - Store: acme::store::BoxedStore, -{ - store: Store, +struct ManagerImpl { + store: sync::Arc, account: sync::Arc, } -pub async fn new( - store: Store, +pub async fn new( + store: sync::Arc, contact_email: &str, -) -> Result -where - Store: acme::store::BoxedStore, -{ +) -> Result, unexpected::Error> { let dir = acme2::DirectoryBuilder::new(LETS_ENCRYPT_URL.to_string()) .build() .await @@ -81,16 +66,12 @@ where Ok(sync::Arc::new(ManagerImpl { store, account })) } -impl BoxedManager for sync::Arc> where Store: acme::store::BoxedStore {} - -impl Manager for sync::Arc> -where - Store: acme::store::BoxedStore, -{ - type SyncDomainFuture<'mgr> = pin::Pin> + Send + 'mgr>> - where Self: 'mgr; - - fn sync_domain(&self, domain: domain::Name) -> Self::SyncDomainFuture<'_> { +impl Manager for ManagerImpl { + fn sync_domain<'mgr>( + &'mgr self, + domain: domain::Name, + ) -> pin::Pin> + Send + 'mgr>> + { Box::pin(async move { // if there's an existing cert, and its expiry (determined by the soonest value of // not_after amongst its parts) is later than 30 days from now, then we consider it to be diff --git a/src/domain/acme/resolver.rs b/src/domain/acme/resolver.rs new file mode 100644 index 0000000..f9d7908 --- /dev/null +++ b/src/domain/acme/resolver.rs @@ -0,0 +1,44 @@ +use crate::domain::acme::store; +use crate::error::unexpected::Mappable; + +use std::sync; + +struct CertResolver(sync::Arc); + +pub fn new( + store: sync::Arc, +) -> sync::Arc { + return sync::Arc::new(CertResolver(store)); +} + +impl rustls::server::ResolvesServerCert for CertResolver { + fn resolve( + &self, + client_hello: rustls::server::ClientHello<'_>, + ) -> Option> { + let domain = client_hello.server_name()?; + + match self.0.get_certificate(domain) { + Err(store::GetCertificateError::NotFound) => { + log::warn!("No cert found for domain {domain}"); + Ok(None) + } + Err(store::GetCertificateError::Unexpected(err)) => Err(err), + Ok((key, cert)) => { + match rustls::sign::any_supported_type(&key.into()).or_unexpected() { + Err(err) => Err(err), + Ok(key) => Ok(Some(sync::Arc::new(rustls::sign::CertifiedKey { + cert: cert.into_iter().map(|cert| cert.into()).collect(), + key, + ocsp: None, + sct_list: None, + }))), + } + } + } + .unwrap_or_else(|err| { + log::error!("Unexpected error getting cert for domain {domain}: {err}"); + None + }) + } +} diff --git a/src/domain/acme/store.rs b/src/domain/acme/store.rs index 1e0c7e3..075327e 100644 --- a/src/domain/acme/store.rs +++ b/src/domain/acme/store.rs @@ -38,7 +38,7 @@ pub enum GetCertificateError { } #[mockall::automock] -pub trait Store { +pub trait Store: Sync + Send { fn set_account_key(&self, k: &PrivateKey) -> Result<(), unexpected::Error>; fn get_account_key(&self) -> Result; @@ -60,8 +60,6 @@ pub trait Store { ) -> Result<(PrivateKey, Vec), GetCertificateError>; } -pub trait BoxedStore: Store + Send + Sync + Clone + 'static {} - #[derive(Debug, Serialize, Deserialize)] struct StoredPKeyCert { private_key: PrivateKey, @@ -72,12 +70,7 @@ struct FSStore { dir_path: path::PathBuf, } -#[derive(Clone)] -struct BoxedFSStore(sync::Arc); - -pub fn new( - dir_path: &path::Path, -) -> Result { +pub fn new(dir_path: &path::Path) -> Result, unexpected::Error> { vec![ dir_path, dir_path.join("http01_challenge_keys").as_ref(), @@ -89,14 +82,14 @@ pub fn new( }) .try_collect()?; - Ok(BoxedFSStore(sync::Arc::new(FSStore { + Ok(sync::Arc::new(FSStore { dir_path: dir_path.into(), - }))) + })) } -impl BoxedFSStore { +impl FSStore { fn account_key_path(&self) -> path::PathBuf { - self.0.dir_path.join("account.key") + self.dir_path.join("account.key") } fn http01_challenge_key_path(&self, token: &str) -> path::PathBuf { @@ -106,20 +99,18 @@ impl BoxedFSStore { .expect("token successfully hashed"); let n = h.finalize().encode_hex::(); - self.0.dir_path.join("http01_challenge_keys").join(n) + self.dir_path.join("http01_challenge_keys").join(n) } fn certificate_path(&self, domain: &str) -> path::PathBuf { let mut domain = domain.to_string(); domain.push_str(".json"); - self.0.dir_path.join("certificates").join(domain) + self.dir_path.join("certificates").join(domain) } } -impl BoxedStore for BoxedFSStore {} - -impl Store for BoxedFSStore { +impl Store for FSStore { fn set_account_key(&self, k: &PrivateKey) -> Result<(), unexpected::Error> { let path = self.account_key_path(); { @@ -232,38 +223,6 @@ impl Store for BoxedFSStore { } } -impl rustls::server::ResolvesServerCert for BoxedFSStore { - fn resolve( - &self, - client_hello: rustls::server::ClientHello<'_>, - ) -> Option> { - let domain = client_hello.server_name()?; - - match self.get_certificate(domain) { - Err(GetCertificateError::NotFound) => { - log::warn!("No cert found for domain {domain}"); - Ok(None) - } - Err(GetCertificateError::Unexpected(err)) => Err(err), - Ok((key, cert)) => { - match rustls::sign::any_supported_type(&key.into()).or_unexpected() { - Err(err) => Err(err), - Ok(key) => Ok(Some(sync::Arc::new(rustls::sign::CertifiedKey { - cert: cert.into_iter().map(|cert| cert.into()).collect(), - key, - ocsp: None, - sct_list: None, - }))), - } - } - } - .unwrap_or_else(|err| { - log::error!("Unexpected error getting cert for domain {domain}: {err}"); - None - }) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/domain/manager.rs b/src/domain/manager.rs index ebb66e9..b149b0f 100644 --- a/src/domain/manager.rs +++ b/src/domain/manager.rs @@ -141,15 +141,14 @@ pub trait Manager: Sync + Send { fn all_domains(&self) -> AllDomainsResult>>; } -struct ManagerImpl +struct ManagerImpl where DomainConfigStore: config::BoxedStore, - AcmeManager: acme::manager::BoxedManager, { origin_store: sync::Arc, domain_config_store: DomainConfigStore, domain_checker: checker::DNSChecker, - acme_manager: Option, + acme_manager: Option>, canceller: CancellationToken, origin_sync_handler: tokio::task::JoinHandle<()>, @@ -171,15 +170,14 @@ fn sync_origins(origin_store: &dyn origin::store::Store) { }); } -pub fn new<'mgr, DomainConfigStore, AcmeManager>( +pub fn new<'mgr, DomainConfigStore>( origin_store: sync::Arc, domain_config_store: DomainConfigStore, domain_checker: checker::DNSChecker, - acme_manager: Option, + acme_manager: Option>, ) -> sync::Arc where DomainConfigStore: config::BoxedStore, - AcmeManager: acme::manager::BoxedManager, { let canceller = CancellationToken::new(); @@ -208,10 +206,9 @@ where }) } -impl ManagerImpl +impl ManagerImpl where DomainConfigStore: config::BoxedStore, - AcmeManager: acme::manager::BoxedManager, { pub async fn stop(self) { self.canceller.cancel(); @@ -221,10 +218,9 @@ where } } -impl Manager for ManagerImpl +impl Manager for ManagerImpl where DomainConfigStore: config::BoxedStore, - AcmeManager: acme::manager::BoxedManager, { fn get_config(&self, domain: &domain::Name) -> Result { Ok(self.domain_config_store.get(domain)?) diff --git a/src/main.rs b/src/main.rs index b745941..35fd20c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,9 +12,6 @@ use std::net::SocketAddr; use std::str::FromStr; use std::{future, path, sync}; -use domiply::domain::acme::manager::Manager as AcmeManager; -use domiply::domain::manager::Manager; - #[derive(Parser, Debug)] #[command(version)] #[command(about = "A domiply to another dimension")] @@ -68,14 +65,10 @@ struct Cli { } #[derive(Clone)] -struct HTTPSParams -where - DomainAcmeStore: domiply::domain::acme::store::BoxedStore, - DomainAcmeManager: domiply::domain::acme::manager::BoxedManager, -{ +struct HTTPSParams { https_listen_addr: SocketAddr, - domain_acme_store: DomainAcmeStore, - domain_acme_manager: DomainAcmeManager, + domain_acme_store: sync::Arc, + domain_acme_manager: sync::Arc, } #[tokio::main] @@ -263,7 +256,6 @@ async fn main() { // HTTPS server wait_group.push({ - let https_params = https_params; let http_domain = config.http_domain.clone(); let canceller = canceller.clone(); let service = service.clone(); @@ -281,12 +273,15 @@ async fn main() { }); tokio::spawn(async move { + let cert_resolver = + domiply::domain::acme::resolver::new(https_params.domain_acme_store); let canceller = canceller.clone(); + let server_config: tokio_rustls::TlsAcceptor = sync::Arc::new( rustls::server::ServerConfig::builder() .with_safe_defaults() .with_no_client_auth() - .with_cert_resolver(sync::Arc::from(https_params.domain_acme_store)), + .with_cert_resolver(cert_resolver), ) .into();