move basic ACME types into src/api2/types/acme.rs
And rename AccountName into AcmeAccountName.
This commit is contained in:
parent
603aa09d54
commit
39c5db7f0f
@ -17,7 +17,8 @@ use proxmox_acme_rs::order::{Order, OrderData};
|
|||||||
use proxmox_acme_rs::Request as AcmeRequest;
|
use proxmox_acme_rs::Request as AcmeRequest;
|
||||||
use proxmox_acme_rs::{Account, Authorization, Challenge, Directory, Error, ErrorResponse};
|
use proxmox_acme_rs::{Account, Authorization, Challenge, Directory, Error, ErrorResponse};
|
||||||
|
|
||||||
use crate::config::acme::{account_path, AccountName};
|
use crate::api2::types::AcmeAccountName;
|
||||||
|
use crate::config::acme::account_path;
|
||||||
use crate::tools::http::SimpleHttp;
|
use crate::tools::http::SimpleHttp;
|
||||||
|
|
||||||
/// Our on-disk format inherited from PVE's proxmox-acme code.
|
/// Our on-disk format inherited from PVE's proxmox-acme code.
|
||||||
@ -76,7 +77,7 @@ impl AcmeClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Load an existing ACME account by name.
|
/// Load an existing ACME account by name.
|
||||||
pub async fn load(account_name: &AccountName) -> Result<Self, anyhow::Error> {
|
pub async fn load(account_name: &AcmeAccountName) -> Result<Self, anyhow::Error> {
|
||||||
Self::load_path(account_path(account_name.as_ref())).await
|
Self::load_path(account_path(account_name.as_ref())).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +99,7 @@ impl AcmeClient {
|
|||||||
|
|
||||||
pub async fn new_account<'a>(
|
pub async fn new_account<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
account_name: &AccountName,
|
account_name: &AcmeAccountName,
|
||||||
tos_agreed: bool,
|
tos_agreed: bool,
|
||||||
contact: Vec<String>,
|
contact: Vec<String>,
|
||||||
rsa_bits: Option<u32>,
|
rsa_bits: Option<u32>,
|
||||||
|
@ -11,7 +11,7 @@ use tokio::process::Command;
|
|||||||
use proxmox_acme_rs::{Authorization, Challenge};
|
use proxmox_acme_rs::{Authorization, Challenge};
|
||||||
|
|
||||||
use crate::acme::AcmeClient;
|
use crate::acme::AcmeClient;
|
||||||
use crate::config::acme::AcmeDomain;
|
use crate::api2::types::AcmeDomain;
|
||||||
use crate::server::WorkerTask;
|
use crate::server::WorkerTask;
|
||||||
|
|
||||||
use crate::config::acme::plugin::{DnsPlugin, PluginData};
|
use crate::config::acme::plugin::{DnsPlugin, PluginData};
|
||||||
|
@ -14,12 +14,11 @@ use proxmox_acme_rs::account::AccountData as AcmeAccountData;
|
|||||||
use proxmox_acme_rs::Account;
|
use proxmox_acme_rs::Account;
|
||||||
|
|
||||||
use crate::acme::AcmeClient;
|
use crate::acme::AcmeClient;
|
||||||
use crate::api2::types::Authid;
|
|
||||||
use crate::config::acl::PRIV_SYS_MODIFY;
|
use crate::config::acl::PRIV_SYS_MODIFY;
|
||||||
use crate::config::acme::plugin::{
|
use crate::config::acme::plugin::{
|
||||||
DnsPlugin, DnsPluginCore, DnsPluginCoreUpdater, PLUGIN_ID_SCHEMA,
|
DnsPlugin, DnsPluginCore, DnsPluginCoreUpdater, PLUGIN_ID_SCHEMA,
|
||||||
};
|
};
|
||||||
use crate::config::acme::{AccountName, KnownAcmeDirectory};
|
use crate::api2::types::{Authid, KnownAcmeDirectory, AcmeAccountName};
|
||||||
use crate::server::WorkerTask;
|
use crate::server::WorkerTask;
|
||||||
use crate::tools::ControlFlow;
|
use crate::tools::ControlFlow;
|
||||||
|
|
||||||
@ -65,7 +64,7 @@ const PLUGIN_ITEM_ROUTER: Router = Router::new()
|
|||||||
|
|
||||||
#[api(
|
#[api(
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: AccountName },
|
name: { type: AcmeAccountName },
|
||||||
},
|
},
|
||||||
)]
|
)]
|
||||||
/// An ACME Account entry.
|
/// An ACME Account entry.
|
||||||
@ -73,7 +72,7 @@ const PLUGIN_ITEM_ROUTER: Router = Router::new()
|
|||||||
/// Currently only contains a 'name' property.
|
/// Currently only contains a 'name' property.
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct AccountEntry {
|
pub struct AccountEntry {
|
||||||
name: AccountName,
|
name: AcmeAccountName,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[api(
|
#[api(
|
||||||
@ -128,7 +127,7 @@ pub struct AccountInfo {
|
|||||||
#[api(
|
#[api(
|
||||||
input: {
|
input: {
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: AccountName },
|
name: { type: AcmeAccountName },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
access: {
|
access: {
|
||||||
@ -138,7 +137,7 @@ pub struct AccountInfo {
|
|||||||
protected: true,
|
protected: true,
|
||||||
)]
|
)]
|
||||||
/// Return existing ACME account information.
|
/// Return existing ACME account information.
|
||||||
pub async fn get_account(name: AccountName) -> Result<AccountInfo, Error> {
|
pub async fn get_account(name: AcmeAccountName) -> Result<AccountInfo, Error> {
|
||||||
let client = AcmeClient::load(&name).await?;
|
let client = AcmeClient::load(&name).await?;
|
||||||
let account = client.account()?;
|
let account = client.account()?;
|
||||||
Ok(AccountInfo {
|
Ok(AccountInfo {
|
||||||
@ -162,7 +161,7 @@ fn account_contact_from_string(s: &str) -> Vec<String> {
|
|||||||
input: {
|
input: {
|
||||||
properties: {
|
properties: {
|
||||||
name: {
|
name: {
|
||||||
type: AccountName,
|
type: AcmeAccountName,
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
contact: {
|
contact: {
|
||||||
@ -186,7 +185,7 @@ fn account_contact_from_string(s: &str) -> Vec<String> {
|
|||||||
)]
|
)]
|
||||||
/// Register an ACME account.
|
/// Register an ACME account.
|
||||||
fn register_account(
|
fn register_account(
|
||||||
name: Option<AccountName>,
|
name: Option<AcmeAccountName>,
|
||||||
// Todo: email & email-list schema
|
// Todo: email & email-list schema
|
||||||
contact: String,
|
contact: String,
|
||||||
tos_url: Option<String>,
|
tos_url: Option<String>,
|
||||||
@ -196,7 +195,7 @@ fn register_account(
|
|||||||
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
|
||||||
|
|
||||||
let name = name
|
let name = name
|
||||||
.unwrap_or_else(|| unsafe { AccountName::from_string_unchecked("default".to_string()) });
|
.unwrap_or_else(|| unsafe { AcmeAccountName::from_string_unchecked("default".to_string()) });
|
||||||
|
|
||||||
if Path::new(&crate::config::acme::account_path(&name)).exists() {
|
if Path::new(&crate::config::acme::account_path(&name)).exists() {
|
||||||
http_bail!(BAD_REQUEST, "account {:?} already exists", name);
|
http_bail!(BAD_REQUEST, "account {:?} already exists", name);
|
||||||
@ -233,7 +232,7 @@ fn register_account(
|
|||||||
|
|
||||||
pub async fn do_register_account<'a>(
|
pub async fn do_register_account<'a>(
|
||||||
client: &'a mut AcmeClient,
|
client: &'a mut AcmeClient,
|
||||||
name: &AccountName,
|
name: &AcmeAccountName,
|
||||||
agree_to_tos: bool,
|
agree_to_tos: bool,
|
||||||
contact: String,
|
contact: String,
|
||||||
rsa_bits: Option<u32>,
|
rsa_bits: Option<u32>,
|
||||||
@ -247,7 +246,7 @@ pub async fn do_register_account<'a>(
|
|||||||
#[api(
|
#[api(
|
||||||
input: {
|
input: {
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: AccountName },
|
name: { type: AcmeAccountName },
|
||||||
contact: {
|
contact: {
|
||||||
description: "List of email addresses.",
|
description: "List of email addresses.",
|
||||||
optional: true,
|
optional: true,
|
||||||
@ -261,7 +260,7 @@ pub async fn do_register_account<'a>(
|
|||||||
)]
|
)]
|
||||||
/// Update an ACME account.
|
/// Update an ACME account.
|
||||||
pub fn update_account(
|
pub fn update_account(
|
||||||
name: AccountName,
|
name: AcmeAccountName,
|
||||||
// Todo: email & email-list schema
|
// Todo: email & email-list schema
|
||||||
contact: Option<String>,
|
contact: Option<String>,
|
||||||
rpcenv: &mut dyn RpcEnvironment,
|
rpcenv: &mut dyn RpcEnvironment,
|
||||||
@ -291,7 +290,7 @@ pub fn update_account(
|
|||||||
#[api(
|
#[api(
|
||||||
input: {
|
input: {
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: AccountName },
|
name: { type: AcmeAccountName },
|
||||||
force: {
|
force: {
|
||||||
description:
|
description:
|
||||||
"Delete account data even if the server refuses to deactivate the account.",
|
"Delete account data even if the server refuses to deactivate the account.",
|
||||||
@ -307,7 +306,7 @@ pub fn update_account(
|
|||||||
)]
|
)]
|
||||||
/// Deactivate an ACME account.
|
/// Deactivate an ACME account.
|
||||||
pub fn deactivate_account(
|
pub fn deactivate_account(
|
||||||
name: AccountName,
|
name: AcmeAccountName,
|
||||||
force: bool,
|
force: bool,
|
||||||
rpcenv: &mut dyn RpcEnvironment,
|
rpcenv: &mut dyn RpcEnvironment,
|
||||||
) -> Result<String, Error> {
|
) -> Result<String, Error> {
|
||||||
|
@ -14,8 +14,8 @@ use proxmox::list_subdirs_api_method;
|
|||||||
use crate::acme::AcmeClient;
|
use crate::acme::AcmeClient;
|
||||||
use crate::api2::types::Authid;
|
use crate::api2::types::Authid;
|
||||||
use crate::api2::types::NODE_SCHEMA;
|
use crate::api2::types::NODE_SCHEMA;
|
||||||
|
use crate::api2::types::AcmeDomain;
|
||||||
use crate::config::acl::PRIV_SYS_MODIFY;
|
use crate::config::acl::PRIV_SYS_MODIFY;
|
||||||
use crate::config::acme::AcmeDomain;
|
|
||||||
use crate::config::node::NodeConfig;
|
use crate::config::node::NodeConfig;
|
||||||
use crate::server::WorkerTask;
|
use crate::server::WorkerTask;
|
||||||
use crate::tools::cert;
|
use crate::tools::cert;
|
||||||
|
126
src/api2/types/acme.rs
Normal file
126
src/api2/types/acme.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use anyhow::Error;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use proxmox::api::{api, schema::{Schema, StringSchema, ApiStringFormat}};
|
||||||
|
|
||||||
|
use crate::api2::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: {
|
||||||
|
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(format: &PROXMOX_SAFE_ID_FORMAT)]
|
||||||
|
/// ACME account name.
|
||||||
|
#[derive(Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct AcmeAccountName(String);
|
||||||
|
|
||||||
|
impl AcmeAccountName {
|
||||||
|
pub fn into_string(self) -> String {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_string(name: String) -> Result<Self, Error> {
|
||||||
|
match &Self::API_SCHEMA {
|
||||||
|
Schema::String(s) => s.check_constraints(&name)?,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
Ok(Self(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn from_string_unchecked(name: String) -> Self {
|
||||||
|
Self(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for AcmeAccountName {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &str {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::DerefMut for AcmeAccountName {
|
||||||
|
#[inline]
|
||||||
|
fn deref_mut(&mut self) -> &mut str {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for AcmeAccountName {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.0.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for AcmeAccountName {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for AcmeAccountName {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,9 @@ pub use tape::*;
|
|||||||
mod file_restore;
|
mod file_restore;
|
||||||
pub use file_restore::*;
|
pub use file_restore::*;
|
||||||
|
|
||||||
|
mod acme;
|
||||||
|
pub use acme::*;
|
||||||
|
|
||||||
// File names: may not contain slashes, may not start with "."
|
// File names: may not contain slashes, may not start with "."
|
||||||
pub const FILENAME_FORMAT: ApiStringFormat = ApiStringFormat::VerifyFn(|name| {
|
pub const FILENAME_FORMAT: ApiStringFormat = ApiStringFormat::VerifyFn(|name| {
|
||||||
if name.starts_with('.') {
|
if name.starts_with('.') {
|
||||||
|
@ -8,8 +8,9 @@ use proxmox::tools::fs::file_get_contents;
|
|||||||
|
|
||||||
use proxmox_backup::acme::AcmeClient;
|
use proxmox_backup::acme::AcmeClient;
|
||||||
use proxmox_backup::api2;
|
use proxmox_backup::api2;
|
||||||
|
use proxmox_backup::api2::types::AcmeAccountName;
|
||||||
use proxmox_backup::config::acme::plugin::DnsPluginCoreUpdater;
|
use proxmox_backup::config::acme::plugin::DnsPluginCoreUpdater;
|
||||||
use proxmox_backup::config::acme::{AccountName, KNOWN_ACME_DIRECTORIES};
|
use proxmox_backup::config::acme::KNOWN_ACME_DIRECTORIES;
|
||||||
|
|
||||||
pub fn acme_mgmt_cli() -> CommandLineInterface {
|
pub fn acme_mgmt_cli() -> CommandLineInterface {
|
||||||
let cmd_def = CliCommandMap::new()
|
let cmd_def = CliCommandMap::new()
|
||||||
@ -49,7 +50,7 @@ fn list_accounts(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<(), Er
|
|||||||
#[api(
|
#[api(
|
||||||
input: {
|
input: {
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: AccountName },
|
name: { type: AcmeAccountName },
|
||||||
"output-format": {
|
"output-format": {
|
||||||
schema: OUTPUT_FORMAT,
|
schema: OUTPUT_FORMAT,
|
||||||
optional: true,
|
optional: true,
|
||||||
@ -83,7 +84,7 @@ async fn get_account(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<()
|
|||||||
#[api(
|
#[api(
|
||||||
input: {
|
input: {
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: AccountName },
|
name: { type: AcmeAccountName },
|
||||||
contact: {
|
contact: {
|
||||||
description: "List of email addresses.",
|
description: "List of email addresses.",
|
||||||
},
|
},
|
||||||
@ -97,7 +98,7 @@ async fn get_account(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<()
|
|||||||
)]
|
)]
|
||||||
/// Register an ACME account.
|
/// Register an ACME account.
|
||||||
async fn register_account(
|
async fn register_account(
|
||||||
name: AccountName,
|
name: AcmeAccountName,
|
||||||
contact: String,
|
contact: String,
|
||||||
directory: Option<String>,
|
directory: Option<String>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
@ -169,7 +170,7 @@ async fn register_account(
|
|||||||
#[api(
|
#[api(
|
||||||
input: {
|
input: {
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: AccountName },
|
name: { type: AcmeAccountName },
|
||||||
contact: {
|
contact: {
|
||||||
description: "List of email addresses.",
|
description: "List of email addresses.",
|
||||||
type: String,
|
type: String,
|
||||||
@ -194,7 +195,7 @@ async fn update_account(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result
|
|||||||
#[api(
|
#[api(
|
||||||
input: {
|
input: {
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: AccountName },
|
name: { type: AcmeAccountName },
|
||||||
force: {
|
force: {
|
||||||
description:
|
description:
|
||||||
"Delete account data even if the server refuses to deactivate the account.",
|
"Delete account data even if the server refuses to deactivate the account.",
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::{bail, format_err, Error};
|
use anyhow::{bail, format_err, Error};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use proxmox::api::{api, schema::{Schema, StringSchema, ApiStringFormat}};
|
|
||||||
use proxmox::sys::error::SysError;
|
use proxmox::sys::error::SysError;
|
||||||
use proxmox::tools::fs::CreateOptions;
|
use proxmox::tools::fs::CreateOptions;
|
||||||
|
|
||||||
use crate::api2::types::{
|
use crate::api2::types::{
|
||||||
DNS_ALIAS_FORMAT, DNS_NAME_FORMAT, PROXMOX_SAFE_ID_FORMAT, PROXMOX_SAFE_ID_REGEX,
|
PROXMOX_SAFE_ID_REGEX,
|
||||||
|
KnownAcmeDirectory,
|
||||||
|
AcmeAccountName,
|
||||||
};
|
};
|
||||||
use crate::tools::ControlFlow;
|
use crate::tools::ControlFlow;
|
||||||
|
|
||||||
@ -44,61 +43,6 @@ pub(crate) fn make_acme_account_dir() -> nix::Result<()> {
|
|||||||
create_acme_subdir(ACME_ACCOUNT_DIR)
|
create_acme_subdir(ACME_ACCOUNT_DIR)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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: {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const KNOWN_ACME_DIRECTORIES: &[KnownAcmeDirectory] = &[
|
pub const KNOWN_ACME_DIRECTORIES: &[KnownAcmeDirectory] = &[
|
||||||
KnownAcmeDirectory {
|
KnownAcmeDirectory {
|
||||||
name: "Let's Encrypt V2",
|
name: "Let's Encrypt V2",
|
||||||
@ -116,70 +60,10 @@ pub fn account_path(name: &str) -> String {
|
|||||||
format!("{}/{}", ACME_ACCOUNT_DIR, name)
|
format!("{}/{}", ACME_ACCOUNT_DIR, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[api(format: &PROXMOX_SAFE_ID_FORMAT)]
|
|
||||||
/// ACME account name.
|
|
||||||
#[derive(Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
|
|
||||||
#[serde(transparent)]
|
|
||||||
pub struct AccountName(String);
|
|
||||||
|
|
||||||
impl AccountName {
|
|
||||||
pub fn into_string(self) -> String {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_string(name: String) -> Result<Self, Error> {
|
|
||||||
match &Self::API_SCHEMA {
|
|
||||||
Schema::String(s) => s.check_constraints(&name)?,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
Ok(Self(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn from_string_unchecked(name: String) -> Self {
|
|
||||||
Self(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for AccountName {
|
|
||||||
type Target = str;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn deref(&self) -> &str {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::DerefMut for AccountName {
|
|
||||||
#[inline]
|
|
||||||
fn deref_mut(&mut self) -> &mut str {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<str> for AccountName {
|
|
||||||
#[inline]
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
self.0.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for AccountName {
|
|
||||||
#[inline]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(&self.0, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for AccountName {
|
|
||||||
#[inline]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt::Display::fmt(&self.0, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn foreach_acme_account<F>(mut func: F) -> Result<(), Error>
|
pub fn foreach_acme_account<F>(mut func: F) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
F: FnMut(AccountName) -> ControlFlow<Result<(), Error>>,
|
F: FnMut(AcmeAccountName) -> ControlFlow<Result<(), Error>>,
|
||||||
{
|
{
|
||||||
match crate::tools::fs::scan_subdir(-1, ACME_ACCOUNT_DIR, &PROXMOX_SAFE_ID_REGEX) {
|
match crate::tools::fs::scan_subdir(-1, ACME_ACCOUNT_DIR, &PROXMOX_SAFE_ID_REGEX) {
|
||||||
Ok(files) => {
|
Ok(files) => {
|
||||||
@ -191,7 +75,10 @@ where
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let account_name = AccountName(file_name.to_owned());
|
let account_name = match AcmeAccountName::from_string(file_name.to_owned()) {
|
||||||
|
Ok(account_name) => account_name,
|
||||||
|
Err(_) => continue,
|
||||||
|
};
|
||||||
|
|
||||||
if let ControlFlow::Break(result) = func(account_name) {
|
if let ControlFlow::Break(result) = func(account_name) {
|
||||||
return result;
|
return result;
|
||||||
|
@ -15,6 +15,8 @@ use crate::api2::types::PROXMOX_SAFE_ID_FORMAT;
|
|||||||
|
|
||||||
pub const PLUGIN_ID_SCHEMA: Schema = StringSchema::new("ACME Challenge Plugin ID.")
|
pub const PLUGIN_ID_SCHEMA: Schema = StringSchema::new("ACME Challenge Plugin ID.")
|
||||||
.format(&PROXMOX_SAFE_ID_FORMAT)
|
.format(&PROXMOX_SAFE_ID_FORMAT)
|
||||||
|
.min_length(1)
|
||||||
|
.max_length(32)
|
||||||
.schema();
|
.schema();
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
@ -10,8 +10,8 @@ use proxmox::api::api;
|
|||||||
use proxmox::api::schema::{ApiStringFormat, Updater};
|
use proxmox::api::schema::{ApiStringFormat, Updater};
|
||||||
use proxmox::tools::fs::{replace_file, CreateOptions};
|
use proxmox::tools::fs::{replace_file, CreateOptions};
|
||||||
|
|
||||||
|
use crate::api2::types::{AcmeDomain, AcmeAccountName, ACME_DOMAIN_PROPERTY_SCHEMA};
|
||||||
use crate::acme::AcmeClient;
|
use crate::acme::AcmeClient;
|
||||||
use crate::config::acme::{AccountName, AcmeDomain, ACME_DOMAIN_PROPERTY_SCHEMA};
|
|
||||||
|
|
||||||
const CONF_FILE: &str = configdir!("/node.cfg");
|
const CONF_FILE: &str = configdir!("/node.cfg");
|
||||||
const LOCK_FILE: &str = configdir!("/.node.lck");
|
const LOCK_FILE: &str = configdir!("/.node.lck");
|
||||||
@ -49,7 +49,7 @@ pub fn save_config(config: &NodeConfig) -> Result<(), Error> {
|
|||||||
|
|
||||||
#[api(
|
#[api(
|
||||||
properties: {
|
properties: {
|
||||||
account: { type: AccountName },
|
account: { type: AcmeAccountName },
|
||||||
}
|
}
|
||||||
)]
|
)]
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
@ -58,7 +58,7 @@ pub fn save_config(config: &NodeConfig) -> Result<(), Error> {
|
|||||||
/// Currently only contains the name of the account use.
|
/// Currently only contains the name of the account use.
|
||||||
pub struct AcmeConfig {
|
pub struct AcmeConfig {
|
||||||
/// Account to use to acquire ACME certificates.
|
/// Account to use to acquire ACME certificates.
|
||||||
account: AccountName,
|
account: AcmeAccountName,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[api(
|
#[api(
|
||||||
|
Loading…
Reference in New Issue
Block a user