From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 8BF831FF13C for ; Thu, 11 Jun 2026 14:03:51 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 8ADD1393B; Thu, 11 Jun 2026 14:03:48 +0200 (CEST) From: Shannon Sterz To: pdm-devel@lists.proxmox.com Subject: [PATCH datacenter-manager 10/17] ui: wizzard: add context if a provided fingerprint did not match remote Date: Thu, 11 Jun 2026 14:03:20 +0200 Message-ID: <20260611120327.257523-11-s.sterz@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260611120327.257523-1-s.sterz@proxmox.com> References: <20260611120327.257523-1-s.sterz@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1781179364560 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.108 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: MCPJLLOTAGF3OLW2P4AM25GP5XUCQBRD X-Message-ID-Hash: MCPJLLOTAGF3OLW2P4AM25GP5XUCQBRD X-MailFrom: s.sterz@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 Datacenter Manager development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: if a remote's certificate does not match the fingerprint provided by a user, add a warning to the probe result dialogs. Signed-off-by: Shannon Sterz --- ui/src/remotes/wizard_page_connect.rs | 26 ++++++++++++++++-- ui/src/remotes/wizard_page_nodes.rs | 38 +++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/ui/src/remotes/wizard_page_connect.rs b/ui/src/remotes/wizard_page_connect.rs index abb2b1da..59031892 100644 --- a/ui/src/remotes/wizard_page_connect.rs +++ b/ui/src/remotes/wizard_page_connect.rs @@ -6,9 +6,9 @@ use serde_json::Value; use yew::html::IntoEventCallback; use yew::virtual_dom::{Key, VComp, VNode}; -use pwt::css::{FlexFit, JustifyContent}; +use pwt::css::{FlexFit, FontColor, JustifyContent}; use pwt::widget::form::{Field, FormContext, FormContextObserver}; -use pwt::widget::{Button, Column, Container, Dialog, InputPanel, Mask, Row, error_message}; +use pwt::widget::{Button, Column, Container, Dialog, Fa, InputPanel, Mask, Row, error_message}; use pwt::{AsyncAbortGuard, prelude::*}; use pwt_macros::builder; @@ -81,6 +81,14 @@ impl PdmWizardPageConnect { Some(Ok(TlsProbeOutcome::UntrustedCertificate(info))) => info.clone(), _ => return None, }; + + let provided_fp = ctx + .props() + .info + .form_ctx + .read() + .get_field_text("fingerprint"); + Some( Dialog::new(tr!("Connection Certificate")) .on_close(link.callback(|_| Msg::ConfirmResult(false))) @@ -92,6 +100,20 @@ impl PdmWizardPageConnect { .with_child(Container::new().with_child(tr!( "The certificate of the remote server is not trusted." ))) + .with_optional_child((!provided_fp.is_empty()).then(||{ + Row::new() + .gap(1) + .margin_x(1) + .with_child( + Fa::new("exclamation-triangle") + .class(FontColor::Warning) + ) + .with_child(tr!( + "The provided SHA-256 fingerprint ({provided_fp}) did not match \ + the remote certificate's fingerprint.", + provided_fp + )) + })) .with_child(Container::new().with_child(tr!( "Do you want to trust the certificate and save its fingerprint?" ))) diff --git a/ui/src/remotes/wizard_page_nodes.rs b/ui/src/remotes/wizard_page_nodes.rs index 15f60404..3428338d 100644 --- a/ui/src/remotes/wizard_page_nodes.rs +++ b/ui/src/remotes/wizard_page_nodes.rs @@ -6,8 +6,8 @@ use proxmox_schema::property_string::PropertyString; use serde_json::Value; use yew::virtual_dom::{VComp, VNode}; -use pwt::css::{FlexFit, FontStyle, JustifyContent, Overflow}; -use pwt::widget::{Button, Column, Container, Dialog, Mask, Row, error_message}; +use pwt::css::{FlexFit, FontColor, FontStyle, JustifyContent, Overflow}; +use pwt::widget::{Button, Column, Container, Dialog, Fa, Mask, Row, error_message}; use pwt::{AsyncAbortGuard, prelude::*}; use pwt_macros::builder; @@ -74,12 +74,46 @@ impl PdmWizardPageNodes { .padding(2) .class(Overflow::Auto) .children(certificates.into_iter().map(|(hostname, certificate)| { + let nodes = + ctx.props().info.form_ctx.read().get_field_value("nodes"); + + let provided_fp = if let Some(Value::Array(nodes_list)) = nodes { + nodes_list + .into_iter() + .find_map(|node| { + serde_json::from_value::>(node) + .ok() + .and_then(|n| { + let node = n.into_inner(); + (node.hostname == *hostname) + .then_some(node.fingerprint) + }) + }) + .flatten() + } else { + None + }; + Column::new() .with_child( Container::new().class(FontStyle::TitleSmall).with_child( format!("{}: {hostname}", tr!("Server Address")), ), ) + .with_optional_child(provided_fp.map(|fp| { + Row::new() + .gap(1) + .margin(2) + .with_child( + Fa::new("exclamation-triangle") + .class(FontColor::Warning), + ) + .with_child(tr!( + "The provided SHA-256 fingerprint ({fp}) did not \ + match the remote certificate's fingerprint.", + fp + )) + })) .with_child( KVGrid::new() .class(FlexFit) -- 2.47.3