Skip to content

Commit

Permalink
Merge pull request #22 from kpcyrd/config-paths
Browse files Browse the repository at this point in the history
Make data and challenge directory configurable in config file
  • Loading branch information
kpcyrd authored May 7, 2021
2 parents 8b14294 + 85c6a0a commit e1f1282
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 69 deletions.
54 changes: 52 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ users = "0.11"
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"
Expand Down
27 changes: 8 additions & 19 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ use std::io::stdout;
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 {
Expand All @@ -30,22 +27,12 @@ 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",
default_value = "/var/lib/acme-redirect",
env = "ACME_DATA_DIR"
)]
pub data_dir: String,
#[structopt(long, default_value=LETSENCRYPT, env="ACME_URL")]
pub acme_url: String,
#[structopt(long, value_name = "path", env = "ACME_CHALL_DIR")]
pub chall_dir: Option<String>,
#[structopt(long, value_name = "path", env = "ACME_DATA_DIR")]
pub data_dir: Option<String>,
#[structopt(long, env = "ACME_URL")]
pub acme_url: Option<String>,
#[structopt(long, env = "ACME_EMAIL")]
pub acme_email: Option<String>,
#[structopt(subcommand)]
Expand All @@ -70,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)]
Expand Down
5 changes: 3 additions & 2 deletions src/chall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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-_";

Expand All @@ -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(),
}
}
Expand Down
74 changes: 43 additions & 31 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
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;
use std::path::Path;
use std::path::PathBuf;
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";
pub const DEFAULT_RENEW_IF_DAYS_LEFT: i64 = 30;

#[derive(Debug, PartialEq, Deserialize)]
Expand All @@ -18,15 +19,17 @@ pub struct ConfigFile {
pub system: SystemConfig,
}

#[derive(Debug, Default, PartialEq, Deserialize)]
#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct AcmeConfig {
pub acme_email: Option<String>,
pub acme_url: Option<String>,
pub renew_if_days_left: Option<i64>,
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: PathBuf,
pub chall_dir: PathBuf,
#[serde(default)]
pub exec: Vec<String>,
#[serde(default)]
Expand Down Expand Up @@ -68,7 +71,7 @@ fn load_from_folder<P: AsRef<Path>>(path: P) -> Result<Vec<CertConfigFile>> {
Ok(configs)
}

#[derive(Debug, PartialEq, Clone, Deserialize)]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct CertConfig {
pub name: String,
pub dns_names: Vec<String>,
Expand All @@ -78,16 +81,11 @@ pub struct CertConfig {
pub exec: Vec<String>,
}

#[derive(Debug, Clone)]
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
pub certs: Vec<CertConfig>,
pub acme_email: Option<String>,
pub acme_url: String,
pub renew_if_days_left: i64,
pub data_dir: PathBuf,
pub chall_dir: PathBuf,
pub exec: Vec<String>,
pub exec_extra: Vec<String>,
pub acme: AcmeConfig,
pub system: SystemConfig,
}

impl Config {
Expand All @@ -101,32 +99,46 @@ impl Config {
}
}

pub fn load(args: &Args) -> Result<Config> {
// TODO: none of this is applied yet, we need to change all the arg parsing code for that
pub fn load(args: Args) -> Result<Config> {
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 config = settings
.try_into::<ConfigFile>()
.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: PathBuf::from(&args.data_dir),
chall_dir: PathBuf::from(&args.chall_dir),
certs,
exec: config.system.exec,
exec_extra: config.system.exec_extra,
acme: config.acme,
system: config.system,
})
}

Expand Down
2 changes: 1 addition & 1 deletion src/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 6 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<()> {
Expand All @@ -22,14 +23,18 @@ 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 {
Cmd::Daemon(args) => daemon::run(config, args)?,
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)?,
Expand Down
2 changes: 1 addition & 1 deletion src/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/renew.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
},
Expand All @@ -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(())
Expand Down
Loading

0 comments on commit e1f1282

Please sign in to comment.