diff --git a/CHANGELOG.md b/CHANGELOG.md index a1c313b..86f8d81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The delay to renew a certificate before its expiration date can be specified in the configuration using the `renew_delay` parameter at either the certificate, endpoint and global level. - It is now possible to specify IP identifiers (RFC 8738). - The hook templates of type `challenge-*` have a new `identifier_tls_alpn` field which contains, if available, the identifier in a form that is suitable to the TLS ALPN challenge. +- Globing is now supported for configuration files inclusion. ### Changed - In the certificate configuration, the `domains` field has been renamed `identifiers`. diff --git a/acme_common/Cargo.toml b/acme_common/Cargo.toml index 27c072c..2c3882e 100644 --- a/acme_common/Cargo.toml +++ b/acme_common/Cargo.toml @@ -22,6 +22,7 @@ attohttpc = { version = "0.15", default-features = false } base64 = "0.12" daemonize = "0.4" env_logger = "0.7" +glob = "0.3" handlebars = "3.0" log = "0.4" native-tls = "0.2" diff --git a/acme_common/src/error.rs b/acme_common/src/error.rs index f59af98..40690b0 100644 --- a/acme_common/src/error.rs +++ b/acme_common/src/error.rs @@ -93,6 +93,12 @@ impl From for Error { } } +impl From for Error { + fn from(error: glob::PatternError) -> Self { + format!("Pattern error: {}", error).into() + } +} + impl From for Error { fn from(error: handlebars::TemplateRenderError) -> Self { format!("Template error: {}", error).into() diff --git a/acmed/Cargo.toml b/acmed/Cargo.toml index c0bc081..139c74d 100644 --- a/acmed/Cargo.toml +++ b/acmed/Cargo.toml @@ -20,6 +20,7 @@ openssl_dyn = ["acme_common/openssl_dyn", "attohttpc/tls"] acme_common = { path = "../acme_common" } attohttpc = { version = "0.15", default-features = false, features = ["charsets", "json"] } clap = "2.32" +glob = "0.3" handlebars = "3.0" log = "0.4" nom = "5.0" diff --git a/acmed/src/config.rs b/acmed/src/config.rs index fbd3cde..daa23a6 100644 --- a/acmed/src/config.rs +++ b/acmed/src/config.rs @@ -3,6 +3,7 @@ use crate::duration::parse_duration; use crate::hooks; use crate::identifier::IdentifierType; use acme_common::error::Error; +use glob::glob; use log::info; use serde::Deserialize; use std::collections::HashMap; @@ -10,6 +11,7 @@ use std::fmt; use std::fs::{self, File}; use std::io::prelude::*; use std::path::{Path, PathBuf}; +use std::result::Result; use std::time::Duration; macro_rules! set_cfg_attr { @@ -456,11 +458,13 @@ fn init_directories(config: &Config) -> Result<(), Error> { Ok(()) } -fn get_cnf_path(from: &PathBuf, file: &str) -> PathBuf { +fn get_cnf_path(from: &PathBuf, file: &str) -> Result, Error> { let mut path = from.clone(); path.pop(); path.push(file); - path + let err = format!("{:?}: invalid UTF-8 path", path); + let raw_path = path.to_str().ok_or(err)?; + Ok(glob(raw_path)?.filter_map(Result::ok).collect()) } fn read_cnf(path: &PathBuf) -> Result { @@ -470,30 +474,31 @@ fn read_cnf(path: &PathBuf) -> Result { file.read_to_string(&mut contents)?; let mut config: Config = toml::from_str(&contents)?; for cnf_name in config.include.iter() { - let cnf_path = get_cnf_path(path, cnf_name); - let mut add_cnf = read_cnf(&cnf_path)?; - config.endpoint.append(&mut add_cnf.endpoint); - config.rate_limit.append(&mut add_cnf.rate_limit); - config.hook.append(&mut add_cnf.hook); - config.group.append(&mut add_cnf.group); - config.account.append(&mut add_cnf.account); - config.certificate.append(&mut add_cnf.certificate); - if config.global.is_none() { - config.global = add_cnf.global; - } else if let Some(new_glob) = add_cnf.global { - let mut tmp_glob = config.global.clone().unwrap(); - set_cfg_attr!(tmp_glob.accounts_directory, new_glob.accounts_directory); - set_cfg_attr!( - tmp_glob.certificates_directory, - new_glob.certificates_directory - ); - set_cfg_attr!(tmp_glob.cert_file_mode, new_glob.cert_file_mode); - set_cfg_attr!(tmp_glob.cert_file_user, new_glob.cert_file_user); - set_cfg_attr!(tmp_glob.cert_file_group, new_glob.cert_file_group); - set_cfg_attr!(tmp_glob.pk_file_mode, new_glob.pk_file_mode); - set_cfg_attr!(tmp_glob.pk_file_user, new_glob.pk_file_user); - set_cfg_attr!(tmp_glob.pk_file_group, new_glob.pk_file_group); - config.global = Some(tmp_glob); + for cnf_path in get_cnf_path(path, cnf_name)? { + let mut add_cnf = read_cnf(&cnf_path)?; + config.endpoint.append(&mut add_cnf.endpoint); + config.rate_limit.append(&mut add_cnf.rate_limit); + config.hook.append(&mut add_cnf.hook); + config.group.append(&mut add_cnf.group); + config.account.append(&mut add_cnf.account); + config.certificate.append(&mut add_cnf.certificate); + if config.global.is_none() { + config.global = add_cnf.global; + } else if let Some(new_glob) = add_cnf.global { + let mut tmp_glob = config.global.clone().unwrap(); + set_cfg_attr!(tmp_glob.accounts_directory, new_glob.accounts_directory); + set_cfg_attr!( + tmp_glob.certificates_directory, + new_glob.certificates_directory + ); + set_cfg_attr!(tmp_glob.cert_file_mode, new_glob.cert_file_mode); + set_cfg_attr!(tmp_glob.cert_file_user, new_glob.cert_file_user); + set_cfg_attr!(tmp_glob.cert_file_group, new_glob.cert_file_group); + set_cfg_attr!(tmp_glob.pk_file_mode, new_glob.pk_file_mode); + set_cfg_attr!(tmp_glob.pk_file_user, new_glob.pk_file_user); + set_cfg_attr!(tmp_glob.pk_file_group, new_glob.pk_file_group); + config.global = Some(tmp_glob); + } } } Ok(config) diff --git a/man/en/acmed.toml.5 b/man/en/acmed.toml.5 index 176d388..f5aa4ce 100644 --- a/man/en/acmed.toml.5 +++ b/man/en/acmed.toml.5 @@ -29,6 +29,8 @@ and .Em C and all three defines the same global option, the final value will be the one defined in file .Em C . +.Pp +Unix style globing is supported. .It Ic global Table containing the global configuration options. .Bl -tag