public inbox for pbs-devel@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 3/4] acme: change API impls to use proxmox-acme-api handlers
Date: Tue, 09 Dec 2025 17:50:56 +0100	[thread overview]
Message-ID: <DETUAESP2WI8.3B7Z5QT0GKAOQ@proxmox.com> (raw)
In-Reply-To: <20251203102217.59923-4-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 api2/config/acme.rs API logic with proxmox-acme-api handlers.
> - Drop local caching and helper types that duplicate proxmox-acme-api.
>
> Signed-off-by: Samuel Rufinatscha <s.rufinatscha@proxmox.com>
> ---
>  src/api2/config/acme.rs                | 385 ++-----------------------
>  src/api2/types/acme.rs                 |  16 -
>  src/bin/proxmox_backup_manager/acme.rs |   6 +-
>  src/config/acme/mod.rs                 |  44 +--
>  4 files changed, 35 insertions(+), 416 deletions(-)
>
> diff --git a/src/api2/config/acme.rs b/src/api2/config/acme.rs
> index 02f88e2e..a112c8ee 100644
> --- a/src/api2/config/acme.rs
> +++ b/src/api2/config/acme.rs
> @@ -1,31 +1,17 @@
> -use std::fs;
> -use std::ops::ControlFlow;
> -use std::path::Path;
> -use std::sync::{Arc, LazyLock, Mutex};
> -use std::time::SystemTime;
> -
> -use anyhow::{bail, format_err, Error};
> -use hex::FromHex;
> -use serde::{Deserialize, Serialize};
> -use serde_json::{json, Value};
> -use tracing::{info, warn};
> -
> -use proxmox_router::{
> -    http_bail, list_subdirs_api_method, Permission, Router, RpcEnvironment, SubdirMap,
> -};
> -use proxmox_schema::{api, param_bail};
> -
> -use proxmox_acme::types::AccountData as AcmeAccountData;
> -
> +use anyhow::Error;
>  use pbs_api_types::{Authid, PRIV_SYS_MODIFY};
> -
> -use crate::api2::types::{AcmeChallengeSchema, KnownAcmeDirectory};
> -use crate::config::acme::plugin::{
> -    self, DnsPlugin, DnsPluginCore, DnsPluginCoreUpdater, PLUGIN_ID_SCHEMA,
> +use proxmox_acme_api::{
> +    AccountEntry, AccountInfo, AcmeAccountName, AcmeChallengeSchema, ChallengeSchemaWrapper,
> +    DeletablePluginProperty, DnsPluginCore, DnsPluginCoreUpdater, KnownAcmeDirectory, PluginConfig,
> +    DEFAULT_ACME_DIRECTORY_ENTRY, PLUGIN_ID_SCHEMA,
>  };
> -use proxmox_acme::async_client::AcmeClient;
> -use proxmox_acme_api::AcmeAccountName;
> +use proxmox_config_digest::ConfigDigest;
>  use proxmox_rest_server::WorkerTask;
> +use proxmox_router::{
> +    http_bail, list_subdirs_api_method, Permission, Router, RpcEnvironment, SubdirMap,
> +};
> +use proxmox_schema::api;
> +use tracing::info;

(See my comment to patch 2/4 for an explanation)

use anyhow::Error;
use tracing::info;

use pbs_api_types::{Authid, PRIV_SYS_MODIFY};
use proxmox_acme_api::{
    AccountEntry, AccountInfo, AcmeAccountName, AcmeChallengeSchema, ChallengeSchemaWrapper,
    DeletablePluginProperty, DnsPluginCore, DnsPluginCoreUpdater, KnownAcmeDirectory, PluginConfig,
    DEFAULT_ACME_DIRECTORY_ENTRY, PLUGIN_ID_SCHEMA,
};
use proxmox_config_digest::ConfigDigest;
use proxmox_rest_server::WorkerTask;
use proxmox_router::{
    http_bail, list_subdirs_api_method, Permission, Router, RpcEnvironment, SubdirMap,
};
use proxmox_schema::api;


>  
>  pub(crate) const ROUTER: Router = Router::new()
>      .get(&list_subdirs_api_method!(SUBDIRS))
> @@ -67,19 +53,6 @@ const PLUGIN_ITEM_ROUTER: Router = Router::new()
>      .put(&API_METHOD_UPDATE_PLUGIN)
>      .delete(&API_METHOD_DELETE_PLUGIN);
>  
> -#[api(
> -    properties: {
> -        name: { type: AcmeAccountName },
> -    },
> -)]
> -/// An ACME Account entry.
> -///
> -/// Currently only contains a 'name' property.
> -#[derive(Serialize)]
> -pub struct AccountEntry {
> -    name: AcmeAccountName,
> -}
> -
>  #[api(
>      access: {
>          permission: &Permission::Privilege(&["system", "certificates"], PRIV_SYS_MODIFY, false),
> @@ -93,40 +66,7 @@ pub struct AccountEntry {
>  )]
>  /// List ACME accounts.
>  pub fn list_accounts() -> Result<Vec<AccountEntry>, Error> {
> -    let mut entries = Vec::new();
> -    crate::config::acme::foreach_acme_account(|name| {
> -        entries.push(AccountEntry { name });
> -        ControlFlow::Continue(())
> -    })?;
> -    Ok(entries)
> -}
> -
> -#[api(
> -    properties: {
> -        account: { type: Object, properties: {}, additional_properties: true },
> -        tos: {
> -            type: String,
> -            optional: true,
> -        },
> -    },
> -)]
> -/// ACME Account information.
> -///
> -/// This is what we return via the API.
> -#[derive(Serialize)]
> -pub struct AccountInfo {
> -    /// Raw account data.
> -    account: AcmeAccountData,
> -
> -    /// The ACME directory URL the account was created at.
> -    directory: String,
> -
> -    /// The account's own URL within the ACME directory.
> -    location: String,
> -
> -    /// The ToS URL, if the user agreed to one.
> -    #[serde(skip_serializing_if = "Option::is_none")]
> -    tos: Option<String>,
> +    proxmox_acme_api::list_accounts()
>  }
>  
>  #[api(
> @@ -143,23 +83,7 @@ pub struct AccountInfo {
>  )]
>  /// Return existing ACME account information.
>  pub async fn get_account(name: AcmeAccountName) -> Result<AccountInfo, Error> {
> -    let account_info = proxmox_acme_api::get_account(name).await?;
> -
> -    Ok(AccountInfo {
> -        location: account_info.location,
> -        tos: account_info.tos,
> -        directory: account_info.directory,
> -        account: AcmeAccountData {
> -            only_return_existing: false, // don't actually write this out in case it's set
> -            ..account_info.account
> -        },
> -    })
> -}
> -
> -fn account_contact_from_string(s: &str) -> Vec<String> {
> -    s.split(&[' ', ';', ',', '\0'][..])
> -        .map(|s| format!("mailto:{s}"))
> -        .collect()
> +    proxmox_acme_api::get_account(name).await
>  }
>  
>  #[api(
> @@ -224,15 +148,11 @@ fn register_account(
>          );
>      }
>  
> -    if Path::new(&crate::config::acme::account_path(&name)).exists() {
> +    if std::path::Path::new(&proxmox_acme_api::account_config_filename(&name)).exists() {
>          http_bail!(BAD_REQUEST, "account {} already exists", name);
>      }
>  
> -    let directory = directory.unwrap_or_else(|| {
> -        crate::config::acme::DEFAULT_ACME_DIRECTORY_ENTRY
> -            .url
> -            .to_owned()
> -    });
> +    let directory = directory.unwrap_or_else(|| DEFAULT_ACME_DIRECTORY_ENTRY.url.to_string());
>  
>      WorkerTask::spawn(
>          "acme-register",
> @@ -288,17 +208,7 @@ pub fn update_account(
>          auth_id.to_string(),
>          true,
>          move |_worker| async move {
> -            let data = match contact {
> -                Some(data) => json!({
> -                    "contact": account_contact_from_string(&data),
> -                }),
> -                None => json!({}),
> -            };
> -
> -            proxmox_acme_api::load_client_with_account(&name)
> -                .await?
> -                .update_account(&data)
> -                .await?;
> +            proxmox_acme_api::update_account(&name, contact).await?;
>  
>              Ok(())
>          },
> @@ -336,18 +246,8 @@ pub fn deactivate_account(
>          auth_id.to_string(),
>          true,
>          move |_worker| async move {
> -            match proxmox_acme_api::load_client_with_account(&name)
> -                .await?
> -                .update_account(&json!({"status": "deactivated"}))
> -                .await
> -            {
> -                Ok(_account) => (),
> -                Err(err) if !force => return Err(err),
> -                Err(err) => {
> -                    warn!("error deactivating account {name}, proceeding anyway - {err}");
> -                }
> -            }
> -            crate::config::acme::mark_account_deactivated(&name)?;
> +            proxmox_acme_api::deactivate_account(&name, force).await?;
> +
>              Ok(())
>          },
>      )
> @@ -374,15 +274,7 @@ pub fn deactivate_account(
>  )]
>  /// Get the Terms of Service URL for an ACME directory.
>  async fn get_tos(directory: Option<String>) -> Result<Option<String>, Error> {
> -    let directory = directory.unwrap_or_else(|| {
> -        crate::config::acme::DEFAULT_ACME_DIRECTORY_ENTRY
> -            .url
> -            .to_owned()
> -    });
> -    Ok(AcmeClient::new(directory)
> -        .terms_of_service_url()
> -        .await?
> -        .map(str::to_owned))
> +    proxmox_acme_api::get_tos(directory).await
>  }
>  
>  #[api(
> @@ -397,52 +289,7 @@ async fn get_tos(directory: Option<String>) -> Result<Option<String>, Error> {
>  )]
>  /// Get named known ACME directory endpoints.
>  fn get_directories() -> Result<&'static [KnownAcmeDirectory], Error> {
> -    Ok(crate::config::acme::KNOWN_ACME_DIRECTORIES)
> -}
> -
> -/// Wrapper for efficient Arc use when returning the ACME challenge-plugin schema for serializing
> -struct ChallengeSchemaWrapper {
> -    inner: Arc<Vec<AcmeChallengeSchema>>,
> -}
> -
> -impl Serialize for ChallengeSchemaWrapper {
> -    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
> -    where
> -        S: serde::Serializer,
> -    {
> -        self.inner.serialize(serializer)
> -    }
> -}
> -
> -struct CachedSchema {
> -    schema: Arc<Vec<AcmeChallengeSchema>>,
> -    cached_mtime: SystemTime,
> -}
> -
> -fn get_cached_challenge_schemas() -> Result<ChallengeSchemaWrapper, Error> {
> -    static CACHE: LazyLock<Mutex<Option<CachedSchema>>> = LazyLock::new(|| Mutex::new(None));
> -
> -    // the actual loading code
> -    let mut last = CACHE.lock().unwrap();
> -
> -    let actual_mtime = fs::metadata(crate::config::acme::ACME_DNS_SCHEMA_FN)?.modified()?;
> -
> -    let schema = match &*last {
> -        Some(CachedSchema {
> -            schema,
> -            cached_mtime,
> -        }) if *cached_mtime >= actual_mtime => schema.clone(),
> -        _ => {
> -            let new_schema = Arc::new(crate::config::acme::load_dns_challenge_schema()?);
> -            *last = Some(CachedSchema {
> -                schema: Arc::clone(&new_schema),
> -                cached_mtime: actual_mtime,
> -            });
> -            new_schema
> -        }
> -    };
> -
> -    Ok(ChallengeSchemaWrapper { inner: schema })
> +    Ok(proxmox_acme_api::KNOWN_ACME_DIRECTORIES)
>  }
>  
>  #[api(
> @@ -457,69 +304,7 @@ fn get_cached_challenge_schemas() -> Result<ChallengeSchemaWrapper, Error> {
>  )]
>  /// Get named known ACME directory endpoints.
>  fn get_challenge_schema() -> Result<ChallengeSchemaWrapper, Error> {
> -    get_cached_challenge_schemas()
> -}
> -
> -#[api]
> -#[derive(Default, Deserialize, Serialize)]
> -#[serde(rename_all = "kebab-case")]
> -/// The API's format is inherited from PVE/PMG:
> -pub struct PluginConfig {
> -    /// Plugin ID.
> -    plugin: String,
> -
> -    /// Plugin type.
> -    #[serde(rename = "type")]
> -    ty: String,
> -
> -    /// DNS Api name.
> -    #[serde(skip_serializing_if = "Option::is_none", default)]
> -    api: Option<String>,
> -
> -    /// Plugin configuration data.
> -    #[serde(skip_serializing_if = "Option::is_none", default)]
> -    data: Option<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)]
> -    validation_delay: Option<u32>,
> -
> -    /// Flag to disable the config.
> -    #[serde(skip_serializing_if = "Option::is_none", default)]
> -    disable: Option<bool>,
> -}
> -
> -// See PMG/PVE's $modify_cfg_for_api sub
> -fn modify_cfg_for_api(id: &str, ty: &str, data: &Value) -> PluginConfig {
> -    let mut entry = data.clone();
> -
> -    let obj = entry.as_object_mut().unwrap();
> -    obj.remove("id");
> -    obj.insert("plugin".to_string(), Value::String(id.to_owned()));
> -    obj.insert("type".to_string(), Value::String(ty.to_owned()));
> -
> -    // FIXME: This needs to go once the `Updater` is fixed.
> -    // None of these should be able to fail unless the user changed the files by hand, in which
> -    // case we leave the unmodified string in the Value for now. This will be handled with an error
> -    // later.
> -    if let Some(Value::String(ref mut data)) = obj.get_mut("data") {
> -        if let Ok(new) = proxmox_base64::url::decode_no_pad(&data) {
> -            if let Ok(utf8) = String::from_utf8(new) {
> -                *data = utf8;
> -            }
> -        }
> -    }
> -
> -    // PVE/PMG do this explicitly for ACME plugins...
> -    // obj.insert("digest".to_string(), Value::String(digest.clone()));
> -
> -    serde_json::from_value(entry).unwrap_or_else(|_| PluginConfig {
> -        plugin: "*Error*".to_string(),
> -        ty: "*Error*".to_string(),
> -        ..Default::default()
> -    })
> +    proxmox_acme_api::get_cached_challenge_schemas()
>  }
>  
>  #[api(
> @@ -535,12 +320,7 @@ fn modify_cfg_for_api(id: &str, ty: &str, data: &Value) -> PluginConfig {
>  )]
>  /// List ACME challenge plugins.
>  pub fn list_plugins(rpcenv: &mut dyn RpcEnvironment) -> Result<Vec<PluginConfig>, Error> {
> -    let (plugins, digest) = plugin::config()?;
> -    rpcenv["digest"] = hex::encode(digest).into();
> -    Ok(plugins
> -        .iter()
> -        .map(|(id, (ty, data))| modify_cfg_for_api(id, ty, data))
> -        .collect())
> +    proxmox_acme_api::list_plugins(rpcenv)
>  }
>  
>  #[api(
> @@ -557,13 +337,7 @@ pub fn list_plugins(rpcenv: &mut dyn RpcEnvironment) -> Result<Vec<PluginConfig>
>  )]
>  /// List ACME challenge plugins.
>  pub fn get_plugin(id: String, rpcenv: &mut dyn RpcEnvironment) -> Result<PluginConfig, Error> {
> -    let (plugins, digest) = plugin::config()?;
> -    rpcenv["digest"] = hex::encode(digest).into();
> -
> -    match plugins.get(&id) {
> -        Some((ty, data)) => Ok(modify_cfg_for_api(&id, ty, data)),
> -        None => http_bail!(NOT_FOUND, "no such plugin"),
> -    }
> +    proxmox_acme_api::get_plugin(id, rpcenv)
>  }
>  
>  // Currently we only have "the" standalone plugin and DNS plugins so we can just flatten a
> @@ -595,30 +369,7 @@ pub fn get_plugin(id: String, rpcenv: &mut dyn RpcEnvironment) -> Result<PluginC
>  )]
>  /// Add ACME plugin configuration.
>  pub fn add_plugin(r#type: String, core: DnsPluginCore, data: String) -> Result<(), Error> {
> -    // Currently we only support DNS plugins and the standalone plugin is "fixed":
> -    if r#type != "dns" {
> -        param_bail!("type", "invalid ACME plugin type: {:?}", r#type);
> -    }
> -
> -    let data = String::from_utf8(proxmox_base64::decode(data)?)
> -        .map_err(|_| format_err!("data must be valid UTF-8"))?;
> -
> -    let id = core.id.clone();
> -
> -    let _lock = plugin::lock()?;
> -
> -    let (mut plugins, _digest) = plugin::config()?;
> -    if plugins.contains_key(&id) {
> -        param_bail!("id", "ACME plugin ID {:?} already exists", id);
> -    }
> -
> -    let plugin = serde_json::to_value(DnsPlugin { core, data })?;
> -
> -    plugins.insert(id, r#type, plugin);
> -
> -    plugin::save_config(&plugins)?;
> -
> -    Ok(())
> +    proxmox_acme_api::add_plugin(r#type, core, data)
>  }
>  
>  #[api(
> @@ -634,26 +385,7 @@ pub fn add_plugin(r#type: String, core: DnsPluginCore, data: String) -> Result<(
>  )]
>  /// Delete an ACME plugin configuration.
>  pub fn delete_plugin(id: String) -> Result<(), Error> {
> -    let _lock = plugin::lock()?;
> -
> -    let (mut plugins, _digest) = plugin::config()?;
> -    if plugins.remove(&id).is_none() {
> -        http_bail!(NOT_FOUND, "no such plugin");
> -    }
> -    plugin::save_config(&plugins)?;
> -
> -    Ok(())
> -}
> -
> -#[api()]
> -#[derive(Serialize, Deserialize)]
> -#[serde(rename_all = "kebab-case")]
> -/// Deletable property name
> -pub enum DeletableProperty {
> -    /// Delete the disable property
> -    Disable,
> -    /// Delete the validation-delay property
> -    ValidationDelay,
> +    proxmox_acme_api::delete_plugin(id)
>  }
>  
>  #[api(
> @@ -675,12 +407,12 @@ pub enum DeletableProperty {
>                  type: Array,
>                  optional: true,
>                  items: {
> -                    type: DeletableProperty,
> +                    type: DeletablePluginProperty,
>                  }
>              },
>              digest: {
> -                description: "Digest to protect against concurrent updates",
>                  optional: true,
> +                type: ConfigDigest,
>              },
>          },
>      },
> @@ -694,65 +426,8 @@ pub fn update_plugin(
>      id: String,
>      update: DnsPluginCoreUpdater,
>      data: Option<String>,
> -    delete: Option<Vec<DeletableProperty>>,
> -    digest: Option<String>,
> +    delete: Option<Vec<DeletablePluginProperty>>,
> +    digest: Option<ConfigDigest>,
>  ) -> Result<(), Error> {
> -    let data = data
> -        .as_deref()
> -        .map(proxmox_base64::decode)
> -        .transpose()?
> -        .map(String::from_utf8)
> -        .transpose()
> -        .map_err(|_| format_err!("data must be valid UTF-8"))?;
> -
> -    let _lock = plugin::lock()?;
> -
> -    let (mut plugins, expected_digest) = plugin::config()?;
> -
> -    if let Some(digest) = digest {
> -        let digest = <[u8; 32]>::from_hex(digest)?;
> -        crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
> -    }
> -
> -    match plugins.get_mut(&id) {
> -        Some((ty, ref mut entry)) => {
> -            if ty != "dns" {
> -                bail!("cannot update plugin of type {:?}", ty);
> -            }
> -
> -            let mut plugin = DnsPlugin::deserialize(&*entry)?;
> -
> -            if let Some(delete) = delete {
> -                for delete_prop in delete {
> -                    match delete_prop {
> -                        DeletableProperty::ValidationDelay => {
> -                            plugin.core.validation_delay = None;
> -                        }
> -                        DeletableProperty::Disable => {
> -                            plugin.core.disable = None;
> -                        }
> -                    }
> -                }
> -            }
> -            if let Some(data) = data {
> -                plugin.data = data;
> -            }
> -            if let Some(api) = update.api {
> -                plugin.core.api = api;
> -            }
> -            if update.validation_delay.is_some() {
> -                plugin.core.validation_delay = update.validation_delay;
> -            }
> -            if update.disable.is_some() {
> -                plugin.core.disable = update.disable;
> -            }
> -
> -            *entry = serde_json::to_value(plugin)?;
> -        }
> -        None => http_bail!(NOT_FOUND, "no such plugin"),
> -    }
> -
> -    plugin::save_config(&plugins)?;
> -
> -    Ok(())
> +    proxmox_acme_api::update_plugin(id, update, data, delete, digest)
>  }
> diff --git a/src/api2/types/acme.rs b/src/api2/types/acme.rs
> index 7c9063c0..2905b41b 100644
> --- a/src/api2/types/acme.rs
> +++ b/src/api2/types/acme.rs
> @@ -44,22 +44,6 @@ pub const ACME_DOMAIN_PROPERTY_SCHEMA: Schema =
>          .format(&ApiStringFormat::PropertyString(&AcmeDomain::API_SCHEMA))
>          .schema();
>  
> -#[api(
> -    properties: {
> -        name: { type: String },
> -        url: { type: String },
> -    },
> -)]
> -/// An ACME directory endpoint with a name and URL.
> -#[derive(Serialize)]
> -pub struct KnownAcmeDirectory {
> -    /// The ACME directory's name.
> -    pub name: &'static str,
> -
> -    /// The ACME directory's endpoint URL.
> -    pub url: &'static str,
> -}
> -
>  #[api(
>      properties: {
>          schema: {
> diff --git a/src/bin/proxmox_backup_manager/acme.rs b/src/bin/proxmox_backup_manager/acme.rs
> index bb987b26..e7bd67af 100644
> --- a/src/bin/proxmox_backup_manager/acme.rs
> +++ b/src/bin/proxmox_backup_manager/acme.rs
> @@ -8,10 +8,8 @@ use proxmox_schema::api;
>  use proxmox_sys::fs::file_get_contents;
>  
>  use proxmox_acme::async_client::AcmeClient;
> -use proxmox_acme_api::AcmeAccountName;
> +use proxmox_acme_api::{AcmeAccountName, DnsPluginCore, KNOWN_ACME_DIRECTORIES};
>  use proxmox_backup::api2;
> -use proxmox_backup::config::acme::plugin::DnsPluginCore;
> -use proxmox_backup::config::acme::KNOWN_ACME_DIRECTORIES;

use proxmox_acme::async_client::AcmeClient;
use proxmox_acme_api::{AcmeAccountName, DnsPluginCore, KNOWN_ACME_DIRECTORIES};
[...]
use proxmox_sys::fs::file_get_contents;

use proxmox_backup::api2;


>  
>  pub fn acme_mgmt_cli() -> CommandLineInterface {
>      let cmd_def = CliCommandMap::new()
> @@ -122,7 +120,7 @@ async fn register_account(
>  
>                  match input.trim().parse::<usize>() {
>                      Ok(n) if n < KNOWN_ACME_DIRECTORIES.len() => {
> -                        break (KNOWN_ACME_DIRECTORIES[n].url.to_owned(), false);
> +                        break (KNOWN_ACME_DIRECTORIES[n].url.to_string(), false);
>                      }
>                      Ok(n) if n == KNOWN_ACME_DIRECTORIES.len() => {
>                          input.clear();
> diff --git a/src/config/acme/mod.rs b/src/config/acme/mod.rs
> index d31b2bc9..35cda50b 100644
> --- a/src/config/acme/mod.rs
> +++ b/src/config/acme/mod.rs
> @@ -1,8 +1,7 @@
>  use std::collections::HashMap;
>  use std::ops::ControlFlow;
> -use std::path::Path;
>  
> -use anyhow::{bail, format_err, Error};
> +use anyhow::Error;
>  use serde_json::Value;
>  
>  use proxmox_sys::error::SysError;

This here is alright 🎉

> @@ -10,8 +9,8 @@ use proxmox_sys::fs::{file_read_string, CreateOptions};
>  
>  use pbs_api_types::PROXMOX_SAFE_ID_REGEX;
>  
> -use crate::api2::types::{AcmeChallengeSchema, KnownAcmeDirectory};
> -use proxmox_acme_api::AcmeAccountName;
> +use crate::api2::types::AcmeChallengeSchema;
> +use proxmox_acme_api::{AcmeAccountName, KnownAcmeDirectory, KNOWN_ACME_DIRECTORIES};

use pbs_api_types::PROXMOX_SAFE_ID_REGEX;
use proxmox_acme_api::{AcmeAccountName, KnownAcmeDirectory, KNOWN_ACME_DIRECTORIES};

use crate::api2::types::AcmeChallengeSchema;

>  
>  pub(crate) const ACME_DIR: &str = pbs_buildcfg::configdir!("/acme");
>  pub(crate) const ACME_ACCOUNT_DIR: &str = pbs_buildcfg::configdir!("/acme/accounts");
> @@ -36,23 +35,8 @@ pub(crate) fn make_acme_dir() -> Result<(), Error> {
>      create_acme_subdir(ACME_DIR)
>  }
>  
> -pub const KNOWN_ACME_DIRECTORIES: &[KnownAcmeDirectory] = &[
> -    KnownAcmeDirectory {
> -        name: "Let's Encrypt V2",
> -        url: "https://acme-v02.api.letsencrypt.org/directory",
> -    },
> -    KnownAcmeDirectory {
> -        name: "Let's Encrypt V2 Staging",
> -        url: "https://acme-staging-v02.api.letsencrypt.org/directory",
> -    },
> -];
> -
>  pub const DEFAULT_ACME_DIRECTORY_ENTRY: &KnownAcmeDirectory = &KNOWN_ACME_DIRECTORIES[0];
>  
> -pub fn account_path(name: &str) -> String {
> -    format!("{ACME_ACCOUNT_DIR}/{name}")
> -}
> -
>  pub fn foreach_acme_account<F>(mut func: F) -> Result<(), Error>
>  where
>      F: FnMut(AcmeAccountName) -> ControlFlow<Result<(), Error>>,
> @@ -83,28 +67,6 @@ where
>      }
>  }
>  
> -pub fn mark_account_deactivated(name: &str) -> Result<(), Error> {
> -    let from = account_path(name);
> -    for i in 0..100 {
> -        let to = account_path(&format!("_deactivated_{name}_{i}"));
> -        if !Path::new(&to).exists() {
> -            return std::fs::rename(&from, &to).map_err(|err| {
> -                format_err!(
> -                    "failed to move account path {:?} to {:?} - {}",
> -                    from,
> -                    to,
> -                    err
> -                )
> -            });
> -        }
> -    }
> -    bail!(
> -        "No free slot to rename deactivated account {:?}, please cleanup {:?}",
> -        from,
> -        ACME_ACCOUNT_DIR
> -    );
> -}
> -
>  pub fn load_dns_challenge_schema() -> Result<Vec<AcmeChallengeSchema>, Error> {
>      let raw = file_read_string(ACME_DNS_SCHEMA_FN)?;
>      let schemas: serde_json::Map<String, Value> = serde_json::from_str(&raw)?;



_______________________________________________
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 [this message]
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
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=DETUAESP2WI8.3B7Z5QT0GKAOQ@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 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