Got rid of Boxed acme types
This commit is contained in:
parent
52f87dc625
commit
3d3dfb34ed
@ -1,4 +1,5 @@
|
||||
pub mod manager;
|
||||
pub mod resolver;
|
||||
pub mod store;
|
||||
|
||||
mod private_key;
|
||||
|
@ -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<Result<(), unexpected::Error>>;
|
||||
)]
|
||||
pub trait Manager {
|
||||
type SyncDomainFuture<'mgr>: future::Future<Output = Result<(), unexpected::Error>>
|
||||
+ 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<Box<dyn future::Future<Output = Result<(), unexpected::Error>> + Send + 'mgr>>;
|
||||
fn get_http01_challenge_key(&self, token: &str) -> Result<String, GetHttp01ChallengeKeyError>;
|
||||
}
|
||||
|
||||
pub trait BoxedManager: Manager + Send + Sync + Clone + 'static {}
|
||||
|
||||
struct ManagerImpl<Store>
|
||||
where
|
||||
Store: acme::store::BoxedStore,
|
||||
{
|
||||
store: Store,
|
||||
struct ManagerImpl {
|
||||
store: sync::Arc<dyn acme::store::Store>,
|
||||
account: sync::Arc<acme2::Account>,
|
||||
}
|
||||
|
||||
pub async fn new<Store>(
|
||||
store: Store,
|
||||
pub async fn new(
|
||||
store: sync::Arc<dyn acme::store::Store>,
|
||||
contact_email: &str,
|
||||
) -> Result<impl BoxedManager, unexpected::Error>
|
||||
where
|
||||
Store: acme::store::BoxedStore,
|
||||
{
|
||||
) -> Result<sync::Arc<dyn Manager>, 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<Store> BoxedManager for sync::Arc<ManagerImpl<Store>> where Store: acme::store::BoxedStore {}
|
||||
|
||||
impl<Store> Manager for sync::Arc<ManagerImpl<Store>>
|
||||
where
|
||||
Store: acme::store::BoxedStore,
|
||||
{
|
||||
type SyncDomainFuture<'mgr> = pin::Pin<Box<dyn future::Future<Output = Result<(), unexpected::Error>> + 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<Box<dyn future::Future<Output = Result<(), unexpected::Error>> + 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
|
||||
|
44
src/domain/acme/resolver.rs
Normal file
44
src/domain/acme/resolver.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use crate::domain::acme::store;
|
||||
use crate::error::unexpected::Mappable;
|
||||
|
||||
use std::sync;
|
||||
|
||||
struct CertResolver(sync::Arc<dyn store::Store>);
|
||||
|
||||
pub fn new(
|
||||
store: sync::Arc<dyn store::Store>,
|
||||
) -> sync::Arc<dyn rustls::server::ResolvesServerCert> {
|
||||
return sync::Arc::new(CertResolver(store));
|
||||
}
|
||||
|
||||
impl rustls::server::ResolvesServerCert for CertResolver {
|
||||
fn resolve(
|
||||
&self,
|
||||
client_hello: rustls::server::ClientHello<'_>,
|
||||
) -> Option<sync::Arc<rustls::sign::CertifiedKey>> {
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
@ -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<PrivateKey, GetAccountKeyError>;
|
||||
|
||||
@ -60,8 +60,6 @@ pub trait Store {
|
||||
) -> Result<(PrivateKey, Vec<Certificate>), 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<FSStore>);
|
||||
|
||||
pub fn new(
|
||||
dir_path: &path::Path,
|
||||
) -> Result<impl BoxedStore + rustls::server::ResolvesServerCert, unexpected::Error> {
|
||||
pub fn new(dir_path: &path::Path) -> Result<sync::Arc<dyn Store>, 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::<String>();
|
||||
|
||||
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<sync::Arc<rustls::sign::CertifiedKey>> {
|
||||
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::*;
|
||||
|
@ -141,15 +141,14 @@ pub trait Manager: Sync + Send {
|
||||
fn all_domains(&self) -> AllDomainsResult<Vec<AllDomainsResult<domain::Name>>>;
|
||||
}
|
||||
|
||||
struct ManagerImpl<DomainConfigStore, AcmeManager>
|
||||
struct ManagerImpl<DomainConfigStore>
|
||||
where
|
||||
DomainConfigStore: config::BoxedStore,
|
||||
AcmeManager: acme::manager::BoxedManager,
|
||||
{
|
||||
origin_store: sync::Arc<dyn origin::store::Store>,
|
||||
domain_config_store: DomainConfigStore,
|
||||
domain_checker: checker::DNSChecker,
|
||||
acme_manager: Option<AcmeManager>,
|
||||
acme_manager: Option<sync::Arc<dyn acme::manager::Manager>>,
|
||||
|
||||
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<dyn origin::store::Store>,
|
||||
domain_config_store: DomainConfigStore,
|
||||
domain_checker: checker::DNSChecker,
|
||||
acme_manager: Option<AcmeManager>,
|
||||
acme_manager: Option<sync::Arc<dyn acme::manager::Manager>>,
|
||||
) -> sync::Arc<dyn Manager>
|
||||
where
|
||||
DomainConfigStore: config::BoxedStore,
|
||||
AcmeManager: acme::manager::BoxedManager,
|
||||
{
|
||||
let canceller = CancellationToken::new();
|
||||
|
||||
@ -208,10 +206,9 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
impl<DomainConfigStore, AcmeManager> ManagerImpl<DomainConfigStore, AcmeManager>
|
||||
impl<DomainConfigStore> ManagerImpl<DomainConfigStore>
|
||||
where
|
||||
DomainConfigStore: config::BoxedStore,
|
||||
AcmeManager: acme::manager::BoxedManager,
|
||||
{
|
||||
pub async fn stop(self) {
|
||||
self.canceller.cancel();
|
||||
@ -221,10 +218,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<DomainConfigStore, AcmeManager> Manager for ManagerImpl<DomainConfigStore, AcmeManager>
|
||||
impl<DomainConfigStore> Manager for ManagerImpl<DomainConfigStore>
|
||||
where
|
||||
DomainConfigStore: config::BoxedStore,
|
||||
AcmeManager: acme::manager::BoxedManager,
|
||||
{
|
||||
fn get_config(&self, domain: &domain::Name) -> Result<config::Config, GetConfigError> {
|
||||
Ok(self.domain_config_store.get(domain)?)
|
||||
|
19
src/main.rs
19
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<DomainAcmeStore, DomainAcmeManager>
|
||||
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<dyn domiply::domain::acme::store::Store>,
|
||||
domain_acme_manager: sync::Arc<dyn domiply::domain::acme::manager::Manager>,
|
||||
}
|
||||
|
||||
#[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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user