From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id E96961FF0E7 for ; Thu, 13 Aug 2026 19:11:09 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 9339121B24; Thu, 13 Aug 2026 19:10:37 +0200 (CEST) From: Christian Ebner To: pbs-devel@lists.proxmox.com Subject: [PATCH proxmox-backup 15/28] sync: push: allow push and ns creation on Remote.DatastoreAppend Date: Thu, 13 Aug 2026 19:09:49 +0200 Message-ID: <20260813171002.809441-16-c.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260813171002.809441-1-c.ebner@proxmox.com> References: <20260813171002.809441-1-c.ebner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1786641016145 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.206 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust RDNS_NONE 1.274 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: IGW3QL6D2H3RESHIEBHQ4CCKZXWCAEZH X-Message-ID-Hash: IGW3QL6D2H3RESHIEBHQ4CCKZXWCAEZH X-MailFrom: c.ebner@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox Backup Server development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: A local sync job user with Remote.DatastoreAppend permissions is intended to be able to append backups on the remote just like Remote.DatastoreBackup, but also create namespaces not present on the remote, which would otherwise require Remote.DatastoreModify. The latter would however also allow for namespace destruction, so cannot be used for this. These are push job source checks only, the user connecting to the remote as configured in the remote config must be setup on the target PBS instance accordingly (i.e. with Datastore.Audit and Datastore.Append on the target datastore and sub-namespace). Signed-off-by: Christian Ebner --- src/server/push.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/server/push.rs b/src/server/push.rs index 9a69f5ce8..83e4ea22d 100644 --- a/src/server/push.rs +++ b/src/server/push.rs @@ -15,8 +15,9 @@ use pbs_api_types::{ ApiVersion, ApiVersionInfo, ArchiveType, Authid, BackupArchiveName, BackupDir, BackupGroup, BackupGroupDeleteStats, BackupNamespace, CLIENT_LOG_BLOB_NAME, CryptMode, GroupFilter, GroupListItem, MANIFEST_BLOB_NAME, NamespaceListItem, Operation, PRIV_DATASTORE_BACKUP, - PRIV_DATASTORE_READ, PRIV_REMOTE_DATASTORE_BACKUP, PRIV_REMOTE_DATASTORE_MODIFY, - PRIV_REMOTE_DATASTORE_PRUNE, RateLimitConfig, Remote, SnapshotListItem, print_store_and_ns, + PRIV_DATASTORE_READ, PRIV_REMOTE_DATASTORE_APPEND, PRIV_REMOTE_DATASTORE_BACKUP, + PRIV_REMOTE_DATASTORE_MODIFY, PRIV_REMOTE_DATASTORE_PRUNE, RateLimitConfig, Remote, + SnapshotListItem, print_store_and_ns, }; use pbs_client::{ BackupRepository, BackupStats, BackupWriter, BackupWriterOptions, HttpClient, IndexType, @@ -211,7 +212,7 @@ fn check_ns_remote_datastore_privs( let acl_path = target_namespace.remote_acl_path(¶ms.target.remote.name, params.target.repo.store()); - user_info.check_privs(¶ms.local_user, &acl_path, privs, false)?; + user_info.check_privs(¶ms.local_user, &acl_path, privs, true)?; Ok(()) } @@ -353,7 +354,8 @@ async fn check_or_create_target_namespace( // Namespace not present on target, create namespace. // Sub-namespaces have to be created by creating parent components first. - check_ns_remote_datastore_privs(params, target_namespace, PRIV_REMOTE_DATASTORE_MODIFY) + let privs = PRIV_REMOTE_DATASTORE_MODIFY | PRIV_REMOTE_DATASTORE_APPEND; + check_ns_remote_datastore_privs(params, target_namespace, privs) .context("Creating remote namespace not allowed")?; let mut parent = BackupNamespace::root(); @@ -550,7 +552,8 @@ pub(crate) async fn push_namespace( ) -> Result<(StoreProgress, SyncStats, bool), Error> { let target_namespace = params.map_to_target(namespace)?; // Check if user is allowed to perform backups on remote datastore - check_ns_remote_datastore_privs(¶ms, &target_namespace, PRIV_REMOTE_DATASTORE_BACKUP) + let privs = PRIV_REMOTE_DATASTORE_BACKUP | PRIV_REMOTE_DATASTORE_APPEND; + check_ns_remote_datastore_privs(¶ms, &target_namespace, privs) .context("Pushing to remote namespace not allowed")?; let mut list: Vec = params -- 2.47.3