iroh_relay/server/
resolver.rs1use std::{path::PathBuf, sync::Arc};
2
3use n0_error::{AnyError, StdResultExt};
4use n0_future::{
5 task::{self, AbortOnDropHandle},
6 time::{self, Duration},
7};
8use reloadable_state::Reloadable;
9use rustls::{
10 crypto::CryptoProvider,
11 server::{ClientHello, ResolvesServerCert},
12 sign::CertifiedKey,
13};
14use rustls_cert_file_reader::{FileReader, Format};
15use rustls_cert_reloadable_resolver::{CertifiedKeyLoader, key_provider::Dyn};
16use tokio_util::sync::CancellationToken;
17
18pub const DEFAULT_CERT_RELOAD_INTERVAL: Duration = Duration::from_secs(60 * 60 * 24);
20
21pub async fn reloading_resolver(
38 crypto_provider: &CryptoProvider,
39 cert_path: PathBuf,
40 key_path: PathBuf,
41 interval: std::time::Duration,
42) -> Result<Arc<dyn ResolvesServerCert>, AnyError> {
43 let key_reader = FileReader::new(key_path, Format::PEM);
44 let certs_reader = FileReader::new(cert_path, Format::PEM);
45 let loader = CertifiedKeyLoader {
46 key_provider: Dyn(crypto_provider.key_provider),
47 key_reader,
48 certs_reader,
49 };
50 let resolver = ReloadingResolver::init(loader, interval)
51 .await
52 .std_context("cert loading")?;
53 Ok(Arc::new(resolver))
54}
55
56#[derive(Debug)]
58struct ReloadingResolver<Loader: Send + 'static> {
59 reloadable: Arc<Reloadable<CertifiedKey, Loader>>,
61 _handle: AbortOnDropHandle<()>,
63}
64
65impl<Loader> ReloadingResolver<Loader>
66where
67 Loader: Send + reloadable_state::core::Loader<Value = CertifiedKey> + 'static,
68{
69 async fn init(loader: Loader, interval: Duration) -> Result<Self, Loader::Error> {
71 let (reloadable, _) = Reloadable::init_load(loader).await?;
72 let reloadable = Arc::new(reloadable);
73
74 let cancel_token = CancellationToken::new();
75
76 let _reloadable = reloadable.clone();
78 let _cancel_token = cancel_token.clone();
79 let _handle = task::spawn(async move {
80 let mut interval = time::interval(interval);
81 loop {
82 tokio::select! {
83 _ = interval.tick() => {
84 let _ = _reloadable.reload().await;
85 tracing::info!("Reloaded the certificate");
86 },
87 _ = _cancel_token.cancelled() => {
88 tracing::trace!("shutting down");
89 break;
90 }
91 }
92 }
93 });
94 let _handle = AbortOnDropHandle::new(_handle);
95
96 Ok(Self {
97 reloadable,
98 _handle,
99 })
100 }
101}
102
103impl<Loader> ResolvesServerCert for ReloadingResolver<Loader>
104where
105 Loader: reloadable_state::core::Loader<Value = CertifiedKey>,
106 Loader: Send,
107 Loader: std::fmt::Debug,
108{
109 fn resolve(&self, _client_hello: ClientHello) -> Option<Arc<CertifiedKey>> {
110 Some(self.reloadable.get())
111 }
112}
113
114impl<Loader: Send> std::ops::Deref for ReloadingResolver<Loader> {
115 type Target = Reloadable<CertifiedKey, Loader>;
116
117 fn deref(&self) -> &Self::Target {
118 &self.reloadable
119 }
120}