ferron/
acme.rs

1use std::{
2  collections::HashMap,
3  error::Error,
4  future::Future,
5  net::IpAddr,
6  ops::{Deref, Sub},
7  path::PathBuf,
8  pin::Pin,
9  sync::Arc,
10  time::{Duration, SystemTime},
11};
12
13use base64::Engine;
14use bytes::Bytes;
15use hyper::Request;
16use hyper_util::client::legacy::Client as HyperClient;
17use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor};
18use instant_acme::{
19  Account, AccountCredentials, AuthorizationStatus, BodyWrapper, BytesResponse, CertificateIdentifier, ChallengeType,
20  ExternalAccountKey, HttpClient, Identifier, NewAccount, NewOrder, OrderStatus, RenewalInfo, RetryPolicy,
21};
22use rcgen::{CertificateParams, CustomExtension, KeyPair};
23use rustls::{
24  crypto::CryptoProvider,
25  server::{ClientHello, ResolvesServerCert},
26  sign::CertifiedKey,
27  ClientConfig,
28};
29use rustls_pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer};
30use serde::{Deserialize, Serialize};
31use tokio::{io::AsyncWriteExt, sync::RwLock, time::Instant};
32use x509_parser::prelude::{FromDer, X509Certificate};
33use xxhash_rust::xxh3::xxh3_128;
34
35use crate::util::SniResolverLock;
36use ferron_common::dns::DnsProvider;
37use ferron_common::logging::ErrorLogger;
38
39pub const ACME_TLS_ALPN_NAME: &[u8] = b"acme-tls/1";
40const SECONDS_BEFORE_RENEWAL: u64 = 86400; // 1 day before expiration
41
42pub type TlsAlpn01DataLock = Arc<RwLock<Option<(Arc<CertifiedKey>, String)>>>;
43pub type Http01DataLock = Arc<RwLock<Option<(String, String)>>>;
44
45/// Represents the configuration for the ACME client.
46pub struct AcmeConfig {
47  /// The Rustls client configuration to use for ACME communication.
48  pub rustls_client_config: ClientConfig,
49  /// The domains for which to request certificates.
50  pub domains: Vec<String>,
51  /// The type of challenge to use for ACME certificate issuance.
52  pub challenge_type: ChallengeType,
53  /// The contact information for the ACME account.
54  pub contact: Vec<String>,
55  /// The directory URL for the ACME server.
56  pub directory: String,
57  /// The optional EAB key
58  pub eab_key: Option<Arc<ExternalAccountKey>>,
59  /// The optional ACME profile name
60  pub profile: Option<String>,
61  /// The cache for storing ACME account information.
62  pub account_cache: AcmeCache,
63  /// The cache for storing ACME certificate information.
64  pub certificate_cache: AcmeCache,
65  /// The lock for managing the certified key.
66  pub certified_key_lock: Arc<RwLock<Option<Arc<CertifiedKey>>>>,
67  /// The lock for managing the TLS-ALPN-01 data.
68  pub tls_alpn_01_data_lock: TlsAlpn01DataLock,
69  /// The lock for managing the HTTP-01 data.
70  pub http_01_data_lock: Http01DataLock,
71  /// The ACME DNS provider.
72  pub dns_provider: Option<Arc<dyn DnsProvider + Send + Sync>>,
73  /// The certificate renewal information.
74  pub renewal_info: Option<(RenewalInfo, Instant)>,
75  /// The ACME account information
76  pub account: Option<Account>,
77  /// The paths to TLS certificate and private key files to save the obtained certificate and private key.
78  pub save_paths: Option<(PathBuf, PathBuf)>,
79  /// The command to execute after certificates and private key are obtained,
80  /// with environment variables `FERRON_ACME_DOMAIN`, `FERRON_ACME_CERT_PATH` and `FERRON_ACME_KEY_PATH` set.
81  pub post_obtain_command: Option<String>,
82}
83
84/// Represents the type of cache to use for storing ACME data.
85pub enum AcmeCache {
86  /// Use an in-memory cache.
87  Memory(Arc<RwLock<HashMap<String, Vec<u8>>>>),
88  /// Use a file-based cache.
89  File(PathBuf),
90}
91
92impl AcmeCache {
93  /// Gets data from the cache.
94  async fn get(&self, key: &str) -> Option<Vec<u8>> {
95    match self {
96      AcmeCache::Memory(cache) => cache.read().await.get(key).cloned(),
97      AcmeCache::File(path) => tokio::fs::read(path.join(key)).await.ok(),
98    }
99  }
100
101  /// Sets data in the cache.
102  async fn set(&self, key: &str, value: Vec<u8>) -> Result<(), std::io::Error> {
103    match self {
104      AcmeCache::Memory(cache) => {
105        cache.write().await.insert(key.to_string(), value);
106        Ok(())
107      }
108      AcmeCache::File(path) => {
109        tokio::fs::create_dir_all(path).await.unwrap_or_default();
110        let mut open_options = tokio::fs::OpenOptions::new();
111        open_options.write(true).create(true).truncate(true);
112
113        #[cfg(unix)]
114        open_options.mode(0o600); // Don't allow others to read or write
115
116        let mut file = open_options.open(path.join(key)).await?;
117        file.write_all(&value).await?;
118        file.flush().await.unwrap_or_default();
119
120        Ok(())
121      }
122    }
123  }
124
125  /// Removes data from the cache.
126  async fn remove(&self, key: &str) {
127    match self {
128      AcmeCache::Memory(cache) => {
129        cache.write().await.remove(key);
130      }
131      AcmeCache::File(path) => {
132        let _ = tokio::fs::remove_file(path.join(key)).await;
133      }
134    }
135  }
136}
137
138#[derive(Serialize, Deserialize)]
139struct CertificateCacheData {
140  certificate_chain_pem: String,
141  private_key_pem: String,
142}
143
144/// Represents the on-demand configuration for the ACME client.
145pub struct AcmeOnDemandConfig {
146  /// The Rustls client configuration to use for ACME communication.
147  pub rustls_client_config: ClientConfig,
148  /// The type of challenge to use for ACME certificate issuance.
149  pub challenge_type: ChallengeType,
150  /// The contact information for the ACME account.
151  pub contact: Vec<String>,
152  /// The directory URL for the ACME server.
153  pub directory: String,
154  /// The optional EAB key
155  pub eab_key: Option<Arc<ExternalAccountKey>>,
156  /// The optional ACME profile name
157  pub profile: Option<String>,
158  /// The path to the cache directory for storing ACME information.
159  pub cache_path: Option<PathBuf>,
160  /// The lock for managing the SNI resolver.
161  pub sni_resolver_lock: SniResolverLock,
162  /// The lock for managing the TLS-ALPN-01 resolver.
163  pub tls_alpn_01_resolver_lock: Arc<RwLock<Vec<TlsAlpn01DataLock>>>,
164  /// The lock for managing the HTTP-01 resolver.
165  pub http_01_resolver_lock: Arc<RwLock<Vec<Http01DataLock>>>,
166  /// The ACME DNS provider.
167  pub dns_provider: Option<Arc<dyn DnsProvider + Send + Sync>>,
168  /// The SNI hostname.
169  pub sni_hostname: Option<String>,
170  /// The port to use for ACME communication.
171  pub port: u16,
172}
173
174/// Checks if the TLS certificate is valid
175fn check_certificate_validity(
176  certificate: &CertificateDer,
177  renewal_info: Option<&RenewalInfo>,
178) -> Result<bool, Box<dyn Error + Send + Sync>> {
179  if let Some(renewal_info) = renewal_info {
180    return Ok(SystemTime::now() < renewal_info.suggested_window.start);
181  }
182  let (_, x509_certificate) = X509Certificate::from_der(certificate)?;
183  let validity = x509_certificate.validity();
184  if let Some(time_to_expiration) = validity.time_to_expiration() {
185    let time_before_expiration = if let Some(valid_duration) = validity.not_after.sub(validity.not_before) {
186      (valid_duration.whole_seconds().unsigned_abs() / 2).min(SECONDS_BEFORE_RENEWAL)
187    } else {
188      SECONDS_BEFORE_RENEWAL
189    };
190    if time_to_expiration >= Duration::from_secs(time_before_expiration) {
191      return Ok(true);
192    }
193  }
194  Ok(false)
195}
196
197/// Determines the account cache key
198fn get_account_cache_key(config: &AcmeConfig) -> String {
199  format!(
200    "account_{}",
201    base64::engine::general_purpose::URL_SAFE_NO_PAD
202      .encode(xxh3_128(format!("{};{}", &config.contact.join(","), &config.directory).as_bytes()).to_be_bytes())
203  )
204}
205
206/// Determines the certificate cache key
207fn get_certificate_cache_key(config: &AcmeConfig) -> String {
208  let mut domains = config.domains.clone();
209  domains.sort_unstable();
210  let domains_joined = domains.join(",");
211  format!(
212    "certificate_{}",
213    base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(
214      xxh3_128(
215        format!(
216          "{}{}",
217          domains_joined,
218          config.profile.as_ref().map_or("".to_string(), |p| format!(";{p}"))
219        )
220        .as_bytes()
221      )
222      .to_be_bytes()
223    )
224  )
225}
226
227/// Determines the account cache key
228fn get_hostname_cache_key(config: &AcmeOnDemandConfig) -> String {
229  format!(
230    "hostname_{}",
231    base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(
232      xxh3_128(
233        format!(
234          "{}{}",
235          &config.port,
236          config.sni_hostname.as_ref().map_or("".to_string(), |h| format!(";{h}"))
237        )
238        .as_bytes()
239      )
240      .to_be_bytes()
241    )
242  )
243}
244
245/// Saves the obtained certificate and private key to files if the save paths are configured, and executes the post-obtain command if configured.
246async fn post_process_obtained_certificate(
247  config: &AcmeConfig,
248  certificate_pem: &str,
249  private_key_pem: &str,
250) -> Result<(), Box<dyn Error + Send + Sync>> {
251  if let Some((cert_path, key_path)) = &config.save_paths {
252    tokio::fs::write(cert_path, certificate_pem).await?;
253
254    let mut open_options = tokio::fs::OpenOptions::new();
255    open_options.write(true).create(true).truncate(true);
256
257    #[cfg(unix)]
258    open_options.mode(0o600); // Don't allow others to read or write the private key
259
260    let mut file = open_options.open(key_path).await?;
261    file.write_all(private_key_pem.as_bytes()).await?;
262    file.flush().await.unwrap_or_default();
263
264    if let Some(command) = &config.post_obtain_command {
265      tokio::process::Command::new(command)
266        .env("FERRON_ACME_DOMAIN", config.domains.join(","))
267        .env("FERRON_ACME_CERT_PATH", cert_path)
268        .env("FERRON_ACME_KEY_PATH", key_path)
269        .stdin(std::process::Stdio::null())
270        .stdout(std::process::Stdio::null())
271        .stderr(std::process::Stdio::null())
272        .spawn()?;
273    }
274  }
275
276  Ok(())
277}
278
279/// Checks if the TLS certificate (cached or live) is valid. If cached certificate is valid, installs the cached certificate
280pub async fn check_certificate_validity_or_install_cached(
281  config: &mut AcmeConfig,
282  acme_account: Option<&Account>,
283) -> Result<bool, Box<dyn Error + Send + Sync>> {
284  if let Some(certified_key) = config.certified_key_lock.read().await.as_deref() {
285    if let Some(certificate) = certified_key.cert.first() {
286      if let Some(acme_account) = acme_account {
287        if config
288          .renewal_info
289          .as_ref()
290          .is_none_or(|v| v.1.elapsed() > Duration::ZERO)
291        {
292          if let Ok(certificate_id) = CertificateIdentifier::try_from(certificate) {
293            if let Ok(renewal_info) = acme_account.renewal_info(&certificate_id).await {
294              let mut renewal_instant = Instant::now();
295              renewal_instant += renewal_info.1;
296              config.renewal_info = Some((renewal_info.0, renewal_instant));
297            }
298          }
299        }
300      }
301      if check_certificate_validity(certificate, config.renewal_info.as_ref().map(|i| &i.0))? {
302        return Ok(true);
303      }
304    }
305  }
306
307  let certificate_cache_key = get_certificate_cache_key(config);
308
309  if let Some(serialized_certificate_cache_data) = config.certificate_cache.get(&certificate_cache_key).await {
310    if let Ok(certificate_data) = serde_json::from_slice::<CertificateCacheData>(&serialized_certificate_cache_data) {
311      // Corrupted certificates would be skipped
312      if let Ok(certs) =
313        CertificateDer::pem_slice_iter(certificate_data.certificate_chain_pem.as_bytes()).collect::<Result<Vec<_>, _>>()
314      {
315        if let Some(certificate) = certs.first() {
316          if let Some(acme_account) = acme_account {
317            if config
318              .renewal_info
319              .as_ref()
320              .is_none_or(|v| v.1.elapsed() > Duration::ZERO)
321            {
322              if let Ok(certificate_id) = CertificateIdentifier::try_from(certificate) {
323                if let Ok(renewal_info) = acme_account.renewal_info(&certificate_id).await {
324                  let mut renewal_instant = Instant::now();
325                  renewal_instant += renewal_info.1;
326                  config.renewal_info = Some((renewal_info.0, renewal_instant));
327                }
328              }
329            }
330          }
331          if check_certificate_validity(certificate, config.renewal_info.as_ref().map(|i| &i.0))? {
332            // Corrupted private key would be skipped
333            if let Ok(private_key) = PrivateKeyDer::from_pem_slice(certificate_data.private_key_pem.as_bytes()) {
334              let signing_key = CryptoProvider::get_default()
335                .ok_or(anyhow::anyhow!("Cannot get default crypto provider"))?
336                .key_provider
337                .load_private_key(private_key)?;
338
339              *config.certified_key_lock.write().await = Some(Arc::new(CertifiedKey::new(certs, signing_key)));
340
341              let _ = post_process_obtained_certificate(
342                config,
343                &certificate_data.certificate_chain_pem,
344                &certificate_data.private_key_pem,
345              )
346              .await;
347
348              return Ok(true);
349            }
350          }
351        }
352      }
353    }
354  }
355
356  Ok(false)
357}
358
359/// Provisions TLS certificates using the ACME protocol.
360pub async fn provision_certificate(
361  config: &mut AcmeConfig,
362  error_logger: &ErrorLogger,
363) -> Result<(), Box<dyn Error + Send + Sync>> {
364  let account_cache_key = get_account_cache_key(config);
365  let certificate_cache_key = get_certificate_cache_key(config);
366  let mut had_cache_error = false;
367
368  let acme_account = if let Some(acme_account) = config.account.take() {
369    acme_account
370  } else {
371    let acme_account_builder =
372      Account::builder_with_http(Box::new(HttpsClientForAcme::new(config.rustls_client_config.clone())));
373
374    if let Some(account_credentials) = config
375      .account_cache
376      .get(&account_cache_key)
377      .await
378      .and_then(|c| serde_json::from_slice::<AccountCredentials>(&c).ok())
379    {
380      acme_account_builder.from_credentials(account_credentials).await?
381    } else {
382      let (account, account_credentials) = acme_account_builder
383        .create(
384          &NewAccount {
385            contact: config.contact.iter().map(|s| s.deref()).collect::<Vec<_>>().as_slice(),
386            terms_of_service_agreed: true,
387            only_return_existing: false,
388          },
389          config.directory.clone(),
390          config.eab_key.as_deref(),
391        )
392        .await?;
393
394      if let Err(err) = config
395        .account_cache
396        .set(&account_cache_key, serde_json::to_vec(&account_credentials)?)
397        .await
398      {
399        if !had_cache_error {
400          error_logger
401            .log(&format!(
402              "Failed to access the ACME cache: {}. Ferron can't use ACME caching",
403              err
404            ))
405            .await;
406          had_cache_error = true;
407        }
408      }
409
410      account
411    }
412  };
413
414  if check_certificate_validity_or_install_cached(config, Some(&acme_account)).await? {
415    // Certificate is still valid, no need to renew
416    config.account.replace(acme_account);
417    return Ok(());
418  }
419
420  let acme_identifiers_vec = config
421    .domains
422    .iter()
423    .map(|s| {
424      if let Ok(ip) = s.parse::<IpAddr>() {
425        Identifier::Ip(ip)
426      } else {
427        Identifier::Dns(s.to_string())
428      }
429    })
430    .collect::<Vec<_>>();
431
432  let mut acme_new_order = NewOrder::new(&acme_identifiers_vec);
433  if let Some(profile) = &config.profile {
434    acme_new_order = acme_new_order.profile(profile);
435  }
436
437  let mut acme_order = match acme_account.new_order(&acme_new_order).await {
438    Ok(order) => order,
439    Err(instant_acme::Error::Api(problem)) => {
440      if problem.r#type.as_deref() == Some("urn:ietf:params:acme:error:accountDoesNotExist") {
441        // Remove non-existent account from the cache
442        config.account_cache.remove(&account_cache_key).await;
443      }
444      Err(instant_acme::Error::Api(problem))?
445    }
446    Err(err) => Err(err)?,
447  };
448  let mut dns_01_identifiers = Vec::new();
449  let mut acme_authorizations = acme_order.authorizations();
450  while let Some(acme_authorization) = acme_authorizations.next().await {
451    let mut acme_authorization = acme_authorization?;
452    match acme_authorization.status {
453      AuthorizationStatus::Pending => {}
454      AuthorizationStatus::Valid => continue,
455      _ => Err(anyhow::anyhow!("Invalid ACME authorization status"))?,
456    }
457
458    let mut challenge = acme_authorization
459      .challenge(config.challenge_type.clone())
460      .ok_or(anyhow::anyhow!(
461        "The ACME server doesn't support the requested challenge type"
462      ))?;
463
464    let identifier = match challenge.identifier().identifier {
465      Identifier::Dns(identifier) => identifier.to_string(),
466      Identifier::Ip(ip) => ip.to_string(),
467      _ => Err(anyhow::anyhow!("Unsupported ACME identifier type",))?,
468    };
469
470    let key_authorization = challenge.key_authorization();
471    match config.challenge_type {
472      ChallengeType::TlsAlpn01 => {
473        let mut params = CertificateParams::new(vec![identifier.clone()])?;
474        params.custom_extensions.push(CustomExtension::new_acme_identifier(
475          key_authorization.digest().as_ref(),
476        ));
477        let key_pair = KeyPair::generate()?;
478        let certificate = params.self_signed(&key_pair)?;
479        let private_key = PrivateKeyDer::try_from(key_pair.serialize_der())?;
480
481        let signing_key = CryptoProvider::get_default()
482          .ok_or(anyhow::anyhow!("Cannot get default crypto provider"))?
483          .key_provider
484          .load_private_key(private_key)?;
485
486        *config.tls_alpn_01_data_lock.write().await = Some((
487          Arc::new(CertifiedKey::new(vec![certificate.der().to_owned()], signing_key)),
488          identifier.clone(),
489        ));
490      }
491      ChallengeType::Http01 => {
492        let key_auth_value = key_authorization.as_str();
493        *config.http_01_data_lock.write().await = Some((challenge.token.clone(), key_auth_value.to_string()));
494      }
495      ChallengeType::Dns01 => {
496        if let Some(dns_provider) = &config.dns_provider {
497          dns_provider
498            .remove_acme_txt_record(&identifier)
499            .await
500            .unwrap_or_default();
501          dns_provider
502            .set_acme_txt_record(&identifier, &key_authorization.dns_value())
503            .await?;
504          // Wait for DNS propagation
505          tokio::time::sleep(Duration::from_secs(60)).await;
506          dns_01_identifiers.push(identifier.clone());
507        } else {
508          Err(anyhow::anyhow!("No DNS provider configured."))?;
509        }
510      }
511      _ => (),
512    }
513
514    challenge.set_ready().await?;
515  }
516
517  let acme_order_status = acme_order.poll_ready(&RetryPolicy::default()).await?;
518  match acme_order_status {
519    OrderStatus::Ready => (), // It's alright!
520    OrderStatus::Invalid => Err(anyhow::anyhow!("ACME order is invalid"))?,
521    _ => Err(anyhow::anyhow!("ACME order is not ready"))?,
522  }
523
524  let finalize_closure = async {
525    let private_key_pem = acme_order.finalize().await?;
526    let certificate_chain_pem = acme_order.poll_certificate(&RetryPolicy::default()).await?;
527
528    if let Err(err) = post_process_obtained_certificate(config, &certificate_chain_pem, &private_key_pem).await {
529      error_logger
530        .log(&format!(
531          "Failed to save or post-process the obtained certificate: {}",
532          err
533        ))
534        .await;
535    }
536
537    let certificate_cache_data = CertificateCacheData {
538      certificate_chain_pem: certificate_chain_pem.clone(),
539      private_key_pem: private_key_pem.clone(),
540    };
541
542    if let Err(err) = config
543      .certificate_cache
544      .set(&certificate_cache_key, serde_json::to_vec(&certificate_cache_data)?)
545      .await
546    {
547      if !had_cache_error {
548        error_logger
549          .log(&format!(
550            "Failed to access the ACME cache: {}. Ferron can't use ACME caching",
551            err
552          ))
553          .await;
554        had_cache_error = true;
555      }
556    }
557
558    let certs = CertificateDer::pem_slice_iter(certificate_chain_pem.as_bytes())
559      .collect::<Result<Vec<_>, _>>()
560      .map_err(|e| match e {
561        rustls_pki_types::pem::Error::Io(err) => err,
562        err => std::io::Error::other(err),
563      })?;
564    let private_key = (match PrivateKeyDer::from_pem_slice(private_key_pem.as_bytes()) {
565      Ok(private_key) => Ok(private_key),
566      Err(rustls_pki_types::pem::Error::Io(err)) => Err(err),
567      Err(err) => Err(std::io::Error::other(err)),
568    })?;
569
570    let signing_key = CryptoProvider::get_default()
571      .ok_or(anyhow::anyhow!("Cannot get default crypto provider"))?
572      .key_provider
573      .load_private_key(private_key)?;
574
575    config.account.replace(acme_account);
576
577    *config.certified_key_lock.write().await = Some(Arc::new(CertifiedKey::new(certs, signing_key)));
578
579    Ok::<_, Box<dyn Error + Send + Sync>>(())
580  };
581
582  let result = finalize_closure.await;
583
584  // Cleanup
585  match config.challenge_type {
586    ChallengeType::TlsAlpn01 => {
587      *config.tls_alpn_01_data_lock.write().await = None;
588    }
589    ChallengeType::Http01 => {
590      *config.http_01_data_lock.write().await = None;
591    }
592    ChallengeType::Dns01 => {
593      if let Some(dns_provider) = &config.dns_provider {
594        for identifier in dns_01_identifiers {
595          dns_provider
596            .remove_acme_txt_record(&identifier)
597            .await
598            .unwrap_or_default();
599        }
600      }
601    }
602    _ => {}
603  };
604
605  result?;
606
607  Ok(())
608}
609
610/// Obtains the list of domains for which `AcmeOnDemandConfig` was converted into `AcmeConfig` from cache.
611pub async fn get_cached_domains(config: &AcmeOnDemandConfig) -> Vec<String> {
612  if let Some(pathbuf) = config.cache_path.clone() {
613    let hostname_cache_key = get_hostname_cache_key(config);
614    let hostname_cache = AcmeCache::File(pathbuf);
615    let cache_data = hostname_cache.get(&hostname_cache_key).await;
616    if let Some(data) = cache_data {
617      serde_json::from_slice(&data).unwrap_or_default()
618    } else {
619      Vec::new()
620    }
621  } else {
622    Vec::new()
623  }
624}
625
626/// Adds the domain to the cache.
627pub async fn add_domain_to_cache(
628  config: &AcmeOnDemandConfig,
629  domain: &str,
630) -> Result<(), Box<dyn Error + Send + Sync>> {
631  if let Some(pathbuf) = config.cache_path.clone() {
632    let hostname_cache_key = get_hostname_cache_key(config);
633    let hostname_cache = AcmeCache::File(pathbuf);
634    let mut cached_domains = get_cached_domains(config).await;
635    cached_domains.push(domain.to_string());
636    let data = serde_json::to_vec(&cached_domains)?;
637    hostname_cache.set(&hostname_cache_key, data).await?;
638  }
639  Ok(())
640}
641
642/// Converts a `AcmeOnDemandConfig` into an `AcmeConfig`
643pub async fn convert_on_demand_config(
644  config: &AcmeOnDemandConfig,
645  sni_hostname: String,
646  memory_acme_account_cache_data: Arc<RwLock<HashMap<String, Vec<u8>>>>,
647) -> AcmeConfig {
648  let (account_cache_path, cert_cache_path) = if let Some(mut pathbuf) = config.cache_path.clone() {
649    let base_pathbuf = pathbuf.clone();
650    let append_hash = base64::engine::general_purpose::URL_SAFE_NO_PAD
651      .encode(xxh3_128(format!("{}-{sni_hostname}", config.port).as_bytes()).to_be_bytes());
652    pathbuf.push(append_hash);
653    (Some(base_pathbuf), Some(pathbuf))
654  } else {
655    (None, None)
656  };
657
658  let certified_key_lock = Arc::new(tokio::sync::RwLock::new(None));
659  let tls_alpn_01_data_lock = Arc::new(tokio::sync::RwLock::new(None));
660  let http_01_data_lock = Arc::new(tokio::sync::RwLock::new(None));
661
662  // Insert new locked data
663  config.sni_resolver_lock.write().await.insert(
664    sni_hostname.clone(),
665    Arc::new(AcmeResolver::new(certified_key_lock.clone())),
666  );
667  match config.challenge_type {
668    ChallengeType::TlsAlpn01 => {
669      config
670        .tls_alpn_01_resolver_lock
671        .write()
672        .await
673        .push(tls_alpn_01_data_lock.clone());
674    }
675    ChallengeType::Http01 => {
676      config
677        .http_01_resolver_lock
678        .write()
679        .await
680        .push(http_01_data_lock.clone());
681    }
682    _ => (),
683  };
684
685  AcmeConfig {
686    rustls_client_config: config.rustls_client_config.clone(),
687    domains: vec![sni_hostname],
688    challenge_type: config.challenge_type.clone(),
689    contact: config.contact.clone(),
690    directory: config.directory.clone(),
691    eab_key: config.eab_key.clone(),
692    profile: config.profile.clone(),
693    account_cache: if let Some(account_cache_path) = account_cache_path {
694      AcmeCache::File(account_cache_path)
695    } else {
696      AcmeCache::Memory(memory_acme_account_cache_data.clone())
697    },
698    certificate_cache: if let Some(cert_cache_path) = cert_cache_path {
699      AcmeCache::File(cert_cache_path)
700    } else {
701      AcmeCache::Memory(Arc::new(tokio::sync::RwLock::new(HashMap::new())))
702    },
703    certified_key_lock: certified_key_lock.clone(),
704    tls_alpn_01_data_lock: tls_alpn_01_data_lock.clone(),
705    http_01_data_lock: http_01_data_lock.clone(),
706    dns_provider: config.dns_provider.clone(),
707    renewal_info: None,
708    account: None,
709    save_paths: None,
710    post_obtain_command: None,
711  }
712}
713
714/// An ACME resolver resolving one certified key
715#[derive(Debug)]
716pub struct AcmeResolver {
717  certified_key_lock: Arc<RwLock<Option<Arc<CertifiedKey>>>>,
718}
719
720impl AcmeResolver {
721  /// Creates an ACME resolver
722  pub fn new(certified_key_lock: Arc<RwLock<Option<Arc<CertifiedKey>>>>) -> Self {
723    Self { certified_key_lock }
724  }
725}
726
727impl ResolvesServerCert for AcmeResolver {
728  fn resolve(&self, _client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
729    self.certified_key_lock.blocking_read().clone()
730  }
731}
732
733struct HttpsClientForAcme(HyperClient<hyper_rustls::HttpsConnector<HttpConnector>, BodyWrapper<Bytes>>);
734
735impl HttpsClientForAcme {
736  fn new(tls_config: ClientConfig) -> Self {
737    Self(
738      HyperClient::builder(TokioExecutor::new()).build(
739        hyper_rustls::HttpsConnectorBuilder::new()
740          .with_tls_config(tls_config)
741          .https_or_http()
742          .enable_http1()
743          .enable_http2()
744          .build(),
745      ),
746    )
747  }
748}
749
750impl HttpClient for HttpsClientForAcme {
751  fn request(
752    &self,
753    req: Request<BodyWrapper<Bytes>>,
754  ) -> Pin<Box<dyn Future<Output = Result<BytesResponse, instant_acme::Error>> + Send>> {
755    HttpClient::request(&self.0, req)
756  }
757}
758
759/// The TLS-ALPN-01 ACME challenge certificate resolver
760#[derive(Debug)]
761pub struct TlsAlpn01Resolver {
762  resolvers: Arc<tokio::sync::RwLock<Vec<TlsAlpn01DataLock>>>,
763}
764
765impl TlsAlpn01Resolver {
766  /// Creates a TLS-ALPN-01 resolver
767  #[allow(dead_code)]
768  pub fn new() -> Self {
769    Self {
770      resolvers: Arc::new(tokio::sync::RwLock::new(Vec::new())),
771    }
772  }
773
774  /// Creates a TLS-ALPN-01 resolver with provided resolver list lock
775  pub fn with_resolvers(resolvers: Arc<tokio::sync::RwLock<Vec<TlsAlpn01DataLock>>>) -> Self {
776    Self { resolvers }
777  }
778
779  /// Loads a certificate resolver lock
780  pub fn load_resolver(&self, resolver: TlsAlpn01DataLock) {
781    self.resolvers.blocking_write().push(resolver);
782  }
783}
784
785impl ResolvesServerCert for TlsAlpn01Resolver {
786  fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
787    let hostname = client_hello.server_name().map(|hn| hn.strip_suffix('.').unwrap_or(hn));
788
789    // If blocking_read() method is used when only Tokio is used, the program would panic on resolving a TLS certificate.
790    #[cfg(feature = "runtime-monoio")]
791    let resolver_locks = self.resolvers.blocking_read();
792    #[cfg(feature = "runtime-tokio")]
793    let resolver_locks = futures_executor::block_on(async { self.resolvers.read().await });
794
795    for resolver_lock in &*resolver_locks {
796      if let Some(hostname) = hostname {
797        #[cfg(feature = "runtime-monoio")]
798        let resolver_data = resolver_lock.blocking_read().clone();
799        #[cfg(feature = "runtime-tokio")]
800        let resolver_data = futures_executor::block_on(async { resolver_lock.read().await }).clone();
801        if let Some(resolver_data) = resolver_data {
802          let (cert, host) = resolver_data;
803          if host == hostname {
804            return Some(cert);
805          }
806        }
807      }
808    }
809    None
810  }
811}