From 87d14d1de6980fa608bcc63f617cbf0f1b5f2987 Mon Sep 17 00:00:00 2001 From: kpcyrd Date: Thu, 6 May 2021 12:14:48 +0200 Subject: [PATCH 1/5] Make data directory configurable in config file --- src/args.rs | 10 +++------- src/config.rs | 13 +++++++++++-- src/main.rs | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/args.rs b/src/args.rs index 161bb5e..93c6589 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,5 +1,6 @@ use crate::errors::*; use std::io::stdout; +use std::path::PathBuf; use structopt::clap::{AppSettings, Shell}; use structopt::StructOpt; @@ -37,13 +38,8 @@ pub struct Args { env = "ACME_CHALL_DIR" )] pub chall_dir: String, - #[structopt( - long, - value_name = "path", - default_value = "/var/lib/acme-redirect", - env = "ACME_DATA_DIR" - )] - pub data_dir: String, + #[structopt(long, value_name = "path", env = "ACME_DATA_DIR")] + pub data_dir: Option, #[structopt(long, default_value=LETSENCRYPT, env="ACME_URL")] pub acme_url: String, #[structopt(long, env = "ACME_EMAIL")] diff --git a/src/config.rs b/src/config.rs index 98f77ef..0fe3a09 100644 --- a/src/config.rs +++ b/src/config.rs @@ -27,6 +27,7 @@ pub struct AcmeConfig { #[derive(Debug, Default, PartialEq, Deserialize)] pub struct SystemConfig { + pub data_dir: Option, #[serde(default)] pub exec: Vec, #[serde(default)] @@ -101,7 +102,7 @@ impl Config { } } -pub fn load(args: &Args) -> Result { +pub fn load(args: Args) -> Result { // TODO: none of this is applied yet, we need to change all the arg parsing code for that let path = &args.config; let mut config = load_file::<_, ConfigFile>(path) @@ -111,6 +112,14 @@ pub fn load(args: &Args) -> Result { config.acme.acme_email = args.acme_email.clone(); } + let data_dir = if let Some(data_dir) = args.data_dir { + data_dir + } else if let Some(data_dir) = config.system.data_dir { + data_dir + } else { + PathBuf::from("/var/lib/acme-redirect") + }; + let certs = load_from_folder(&args.config_dir)? .into_iter() .map(|c| c.cert) @@ -122,7 +131,7 @@ pub fn load(args: &Args) -> Result { .acme .renew_if_days_left .unwrap_or(DEFAULT_RENEW_IF_DAYS_LEFT), - data_dir: PathBuf::from(&args.data_dir), + data_dir, chall_dir: PathBuf::from(&args.chall_dir), certs, exec: config.system.exec, diff --git a/src/main.rs b/src/main.rs index f07f03c..8e159ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,7 +22,7 @@ fn main() -> Result<()> { match args.subcommand.clone() { SubCommand::Cmds(subcommand) => { - let config = config::load(&args)?; + let config = config::load(args)?; trace!("Loaded runtime config: {:?}", config); match subcommand { From 05f59740260e40f738c27205d380d7921050b64c Mon Sep 17 00:00:00 2001 From: kpcyrd Date: Thu, 6 May 2021 12:25:31 +0200 Subject: [PATCH 2/5] Make challenge directory configurable in config file --- src/args.rs | 9 ++------- src/config.rs | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/args.rs b/src/args.rs index 93c6589..e52c82d 100644 --- a/src/args.rs +++ b/src/args.rs @@ -31,13 +31,8 @@ pub struct Args { env = "ACME_CONFIG_DIR" )] pub config_dir: String, - #[structopt( - long, - value_name = "path", - default_value = "/run/acme-redirect", - env = "ACME_CHALL_DIR" - )] - pub chall_dir: String, + #[structopt(long, value_name = "path", env = "ACME_CHALL_DIR")] + pub chall_dir: Option, #[structopt(long, value_name = "path", env = "ACME_DATA_DIR")] pub data_dir: Option, #[structopt(long, default_value=LETSENCRYPT, env="ACME_URL")] diff --git a/src/config.rs b/src/config.rs index 0fe3a09..1a0c706 100644 --- a/src/config.rs +++ b/src/config.rs @@ -28,6 +28,7 @@ pub struct AcmeConfig { #[derive(Debug, Default, PartialEq, Deserialize)] pub struct SystemConfig { pub data_dir: Option, + pub chall_dir: Option, #[serde(default)] pub exec: Vec, #[serde(default)] @@ -102,6 +103,24 @@ impl Config { } } +// We resolve the path in this order: +// - Commandline argument, if set +// - Config file value, if set +// - Builtin default +fn resolve_path_from_arg_config_default( + arg: Option, + config: Option, + default: &str, +) -> PathBuf { + if let Some(path) = arg { + path + } else if let Some(path) = config { + path + } else { + PathBuf::from(default) + } +} + pub fn load(args: Args) -> Result { // TODO: none of this is applied yet, we need to change all the arg parsing code for that let path = &args.config; @@ -112,13 +131,16 @@ pub fn load(args: Args) -> Result { config.acme.acme_email = args.acme_email.clone(); } - let data_dir = if let Some(data_dir) = args.data_dir { - data_dir - } else if let Some(data_dir) = config.system.data_dir { - data_dir - } else { - PathBuf::from("/var/lib/acme-redirect") - }; + let data_dir = resolve_path_from_arg_config_default( + args.data_dir, + config.system.data_dir, + "/var/lib/acme-redirect", + ); + let chall_dir = resolve_path_from_arg_config_default( + args.chall_dir, + config.system.chall_dir, + "/run/acme-redirect", + ); let certs = load_from_folder(&args.config_dir)? .into_iter() @@ -132,7 +154,7 @@ pub fn load(args: Args) -> Result { .renew_if_days_left .unwrap_or(DEFAULT_RENEW_IF_DAYS_LEFT), data_dir, - chall_dir: PathBuf::from(&args.chall_dir), + chall_dir, certs, exec: config.system.exec, exec_extra: config.system.exec_extra, From 4b2e203d36b39093b1ae9bef7e0e3b279a109e0d Mon Sep 17 00:00:00 2001 From: kpcyrd Date: Thu, 6 May 2021 15:44:02 +0200 Subject: [PATCH 3/5] Use config crate to resolve settings --- Cargo.lock | 53 +++++++++++++++++++++++++++-- Cargo.toml | 1 + src/args.rs | 12 +++---- src/chall.rs | 5 +-- src/config.rs | 91 ++++++++++++++++++++------------------------------ src/daemon.rs | 2 +- src/persist.rs | 2 +- src/renew.rs | 10 +++--- src/status.rs | 12 +++---- 9 files changed, 107 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d65beb..20ea8b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,7 @@ dependencies = [ "boxxy", "caps 0.5.1", "colored", + "config", "env_logger", "log", "nix 0.20.0", @@ -341,6 +342,12 @@ version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "async-trait" version = "0.1.50" @@ -652,6 +659,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "config" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" +dependencies = [ + "lazy_static", + "nom", + "serde", + "toml", +] + [[package]] name = "const_fn" version = "0.4.7" @@ -1374,6 +1393,19 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if 1.0.0", + "ryu", + "static_assertions", +] + [[package]] name = "libc" version = "0.2.94" @@ -1562,6 +1594,17 @@ dependencies = [ "libc", ] +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "lexical-core", + "memchr", + "version_check", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -1612,9 +1655,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.62" +version = "0.9.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52160d45fa2e7608d504b7c3a3355afed615e6d8b627a74458634ba21b69bd" +checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98" dependencies = [ "autocfg", "cc", @@ -2229,6 +2272,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "stdweb" version = "0.4.20" diff --git a/Cargo.toml b/Cargo.toml index 51a8ae5..35edfe3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ users = "0.11" rand = "0.8" ureq = "1.1.1" pem = "0.8" +config = { version = "0.11", default-features = false, features = ["toml"] } [target.'cfg(target_os="linux")'.dependencies] caps = "0.5" diff --git a/src/args.rs b/src/args.rs index e52c82d..ac9a7d6 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,12 +1,8 @@ use crate::errors::*; use std::io::stdout; -use std::path::PathBuf; use structopt::clap::{AppSettings, Shell}; use structopt::StructOpt; -const LETSENCRYPT: &str = "https://acme-v02.api.letsencrypt.org/directory"; -// const LETSENCRYPT_STAGING: &str = "https://acme-staging-v02.api.letsencrypt.org/directory"; - #[derive(Debug, StructOpt)] #[structopt(global_settings = &[AppSettings::ColoredHelp])] pub struct Args { @@ -32,11 +28,11 @@ pub struct Args { )] pub config_dir: String, #[structopt(long, value_name = "path", env = "ACME_CHALL_DIR")] - pub chall_dir: Option, + pub chall_dir: Option, #[structopt(long, value_name = "path", env = "ACME_DATA_DIR")] - pub data_dir: Option, - #[structopt(long, default_value=LETSENCRYPT, env="ACME_URL")] - pub acme_url: String, + pub data_dir: Option, + #[structopt(long, env = "ACME_URL")] + pub acme_url: Option, #[structopt(long, env = "ACME_EMAIL")] pub acme_email: Option, #[structopt(subcommand)] diff --git a/src/chall.rs b/src/chall.rs index 501038d..50808aa 100644 --- a/src/chall.rs +++ b/src/chall.rs @@ -2,7 +2,7 @@ use crate::config::Config; use crate::errors::*; use rand::seq::SliceRandom; use std::fs; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; const VALID_CHARS: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; @@ -18,9 +18,10 @@ pub struct Challenge { impl Challenge { pub fn new(config: &Config) -> Challenge { + let chall_dir = Path::new(&config.system.chall_dir); // TODO: consider creating the directory Challenge { - path: config.chall_dir.join("challs"), + path: chall_dir.join("challs"), written: Vec::new(), } } diff --git a/src/config.rs b/src/config.rs index 1a0c706..be52cf2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,8 +6,9 @@ use std::collections::HashSet; use std::ffi::OsStr; use std::fs; use std::path::Path; -use std::path::PathBuf; +const LETSENCRYPT: &str = "https://acme-v02.api.letsencrypt.org/directory"; +// const LETSENCRYPT_STAGING: &str = "https://acme-staging-v02.api.letsencrypt.org/directory"; pub const DEFAULT_RENEW_IF_DAYS_LEFT: i64 = 30; #[derive(Debug, PartialEq, Deserialize)] @@ -21,14 +22,14 @@ pub struct ConfigFile { #[derive(Debug, Default, PartialEq, Deserialize)] pub struct AcmeConfig { pub acme_email: Option, - pub acme_url: Option, - pub renew_if_days_left: Option, + pub acme_url: String, + pub renew_if_days_left: i64, } #[derive(Debug, Default, PartialEq, Deserialize)] pub struct SystemConfig { - pub data_dir: Option, - pub chall_dir: Option, + pub data_dir: String, + pub chall_dir: String, #[serde(default)] pub exec: Vec, #[serde(default)] @@ -80,16 +81,11 @@ pub struct CertConfig { pub exec: Vec, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Config { pub certs: Vec, - pub acme_email: Option, - pub acme_url: String, - pub renew_if_days_left: i64, - pub data_dir: PathBuf, - pub chall_dir: PathBuf, - pub exec: Vec, - pub exec_extra: Vec, + pub acme: AcmeConfig, + pub system: SystemConfig, } impl Config { @@ -103,61 +99,46 @@ impl Config { } } -// We resolve the path in this order: -// - Commandline argument, if set -// - Config file value, if set -// - Builtin default -fn resolve_path_from_arg_config_default( - arg: Option, - config: Option, - default: &str, -) -> PathBuf { - if let Some(path) = arg { - path - } else if let Some(path) = config { - path - } else { - PathBuf::from(default) - } -} - pub fn load(args: Args) -> Result { - // TODO: none of this is applied yet, we need to change all the arg parsing code for that + let mut settings = config::Config::default(); + + settings.set_default("acme.acme_url", LETSENCRYPT)?; + settings.set_default("acme.renew_if_days_left", DEFAULT_RENEW_IF_DAYS_LEFT)?; + + settings.set_default("system.data_dir", "/var/lib/acme-redirect")?; + settings.set_default("system.chall_dir", "/run/acme-redirect")?; + let path = &args.config; - let mut config = load_file::<_, ConfigFile>(path) + settings + .merge(config::File::new(path, config::FileFormat::Toml)) .with_context(|| anyhow!("Failed to load config file {:?}", path))?; - if args.acme_email.is_some() { - config.acme.acme_email = args.acme_email.clone(); + if let Some(acme_email) = args.acme_email { + settings.set("acme.acme_email", acme_email)?; + } + if let Some(acme_url) = args.acme_url { + settings.set("acme.acme_url", acme_url)?; + } + if let Some(data_dir) = args.data_dir { + settings.set("system.data_dir", data_dir)?; + } + if let Some(chall_dir) = args.chall_dir { + settings.set("system.chall_dir", chall_dir)?; } - let data_dir = resolve_path_from_arg_config_default( - args.data_dir, - config.system.data_dir, - "/var/lib/acme-redirect", - ); - let chall_dir = resolve_path_from_arg_config_default( - args.chall_dir, - config.system.chall_dir, - "/run/acme-redirect", - ); + let config = settings + .try_into::() + .context("Failed to parse config")?; let certs = load_from_folder(&args.config_dir)? .into_iter() .map(|c| c.cert) .collect(); + Ok(Config { - acme_email: args.acme_email.clone(), - acme_url: args.acme_url.to_string(), - renew_if_days_left: config - .acme - .renew_if_days_left - .unwrap_or(DEFAULT_RENEW_IF_DAYS_LEFT), - data_dir, - chall_dir, certs, - exec: config.system.exec, - exec_extra: config.system.exec_extra, + acme: config.acme, + system: config.system, }) } diff --git a/src/daemon.rs b/src/daemon.rs index a8d10ef..9ebb042 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -93,7 +93,7 @@ pub async fn spawn(socket: TcpListener) -> Result<()> { } pub fn run(config: Config, args: DaemonArgs) -> Result<()> { - env::set_current_dir(&config.chall_dir)?; + env::set_current_dir(&config.system.chall_dir)?; let socket = TcpListener::bind(&args.bind_addr).context("Failed to bind socket")?; sandbox::init(&args).context("Failed to drop privileges")?; spawn(socket) diff --git a/src/persist.rs b/src/persist.rs index 33b10c7..114e2f1 100644 --- a/src/persist.rs +++ b/src/persist.rs @@ -21,7 +21,7 @@ pub struct FilePersist { impl FilePersist { pub fn new(config: &Config) -> FilePersist { FilePersist { - path: config.data_dir.to_owned(), + path: PathBuf::from(&config.system.data_dir), } } diff --git a/src/renew.rs b/src/renew.rs index a81ec5f..1272635 100644 --- a/src/renew.rs +++ b/src/renew.rs @@ -20,7 +20,7 @@ fn should_request_cert( Ok(true) } else if let Some(existing) = persist.load_cert_info(&cert.name)? { let days_left = existing.days_left(); - if days_left <= config.renew_if_days_left { + if days_left <= config.acme.renew_if_days_left { info!("{:?}: existing cert is below threshold", cert.name); Ok(true) } else { @@ -75,8 +75,8 @@ fn renew_cert( persist.clone(), &mut challenge, &acme::Request { - account_email: config.acme_email.as_deref(), - acme_url: &config.acme_url, + account_email: config.acme.acme_email.as_deref(), + acme_url: &config.acme.acme_url, primary_name: &cert.name, alt_names: &cert.dns_names, }, @@ -91,11 +91,11 @@ fn renew_cert( execute_hooks(&cert.exec, args.dry_run)?; } else { debug!("Executing global default hooks"); - execute_hooks(&config.exec, args.dry_run)?; + execute_hooks(&config.system.exec, args.dry_run)?; } debug!("Executing global exec_extra hooks"); - execute_hooks(&config.exec_extra, args.dry_run)?; + execute_hooks(&config.system.exec_extra, args.dry_run)?; } Ok(()) diff --git a/src/status.rs b/src/status.rs index a4e804b..a4ec36f 100644 --- a/src/status.rs +++ b/src/status.rs @@ -3,16 +3,14 @@ use crate::errors::*; use crate::persist::FilePersist; use colored::Colorize; use nix::unistd::AccessFlags; +use std::path::Path; pub fn run(config: Config) -> Result<()> { let persist = FilePersist::new(&config); - nix::unistd::access(&config.data_dir, AccessFlags::X_OK).with_context(|| { - anyhow!( - "Detected insufficient permissions to access {:?}", - config.data_dir - ) - })?; + let data_dir = Path::new(&config.system.data_dir); + nix::unistd::access(data_dir, AccessFlags::X_OK) + .with_context(|| anyhow!("Detected insufficient permissions to access {:?}", data_dir))?; for cert in config.certs { let name = cert.name; @@ -20,7 +18,7 @@ pub fn run(config: Config) -> Result<()> { if let Some(cert) = persist.load_cert_info(&name)? { let days_left = cert.days_left(); let status = format!("{} days left", days_left); - let status = if days_left > config.renew_if_days_left { + let status = if days_left > config.acme.renew_if_days_left { status.green() } else if days_left > 0 { status.yellow() From d7e0cac3519d70c285d1b23f334a3ca721d66418 Mon Sep 17 00:00:00 2001 From: kpcyrd Date: Thu, 6 May 2021 15:56:28 +0200 Subject: [PATCH 4/5] Add subcommand to dump config as json --- Cargo.lock | 1 + Cargo.toml | 1 + src/args.rs | 2 ++ src/config.rs | 10 +++++----- src/main.rs | 5 +++++ 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20ea8b5..1d35fd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,7 @@ dependencies = [ "pem", "rand 0.8.3", "serde", + "serde_json", "structopt", "time 0.1.43", "toml", diff --git a/Cargo.toml b/Cargo.toml index 35edfe3..cd76eb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ rand = "0.8" ureq = "1.1.1" pem = "0.8" config = { version = "0.11", default-features = false, features = ["toml"] } +serde_json = "1" [target.'cfg(target_os="linux")'.dependencies] caps = "0.5" diff --git a/src/args.rs b/src/args.rs index ac9a7d6..d26d631 100644 --- a/src/args.rs +++ b/src/args.rs @@ -57,6 +57,8 @@ pub enum Cmd { Renew(RenewArgs), /// Check if the challenges could be completed Check(CheckArgs), + /// Load the configuration and dump it to stdout as json + DumpConfig, } #[derive(Debug, Clone, StructOpt)] diff --git a/src/config.rs b/src/config.rs index be52cf2..ac99099 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ use crate::args::Args; use crate::errors::*; use serde::de::DeserializeOwned; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::collections::HashSet; use std::ffi::OsStr; use std::fs; @@ -19,14 +19,14 @@ pub struct ConfigFile { pub system: SystemConfig, } -#[derive(Debug, Default, PartialEq, Deserialize)] +#[derive(Debug, Default, PartialEq, Serialize, Deserialize)] pub struct AcmeConfig { pub acme_email: Option, pub acme_url: String, pub renew_if_days_left: i64, } -#[derive(Debug, Default, PartialEq, Deserialize)] +#[derive(Debug, Default, PartialEq, Serialize, Deserialize)] pub struct SystemConfig { pub data_dir: String, pub chall_dir: String, @@ -71,7 +71,7 @@ fn load_from_folder>(path: P) -> Result> { Ok(configs) } -#[derive(Debug, PartialEq, Clone, Deserialize)] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct CertConfig { pub name: String, pub dns_names: Vec, @@ -81,7 +81,7 @@ pub struct CertConfig { pub exec: Vec, } -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct Config { pub certs: Vec, pub acme: AcmeConfig, diff --git a/src/main.rs b/src/main.rs index 8e159ae..32ca761 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use acme_redirect::errors::*; use acme_redirect::renew; use acme_redirect::status; use env_logger::Env; +use std::io; use structopt::StructOpt; fn main() -> Result<()> { @@ -30,6 +31,10 @@ fn main() -> Result<()> { Cmd::Status => status::run(config)?, Cmd::Renew(args) => renew::run(config, args)?, Cmd::Check(args) => check::run(config, args)?, + Cmd::DumpConfig => { + serde_json::to_writer_pretty(io::stdout(), &config)?; + println!(); + } } } SubCommand::Completions(completions) => args::gen_completions(&completions)?, From 85c6a0abec40e27fd3ea8fb742b813cca131df21 Mon Sep 17 00:00:00 2001 From: kpcyrd Date: Thu, 6 May 2021 17:13:28 +0200 Subject: [PATCH 5/5] Use PathBuf for paths in config file --- src/config.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config.rs b/src/config.rs index ac99099..e29ec41 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashSet; use std::ffi::OsStr; use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; const LETSENCRYPT: &str = "https://acme-v02.api.letsencrypt.org/directory"; // const LETSENCRYPT_STAGING: &str = "https://acme-staging-v02.api.letsencrypt.org/directory"; @@ -28,8 +28,8 @@ pub struct AcmeConfig { #[derive(Debug, Default, PartialEq, Serialize, Deserialize)] pub struct SystemConfig { - pub data_dir: String, - pub chall_dir: String, + pub data_dir: PathBuf, + pub chall_dir: PathBuf, #[serde(default)] pub exec: Vec, #[serde(default)]