From 426fb63c50ada2d11c8a6747063dc4baaf00219a Mon Sep 17 00:00:00 2001 From: Rodolphe Breard Date: Sat, 24 Oct 2020 23:39:20 +0200 Subject: [PATCH] Allow to specify a unique name for each certificate --- CHANGELOG.md | 1 + acmed/src/certificate.rs | 4 +-- acmed/src/config.rs | 61 ++++++++++++++++++------------------ acmed/src/main_event_loop.rs | 9 ++++-- man/en/acmed.toml.5 | 9 ++++++ 5 files changed, 47 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41625e0..b9c2c87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Add proxy support through the `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY` environment variables. +- Allow to specify a unique name for each certificate. ### Changed - The minimal required Rust version is 1.42.0. diff --git a/acmed/src/certificate.rs b/acmed/src/certificate.rs index 845ac2e..71afa34 100644 --- a/acmed/src/certificate.rs +++ b/acmed/src/certificate.rs @@ -22,15 +22,13 @@ pub struct Certificate { pub hooks: Vec, pub crt_name: String, pub env: HashMap, - pub id: usize, pub renew_delay: Duration, pub file_manager: FileManager, } impl fmt::Display for Certificate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // TODO: set a more "funky" id - write!(f, "crt-{:x}", self.id) + write!(f, "{}", self.crt_name) } } diff --git a/acmed/src/config.rs b/acmed/src/config.rs index c630e8d..e228416 100644 --- a/acmed/src/config.rs +++ b/acmed/src/config.rs @@ -176,15 +176,15 @@ impl Config { #[serde(deny_unknown_fields)] pub struct GlobalOptions { pub accounts_directory: Option, - pub certificates_directory: Option, + pub cert_file_group: Option, pub cert_file_mode: Option, pub cert_file_user: Option, - pub cert_file_group: Option, - pub pk_file_mode: Option, - pub pk_file_user: Option, - pub pk_file_group: Option, + pub certificates_directory: Option, #[serde(default)] pub env: HashMap, + pub pk_file_group: Option, + pub pk_file_mode: Option, + pub pk_file_user: Option, pub renew_delay: Option, pub root_certificates: Option>, } @@ -202,12 +202,12 @@ impl GlobalOptions { #[serde(deny_unknown_fields)] pub struct Endpoint { pub name: String, - pub url: String, - pub tos_agreed: bool, #[serde(default)] pub rate_limits: Vec, pub renew_delay: Option, pub root_certificates: Option>, + pub tos_agreed: bool, + pub url: String, } impl Endpoint { @@ -262,16 +262,16 @@ pub struct RateLimit { #[derive(Deserialize)] #[serde(deny_unknown_fields)] pub struct Hook { - pub name: String, - #[serde(rename = "type")] - pub hook_type: Vec, - pub cmd: String, + pub allow_failure: Option, pub args: Option>, + pub cmd: String, + pub name: String, + pub stderr: Option, pub stdin: Option, pub stdin_str: Option, pub stdout: Option, - pub stderr: Option, - pub allow_failure: Option, + #[serde(rename = "type")] + pub hook_type: Vec, } #[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize)] @@ -299,8 +299,8 @@ pub enum HookType { #[derive(Deserialize)] #[serde(deny_unknown_fields)] pub struct Group { - pub name: String, pub hooks: Vec, + pub name: String, } #[derive(Clone, Debug, Deserialize)] @@ -340,14 +340,14 @@ impl ExternalAccount { #[derive(Clone, Debug, Deserialize)] #[serde(deny_unknown_fields)] pub struct Account { - pub name: String, pub contacts: Vec, - pub key_type: Option, - pub signature_algorithm: Option, - pub hooks: Option>, - pub external_account: Option, #[serde(default)] pub env: HashMap, + pub external_account: Option, + pub hooks: Option>, + pub key_type: Option, + pub name: String, + pub signature_algorithm: Option, } impl Account { @@ -407,21 +407,20 @@ impl AccountContact { #[serde(deny_unknown_fields)] pub struct Certificate { pub account: String, + pub csr_digest: Option, + pub directory: Option, pub endpoint: String, - pub identifiers: Vec, #[serde(default)] - pub subject_attributes: SubjectAttributes, + pub env: HashMap, + pub file_name_format: Option, + pub hooks: Vec, + pub identifiers: Vec, pub key_type: Option, - pub csr_digest: Option, pub kp_reuse: Option, - pub directory: Option, pub name: Option, - pub name_format: Option, - pub formats: Option>, - pub hooks: Vec, - #[serde(default)] - pub env: HashMap, pub renew_delay: Option, + #[serde(default)] + pub subject_attributes: SubjectAttributes, } impl Certificate { @@ -465,12 +464,12 @@ impl Certificate { id.to_string() } }; - let name = name.replace("*", "_").replace(":", "_"); + let name = name.replace("*", "_").replace(":", "_").replace("/", "_"); Ok(name) } pub fn get_crt_name_format(&self) -> String { - match &self.name_format { + match &self.file_name_format { Some(n) => n.to_string(), None => crate::DEFAULT_CERT_FORMAT.to_string(), } @@ -534,9 +533,9 @@ impl Certificate { pub struct Identifier { pub challenge: String, pub dns: Option, - pub ip: Option, #[serde(default)] pub env: HashMap, + pub ip: Option, } impl<'de> Deserialize<'de> for Identifier { diff --git a/acmed/src/main_event_loop.rs b/acmed/src/main_event_loop.rs index e8a8eda..669a384 100644 --- a/acmed/src/main_event_loop.rs +++ b/acmed/src/main_event_loop.rs @@ -89,12 +89,16 @@ impl MainEventLoop { accounts.insert(acc.name.clone(), account); } - let mut certs = Vec::new(); + let mut certs: Vec = Vec::new(); let mut endpoints = HashMap::new(); - for (i, crt) in cnf.certificate.iter().enumerate() { + for crt in cnf.certificate.iter() { let endpoint = crt.get_endpoint(&cnf, root_certs)?; let endpoint_name = endpoint.name.clone(); let crt_name = crt.get_crt_name()?; + if certs.iter().any(|c| c.crt_name == crt_name) { + let msg = format!("{}: duplicate certificate name", crt_name); + return Err(msg.into()); + } let key_type = crt.get_key_type()?; let hooks = crt.get_hooks(&cnf)?; let fm = FileManager { @@ -132,7 +136,6 @@ impl MainEventLoop { .collect(), crt_name, env: crt.env.to_owned(), - id: i + 1, renew_delay: crt.get_renew_delay(&cnf)?, file_manager: fm, }; diff --git a/man/en/acmed.toml.5 b/man/en/acmed.toml.5 index d5df4af..ed69496 100644 --- a/man/en/acmed.toml.5 +++ b/man/en/acmed.toml.5 @@ -153,6 +153,15 @@ rsa4096 .El .It Ic kp_reuse Ar boolean Set whether or not the private key should be reused when renewing the certificate. Default is false. +.It Ic name +Name of the certificate. Must be unique. Will be used in logs and in the associated file's name. The +.Sq * , +.So +: +.Sc +and +.Sq / +characters will be replaced by an underscore. Default is the first identifier. .It Cm renew_delay Ar string Period of time between the certificate renewal and its expiration date. The format is described in the .Sx TIME PERIODS