Skip to content

Commit

Permalink
Merge pull request #54 from dvdsk/improve-path-deduction
Browse files Browse the repository at this point in the history
Improve path deduction
  • Loading branch information
dvdsk authored Jun 3, 2023
2 parents 94eceef + 39d6ec2 commit c35ce30
Show file tree
Hide file tree
Showing 18 changed files with 408 additions and 285 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
25 changes: 24 additions & 1 deletion Cargo.lock

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

6 changes: 3 additions & 3 deletions main/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "renewc"
version = "0.2.5"
version = "0.2.6"
authors = ["David Kleingeld <opensource@davidsk.dev>"]
edition = "2021"
description = "Certificate renewal, with advanced diagnostics without installing anything"
Expand All @@ -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"] }
Expand Down Expand Up @@ -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"
Expand Down
3 changes: 0 additions & 3 deletions main/src/cert/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
}
61 changes: 1 addition & 60 deletions main/src/cert/io.rs
Original file line number Diff line number Diff line change
@@ -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<Option<Vec<u8>>> {
match fs::read(path) {
Expand All @@ -39,41 +18,3 @@ pub(super) fn read_any_file(path: &Path) -> eyre::Result<Option<Vec<u8>>> {
Ok(bytes) => Ok(Some(bytes)),
}
}

pub(super) fn name(domains: &[impl AsRef<str>]) -> eyre::Result<String> {
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");
}
}
86 changes: 12 additions & 74 deletions main/src/cert/load.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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)]
Expand Down Expand Up @@ -75,27 +44,17 @@ pub fn from_disk<P: PemItem>(config: &Config) -> eyre::Result<Option<Signed<P>>>
#[instrument(level = "debug", skip(config), err)]
fn load_seperate_chain<P: PemItem>(config: &Config) -> eyre::Result<Vec<P>> {
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,
Expand All @@ -110,7 +69,7 @@ fn load_seperate_chain<P: PemItem>(config: &Config) -> eyre::Result<Vec<P>> {
}

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)
Expand All @@ -122,23 +81,12 @@ fn load_seperate_chain<P: PemItem>(config: &Config) -> eyre::Result<Vec<P>> {
fn load_seperate_private_key<P: PemItem>(config: &Config) -> eyre::Result<Option<P>> {
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);
};

Expand All @@ -152,25 +100,15 @@ fn load_seperate_private_key<P: PemItem>(config: &Config) -> eyre::Result<Option
fn load_certificate<P: PemItem>(config: &Config) -> eyre::Result<Option<MaybeSigned<P>>> {
let OutputConfig {
output,
certificate_path,
cert_path,
..
} = &config.output_config;
let encoding = Encoding::from(output);
let path = if certificate_path.is_dir() {
derive_path(
certificate_path,
&name(&config.domains)?,
"cert",
encoding.extension(),
)
} else {
certificate_path.clone()
};

let Some(bytes) = read_any_file(&path)? else {
let Some(bytes) = read_any_file(cert_path.as_path())? else {
return Ok(None);
};

let encoding = Encoding::from(output);
match encoding {
Encoding::PEM => MaybeSigned::from_pem(bytes)
.map(Option::Some)
Expand Down
Loading

0 comments on commit c35ce30

Please sign in to comment.