acme: use proxmox-acme-plugins and load schema from there

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht 2021-05-10 14:41:12 +02:00
parent 60643023ad
commit d308dc8af7
4 changed files with 69 additions and 13 deletions

1
debian/control vendored
View File

@ -113,6 +113,7 @@ Architecture: any
Depends: fonts-font-awesome,
libjs-extjs (>= 6.0.1),
libjs-qrcodejs (>= 1.20201119),
libproxmox-acme-plugins,
libsgutils2-2,
libzstd1 (>= 1.3.8),
lvm2,

1
debian/control.in vendored
View File

@ -3,6 +3,7 @@ Architecture: any
Depends: fonts-font-awesome,
libjs-extjs (>= 6.0.1),
libjs-qrcodejs (>= 1.20201119),
libproxmox-acme-plugins,
libsgutils2-2,
libzstd1 (>= 1.3.8),
lvm2,

View File

@ -1,6 +1,10 @@
use std::fs;
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::time::SystemTime;
use anyhow::{bail, format_err, Error};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
@ -386,6 +390,43 @@ 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)
}
}
fn get_cached_challenge_schemas() -> Result<ChallengeSchemaWrapper, Error> {
lazy_static! {
static ref CACHE: Mutex<Option<(Arc<Vec<AcmeChallengeSchema>>, SystemTime)>> =
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((schema, cached_mtime)) if *cached_mtime >= actual_mtime => schema.clone(),
_ => {
let new_schema = Arc::new(crate::config::acme::load_dns_challenge_schema()?);
*last = Some((Arc::clone(&new_schema), actual_mtime));
new_schema
}
};
Ok(ChallengeSchemaWrapper { inner: schema })
}
#[api(
access: {
permission: &Permission::Anybody,
@ -397,18 +438,8 @@ fn get_directories() -> Result<&'static [KnownAcmeDirectory], Error> {
},
)]
/// Get named known ACME directory endpoints.
fn get_challenge_schema() -> Result<Vec<AcmeChallengeSchema>, Error> {
let mut out = Vec::new();
crate::config::acme::foreach_dns_plugin(|id| {
out.push(AcmeChallengeSchema {
id: id.to_owned(),
name: id.to_owned(),
ty: "dns",
schema: Value::Object(Default::default()),
});
ControlFlow::Continue(())
})?;
Ok(out)
fn get_challenge_schema() -> Result<ChallengeSchemaWrapper, Error> {
get_cached_challenge_schemas()
}
#[api]

View File

@ -2,12 +2,14 @@ use std::collections::HashMap;
use std::path::Path;
use anyhow::{bail, format_err, Error};
use serde_json::Value;
use proxmox::sys::error::SysError;
use proxmox::tools::fs::CreateOptions;
use proxmox::tools::fs::{CreateOptions, file_read_string};
use crate::api2::types::{
PROXMOX_SAFE_ID_REGEX,
AcmeChallengeSchema,
KnownAcmeDirectory,
AcmeAccountName,
};
@ -16,6 +18,8 @@ use crate::tools::ControlFlow;
pub(crate) const ACME_DIR: &str = configdir!("/acme");
pub(crate) const ACME_ACCOUNT_DIR: &str = configdir!("/acme/accounts");
pub(crate) const ACME_DNS_SCHEMA_FN: &str = "/usr/share/proxmox-acme/dns-challenge-schema.json";
pub mod plugin;
// `const fn`ify this once it is supported in `proxmox`
@ -141,6 +145,25 @@ pub fn mark_account_deactivated(name: &str) -> Result<(), Error> {
);
}
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)?;
Ok(schemas
.iter()
.map(|(id, schema)| AcmeChallengeSchema {
id: id.to_owned(),
name: schema
.get("name")
.and_then(Value::as_str)
.unwrap_or(id)
.to_owned(),
ty: "dns",
schema: schema.to_owned(),
})
.collect())
}
pub fn complete_acme_account(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
let mut out = Vec::new();
let _ = foreach_acme_account(|name| {