diff --git a/CHANGELOG.md b/CHANGELOG.md index 88199b0..9e73838 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.6] - 2023-06-03 + +### Added + - Allow path arguments with or without the correct extension + - Warn user before renewal if path has (wrong) extension ## [0.2.5] - 2023-06-02 diff --git a/Cargo.lock b/Cargo.lock index 690f292..cc1e9f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1438,7 +1438,7 @@ checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "renewc" -version = "0.2.4" +version = "0.2.5" dependencies = [ "async-trait", "axum", @@ -1463,6 +1463,7 @@ dependencies = [ "rand", "rcgen", "shared_memory", + "strum", "tempfile", "time", "tokio", @@ -1792,6 +1793,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/main/Cargo.toml b/main/Cargo.toml index e7c23fd..6fb6053 100644 --- a/main/Cargo.toml +++ b/main/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "renewc" -version = "0.2.5" +version = "0.2.6" authors = ["David Kleingeld "] edition = "2021" description = "Certificate renewal, with advanced diagnostics without installing anything" @@ -12,8 +12,7 @@ keywords = ["cli", "certificate", "acme"] categories = ["command-line-utilities"] [dependencies] -# instant-acme = { version = "^0.3.2" } -instant-acme = { git = "https://github.com/instant-labs/instant-acme", branch = "http-client-bounds" } +instant-acme = { version = "^0.3.2" } x509-parser = "0.15" tracing = { version = "0.1" } tracing-subscriber = { version = "0.3", features = ["env-filter"] } @@ -43,6 +42,7 @@ yasna = "0.5" async-trait = "0.1" data-encoding = "2.4" pem = "2" +strum = { version = "0.24.1", features = ["derive"] } [dev-dependencies] libc = "0.2" diff --git a/main/src/cert/format.rs b/main/src/cert/format.rs index 61d953e..a787809 100644 --- a/main/src/cert/format.rs +++ b/main/src/cert/format.rs @@ -120,7 +120,4 @@ mod tests { let pem: Pem = der.to_pem(Label::Certificate); assert_eq!(pem.into_bytes(), ROOT_CA); } - - #[test] - fn parse_chain() {} } diff --git a/main/src/cert/io.rs b/main/src/cert/io.rs index e0dfa17..94337ac 100644 --- a/main/src/cert/io.rs +++ b/main/src/cert/io.rs @@ -1,30 +1,9 @@ use std::fs; use std::io::ErrorKind; -use std::path::{Path, PathBuf}; +use std::path::Path; use color_eyre::eyre::{self, Context}; use color_eyre::Help; -use tracing::instrument; - -#[instrument(level = "debug", ret)] -pub(crate) fn derive_path(cert_path: &Path, name: &str, ty: &str, extension: &str) -> PathBuf { - let mut path = dir(cert_path); - path.set_file_name(format!("{name}_{ty}")); - path.set_extension(extension); - path -} - -pub(super) fn dir(cert_path: &Path) -> PathBuf { - let dir = if cert_path.is_file() { - cert_path - .parent() - .expect("is never none if parent was a file") - } else { - cert_path - }; - - dir.to_path_buf() -} pub(super) fn read_any_file(path: &Path) -> eyre::Result>> { match fs::read(path) { @@ -39,41 +18,3 @@ pub(super) fn read_any_file(path: &Path) -> eyre::Result>> { Ok(bytes) => Ok(Some(bytes)), } } - -pub(super) fn name(domains: &[impl AsRef]) -> eyre::Result { - let shortest = domains - .iter() - .map(AsRef::as_ref) - .min_by_key(|d| d.len()) - .unwrap(); - let last_dot = shortest - .rfind('.') - .ok_or_else(|| eyre::eyre!("shortest domain has no top level domain [org/net/com etc]"))?; - let (name, _extension) = shortest.split_at(last_dot); - if let Some(last_dot) = name.rfind('.') { - let (_subdomains, name) = name.split_at(last_dot + 1); - Ok(name.to_string()) - } else { - Ok(name.to_string()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn extract_name() { - let domains = [ - "example.org", - "subdomain.example.org", - "subsubdomain.subdomain.example.org", - "another.example.org", - "even_more.example.org", - "a.nm.org", - "subdomain.nm.org", - ]; - - assert_eq!(name(&domains).unwrap(), "nm"); - } -} diff --git a/main/src/cert/load.rs b/main/src/cert/load.rs index df82d3d..c6fcd7a 100644 --- a/main/src/cert/load.rs +++ b/main/src/cert/load.rs @@ -1,6 +1,6 @@ use std::io::ErrorKind; -use crate::config::{Output, OutputConfig}; +use crate::config::{Encoding, OutputConfig}; use crate::Config; use color_eyre::eyre::{self, Context}; use color_eyre::Help; @@ -9,38 +9,7 @@ use tracing::instrument; use super::format::{Der, Label, PemItem}; use super::{MaybeSigned, Signed}; -use super::io::{derive_path, name, read_any_file}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Encoding { - PEM, - DER, - #[cfg(feature = "derchain")] - PKCS12, -} - -impl Encoding { - pub(crate) fn extension(self) -> &'static str { - match self { - Encoding::PEM => "pem", - Encoding::DER => "der", - #[cfg(feature = "derchain")] - Encoding::PKCS12 => "pkcs12", - } - } -} - -impl From<&Output> for Encoding { - fn from(output: &Output) -> Self { - match output { - Output::Pem - | Output::PemSeperateKey - | Output::PemSeperateChain - | Output::PemAllSeperate => Encoding::PEM, - Output::Der => Encoding::DER, - } - } -} +use super::io::read_any_file; // TODO: remove Option, report errors upstream as warnings <03-05-23, dvdsk> #[instrument(level = "debug", skip(config), ret)] @@ -75,27 +44,17 @@ pub fn from_disk(config: &Config) -> eyre::Result>> #[instrument(level = "debug", skip(config), err)] fn load_seperate_chain(config: &Config) -> eyre::Result> { let OutputConfig { - output, - certificate_path, - chain_path, - .. + output, chain_path, .. } = &config.output_config; - let encoding = Encoding::from(output); - let path = match chain_path { - None => derive_path( - certificate_path, - &name(&config.domains)?, - "chain", - encoding.extension(), - ), - Some(path) => path.clone(), - }; + let encoding = Encoding::from(output); match encoding { Encoding::DER => { let mut chain = Vec::new(); for i in 0.. { - let path = path.with_file_name(format!("{i}_chain.der")); + let path = chain_path + .as_path() + .with_file_name(format!("{i}_chain.der")); let bytes = match std::fs::read(&path) { Ok(bytes) => bytes, Err(e) if e.kind() == ErrorKind::NotFound => break, @@ -110,7 +69,7 @@ fn load_seperate_chain(config: &Config) -> eyre::Result> { } Encoding::PEM => { - let Some(bytes) = read_any_file(&path)? else { + let Some(bytes) = read_any_file(chain_path.as_path())? else { return Ok(Vec::new()); }; P::chain_from_pem(bytes) @@ -122,23 +81,12 @@ fn load_seperate_chain(config: &Config) -> eyre::Result> { fn load_seperate_private_key(config: &Config) -> eyre::Result> { let OutputConfig { output, - certificate_path, key_path, .. } = &config.output_config; let encoding = Encoding::from(output); - let path = match key_path { - None => derive_path( - certificate_path, - &name(&config.domains)?, - "key", - encoding.extension(), - ), - Some(path) => path.clone(), - }; - - let Some(bytes) = read_any_file(&path)? else { + let Some(bytes) = read_any_file(key_path.as_path())? else { return Ok(None); }; @@ -152,25 +100,15 @@ fn load_seperate_private_key(config: &Config) -> eyre::Result