all lists on 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal