public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: "Shannon Sterz" <s.sterz@proxmox.com>
To: "Shannon Sterz" <s.sterz@proxmox.com>, <pdm-devel@lists.proxmox.com>
Subject: Re: [PATCH datacenter-manager 07/15] server: connection: report mismatching fingerprint as untrusted on probe
Date: Fri, 31 Jul 2026 16:44:57 +0200	[thread overview]
Message-ID: <DKCU5FIN0ZIW.2XBTWGILLOSG7@proxmox.com> (raw)
In-Reply-To: <20260731091655.93282-8-s.sterz@proxmox.com>

On Fri Jul 31, 2026 at 11:16 AM CEST, Shannon Sterz wrote:
> instead of erroring out. previously this function returned a
> connection error if the provided fingerprint did not match the
> remote's fingerprint. instead, report the certificate as untrusted,
> giving client's more appropriate information in such cases.
>
> note that while the documentation for this function was technically
> correct, the probe_tls endpoints for pve and pbs remotes stated:
>
>> If the certificate is not trusted with the given parameters, returns
>> the certificate information.
>
> however, that was incorrect, since the endpoints returned an error if
> the fingerprint did not match. since those two endpoints are currently
> the only users that could actually provide a fingerprint (all other
> callers explicitly provide `None`), this is more of a bug fix than a
> public api break.
>
> Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
> ---
>  server/src/connection.rs | 62 ++++++++++++++++++++++++++++------------
>  1 file changed, 43 insertions(+), 19 deletions(-)
>
> diff --git a/server/src/connection.rs b/server/src/connection.rs
> index f635b74e..51bbb611 100644
> --- a/server/src/connection.rs
> +++ b/server/src/connection.rs
> @@ -15,6 +15,7 @@ use std::time::{Duration, SystemTime};
>  use anyhow::{Error, bail, format_err};
>  use http::Method;
>  use http::uri::Authority;
> +use openssl::hash::MessageDigest;
>  use openssl::x509::X509StoreContextRef;
>  use serde::Serialize;
>
> @@ -22,6 +23,7 @@ use proxmox_acme_api::CertificateInfo;
>  use proxmox_client::{Client, HttpApiClient, HttpApiResponse, HttpApiResponseStream, TlsOptions};
>  use proxmox_time::epoch_i64;
>
> +use pdm_api_types::Fingerprint;
>  use pdm_api_types::remotes::{NodeUrl, Remote, RemoteType, TlsProbeOutcome};
>  use pve_api_types::client::PveClientImpl;
>
> @@ -929,7 +931,7 @@ impl HttpApiClient for MultiClient {
>  /// Checks TLS connection to the given remote
>  ///
>  /// Returns `Ok(TlsProbeOutcome::TrustedCertificate)` if connecting with the given parameters works
> -/// Returns `Ok(TlsProbeOutcome::UntrustedCertificate)` if no fingerprint was given and some certificate could not be validated
> +/// Returns `Ok(TlsProbeOutcome::UntrustedCertificate)` if the provided fingerprint does not match or a certificate could not be validated
>  /// Returns `Err(err)` if some other error occurred
>  ///
>  /// # Example
> @@ -964,25 +966,47 @@ pub async fn probe_tls_connection(
>      // to save the invalid cert we find
>      let invalid_cert = Arc::new(StdMutex::new(None));
>
> -    let options = if let Some(fp) = &fingerprint {
> -        TlsOptions::parse_fingerprint(fp)?
> -    } else {
> -        TlsOptions::Callback(Box::new({
> -            let invalid_cert = invalid_cert.clone();
> -            move |valid: bool, chain: &mut X509StoreContextRef| {
> -                if let Some(cert) = chain.current_cert() {
> -                    if !valid {
> -                        let cert = cert
> -                            .to_pem()
> -                            .map_err(Error::from)
> -                            .and_then(|pem| CertificateInfo::from_pem("", &pem));
> -                        *invalid_cert.lock().unwrap() = Some(cert);
> -                    }
> -                }
> -                true
> +    let fingerprint = fingerprint
> +        .map(|fp| fp.parse::<Fingerprint>())
> +        .transpose()?;
> +
> +    let options = TlsOptions::Callback(Box::new({
> +        let invalid_cert = invalid_cert.clone();
> +        move |valid: bool, chain: &mut X509StoreContextRef| {
> +            // If no fingerprint was provided and the trust store trusts the certificate, the
> +            // connection is valid.
> +            if fingerprint.is_none() && valid {
> +                return true;
>              }
> -        }))
> -    };
> +
> +            let Some(cert) = chain.current_cert() else {

something i just noticed is that this here should also always get the
leaf certificate. sorry for missing that, i'll clean that up in a v2

for the record, all that needs to change is that the above line should
read:

    let Some(cert) = chain.chain().and_then(|c| c.get(0)) else {

to renaming the (pre-existing) chain parameter to "context" or similar
would probably also help with clarity here.

> +                return true;
> +            };
> +
> +            // If a fingerprint was provided and the certificate matches it, the connection is
> +            // valid.
> +            if let Some(provided_fp) = &fingerprint {
> +                if cert
> +                    .digest(MessageDigest::sha256())
> +                    .map(|fp| *fp == **provided_fp)
> +                    .unwrap_or(false)
> +                {
> +                    return true;
> +                }
> +            }
> +
> +            // Otherwise, the certificate is not trusted.
> +            let cert = cert
> +                .to_pem()
> +                .map_err(Error::from)
> +                .and_then(|pem| CertificateInfo::from_pem("", &pem));
> +
> +            *invalid_cert.lock().unwrap() = Some(cert);
> +
> +            true
> +        }
> +    }));
> +
>      let client = proxmox_client::Client::with_options(uri, options, Default::default())?;
>
>      // set fake auth info. we don't need any, but the proxmox client will return unauthenticated if





  reply	other threads:[~2026-07-31 14:45 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-31  9:16 [PATCH cluster/datacenter-manager/manager/proxmox 00/15] TLS Certificate Staging Shannon Sterz
2026-07-31  9:16 ` [PATCH cluster 01/15] setup: allow caller to provide the certificate filename Shannon Sterz
2026-07-31  9:16 ` [PATCH manager 02/15] bin/api: add a new staged certificate when renewing self-signed cert Shannon Sterz
2026-07-31  9:16 ` [PATCH manager 03/15] api: certificates: if node parameter is 'localhost' return local certs Shannon Sterz
2026-07-31  9:16 ` [PATCH proxmox 04/15] pve-api-types: expose certificates info endpoint Shannon Sterz
2026-07-31  9:16 ` [PATCH datacenter-manager 05/15] client: allow users to update a changed fingerprint interactively Shannon Sterz
2026-07-31  9:16 ` [PATCH datacenter-manager 06/15] cli/api-types: move Fingerprint to common api type crate Shannon Sterz
2026-07-31  9:16 ` [PATCH datacenter-manager 07/15] server: connection: report mismatching fingerprint as untrusted on probe Shannon Sterz
2026-07-31 14:44   ` Shannon Sterz [this message]
2026-07-31  9:16 ` [PATCH datacenter-manager 08/15] ui: wizzard: add context if a provided fingerprint did not match remote Shannon Sterz
2026-07-31  9:16 ` [PATCH datacenter-manager 09/15] ui: wizzard: nodes page: always update fingerprints on user confirmation Shannon Sterz
2026-07-31  9:16 ` [PATCH datacenter-manager 10/15] pdm-api-types: implement ApiType for Fingerprint Shannon Sterz
2026-07-31  9:16 ` [PATCH datacenter-manager 11/15] pdm-api-types: add staged_fingerprints field to NodeUrl Shannon Sterz
2026-07-31  9:16 ` [PATCH datacenter-manager 12/15] server: remotes: lock remotes config when updating it Shannon Sterz
2026-07-31  9:16 ` [PATCH datacenter-manager 13/15] server: connection: rotate in staged fingerprints when encountering them Shannon Sterz
2026-07-31  9:16 ` [PATCH datacenter-manager 14/15] server: api: tasks: move `spawn_aborted_on_shutdown()` to super module Shannon Sterz
2026-07-31  9:16 ` [PATCH datacenter-manager 15/15] 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=DKCU5FIN0ZIW.2XBTWGILLOSG7@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
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal