all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: "Max R. Carrara" <m.carrara@proxmox.com>
To: "Proxmox Backup Server development discussion"
	<pbs-devel@lists.proxmox.com>
Subject: Re: [pbs-devel] [PATCH proxmox-backup v4 4/4] acme: certificate ordering through proxmox-acme-api
Date: Tue, 09 Dec 2025 17:50:59 +0100	[thread overview]
Message-ID: <DETUAGFMBQ02.1S9DIJMYYJSVC@proxmox.com> (raw)
In-Reply-To: <20251203102217.59923-5-s.rufinatscha@proxmox.com>

On Wed Dec 3, 2025 at 11:22 AM CET, Samuel Rufinatscha wrote:
> PBS currently uses its own ACME client and API logic, while PDM uses the
> factored out proxmox-acme and proxmox-acme-api crates. This duplication
> risks differences in behaviour and requires ACME maintenance in two
> places. This patch is part of a series to move PBS over to the shared
> ACME stack.
>
> Changes:
> - Replace the custom ACME order/authorization loop in node certificates
> with a call to proxmox_acme_api::order_certificate.
> - Build domain + config data as proxmox-acme-api types
> - Remove obsolete local ACME ordering and plugin glue code.
>
> Signed-off-by: Samuel Rufinatscha <s.rufinatscha@proxmox.com>
> ---
>  src/acme/mod.rs               |   2 -
>  src/acme/plugin.rs            | 336 ----------------------------------
>  src/api2/node/certificates.rs | 240 ++++--------------------
>  src/api2/types/acme.rs        |  74 --------
>  src/api2/types/mod.rs         |   3 -
>  src/config/acme/mod.rs        |   7 +-
>  src/config/acme/plugin.rs     |  99 +---------
>  src/config/node.rs            |  22 +--
>  src/lib.rs                    |   2 -
>  9 files changed, 46 insertions(+), 739 deletions(-)
>  delete mode 100644 src/acme/mod.rs
>  delete mode 100644 src/acme/plugin.rs
>  delete mode 100644 src/api2/types/acme.rs
>
> diff --git a/src/acme/mod.rs b/src/acme/mod.rs
> deleted file mode 100644
> index cc561f9a..00000000
> --- a/src/acme/mod.rs
> +++ /dev/null
> @@ -1,2 +0,0 @@
> -pub(crate) mod plugin;
> -pub(crate) use plugin::get_acme_plugin;
> diff --git a/src/acme/plugin.rs b/src/acme/plugin.rs
> deleted file mode 100644
> index 5bc09e1f..00000000
> --- a/src/acme/plugin.rs
> +++ /dev/null
> @@ -1,336 +0,0 @@

snip 8<-------------

> diff --git a/src/api2/node/certificates.rs b/src/api2/node/certificates.rs
> index 31196715..2a645b4a 100644
> --- a/src/api2/node/certificates.rs
> +++ b/src/api2/node/certificates.rs
> @@ -1,27 +1,19 @@
> -use std::sync::Arc;
> -use std::time::Duration;
> -
>  use anyhow::{bail, format_err, Error};
>  use openssl::pkey::PKey;
>  use openssl::x509::X509;
>  use serde::{Deserialize, Serialize};
>  use tracing::info;
>  
> -use proxmox_router::list_subdirs_api_method;
> -use proxmox_router::SubdirMap;
> -use proxmox_router::{Permission, Router, RpcEnvironment};
> -use proxmox_schema::api;
> -
> +use crate::server::send_certificate_renewal_mail;
>  use pbs_api_types::{NODE_SCHEMA, PRIV_SYS_MODIFY};
>  use pbs_buildcfg::configdir;
>  use pbs_tools::cert;
> -use tracing::warn;
> -
> -use crate::api2::types::AcmeDomain;
> -use crate::config::node::NodeConfig;
> -use crate::server::send_certificate_renewal_mail;
> -use proxmox_acme::async_client::AcmeClient;
> +use proxmox_acme_api::AcmeDomain;
>  use proxmox_rest_server::WorkerTask;
> +use proxmox_router::list_subdirs_api_method;
> +use proxmox_router::SubdirMap;
> +use proxmox_router::{Permission, Router, RpcEnvironment};
> +use proxmox_schema::api;

(See my comment on patch 2/4 for more information / context)

use anyhow::{bail, format_err, Error};
use openssl::pkey::PKey;
use openssl::x509::X509;
use serde::{Deserialize, Serialize};
use tracing::info;

use pbs_api_types::{NODE_SCHEMA, PRIV_SYS_MODIFY};
use proxmox_acme_api::AcmeDomain;
use proxmox_rest_server::WorkerTask;
use proxmox_router::SubdirMap;
use proxmox_router::list_subdirs_api_method;
use proxmox_router::{Permission, Router, RpcEnvironment};
use proxmox_schema::api;

use pbs_buildcfg::configdir;
use pbs_tools::cert;

use crate::server::send_certificate_renewal_mail;

>  
>  pub const ROUTER: Router = Router::new()
>      .get(&list_subdirs_api_method!(SUBDIRS))
> @@ -269,193 +261,6 @@ pub async fn delete_custom_certificate() -> Result<(), Error> {
>      Ok(())
>  }
>  
> -struct OrderedCertificate {
> -    certificate: hyper::body::Bytes,
> -    private_key_pem: Vec<u8>,
> -}
> -
> -async fn order_certificate(
> -    worker: Arc<WorkerTask>,
> -    node_config: &NodeConfig,
> -) -> Result<Option<OrderedCertificate>, Error> {
> -    use proxmox_acme::authorization::Status;
> -    use proxmox_acme::order::Identifier;
> -
> -    let domains = node_config.acme_domains().try_fold(
> -        Vec::<AcmeDomain>::new(),
> -        |mut acc, domain| -> Result<_, Error> {
> -            let mut domain = domain?;
> -            domain.domain.make_ascii_lowercase();
> -            if let Some(alias) = &mut domain.alias {
> -                alias.make_ascii_lowercase();
> -            }
> -            acc.push(domain);
> -            Ok(acc)
> -        },
> -    )?;
> -
> -    let get_domain_config = |domain: &str| {
> -        domains
> -            .iter()
> -            .find(|d| d.domain == domain)
> -            .ok_or_else(|| format_err!("no config for domain '{}'", domain))
> -    };
> -
> -    if domains.is_empty() {
> -        info!("No domains configured to be ordered from an ACME server.");
> -        return Ok(None);
> -    }
> -
> -    let (plugins, _) = crate::config::acme::plugin::config()?;
> -
> -    let mut acme = node_config.acme_client().await?;
> -
> -    info!("Placing ACME order");
> -    let order = acme
> -        .new_order(domains.iter().map(|d| d.domain.to_ascii_lowercase()))
> -        .await?;
> -    info!("Order URL: {}", order.location);
> -
> -    let identifiers: Vec<String> = order
> -        .data
> -        .identifiers
> -        .iter()
> -        .map(|identifier| match identifier {
> -            Identifier::Dns(domain) => domain.clone(),
> -        })
> -        .collect();
> -
> -    for auth_url in &order.data.authorizations {
> -        info!("Getting authorization details from '{auth_url}'");
> -        let mut auth = acme.get_authorization(auth_url).await?;
> -
> -        let domain = match &mut auth.identifier {
> -            Identifier::Dns(domain) => domain.to_ascii_lowercase(),
> -        };
> -
> -        if auth.status == Status::Valid {
> -            info!("{domain} is already validated!");
> -            continue;
> -        }
> -
> -        info!("The validation for {domain} is pending");
> -        let domain_config: &AcmeDomain = get_domain_config(&domain)?;
> -        let plugin_id = domain_config.plugin.as_deref().unwrap_or("standalone");
> -        let mut plugin_cfg = crate::acme::get_acme_plugin(&plugins, plugin_id)?
> -            .ok_or_else(|| format_err!("plugin '{plugin_id}' for domain '{domain}' not found!"))?;
> -
> -        info!("Setting up validation plugin");
> -        let validation_url = plugin_cfg
> -            .setup(&mut acme, &auth, domain_config, Arc::clone(&worker))
> -            .await?;
> -
> -        let result = request_validation(&mut acme, auth_url, validation_url).await;
> -
> -        if let Err(err) = plugin_cfg
> -            .teardown(&mut acme, &auth, domain_config, Arc::clone(&worker))
> -            .await
> -        {
> -            warn!("Failed to teardown plugin '{plugin_id}' for domain '{domain}' - {err}");
> -        }
> -
> -        result?;
> -    }
> -
> -    info!("All domains validated");
> -    info!("Creating CSR");
> -
> -    let csr = proxmox_acme::util::Csr::generate(&identifiers, &Default::default())?;
> -    let mut finalize_error_cnt = 0u8;
> -    let order_url = &order.location;
> -    let mut order;
> -    loop {
> -        use proxmox_acme::order::Status;
> -
> -        order = acme.get_order(order_url).await?;
> -
> -        match order.status {
> -            Status::Pending => {
> -                info!("still pending, trying to finalize anyway");
> -                let finalize = order
> -                    .finalize
> -                    .as_deref()
> -                    .ok_or_else(|| format_err!("missing 'finalize' URL in order"))?;
> -                if let Err(err) = acme.finalize(finalize, &csr.data).await {
> -                    if finalize_error_cnt >= 5 {
> -                        return Err(err);
> -                    }
> -
> -                    finalize_error_cnt += 1;
> -                }
> -                tokio::time::sleep(Duration::from_secs(5)).await;
> -            }
> -            Status::Ready => {
> -                info!("order is ready, finalizing");
> -                let finalize = order
> -                    .finalize
> -                    .as_deref()
> -                    .ok_or_else(|| format_err!("missing 'finalize' URL in order"))?;
> -                acme.finalize(finalize, &csr.data).await?;
> -                tokio::time::sleep(Duration::from_secs(5)).await;
> -            }
> -            Status::Processing => {
> -                info!("still processing, trying again in 30 seconds");
> -                tokio::time::sleep(Duration::from_secs(30)).await;
> -            }
> -            Status::Valid => {
> -                info!("valid");
> -                break;
> -            }
> -            other => bail!("order status: {:?}", other),
> -        }
> -    }
> -
> -    info!("Downloading certificate");
> -    let certificate = acme
> -        .get_certificate(
> -            order
> -                .certificate
> -                .as_deref()
> -                .ok_or_else(|| format_err!("missing certificate url in finalized order"))?,
> -        )
> -        .await?;
> -
> -    Ok(Some(OrderedCertificate {
> -        certificate,
> -        private_key_pem: csr.private_key_pem,
> -    }))
> -}
> -
> -async fn request_validation(
> -    acme: &mut AcmeClient,
> -    auth_url: &str,
> -    validation_url: &str,
> -) -> Result<(), Error> {
> -    info!("Triggering validation");
> -    acme.request_challenge_validation(validation_url).await?;
> -
> -    info!("Sleeping for 5 seconds");
> -    tokio::time::sleep(Duration::from_secs(5)).await;
> -
> -    loop {
> -        use proxmox_acme::authorization::Status;
> -
> -        let auth = acme.get_authorization(auth_url).await?;
> -        match auth.status {
> -            Status::Pending => {
> -                info!("Status is still 'pending', trying again in 10 seconds");
> -                tokio::time::sleep(Duration::from_secs(10)).await;
> -            }
> -            Status::Valid => return Ok(()),
> -            other => bail!(
> -                "validating challenge '{}' failed - status: {:?}",
> -                validation_url,
> -                other
> -            ),
> -        }
> -    }
> -}
> -
>  #[api(
>      input: {
>          properties: {
> @@ -525,9 +330,30 @@ fn spawn_certificate_worker(
>  
>      let auth_id = rpcenv.get_auth_id().unwrap();
>  
> +    let acme_config = if let Some(cfg) = node_config.acme_config().transpose()? {
> +        cfg
> +    } else {
> +        proxmox_acme_api::parse_acme_config_string("account=default")?
> +    };
> +
> +    let domains = node_config.acme_domains().try_fold(
> +        Vec::<AcmeDomain>::new(),
> +        |mut acc, domain| -> Result<_, Error> {
> +            let mut domain = domain?;
> +            domain.domain.make_ascii_lowercase();
> +            if let Some(alias) = &mut domain.alias {
> +                alias.make_ascii_lowercase();
> +            }
> +            acc.push(domain);
> +            Ok(acc)
> +        },
> +    )?;
> +
>      WorkerTask::spawn(name, None, auth_id, true, move |worker| async move {
>          let work = || async {
> -            if let Some(cert) = order_certificate(worker, &node_config).await? {
> +            if let Some(cert) =
> +                proxmox_acme_api::order_certificate(worker, &acme_config, &domains).await?
> +            {
>                  crate::config::set_proxy_certificate(&cert.certificate, &cert.private_key_pem)?;
>                  crate::server::reload_proxy_certificate().await?;
>              }
> @@ -563,16 +389,20 @@ pub fn revoke_acme_cert(rpcenv: &mut dyn RpcEnvironment) -> Result<String, Error
>  
>      let auth_id = rpcenv.get_auth_id().unwrap();
>  
> +    let acme_config = if let Some(cfg) = node_config.acme_config().transpose()? {
> +        cfg
> +    } else {
> +        proxmox_acme_api::parse_acme_config_string("account=default")?
> +    };
> +
>      WorkerTask::spawn(
>          "acme-revoke-cert",
>          None,
>          auth_id,
>          true,
>          move |_worker| async move {
> -            info!("Loading ACME account");
> -            let mut acme = node_config.acme_client().await?;
>              info!("Revoking old certificate");
> -            acme.revoke_certificate(cert_pem.as_bytes(), None).await?;
> +            proxmox_acme_api::revoke_certificate(&acme_config, &cert_pem.as_bytes()).await?;
>              info!("Deleting certificate and regenerating a self-signed one");
>              delete_custom_certificate().await?;
>              Ok(())
> diff --git a/src/api2/types/acme.rs b/src/api2/types/acme.rs
> deleted file mode 100644
> index 2905b41b..00000000
> --- a/src/api2/types/acme.rs
> +++ /dev/null
> @@ -1,74 +0,0 @@
> -use serde::{Deserialize, Serialize};
> -use serde_json::Value;
> -
> -use proxmox_schema::{api, ApiStringFormat, ApiType, Schema, StringSchema};
> -
> -use pbs_api_types::{DNS_ALIAS_FORMAT, DNS_NAME_FORMAT, PROXMOX_SAFE_ID_FORMAT};
> -
> -#[api(
> -    properties: {
> -        "domain": { format: &DNS_NAME_FORMAT },
> -        "alias": {
> -            optional: true,
> -            format: &DNS_ALIAS_FORMAT,
> -        },
> -        "plugin": {
> -            optional: true,
> -            format: &PROXMOX_SAFE_ID_FORMAT,
> -        },
> -    },
> -    default_key: "domain",
> -)]
> -#[derive(Deserialize, Serialize)]
> -/// A domain entry for an ACME certificate.
> -pub struct AcmeDomain {
> -    /// The domain to certify for.
> -    pub domain: String,
> -
> -    /// The domain to use for challenges instead of the default acme challenge domain.
> -    ///
> -    /// This is useful if you use CNAME entries to redirect `_acme-challenge.*` domains to a
> -    /// different DNS server.
> -    #[serde(skip_serializing_if = "Option::is_none")]
> -    pub alias: Option<String>,
> -
> -    /// The plugin to use to validate this domain.
> -    ///
> -    /// Empty means standalone HTTP validation is used.
> -    #[serde(skip_serializing_if = "Option::is_none")]
> -    pub plugin: Option<String>,
> -}
> -
> -pub const ACME_DOMAIN_PROPERTY_SCHEMA: Schema =
> -    StringSchema::new("ACME domain configuration string")
> -        .format(&ApiStringFormat::PropertyString(&AcmeDomain::API_SCHEMA))
> -        .schema();
> -
> -#[api(
> -    properties: {
> -        schema: {
> -            type: Object,
> -            additional_properties: true,
> -            properties: {},
> -        },
> -        type: {
> -            type: String,
> -        },
> -    },
> -)]
> -#[derive(Serialize)]
> -/// Schema for an ACME challenge plugin.
> -pub struct AcmeChallengeSchema {
> -    /// Plugin ID.
> -    pub id: String,
> -
> -    /// Human readable name, falls back to id.
> -    pub name: String,
> -
> -    /// Plugin Type.
> -    #[serde(rename = "type")]
> -    pub ty: &'static str,
> -
> -    /// The plugin's parameter schema.
> -    pub schema: Value,
> -}
> diff --git a/src/api2/types/mod.rs b/src/api2/types/mod.rs
> index afc34b30..34193685 100644
> --- a/src/api2/types/mod.rs
> +++ b/src/api2/types/mod.rs
> @@ -4,9 +4,6 @@ use anyhow::bail;
>  
>  use proxmox_schema::*;
>  
> -mod acme;
> -pub use acme::*;
> -
>  // File names: may not contain slashes, may not start with "."
>  pub const FILENAME_FORMAT: ApiStringFormat = ApiStringFormat::VerifyFn(|name| {
>      if name.starts_with('.') {
> diff --git a/src/config/acme/mod.rs b/src/config/acme/mod.rs
> index 35cda50b..afd7abf8 100644
> --- a/src/config/acme/mod.rs
> +++ b/src/config/acme/mod.rs
> @@ -9,8 +9,7 @@ use proxmox_sys::fs::{file_read_string, CreateOptions};
>  
>  use pbs_api_types::PROXMOX_SAFE_ID_REGEX;
>  
> -use crate::api2::types::AcmeChallengeSchema;
> -use proxmox_acme_api::{AcmeAccountName, KnownAcmeDirectory, KNOWN_ACME_DIRECTORIES};
> +use proxmox_acme_api::{AcmeAccountName, AcmeChallengeSchema};


use pbs_api_types::PROXMOX_SAFE_ID_REGEX;
use proxmox_acme_api::{AcmeAccountName, AcmeChallengeSchema};

>  
>  pub(crate) const ACME_DIR: &str = pbs_buildcfg::configdir!("/acme");
>  pub(crate) const ACME_ACCOUNT_DIR: &str = pbs_buildcfg::configdir!("/acme/accounts");
> @@ -35,8 +34,6 @@ pub(crate) fn make_acme_dir() -> Result<(), Error> {
>      create_acme_subdir(ACME_DIR)
>  }
>  
> -pub const DEFAULT_ACME_DIRECTORY_ENTRY: &KnownAcmeDirectory = &KNOWN_ACME_DIRECTORIES[0];
> -
>  pub fn foreach_acme_account<F>(mut func: F) -> Result<(), Error>
>  where
>      F: FnMut(AcmeAccountName) -> ControlFlow<Result<(), Error>>,
> @@ -80,7 +77,7 @@ pub fn load_dns_challenge_schema() -> Result<Vec<AcmeChallengeSchema>, Error> {
>                  .and_then(Value::as_str)
>                  .unwrap_or(id)
>                  .to_owned(),
> -            ty: "dns",
> +            ty: "dns".into(),
>              schema: schema.to_owned(),
>          })
>          .collect())
> diff --git a/src/config/acme/plugin.rs b/src/config/acme/plugin.rs
> index 18e71199..2e979ffe 100644
> --- a/src/config/acme/plugin.rs
> +++ b/src/config/acme/plugin.rs
> @@ -1,104 +1,15 @@
>  use std::sync::LazyLock;
>  
>  use anyhow::Error;
> -use serde::{Deserialize, Serialize};
> -use serde_json::Value;
> -
> -use proxmox_schema::{api, ApiType, Schema, StringSchema, Updater};
> -use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
> -
> -use pbs_api_types::PROXMOX_SAFE_ID_FORMAT;
>  use pbs_config::{open_backup_lockfile, BackupLockGuard};
> -
> -pub const PLUGIN_ID_SCHEMA: Schema = StringSchema::new("ACME Challenge Plugin ID.")
> -    .format(&PROXMOX_SAFE_ID_FORMAT)
> -    .min_length(1)
> -    .max_length(32)
> -    .schema();
> +use proxmox_acme_api::PLUGIN_ID_SCHEMA;
> +use proxmox_acme_api::{DnsPlugin, StandalonePlugin};
> +use proxmox_schema::{ApiType, Schema};
> +use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
> +use serde_json::Value;

use std::sync::LazyLock;

use anyhow::Error;
use serde::{Deserialize, Serialize};
use serde_json::Value;

use proxmox_acme_api::PLUGIN_ID_SCHEMA;
use proxmox_acme_api::{DnsPlugin, StandalonePlugin};
use proxmox_schema::{ApiType, Schema};
use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};

use pbs_config::{open_backup_lockfile, BackupLockGuard};

>  
>  pub static CONFIG: LazyLock<SectionConfig> = LazyLock::new(init);
>  
> -#[api(
> -    properties: {
> -        id: { schema: PLUGIN_ID_SCHEMA },
> -    },
> -)]
> -#[derive(Deserialize, Serialize)]
> -/// Standalone ACME Plugin for the http-1 challenge.
> -pub struct StandalonePlugin {
> -    /// Plugin ID.
> -    id: String,
> -}
> -
> -impl Default for StandalonePlugin {
> -    fn default() -> Self {
> -        Self {
> -            id: "standalone".to_string(),
> -        }
> -    }
> -}
> -
> -#[api(
> -    properties: {
> -        id: { schema: PLUGIN_ID_SCHEMA },
> -        disable: {
> -            optional: true,
> -            default: false,
> -        },
> -        "validation-delay": {
> -            default: 30,
> -            optional: true,
> -            minimum: 0,
> -            maximum: 2 * 24 * 60 * 60,
> -        },
> -    },
> -)]
> -/// DNS ACME Challenge Plugin core data.
> -#[derive(Deserialize, Serialize, Updater)]
> -#[serde(rename_all = "kebab-case")]
> -pub struct DnsPluginCore {
> -    /// Plugin ID.
> -    #[updater(skip)]
> -    pub id: String,
> -
> -    /// DNS API Plugin Id.
> -    pub api: String,
> -
> -    /// Extra delay in seconds to wait before requesting validation.
> -    ///
> -    /// Allows to cope with long TTL of DNS records.
> -    #[serde(skip_serializing_if = "Option::is_none", default)]
> -    pub validation_delay: Option<u32>,
> -
> -    /// Flag to disable the config.
> -    #[serde(skip_serializing_if = "Option::is_none", default)]
> -    pub disable: Option<bool>,
> -}
> -
> -#[api(
> -    properties: {
> -        core: { type: DnsPluginCore },
> -    },
> -)]
> -/// DNS ACME Challenge Plugin.
> -#[derive(Deserialize, Serialize)]
> -#[serde(rename_all = "kebab-case")]
> -pub struct DnsPlugin {
> -    #[serde(flatten)]
> -    pub core: DnsPluginCore,
> -
> -    // We handle this property separately in the API calls.
> -    /// DNS plugin data (base64url encoded without padding).
> -    #[serde(with = "proxmox_serde::string_as_base64url_nopad")]
> -    pub data: String,
> -}
> -
> -impl DnsPlugin {
> -    pub fn decode_data(&self, output: &mut Vec<u8>) -> Result<(), Error> {
> -        Ok(proxmox_base64::url::decode_to_vec(&self.data, output)?)
> -    }
> -}
> -
>  fn init() -> SectionConfig {
>      let mut config = SectionConfig::new(&PLUGIN_ID_SCHEMA);
>  
> diff --git a/src/config/node.rs b/src/config/node.rs
> index d2a17a49..b9257adf 100644
> --- a/src/config/node.rs
> +++ b/src/config/node.rs
> @@ -6,17 +6,17 @@ use serde::{Deserialize, Serialize};
>  
>  use proxmox_schema::{api, ApiStringFormat, ApiType, Updater};
>  
> -use proxmox_http::ProxyConfig;
> -
>  use pbs_api_types::{
>      EMAIL_SCHEMA, MULTI_LINE_COMMENT_SCHEMA, OPENSSL_CIPHERS_TLS_1_2_SCHEMA,
>      OPENSSL_CIPHERS_TLS_1_3_SCHEMA,
>  };
> +use proxmox_acme_api::{AcmeConfig, AcmeDomain, ACME_DOMAIN_PROPERTY_SCHEMA};
> +use proxmox_http::ProxyConfig;
>  
>  use pbs_buildcfg::configdir;
>  use pbs_config::{open_backup_lockfile, BackupLockGuard};
>  
> -use crate::api2::types::{AcmeDomain, ACME_DOMAIN_PROPERTY_SCHEMA, HTTP_PROXY_SCHEMA};
> +use crate::api2::types::HTTP_PROXY_SCHEMA;
>  use proxmox_acme::async_client::AcmeClient;
>  use proxmox_acme_api::AcmeAccountName;

use serde::{Deserialize, Serialize};

use pbs_api_types::{
    EMAIL_SCHEMA, MULTI_LINE_COMMENT_SCHEMA, OPENSSL_CIPHERS_TLS_1_2_SCHEMA,
    OPENSSL_CIPHERS_TLS_1_3_SCHEMA,
};
use proxmox_acme::async_client::AcmeClient;
use proxmox_acme_api::AcmeAccountName;
use proxmox_acme_api::{AcmeConfig, AcmeDomain, ACME_DOMAIN_PROPERTY_SCHEMA};
use proxmox_http::ProxyConfig;
use proxmox_http::ProxyConfig;
use proxmox_schema::{api, ApiStringFormat, ApiType, Updater};

pbs_buildcfg::configdir;
pbs_config::{open_backup_lockfile, BackupLockGuard};

use crate::api2::types::HTTP_PROXY_SCHEMA;

>  
> @@ -45,20 +45,6 @@ pub fn save_config(config: &NodeConfig) -> Result<(), Error> {
>      pbs_config::replace_backup_config(CONF_FILE, &raw)
>  }
>  
> -#[api(
> -    properties: {
> -        account: { type: AcmeAccountName },
> -    }
> -)]
> -#[derive(Deserialize, Serialize)]
> -/// The ACME configuration.
> -///
> -/// Currently only contains the name of the account use.
> -pub struct AcmeConfig {
> -    /// Account to use to acquire ACME certificates.
> -    account: AcmeAccountName,
> -}
> -
>  /// All available languages in Proxmox. Taken from proxmox-i18n repository.
>  /// pt_BR, zh_CN, and zh_TW use the same case in the translation files.
>  // TODO: auto-generate from available translations
> @@ -244,7 +230,7 @@ impl NodeConfig {
>  
>      pub async fn acme_client(&self) -> Result<AcmeClient, Error> {
>          let account = if let Some(cfg) = self.acme_config().transpose()? {
> -            cfg.account
> +            AcmeAccountName::from_string(cfg.account)?
>          } else {
>              AcmeAccountName::from_string("default".to_string())? // should really not happen
>          };
> diff --git a/src/lib.rs b/src/lib.rs
> index 8633378c..828f5842 100644
> --- a/src/lib.rs
> +++ b/src/lib.rs
> @@ -27,8 +27,6 @@ pub(crate) mod auth;
>  
>  pub mod tape;
>  
> -pub mod acme;
> -
>  pub mod client_helpers;
>  
>  pub mod traffic_control_cache;



_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel


  reply	other threads:[~2025-12-09 16:50 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-03 10:22 [pbs-devel] [PATCH proxmox{-backup, } v4 0/8] fix #6939: acme: support servers returning 204 for nonce requests Samuel Rufinatscha
2025-12-03 10:22 ` [pbs-devel] [PATCH proxmox-backup v4 1/4] acme: include proxmox-acme-api dependency Samuel Rufinatscha
2025-12-03 10:22 ` [pbs-devel] [PATCH proxmox-backup v4 2/4] acme: drop local AcmeClient Samuel Rufinatscha
2025-12-09 16:50   ` Max R. Carrara
2025-12-03 10:22 ` [pbs-devel] [PATCH proxmox-backup v4 3/4] acme: change API impls to use proxmox-acme-api handlers Samuel Rufinatscha
2025-12-09 16:50   ` Max R. Carrara
2025-12-03 10:22 ` [pbs-devel] [PATCH proxmox-backup v4 4/4] acme: certificate ordering through proxmox-acme-api Samuel Rufinatscha
2025-12-09 16:50   ` Max R. Carrara [this message]
2025-12-03 10:22 ` [pbs-devel] [PATCH proxmox v4 1/4] acme-api: add helper to load client for an account Samuel Rufinatscha
2025-12-09 16:51   ` Max R. Carrara
2025-12-10 10:08     ` Samuel Rufinatscha
2025-12-03 10:22 ` [pbs-devel] [PATCH proxmox v4 2/4] acme: reduce visibility of Request type Samuel Rufinatscha
2025-12-09 16:51   ` Max R. Carrara
2025-12-03 10:22 ` [pbs-devel] [PATCH proxmox v4 3/4] acme: introduce http_status module Samuel Rufinatscha
2025-12-03 10:22 ` [pbs-devel] [PATCH proxmox v4 4/4] fix #6939: acme: support servers returning 204 for nonce requests Samuel Rufinatscha
2025-12-09 16:50 ` [pbs-devel] [PATCH proxmox{-backup, } v4 0/8] " Max R. Carrara
2025-12-10  9:44   ` Samuel Rufinatscha

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=DETUAGFMBQ02.1S9DIJMYYJSVC@proxmox.com \
    --to=m.carrara@proxmox.com \
    --cc=pbs-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