From: Shannon Sterz <s.sterz@proxmox.com>
To: pdm-devel@lists.proxmox.com
Subject: [PATCH datacenter-manager 13/17] pdm-api-types: add staged_fingerprints field to NodeUrl
Date: Thu, 11 Jun 2026 14:03:23 +0200 [thread overview]
Message-ID: <20260611120327.257523-14-s.sterz@proxmox.com> (raw)
In-Reply-To: <20260611120327.257523-1-s.sterz@proxmox.com>
and fix up all use sides as well as the update endpoint.
Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
---
lib/pdm-api-types/src/remotes.rs | 15 ++++++++++++++-
server/src/api/pbs/mod.rs | 2 ++
server/src/api/pve/mod.rs | 3 +++
server/src/api/remotes/mod.rs | 27 ++++++++++++++++++++++++++-
ui/src/remotes/config.rs | 1 +
ui/src/remotes/node_url_list.rs | 1 +
ui/src/remotes/wizard_page_info.rs | 1 +
7 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/lib/pdm-api-types/src/remotes.rs b/lib/pdm-api-types/src/remotes.rs
index 50c7892e..0a2e8651 100644
--- a/lib/pdm-api-types/src/remotes.rs
+++ b/lib/pdm-api-types/src/remotes.rs
@@ -8,7 +8,7 @@ use proxmox_schema::{ApiType, Schema, StringSchema, Updater, api};
use proxmox_section_config::typed::ApiSectionDataEntry;
use proxmox_section_config::{SectionConfig, SectionConfigPlugin};
-use crate::{Authid, HOST_OPTIONAL_PORT_FORMAT};
+use crate::{Authid, Fingerprint, HOST_OPTIONAL_PORT_FORMAT};
pub const REMOTE_ID_SCHEMA: Schema = StringSchema::new("Remote ID.")
.format(&crate::PROXMOX_SAFE_ID_FORMAT)
@@ -26,11 +26,19 @@ pub const REMOTE_ID_SCHEMA: Schema = StringSchema::new("Remote ID.")
format: &crate::FINGERPRINT_SHA256_FORMAT,
optional: true,
},
+ "staged-fingerprints": {
+ type: Array,
+ optional: true,
+ items: {
+ type: Fingerprint,
+ }
+ }
},
default_key: "hostname",
)]
/// A node and its certificate information.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
+#[serde(rename_all = "kebab-case")]
pub struct NodeUrl {
/// The node address.
pub hostname: String,
@@ -38,6 +46,11 @@ pub struct NodeUrl {
/// Certificate fingerprint.
#[serde(skip_serializing_if = "Option::is_none")]
pub fingerprint: Option<String>,
+
+ /// A list of staged fingerprints. If one of these is encountered while connecting to a node,
+ /// they'll replace the main certificate fingerprint. The connection will be deemed valid.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub staged_fingerprints: Option<Vec<Fingerprint>>,
}
#[api]
diff --git a/server/src/api/pbs/mod.rs b/server/src/api/pbs/mod.rs
index 1fc75c34..98a88a23 100644
--- a/server/src/api/pbs/mod.rs
+++ b/server/src/api/pbs/mod.rs
@@ -273,6 +273,7 @@ pub async fn scan_remote_pbs(
nodes: vec![PropertyString::new(NodeUrl {
hostname,
fingerprint,
+ staged_fingerprints: None,
})],
authid: authid.clone(),
token,
@@ -325,6 +326,7 @@ pub async fn list_realm_remote_pbs(
nodes: vec![PropertyString::new(NodeUrl {
hostname,
fingerprint,
+ staged_fingerprints: None,
})],
authid: "root@pam".parse()?,
token: String::new(),
diff --git a/server/src/api/pve/mod.rs b/server/src/api/pve/mod.rs
index 0970f2ff..64413a3e 100644
--- a/server/src/api/pve/mod.rs
+++ b/server/src/api/pve/mod.rs
@@ -483,6 +483,7 @@ pub async fn scan_remote_pve(
nodes: vec![PropertyString::new(NodeUrl {
hostname,
fingerprint,
+ staged_fingerprints: None,
})],
authid: authid.clone(),
token,
@@ -508,6 +509,7 @@ pub async fn scan_remote_pve(
nodes.push(PropertyString::new(NodeUrl {
hostname: node.node,
fingerprint,
+ staged_fingerprints: None,
}));
}
@@ -574,6 +576,7 @@ pub async fn list_realm_remote_pve(
nodes: vec![PropertyString::new(NodeUrl {
hostname,
fingerprint,
+ staged_fingerprints: None,
})],
authid: "root@pam".parse()?,
token: String::new(),
diff --git a/server/src/api/remotes/mod.rs b/server/src/api/remotes/mod.rs
index e416f619..090a3b32 100644
--- a/server/src/api/remotes/mod.rs
+++ b/server/src/api/remotes/mod.rs
@@ -412,7 +412,32 @@ pub fn update_remote(
}
}
- if let Some(v) = updater.nodes {
+ if let Some(mut v) = updater.nodes {
+ for node in &mut v {
+ // If the updater included staged fingerprints for the remote, update them.
+ if node.staged_fingerprints.is_some() {
+ continue;
+ }
+
+ // If not, keep the previous fingerprints intact.
+ let staged_fp = entry
+ .nodes
+ .iter()
+ .find_map(|n| {
+ if n.hostname == node.hostname
+ && n.fingerprint.as_ref().map(|f| f.to_lowercase())
+ == node.fingerprint.as_ref().map(|f| f.to_lowercase())
+ {
+ return Some(n.staged_fingerprints.clone());
+ }
+
+ None
+ })
+ .flatten();
+
+ node.staged_fingerprints = staged_fp;
+ }
+
entry.nodes = v;
}
if let Some(v) = updater.authid {
diff --git a/ui/src/remotes/config.rs b/ui/src/remotes/config.rs
index ea3c5bcd..0e272c2c 100644
--- a/ui/src/remotes/config.rs
+++ b/ui/src/remotes/config.rs
@@ -59,6 +59,7 @@ pub async fn create_remote(mut data: Value, remote_type: RemoteType) -> Result<(
let nodes = vec![PropertyString::new(NodeUrl {
hostname: data["hostname"].as_str().unwrap_or_default().to_string(),
fingerprint: data["fingerprint"].as_str().map(|fp| fp.to_string()),
+ staged_fingerprints: None,
})];
data["nodes"] = serde_json::to_value(nodes)?;
}
diff --git a/ui/src/remotes/node_url_list.rs b/ui/src/remotes/node_url_list.rs
index d18d8a23..4afc6482 100644
--- a/ui/src/remotes/node_url_list.rs
+++ b/ui/src/remotes/node_url_list.rs
@@ -220,6 +220,7 @@ impl ManagedField for PdmNodeUrlField {
data: NodeUrl {
hostname: String::new(),
fingerprint: None,
+ staged_fingerprints: None,
},
})
}
diff --git a/ui/src/remotes/wizard_page_info.rs b/ui/src/remotes/wizard_page_info.rs
index 1e56c4e1..4f8e3302 100644
--- a/ui/src/remotes/wizard_page_info.rs
+++ b/ui/src/remotes/wizard_page_info.rs
@@ -131,6 +131,7 @@ async fn scan(
PropertyString::new(NodeUrl {
hostname,
fingerprint: fingerprint.map(|fp| fp.to_uppercase()),
+ staged_fingerprints: None,
}),
);
}
--
2.47.3
next prev parent reply other threads:[~2026-06-11 12:03 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-11 12:03 [RFC cluster/datacenter-manager/manager/proxmox 00/17] TLS Certificate Staging Shannon Sterz
2026-06-11 12:03 ` [PATCH cluster 01/17] setup: allow caller to provide the certificate filename Shannon Sterz
2026-06-11 12:03 ` [PATCH manager 02/17] bin/api: add a new staged certificate when renewing self-signed cert Shannon Sterz
2026-06-11 12:03 ` [PATCH manager 03/17] api: certificates: if node parameter is 'localhost' return local certs Shannon Sterz
2026-06-11 12:03 ` [PATCH proxmox 04/17] client: ignore certificate trust store validation result on fp option Shannon Sterz
2026-06-11 12:03 ` [PATCH proxmox 05/17] pve-api-types: expose certificates info endpoint Shannon Sterz
2026-06-11 12:03 ` [PATCH datacenter-manager 06/17] client: don't short-circuit on valid certificate when tls fp exists Shannon Sterz
2026-06-11 12:03 ` [PATCH datacenter-manager 07/17] client: allow users to update a changed fingerprint interactively Shannon Sterz
2026-06-11 12:03 ` [PATCH datacenter-manager 08/17] cli/api-types: move Fingerprint to common api type crate Shannon Sterz
2026-06-11 12:03 ` [PATCH datacenter-manager 09/17] server: connection: report mismatching fingerprint as untrusted on probe Shannon Sterz
2026-06-11 12:03 ` [PATCH datacenter-manager 10/17] ui: wizzard: add context if a provided fingerprint did not match remote Shannon Sterz
2026-06-11 12:03 ` [PATCH datacenter-manager 11/17] ui: wizzard: nodes page: always update fingerprints on user confirmation Shannon Sterz
2026-06-11 12:03 ` [PATCH datacenter-manager 12/17] pdm-api-types: implement ApiType for Fingerprint Shannon Sterz
2026-06-11 12:03 ` Shannon Sterz [this message]
2026-06-11 12:03 ` [PATCH datacenter-manager 14/17] server: remotes: lock remotes config when updating it Shannon Sterz
2026-06-11 12:03 ` [PATCH datacenter-manager 15/17] server: connection: rotate in staged fingerprints when encountering them Shannon Sterz
2026-06-11 12:03 ` [PATCH datacenter-manager 16/17] server: api: tasks: move `spawn_aborted_on_shutdown()` to super module Shannon Sterz
2026-06-11 12:03 ` [PATCH datacenter-manager 17/17] server: bin: api: tasks: add task to discover new staged certificates Shannon Sterz
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=20260611120327.257523-14-s.sterz@proxmox.com \
--to=s.sterz@proxmox.com \
--cc=pdm-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox