Skip to content

Commit 08a28ce

Browse files
committed
perf(dgw): caching of system store certificates
Basic caching is implemented for certificates fetched from the system store reducing considerably the number of system calls. The lifetime is 45 seconds, so the certificate is still refreshed on a regular basis. Issue: DGW-262
1 parent d8b2fdf commit 08a28ce

1 file changed

Lines changed: 33 additions & 4 deletions

File tree

devolutions-gateway/src/tls.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ pub mod windows {
106106
use std::sync::Arc;
107107

108108
use anyhow::Context as _;
109+
use parking_lot::Mutex;
109110
use rustls_cng::signer::CngSigningKey;
110111
use rustls_cng::store::{CertStore, CertStoreType};
111112
use tokio_rustls::rustls::pki_types::CertificateDer;
@@ -114,12 +115,21 @@ pub mod windows {
114115

115116
use crate::config::dto;
116117

118+
const CACHE_DURATION: time::Duration = time::Duration::seconds(45);
119+
117120
#[derive(Debug)]
118121
pub struct ServerCertResolver {
119122
machine_hostname: String,
120123
subject_name: String,
121124
store_type: CertStoreType,
122125
store_name: String,
126+
cached_key: Mutex<Option<KeyCache>>,
127+
}
128+
129+
#[derive(Debug)]
130+
struct KeyCache {
131+
key: Arc<CertifiedKey>,
132+
expires_at: time::OffsetDateTime,
123133
}
124134

125135
impl ServerCertResolver {
@@ -140,6 +150,7 @@ pub mod windows {
140150
subject_name: cert_subject_name,
141151
store_type,
142152
store_name,
153+
cached_key: Mutex::new(None),
143154
})
144155
}
145156

@@ -170,10 +181,20 @@ pub mod windows {
170181
)
171182
}
172183

184+
let mut cache_guard = self.cached_key.lock();
185+
186+
let now = time::OffsetDateTime::now_utc();
187+
188+
if let Some(cache) = cache_guard.as_ref() {
189+
if now < cache.expires_at {
190+
trace!("Used certified key from cache");
191+
return Ok(Arc::clone(&cache.key));
192+
}
193+
}
194+
173195
let store = CertStore::open(self.store_type, &self.store_name).context("open Windows certificate store")?;
174196

175197
// Look up certificate by subject.
176-
// TODO(perf): the resolution result could probably be cached.
177198
let contexts = store.find_by_subject_str(&self.subject_name).with_context(|| {
178199
format!(
179200
"failed to find server certificate for {} from system store",
@@ -284,12 +305,20 @@ pub mod windows {
284305
.context("certification chain is not available for this certificate")?;
285306
let certs = chain.into_iter().map(CertificateDer::from).collect();
286307

287-
// Return CertifiedKey instance.
288-
return Ok(Arc::new(CertifiedKey {
308+
let key = Arc::new(CertifiedKey {
289309
cert: certs,
290310
key: Arc::new(key),
291311
ocsp: None,
292-
}));
312+
});
313+
314+
*cache_guard = Some(KeyCache {
315+
key: key.clone(),
316+
expires_at: now + CACHE_DURATION,
317+
});
318+
trace!("Cached certified key");
319+
320+
// Return CertifiedKey instance.
321+
return Ok(key);
293322

294323
struct CertHandleCtx {
295324
idx: usize,

0 commit comments

Comments
 (0)