From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 426A51FF140 for ; Fri, 10 Apr 2026 18:55:24 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 7365723F63; Fri, 10 Apr 2026 18:55:49 +0200 (CEST) From: Christian Ebner To: pbs-devel@lists.proxmox.com Subject: [PATCH proxmox-backup v2 14/27] api: push sync: expose optional encryption key for push sync Date: Fri, 10 Apr 2026 18:54:41 +0200 Message-ID: <20260410165454.1578501-15-c.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260410165454.1578501-1-c.ebner@proxmox.com> References: <20260410165454.1578501-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: 1775840038625 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.070 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment 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: P4D3MSVJXBLLHTVCS64YZXA3HHZ35OXB X-Message-ID-Hash: P4D3MSVJXBLLHTVCS64YZXA3HHZ35OXB 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: Exposes the optional encryption key id to be used for server side encryption of contents during push sync jobs. Only expose the parameter for now and load the key if given, the logic to use it will be implemented in subsequent code changes. Signed-off-by: Christian Ebner --- src/api2/push.rs | 8 +++++++- src/server/push.rs | 12 ++++++++++++ src/server/sync.rs | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/api2/push.rs b/src/api2/push.rs index e5edc13e0..84d93621b 100644 --- a/src/api2/push.rs +++ b/src/api2/push.rs @@ -2,7 +2,7 @@ use anyhow::{format_err, Error}; use futures::{future::FutureExt, select}; use pbs_api_types::{ - Authid, BackupNamespace, GroupFilter, RateLimitConfig, DATASTORE_SCHEMA, + Authid, BackupNamespace, GroupFilter, RateLimitConfig, CRYPT_KEY_ID_SCHEMA, DATASTORE_SCHEMA, GROUP_FILTER_LIST_SCHEMA, NS_MAX_DEPTH_REDUCED_SCHEMA, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_READ, PRIV_REMOTE_DATASTORE_BACKUP, PRIV_REMOTE_DATASTORE_PRUNE, REMOTE_ID_SCHEMA, REMOVE_VANISHED_BACKUPS_SCHEMA, SYNC_ENCRYPTED_ONLY_SCHEMA, @@ -108,6 +108,10 @@ fn check_push_privs( schema: TRANSFER_LAST_SCHEMA, optional: true, }, + "encryption-key": { + schema: CRYPT_KEY_ID_SCHEMA, + optional: true, + }, }, }, access: { @@ -133,6 +137,7 @@ async fn push( verified_only: Option, limit: RateLimitConfig, transfer_last: Option, + encryption_key: Option, rpcenv: &mut dyn RpcEnvironment, ) -> Result { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; @@ -164,6 +169,7 @@ async fn push( verified_only, limit, transfer_last, + encryption_key, ) .await?; diff --git a/src/server/push.rs b/src/server/push.rs index 9e5a19560..93434bedc 100644 --- a/src/server/push.rs +++ b/src/server/push.rs @@ -27,6 +27,7 @@ use pbs_datastore::fixed_index::FixedIndexReader; use pbs_datastore::index::IndexFile; use pbs_datastore::read_chunk::AsyncReadChunk; use pbs_datastore::{BackupManifest, DataStore, StoreProgress}; +use pbs_tools::crypt_config::CryptConfig; use super::sync::{ check_namespace_depth_limit, exclude_not_verified_or_encrypted, @@ -83,6 +84,9 @@ pub(crate) struct PushParameters { verified_only: bool, /// How many snapshots should be transferred at most (taking the newest N snapshots) transfer_last: Option, + /// Encryption key to use for pushing unencrypted backup snapshots. Does not affect + /// already encrypted snapshots. + crypt_config: Option>, } impl PushParameters { @@ -102,6 +106,7 @@ impl PushParameters { verified_only: Option, limit: RateLimitConfig, transfer_last: Option, + active_encryption_key: Option, ) -> Result { if let Some(max_depth) = max_depth { ns.check_max_depth(max_depth)?; @@ -155,6 +160,12 @@ impl PushParameters { }; let group_filter = group_filter.unwrap_or_default(); + let crypt_config = if let Some(key_id) = &active_encryption_key { + crate::server::sync::check_privs_and_load_key_config(key_id, &local_user, true)? + } else { + None + }; + Ok(Self { source, target, @@ -165,6 +176,7 @@ impl PushParameters { encrypted_only, verified_only, transfer_last, + crypt_config, }) } diff --git a/src/server/sync.rs b/src/server/sync.rs index 9c070cd9c..6b84ae6d7 100644 --- a/src/server/sync.rs +++ b/src/server/sync.rs @@ -677,6 +677,7 @@ pub fn do_sync_job( sync_job.verified_only, sync_job.limit.clone(), sync_job.transfer_last, + sync_job.active_encryption_key, ) .await?; push_store(push_params).await? -- 2.47.3