[pbs-devel] [PATCH proxmox 3/3] proxmox-access-control: add TTL window to token secret cache
Samuel Rufinatscha
s.rufinatscha at proxmox.com
Fri Dec 5 14:25:59 CET 2025
Verify_secret() currently calls refresh_cache_if_file_changed() on every
request, which performs a metadata() call on token.shadow each time.
Under load this adds unnecessary overhead, considering also the file
should rarely change.
This patch introduces a TTL boundary, controlled by
TOKEN_SECRET_CACHE_TTL_SECS. File metadata is only re-loaded once the
TTL has expired.
Signed-off-by: Samuel Rufinatscha <s.rufinatscha at proxmox.com>
---
proxmox-access-control/src/token_shadow.rs | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/proxmox-access-control/src/token_shadow.rs b/proxmox-access-control/src/token_shadow.rs
index d08fb06a..885e629d 100644
--- a/proxmox-access-control/src/token_shadow.rs
+++ b/proxmox-access-control/src/token_shadow.rs
@@ -9,6 +9,7 @@ use serde_json::{from_value, Value};
use proxmox_auth_api::types::Authid;
use proxmox_product_config::{open_api_lockfile, replace_config, ApiLockGuard};
+use proxmox_time::epoch_i64;
use crate::init::impl_feature::{token_shadow, token_shadow_lock};
@@ -18,6 +19,8 @@ use crate::init::impl_feature::{token_shadow, token_shadow_lock};
/// subsequent authentications for the same token+secret combination, avoiding
/// recomputing the password hash on every request.
static TOKEN_SECRET_CACHE: OnceLock<RwLock<ApiTokenSecretCache>> = OnceLock::new();
+/// Max age in seconds of the token secret cache before checking for file changes.
+const TOKEN_SECRET_CACHE_TTL_SECS: i64 = 60;
// Get exclusive lock
fn lock_config() -> Result<ApiLockGuard, Error> {
@@ -44,6 +47,15 @@ fn write_file(data: HashMap<Authid, String>) -> Result<(), Error> {
fn refresh_cache_if_file_changed() -> Result<(), Error> {
let mut cache = token_secret_cache().write().unwrap();
+ let now = epoch_i64();
+
+ // Fast path: Within TTL boundary
+ if let Some(last) = cache.last_checked {
+ if now - last < TOKEN_SECRET_CACHE_TTL_SECS {
+ return Ok(());
+ }
+ }
+
// Fetch the current token.shadow metadata
let (new_mtime, new_len) = match fs::metadata(token_shadow().as_path()) {
Ok(meta) => (meta.modified().ok(), Some(meta.len())),
@@ -60,6 +72,7 @@ fn refresh_cache_if_file_changed() -> Result<(), Error> {
cache.secrets.clear();
cache.file_mtime = new_mtime;
cache.file_len = new_len;
+ cache.last_checked = Some(now);
Ok(())
}
@@ -150,6 +163,8 @@ struct ApiTokenSecretCache {
file_mtime: Option<SystemTime>,
// shadow file length to detect changes
file_len: Option<u64>,
+ // last time the file metadata was checked
+ last_checked: Option<i64>,
}
fn token_secret_cache() -> &'static RwLock<ApiTokenSecretCache> {
@@ -158,6 +173,7 @@ fn token_secret_cache() -> &'static RwLock<ApiTokenSecretCache> {
secrets: HashMap::new(),
file_mtime: None,
file_len: None,
+ last_checked: None,
})
})
}
--
2.47.3
More information about the pbs-devel
mailing list