all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Robert Obkircher <r.obkircher@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [PATCH v1 proxmox-backup] config: invalidate CachedUserInfo after ACL changes
Date: Tue, 18 Aug 2026 15:34:01 +0200	[thread overview]
Message-ID: <20260818133556.298498-1-r.obkircher@proxmox.com> (raw)

The ACL tree was cached for up to 5 seconds in CachedUserInfo, even
when the configuration was modified. This forced automated scripts to
wait for an arbitrary amount of time, and it was an unnecessary
security risk to keep revoked permissions for this long.

Add a generation counter for acl.cfg and bump it upon save to force
subsequent requests to reload the file. A separate counter was chosen
for clarity, though incrementing the user_cache_generation would have
worked as well.

Fixes: https://forum.proxmox.com/threads/185745
Signed-off-by: Robert Obkircher <r.obkircher@proxmox.com>
---
 pbs-config/src/acl.rs                  | 11 +++++++++--
 pbs-config/src/cached_user_info.rs     |  5 +++++
 pbs-config/src/config_version_cache.rs | 18 ++++++++++++++++++
 3 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/pbs-config/src/acl.rs b/pbs-config/src/acl.rs
index 8612abed7..584238cde 100644
--- a/pbs-config/src/acl.rs
+++ b/pbs-config/src/acl.rs
@@ -11,7 +11,7 @@ use proxmox_schema::{ApiStringFormat, ApiType, Schema, StringSchema};
 
 use pbs_api_types::{Authid, ROLE_NAME_NO_ACCESS, Role, Userid};
 
-use crate::{BackupLockGuard, open_backup_lockfile};
+use crate::{BackupLockGuard, ConfigVersionCache, open_backup_lockfile};
 
 /// Map of pre-defined [Roles](Role) to their associated
 /// [privileges](pbs_api_types::PRIVILEGES) combination and description.
@@ -768,7 +768,14 @@ pub fn save_config(acl: &AclTree) -> Result<(), Error> {
 
     acl.write_config(&mut raw)?;
 
-    replace_privileged_config(ACL_CFG_FILENAME, &raw)
+    replace_privileged_config(ACL_CFG_FILENAME, &raw)?;
+
+    // increase acl version
+    // We use this in CachedUserInfo
+    let version_cache = ConfigVersionCache::new()?;
+    version_cache.increase_acl_cache_generation();
+
+    Ok(())
 }
 
 #[cfg(test)]
diff --git a/pbs-config/src/cached_user_info.rs b/pbs-config/src/cached_user_info.rs
index 0c1c1ed6d..511c8f160 100644
--- a/pbs-config/src/cached_user_info.rs
+++ b/pbs-config/src/cached_user_info.rs
@@ -23,6 +23,7 @@ struct ConfigCache {
     data: Option<Arc<CachedUserInfo>>,
     last_update: i64,
     last_user_cache_generation: usize,
+    last_acl_cache_generation: usize,
 }
 
 static CACHED_CONFIG: LazyLock<RwLock<ConfigCache>> = LazyLock::new(|| {
@@ -30,6 +31,7 @@ static CACHED_CONFIG: LazyLock<RwLock<ConfigCache>> = LazyLock::new(|| {
         data: None,
         last_update: 0,
         last_user_cache_generation: 0,
+        last_acl_cache_generation: 0,
     })
 });
 
@@ -40,11 +42,13 @@ impl CachedUserInfo {
 
         let version_cache = ConfigVersionCache::new()?;
         let user_cache_generation = version_cache.user_cache_generation();
+        let acl_cache_generation = version_cache.acl_cache_generation();
 
         {
             // limit scope
             let cache = CACHED_CONFIG.read().unwrap();
             if (user_cache_generation == cache.last_user_cache_generation)
+                && (acl_cache_generation == cache.last_acl_cache_generation)
                 && ((now - cache.last_update) < 5)
             {
                 if let Some(ref config) = cache.data {
@@ -61,6 +65,7 @@ impl CachedUserInfo {
         let mut cache = CACHED_CONFIG.write().unwrap();
         cache.last_update = now;
         cache.last_user_cache_generation = user_cache_generation;
+        cache.last_acl_cache_generation = acl_cache_generation;
         cache.data = Some(config.clone());
 
         Ok(config)
diff --git a/pbs-config/src/config_version_cache.rs b/pbs-config/src/config_version_cache.rs
index 5053f6dc6..600b7e0a1 100644
--- a/pbs-config/src/config_version_cache.rs
+++ b/pbs-config/src/config_version_cache.rs
@@ -29,6 +29,8 @@ struct ConfigVersionCacheDataInner {
     datastore_generation: AtomicUsize,
     // Token shadow (token.shadow) generation/version.
     token_shadow_generation: AtomicUsize,
+    // ACL (acl.cfg) cache generation/version.
+    acl_cache_generation: AtomicUsize,
     // Add further atomics here
 }
 
@@ -177,4 +179,20 @@ impl ConfigVersionCache {
             .token_shadow_generation
             .fetch_add(1, Ordering::AcqRel)
     }
+
+    /// Returns the acl cache generation number.
+    pub fn acl_cache_generation(&self) -> usize {
+        self.shmem
+            .data()
+            .acl_cache_generation
+            .load(Ordering::Acquire)
+    }
+
+    /// Increase the acl cache generation number.
+    pub fn increase_acl_cache_generation(&self) {
+        self.shmem
+            .data()
+            .acl_cache_generation
+            .fetch_add(1, Ordering::AcqRel);
+    }
 }
-- 
2.47.3





                 reply	other threads:[~2026-08-18 13:36 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260818133556.298498-1-r.obkircher@proxmox.com \
    --to=r.obkircher@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal