Make domain manager do cert resolving for tls
This commit is contained in:
parent
51cb6aadce
commit
289a185d42
1
TODO
1
TODO
@ -1,2 +1 @@
|
||||
- make domain_manager implement rusttls cert resolver
|
||||
- Try to switch from Arc to Box where possible
|
||||
|
@ -1,5 +1,4 @@
|
||||
pub mod manager;
|
||||
pub mod resolver;
|
||||
pub mod store;
|
||||
|
||||
mod private_key;
|
||||
|
@ -1,11 +1,13 @@
|
||||
use std::{future, pin, sync, time};
|
||||
|
||||
use crate::domain::acme::{Certificate, PrivateKey};
|
||||
use crate::domain::{self, acme};
|
||||
use crate::error::unexpected::{self, Intoable, Mappable};
|
||||
|
||||
const LETS_ENCRYPT_URL: &str = "https://acme-v02.api.letsencrypt.org/directory";
|
||||
|
||||
pub type GetHttp01ChallengeKeyError = acme::store::GetHttp01ChallengeKeyError;
|
||||
pub type GetCertificateError = acme::store::GetCertificateError;
|
||||
|
||||
#[mockall::automock]
|
||||
pub trait Manager: Sync + Send {
|
||||
@ -14,6 +16,12 @@ pub trait Manager: Sync + Send {
|
||||
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>;
|
||||
|
||||
/// Returned vec is guaranteed to have len > 0
|
||||
fn get_certificate(
|
||||
&self,
|
||||
domain: &str,
|
||||
) -> Result<(PrivateKey, Vec<Certificate>), GetCertificateError>;
|
||||
}
|
||||
|
||||
struct ManagerImpl {
|
||||
@ -282,4 +290,12 @@ impl Manager for ManagerImpl {
|
||||
fn get_http01_challenge_key(&self, token: &str) -> Result<String, GetHttp01ChallengeKeyError> {
|
||||
self.store.get_http01_challenge_key(token)
|
||||
}
|
||||
|
||||
/// Returned vec is guaranteed to have len > 0
|
||||
fn get_certificate(
|
||||
&self,
|
||||
domain: &str,
|
||||
) -> Result<(PrivateKey, Vec<Certificate>), GetCertificateError> {
|
||||
self.store.get_certificate(domain)
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
@ -117,8 +117,8 @@ impl From<config::SetError> for SyncWithConfigError {
|
||||
|
||||
pub type GetAcmeHttp01ChallengeKeyError = acme::manager::GetHttp01ChallengeKeyError;
|
||||
|
||||
#[mockall::automock]
|
||||
pub trait Manager: Sync + Send {
|
||||
//#[mockall::automock]
|
||||
pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert {
|
||||
fn get_config(&self, domain: &domain::Name) -> Result<config::Config, GetConfigError>;
|
||||
|
||||
fn get_origin(
|
||||
@ -272,3 +272,35 @@ impl Manager for ManagerImpl {
|
||||
self.domain_config_store.all_domains()
|
||||
}
|
||||
}
|
||||
|
||||
impl rustls::server::ResolvesServerCert for ManagerImpl {
|
||||
fn resolve(
|
||||
&self,
|
||||
client_hello: rustls::server::ClientHello<'_>,
|
||||
) -> Option<sync::Arc<rustls::sign::CertifiedKey>> {
|
||||
let domain = client_hello.server_name()?;
|
||||
|
||||
match self.acme_manager.as_ref()?.get_certificate(domain) {
|
||||
Err(acme::manager::GetCertificateError::NotFound) => {
|
||||
log::warn!("No cert found for domain {domain}");
|
||||
Ok(None)
|
||||
}
|
||||
Err(acme::manager::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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
16
src/main.rs
16
src/main.rs
@ -1,3 +1,5 @@
|
||||
#![feature(trait_upcasting)]
|
||||
|
||||
use clap::Parser;
|
||||
use futures::stream::StreamExt;
|
||||
use signal_hook_tokio::Signals;
|
||||
@ -61,7 +63,6 @@ struct Cli {
|
||||
#[derive(Clone)]
|
||||
struct HTTPSParams {
|
||||
https_listen_addr: SocketAddr,
|
||||
domain_acme_store: sync::Arc<dyn domiply::domain::acme::store::Store>,
|
||||
domain_acme_manager: sync::Arc<dyn domiply::domain::acme::manager::Manager>,
|
||||
}
|
||||
|
||||
@ -101,16 +102,13 @@ async fn main() {
|
||||
// settings.
|
||||
let domain_acme_contact_email = config.domain_acme_contact_email.unwrap();
|
||||
|
||||
let domain_acme_manager = domiply::domain::acme::manager::new(
|
||||
domain_acme_store.clone(),
|
||||
&domain_acme_contact_email,
|
||||
)
|
||||
.await
|
||||
.expect("domain acme manager initialization failed");
|
||||
let domain_acme_manager =
|
||||
domiply::domain::acme::manager::new(domain_acme_store, &domain_acme_contact_email)
|
||||
.await
|
||||
.expect("domain acme manager initialization failed");
|
||||
|
||||
Some(HTTPSParams {
|
||||
https_listen_addr,
|
||||
domain_acme_store,
|
||||
domain_acme_manager,
|
||||
})
|
||||
} else {
|
||||
@ -136,7 +134,7 @@ async fn main() {
|
||||
config.http_domain.clone(),
|
||||
https_params.map(|p| domiply::service::http::HTTPSParams {
|
||||
listen_addr: p.https_listen_addr,
|
||||
cert_resolver: domiply::domain::acme::resolver::new(p.domain_acme_store),
|
||||
cert_resolver: domain_manager.clone(),
|
||||
}),
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user