use std::path::PathBuf;
use std::sync::Arc;
use rustls_acme::AcmeConfig as RustlsAcmeConfig;
use rustls_acme::caches::DirCache;
use crate::RuntimeError;
use crate::config::AcmeBase;
pub use rustls_acme::AcmeState;
#[derive(Debug, Clone)]
pub struct AcmeConfig {
base: AcmeBase,
}
impl AcmeConfig {
pub fn new(tool_name: &str, domains: impl IntoIterator<Item = impl Into<Box<str>>>) -> Self {
Self {
base: AcmeBase::new(tool_name, domains),
}
}
pub fn email(mut self, email: impl Into<Box<str>>) -> Self {
self.base = self.base.email(email);
self
}
pub fn cache_dir(mut self, path: impl Into<PathBuf>) -> Self {
self.base = self.base.cache_dir(path);
self
}
pub fn staging(mut self, staging: bool) -> Self {
self.base = self.base.staging(staging);
self
}
pub fn cache_path(&self) -> &std::path::Path {
self.base.cache_path()
}
pub fn build(
self,
) -> Result<
(
Arc<rustls::ServerConfig>,
rustls_acme::AcmeState<std::io::Error>,
),
RuntimeError,
> {
let domain_strings: Vec<String> = self.base.domains.iter().map(|d| d.to_string()).collect();
let mut acme_cfg = RustlsAcmeConfig::new(domain_strings)
.cache(DirCache::new(self.base.cache_dir))
.directory_lets_encrypt(!self.base.staging);
if let Some(email) = &self.base.email {
acme_cfg = acme_cfg.contact_push(format!("mailto:{email}"));
}
let state = acme_cfg.state();
let resolver = state.resolver();
let mut server_config = rustls::ServerConfig::builder_with_provider(Arc::new(
rustls::crypto::aws_lc_rs::default_provider(),
))
.with_safe_default_protocol_versions()
.map_err(|e| {
RuntimeError::Tls(format!("failed to configure TLS protocol versions: {e}").into())
})?
.with_no_client_auth()
.with_cert_resolver(resolver);
server_config.alpn_protocols = vec![
rustls_acme::acme::ACME_TLS_ALPN_NAME.to_vec(),
b"h2".to_vec(),
b"http/1.1".to_vec(),
];
Ok((Arc::new(server_config), state))
}
}