Seperated code into different files and functons Part 1
Dieser Commit ist enthalten in:
Ursprung
daff7b814f
Commit
64a2c7aa13
9 geänderte Dateien mit 339 neuen und 253 gelöschten Zeilen
54
src/main.rs
54
src/main.rs
|
@ -3,20 +3,25 @@
|
|||
#![allow(clippy::clone_on_copy)]
|
||||
#![allow(clippy::identity_op)]
|
||||
|
||||
pub(crate) mod config;
|
||||
pub(crate) mod consts;
|
||||
pub(crate) mod macros;
|
||||
pub(crate) mod process;
|
||||
pub(crate) mod structs;
|
||||
pub(crate) mod types;
|
||||
pub(crate) mod utils;
|
||||
|
||||
use crate::{
|
||||
config::SiteConfig,
|
||||
consts::*,
|
||||
macros::match_error,
|
||||
structs::{
|
||||
Arguments,
|
||||
ProcessorArgs,
|
||||
types::{
|
||||
config,
|
||||
config::{
|
||||
CA,
|
||||
SiteConfig,
|
||||
},
|
||||
structs::{
|
||||
Arguments,
|
||||
ProcessorArgs,
|
||||
},
|
||||
},
|
||||
utils::prefix_emails,
|
||||
};
|
||||
|
@ -28,7 +33,6 @@ use acme2_eab::{
|
|||
};
|
||||
use async_scoped::TokioScope;
|
||||
use clap::Parser;
|
||||
use config::CA;
|
||||
use env_logger::init as log_init;
|
||||
use libsystemd::daemon;
|
||||
use log::*;
|
||||
|
@ -39,7 +43,10 @@ use openssl::{
|
|||
Private,
|
||||
},
|
||||
};
|
||||
use process::process_site;
|
||||
use process::{
|
||||
process_site,
|
||||
services,
|
||||
};
|
||||
use reqwest::{
|
||||
Client,
|
||||
tls::Version,
|
||||
|
@ -72,9 +79,10 @@ use tokio_stream::{
|
|||
StreamExt,
|
||||
wrappers::ReadDirStream,
|
||||
};
|
||||
use zbus_systemd::systemd1;
|
||||
|
||||
type SafeSet<T> = Mutex<HashSet<T>>;
|
||||
use types::{
|
||||
config::GeneralConfig,
|
||||
traits::FromFile as _,
|
||||
};
|
||||
|
||||
|
||||
fn default_client() -> reqwest::Client {
|
||||
|
@ -201,13 +209,12 @@ async fn process_accounts(
|
|||
async fn racme(flags: Arguments) {
|
||||
let client = default_client();
|
||||
let systemd_access = daemon::booted();
|
||||
let mainconfig =
|
||||
config::read_config::<config::GeneralConfig>(match_error!(FILE_MODE.open(flags.config).await=>Err(error)-> "error reading the config: {error}")).await;
|
||||
let mainconfig = GeneralConfig::from_file(match_error!(FILE_MODE.open(flags.config).await=>Err(error)-> "error reading the config: {error}")).await;
|
||||
trace!("Parsed Config: {mainconfig:?}");
|
||||
let files = ReadDirStream::new(match_error!(read_dir(mainconfig.sites_path.clone()).await=>Err(error)-> "could not read files from sites dir: {error}"));
|
||||
let mut siteconfigs = Vec::new();
|
||||
for file in files.filter(Result::is_ok).map(|file| file.unwrap().path()).collect::<Vec<PathBuf>>().await {
|
||||
let mut site = config::read_config::<SiteConfig>(FILE_MODE.open(file.clone()).await.unwrap()).await;
|
||||
let mut site = SiteConfig::from_file(FILE_MODE.open(file.clone()).await.unwrap()).await;
|
||||
site.name = file.file_stem().unwrap().to_str().unwrap().to_string();
|
||||
siteconfigs.push(site);
|
||||
}
|
||||
|
@ -262,24 +269,7 @@ async fn racme(flags: Arguments) {
|
|||
.await;
|
||||
|
||||
if systemd_access {
|
||||
let conn = match_error!(zbus_systemd::zbus::Connection::system().await=>Err(error)-> "Failed to connect with the systemd manager: {error}");
|
||||
|
||||
let systemd_manager = systemd1::ManagerProxy::new(&conn).await.unwrap();
|
||||
let restart_services = restart_services.into_inner();
|
||||
|
||||
for service in reload_services.into_inner().difference(&restart_services.clone()) {
|
||||
match systemd_manager.reload_unit(service.to_owned(), "replace".to_string()).await {
|
||||
Ok(_) => info!("Reloaded {service}"),
|
||||
Err(error) => error!("Failed to reload service {service}: {error}"),
|
||||
};
|
||||
}
|
||||
|
||||
for service in restart_services.iter() {
|
||||
match systemd_manager.restart_unit(service.to_owned(), "replace".to_string()).await {
|
||||
Ok(_) => info!("Restarted {service}"),
|
||||
Err(error) => error!("Failed to restart service {service}: {error}"),
|
||||
};
|
||||
}
|
||||
services(restart_services.into_inner(), reload_services.into_inner()).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
use std::{
|
||||
collections::HashSet,
|
||||
fs::Permissions,
|
||||
os::unix::fs::PermissionsExt,
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
config::{
|
||||
Algorithm,
|
||||
match_algo,
|
||||
},
|
||||
consts::{
|
||||
ATTEMPTS,
|
||||
FILE_MODE,
|
||||
|
@ -16,15 +13,19 @@ use crate::{
|
|||
WAIT_TIME,
|
||||
},
|
||||
load_privkey,
|
||||
match_error,
|
||||
structs::{
|
||||
ProcessorArgs,
|
||||
San,
|
||||
},
|
||||
utils::{
|
||||
gen_key,
|
||||
is_matching,
|
||||
macros::match_error,
|
||||
types::{
|
||||
cryptography::Algorithm,
|
||||
structs::{
|
||||
ProcessorArgs,
|
||||
San,
|
||||
},
|
||||
traits::{
|
||||
MatchAlgorithm as _,
|
||||
MatchX509,
|
||||
},
|
||||
},
|
||||
utils::gen_key,
|
||||
};
|
||||
use acme2_eab::{
|
||||
Authorization,
|
||||
|
@ -62,6 +63,7 @@ use tokio::{
|
|||
AsyncWriteExt,
|
||||
},
|
||||
};
|
||||
use zbus_systemd::systemd1;
|
||||
|
||||
fn gen_stack(args: &ProcessorArgs, context: X509v3Context) -> Stack<X509Extension> {
|
||||
let mut stack = Stack::new().unwrap();
|
||||
|
@ -104,7 +106,7 @@ pub async fn process_site(args: ProcessorArgs<'_>) {
|
|||
private_key = match_error!(gen_key(args.algorithm(), args.strength())=>Err(error)-> "Aborting processing the site due to problem with the certificate generation: {error}");
|
||||
} else if let Ok(key) = load_privkey(private_key_file.clone()).await {
|
||||
private_key = key;
|
||||
if !match_algo(&private_key, args.algorithm(), args.strength()) {
|
||||
if !private_key.matches(args.algorithm(), args.strength()) {
|
||||
info!("Algorithm for the private key has changed, updating the key");
|
||||
cert_renew = true;
|
||||
write_pkey = true;
|
||||
|
@ -133,7 +135,10 @@ pub async fn process_site(args: ProcessorArgs<'_>) {
|
|||
Ok(key) => key,
|
||||
Err(_) => todo!(),
|
||||
};
|
||||
if !is_matching(pubkey, args.refresh_time(), args.san()) {
|
||||
if !pubkey.days_left(args.refresh_time()) {
|
||||
info!("Certificate is running out of time");
|
||||
cert_renew = true
|
||||
} else if !pubkey.match_san(args.san()) {
|
||||
info!("Subject Alternative Names differ from Certifcate");
|
||||
cert_renew = true;
|
||||
};
|
||||
|
@ -249,3 +254,23 @@ pub async fn process_auth(auth: Authorization, challenge_dir: Option<PathBuf>, d
|
|||
}
|
||||
error!("Cannot prove the challenges: {}", auth.challenges.iter().map(|c| c.r#type.clone()).collect::<Vec<_>>().join(", "))
|
||||
}
|
||||
|
||||
pub async fn services(restart_services: HashSet<String>, reload_services: HashSet<String>) {
|
||||
let conn = match_error!(zbus_systemd::zbus::Connection::system().await=>Err(error)-> "Failed to connect with the systemd manager: {error}");
|
||||
|
||||
let systemd_manager = systemd1::ManagerProxy::new(&conn).await.unwrap();
|
||||
|
||||
for service in reload_services.difference(&restart_services.clone()) {
|
||||
match systemd_manager.reload_unit(service.to_owned(), "replace".to_string()).await {
|
||||
Ok(_) => info!("Reloaded {service}"),
|
||||
Err(error) => error!("Failed to reload service {service}: {error}"),
|
||||
};
|
||||
}
|
||||
|
||||
for service in restart_services.iter() {
|
||||
match systemd_manager.restart_unit(service.to_owned(), "replace".to_string()).await {
|
||||
Ok(_) => info!("Restarted {service}"),
|
||||
Err(error) => error!("Failed to restart service {service}: {error}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +1,28 @@
|
|||
use crate::{
|
||||
consts::{
|
||||
BRAINPOOL_MIDDLE,
|
||||
BRAINPOOL_STRONG,
|
||||
BRAINPOOL_WEAK,
|
||||
SECP_MIDDLE,
|
||||
SECP_STRONG,
|
||||
SECP_WEAK,
|
||||
macros::{
|
||||
DefDer,
|
||||
match_error,
|
||||
},
|
||||
types::{
|
||||
VString,
|
||||
cryptography::{
|
||||
Algorithm,
|
||||
Strength,
|
||||
},
|
||||
structs::Error,
|
||||
},
|
||||
macros::DefDer,
|
||||
match_error,
|
||||
structs::Error,
|
||||
};
|
||||
use log::*;
|
||||
|
||||
use macro_rules_attribute::macro_rules_derive;
|
||||
use openssl::pkey::{
|
||||
Id,
|
||||
PKey,
|
||||
Private,
|
||||
};
|
||||
use serde::{
|
||||
Deserialize,
|
||||
de::DeserializeOwned,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::IpAddr,
|
||||
};
|
||||
use tokio::{
|
||||
fs::File,
|
||||
io::AsyncReadExt,
|
||||
};
|
||||
|
||||
type VString = Vec<String>;
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
#[derive(Deserialize)]
|
||||
|
@ -50,48 +41,34 @@ pub struct GeneralConfig {
|
|||
pub ca: HashMap<String, CA>,
|
||||
}
|
||||
|
||||
impl Default for GeneralConfig {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
accounts_path: Self::default_accounts(),
|
||||
sites_path: Self::default_sites(),
|
||||
http_challenge_path: Self::default_challenge(),
|
||||
dns: Self::default_dns(),
|
||||
certificates_path: Self::default_certificates(),
|
||||
ca: Self::default_cas(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GeneralConfig {
|
||||
#[inline]
|
||||
fn default_accounts() -> String {
|
||||
pub(super) fn default_accounts() -> String {
|
||||
"accounts".into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn default_sites() -> String {
|
||||
pub(super) fn default_sites() -> String {
|
||||
"sites".into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn default_challenge() -> Option<String> {
|
||||
pub(super) fn default_challenge() -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn default_dns() -> Option<Dns> {
|
||||
pub(super) fn default_dns() -> Option<Dns> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn default_cas() -> HashMap<String, CA> {
|
||||
pub(super) fn default_cas() -> HashMap<String, CA> {
|
||||
HashMap::new()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn default_certificates() -> String {
|
||||
pub(super) fn default_certificates() -> String {
|
||||
"certificates".into()
|
||||
}
|
||||
}
|
||||
|
@ -143,52 +120,6 @@ impl CA {
|
|||
}
|
||||
}
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
#[derive(Copy, Deserialize, Default)]
|
||||
pub enum Algorithm {
|
||||
Rsa,
|
||||
Brainpool,
|
||||
Secp,
|
||||
#[default]
|
||||
ED25519,
|
||||
}
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
#[derive(Copy, Deserialize, Default)]
|
||||
pub enum Strength {
|
||||
Weak,
|
||||
Middle,
|
||||
#[default]
|
||||
Strong,
|
||||
}
|
||||
|
||||
impl Strength {
|
||||
pub fn rsabits(self) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_algo(key: &PKey<Private>, algorithm: Algorithm, strength: Strength) -> bool {
|
||||
match (key.id(), algorithm) {
|
||||
(Id::ED25519, Algorithm::ED25519) => true,
|
||||
(Id::RSA, Algorithm::Rsa) if key.bits() == strength.rsabits() => true,
|
||||
(Id::EC, Algorithm::Secp) | (Id::EC, Algorithm::Brainpool) => {
|
||||
let pkey = key.ec_key().unwrap();
|
||||
let curve = pkey.group().curve_name().unwrap();
|
||||
match (algorithm, strength) {
|
||||
(Algorithm::Secp, Strength::Weak) if SECP_WEAK == curve => true,
|
||||
(Algorithm::Secp, Strength::Middle) if SECP_MIDDLE == curve => true,
|
||||
(Algorithm::Secp, Strength::Strong) if SECP_STRONG == curve => true,
|
||||
(Algorithm::Brainpool, Strength::Weak) if BRAINPOOL_WEAK == curve => true,
|
||||
(Algorithm::Brainpool, Strength::Middle) if BRAINPOOL_MIDDLE == curve => true,
|
||||
(Algorithm::Brainpool, Strength::Strong) if BRAINPOOL_STRONG == curve => true,
|
||||
_ => false,
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
#[derive(Deserialize, Default)]
|
||||
pub struct SiteConfig {
|
||||
|
@ -229,25 +160,3 @@ pub struct SiteConfig {
|
|||
#[serde(skip)]
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
pub async fn read_config<T: Default + DeserializeOwned>(mut file: File) -> T {
|
||||
let mut data = String::new();
|
||||
|
||||
match file.read_to_string(&mut data).await {
|
||||
Ok(_) => {},
|
||||
Err(error) => {
|
||||
warn!("Failed to load config: {error}");
|
||||
|
||||
return Default::default();
|
||||
},
|
||||
}
|
||||
|
||||
match toml::from_str(&data) {
|
||||
Ok(output) => output,
|
||||
Err(error) => {
|
||||
warn!("Failed to parse toml file: {error}");
|
||||
|
||||
Default::default()
|
||||
},
|
||||
}
|
||||
}
|
31
src/types/cryptography.rs
Normale Datei
31
src/types/cryptography.rs
Normale Datei
|
@ -0,0 +1,31 @@
|
|||
use macro_rules_attribute::macro_rules_derive;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::macros::DefDer;
|
||||
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
#[derive(Copy, Deserialize, Default)]
|
||||
pub enum Algorithm {
|
||||
Rsa,
|
||||
Brainpool,
|
||||
Secp,
|
||||
#[default]
|
||||
ED25519,
|
||||
}
|
||||
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
#[derive(Copy, Deserialize, Default)]
|
||||
pub enum Strength {
|
||||
Weak,
|
||||
Middle,
|
||||
#[default]
|
||||
Strong,
|
||||
}
|
||||
|
||||
impl Strength {
|
||||
pub fn rsabits(self) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
}
|
83
src/types/foreign_impl.rs
Normale Datei
83
src/types/foreign_impl.rs
Normale Datei
|
@ -0,0 +1,83 @@
|
|||
use std::{
|
||||
fmt::Display,
|
||||
net::IpAddr,
|
||||
};
|
||||
|
||||
use acme2_eab::Identifier;
|
||||
use openssl::x509::GeneralName;
|
||||
|
||||
use super::{
|
||||
config::GeneralConfig,
|
||||
structs::{
|
||||
Error,
|
||||
San,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
impl Default for GeneralConfig {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
accounts_path: Self::default_accounts(),
|
||||
sites_path: Self::default_sites(),
|
||||
http_challenge_path: Self::default_challenge(),
|
||||
dns: Self::default_dns(),
|
||||
certificates_path: Self::default_certificates(),
|
||||
ca: Self::default_cas(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GeneralName> for San {
|
||||
fn from(value: GeneralName) -> Self {
|
||||
if let Some(dns) = value.dnsname() {
|
||||
return Self::Dns(dns.to_owned());
|
||||
}
|
||||
if let Some(ipaddr) = value.ipaddress() {
|
||||
if ipaddr.len() == 4 {
|
||||
let mut addr = [0u8; 4];
|
||||
addr.copy_from_slice(ipaddr);
|
||||
return Self::IPAddress(IpAddr::from(addr));
|
||||
} else {
|
||||
let mut addr = [0u8; 16];
|
||||
addr.copy_from_slice(ipaddr);
|
||||
return Self::IPAddress(IpAddr::from(addr));
|
||||
}
|
||||
}
|
||||
if let Some(email) = value.email() {
|
||||
return Self::Email(email.to_owned());
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
impl From<San> for Identifier {
|
||||
fn from(value: San) -> Self {
|
||||
match value {
|
||||
San::Dns(domain) => {
|
||||
Identifier {
|
||||
r#type: "dns".into(),
|
||||
value: domain,
|
||||
}
|
||||
},
|
||||
San::Email(email) => {
|
||||
Identifier {
|
||||
r#type: "email".into(),
|
||||
value: email,
|
||||
}
|
||||
},
|
||||
San::IPAddress(ip) => {
|
||||
Identifier {
|
||||
r#type: "ip".into(),
|
||||
value: ip.to_string(),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&self.0)
|
||||
}
|
||||
}
|
14
src/types/mod.rs
Normale Datei
14
src/types/mod.rs
Normale Datei
|
@ -0,0 +1,14 @@
|
|||
use std::collections::HashSet;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub mod config;
|
||||
pub mod cryptography;
|
||||
mod foreign_impl;
|
||||
pub mod structs;
|
||||
pub mod traits;
|
||||
|
||||
/// Alias for Vec\<String\>
|
||||
pub type VString = Vec<String>;
|
||||
|
||||
/// Alias for an Safe Hashset
|
||||
pub type SafeSet<T> = Mutex<HashSet<T>>;
|
|
@ -1,32 +1,29 @@
|
|||
use std::{
|
||||
collections::HashSet,
|
||||
fmt::Display,
|
||||
net::IpAddr,
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use acme2_eab::{
|
||||
Account,
|
||||
Identifier,
|
||||
};
|
||||
use acme2_eab::Account;
|
||||
use clap::Parser;
|
||||
use macro_rules_attribute::macro_rules_derive;
|
||||
use openssl::x509::GeneralName;
|
||||
use tokio::sync::MutexGuard;
|
||||
|
||||
use crate::{
|
||||
SafeSet,
|
||||
config::{
|
||||
Algorithm,
|
||||
SiteConfig,
|
||||
Strength,
|
||||
},
|
||||
config::SiteConfig,
|
||||
macros::{
|
||||
DefDer,
|
||||
Hashable,
|
||||
attr_function,
|
||||
},
|
||||
types::{
|
||||
SafeSet,
|
||||
cryptography::{
|
||||
Algorithm,
|
||||
Strength,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
@ -129,56 +126,8 @@ pub enum San {
|
|||
IPAddress(IpAddr),
|
||||
}
|
||||
|
||||
impl From<GeneralName> for San {
|
||||
fn from(value: GeneralName) -> Self {
|
||||
if let Some(dns) = value.dnsname() {
|
||||
return Self::Dns(dns.to_owned());
|
||||
}
|
||||
if let Some(ipaddr) = value.ipaddress() {
|
||||
if ipaddr.len() == 4 {
|
||||
let mut addr = [0u8; 4];
|
||||
addr.copy_from_slice(ipaddr);
|
||||
return Self::IPAddress(IpAddr::from(addr));
|
||||
} else {
|
||||
let mut addr = [0u8; 16];
|
||||
addr.copy_from_slice(ipaddr);
|
||||
return Self::IPAddress(IpAddr::from(addr));
|
||||
}
|
||||
}
|
||||
if let Some(email) = value.email() {
|
||||
return Self::Email(email.to_owned());
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
impl From<San> for Identifier {
|
||||
fn from(value: San) -> Self {
|
||||
match value {
|
||||
San::Dns(domain) => {
|
||||
Identifier {
|
||||
r#type: "dns".into(),
|
||||
value: domain,
|
||||
}
|
||||
},
|
||||
San::Email(email) => {
|
||||
Identifier {
|
||||
r#type: "email".into(),
|
||||
value: email,
|
||||
}
|
||||
},
|
||||
San::IPAddress(ip) => {
|
||||
Identifier {
|
||||
r#type: "ip".into(),
|
||||
value: ip.to_string(),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
pub struct Error(String);
|
||||
pub struct Error(pub(super) String);
|
||||
|
||||
impl Error {
|
||||
#[inline]
|
||||
|
@ -191,9 +140,3 @@ impl Error {
|
|||
Self(message)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&self.0)
|
||||
}
|
||||
}
|
117
src/types/traits.rs
Normale Datei
117
src/types/traits.rs
Normale Datei
|
@ -0,0 +1,117 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use crate::consts::{
|
||||
BRAINPOOL_MIDDLE,
|
||||
BRAINPOOL_STRONG,
|
||||
BRAINPOOL_WEAK,
|
||||
SECP_MIDDLE,
|
||||
SECP_STRONG,
|
||||
SECP_WEAK,
|
||||
};
|
||||
use log::*;
|
||||
use openssl::{
|
||||
asn1::Asn1Time,
|
||||
pkey::{
|
||||
Id,
|
||||
PKey,
|
||||
Private,
|
||||
},
|
||||
x509::X509,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use tokio::{
|
||||
fs::File,
|
||||
io::AsyncReadExt,
|
||||
};
|
||||
|
||||
use super::{
|
||||
cryptography::{
|
||||
Algorithm,
|
||||
Strength,
|
||||
},
|
||||
structs::San,
|
||||
};
|
||||
|
||||
pub trait FromFile: Default + DeserializeOwned {
|
||||
async fn from_file(file: File) -> Self;
|
||||
}
|
||||
|
||||
impl<T: Default + DeserializeOwned> FromFile for T {
|
||||
async fn from_file(mut file: File) -> Self {
|
||||
let mut data = String::new();
|
||||
|
||||
match file.read_to_string(&mut data).await {
|
||||
Ok(_) => {},
|
||||
Err(error) => {
|
||||
warn!("Failed to load config: {error}");
|
||||
|
||||
return Default::default();
|
||||
},
|
||||
}
|
||||
|
||||
match toml::from_str(&data) {
|
||||
Ok(output) => output,
|
||||
Err(error) => {
|
||||
warn!("Failed to parse toml file: {error}");
|
||||
|
||||
Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait MatchAlgorithm {
|
||||
fn matches(&self, algorithm: Algorithm, strength: Strength) -> bool;
|
||||
}
|
||||
|
||||
impl MatchAlgorithm for PKey<Private> {
|
||||
fn matches(&self, algorithm: Algorithm, strength: Strength) -> bool {
|
||||
match (self.id(), algorithm) {
|
||||
(Id::ED25519, Algorithm::ED25519) => true,
|
||||
(Id::RSA, Algorithm::Rsa) if self.bits() == strength.rsabits() => true,
|
||||
(Id::EC, Algorithm::Secp) | (Id::EC, Algorithm::Brainpool) => {
|
||||
let pkey = self.ec_key().unwrap();
|
||||
let curve = pkey.group().curve_name().unwrap();
|
||||
match (algorithm, strength) {
|
||||
(Algorithm::Secp, Strength::Weak) if SECP_WEAK == curve => true,
|
||||
(Algorithm::Secp, Strength::Middle) if SECP_MIDDLE == curve => true,
|
||||
(Algorithm::Secp, Strength::Strong) if SECP_STRONG == curve => true,
|
||||
(Algorithm::Brainpool, Strength::Weak) if BRAINPOOL_WEAK == curve => true,
|
||||
(Algorithm::Brainpool, Strength::Middle) if BRAINPOOL_MIDDLE == curve => true,
|
||||
(Algorithm::Brainpool, Strength::Strong) if BRAINPOOL_STRONG == curve => true,
|
||||
_ => false,
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MatchX509 {
|
||||
fn days_left(&self, days: u32) -> bool;
|
||||
fn match_san<T: IntoIterator<Item = San>>(&self, names: T) -> bool;
|
||||
}
|
||||
|
||||
impl MatchX509 for X509 {
|
||||
fn days_left(&self, days: u32) -> bool {
|
||||
let future_date = Asn1Time::days_from_now(days).unwrap();
|
||||
self.not_after().compare(&future_date).is_ok_and(|order| order.is_le())
|
||||
}
|
||||
|
||||
fn match_san<T: IntoIterator<Item = San>>(&self, names: T) -> bool {
|
||||
let alt_names = match self.subject_alt_names() {
|
||||
None => return false,
|
||||
Some(x) => x,
|
||||
};
|
||||
let mut cert_san = HashSet::<San>::new();
|
||||
for san in alt_names {
|
||||
cert_san.insert(san.into());
|
||||
}
|
||||
let mut config_san = HashSet::new();
|
||||
for san in names {
|
||||
config_san.insert(san);
|
||||
}
|
||||
config_san.difference(&cert_san).count() == 0
|
||||
}
|
||||
}
|
34
src/utils.rs
34
src/utils.rs
|
@ -1,10 +1,4 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use crate::{
|
||||
config::{
|
||||
Algorithm,
|
||||
Strength,
|
||||
},
|
||||
consts::{
|
||||
BRAINPOOL_MIDDLE,
|
||||
BRAINPOOL_STRONG,
|
||||
|
@ -13,11 +7,13 @@ use crate::{
|
|||
SECP_STRONG,
|
||||
SECP_WEAK,
|
||||
},
|
||||
structs::San,
|
||||
types::cryptography::{
|
||||
Algorithm,
|
||||
Strength,
|
||||
},
|
||||
};
|
||||
use log::*;
|
||||
use openssl::{
|
||||
asn1::Asn1Time,
|
||||
ec::EcKey,
|
||||
error::ErrorStack,
|
||||
nid::Nid,
|
||||
|
@ -27,7 +23,6 @@ use openssl::{
|
|||
},
|
||||
rsa::Rsa,
|
||||
x509::{
|
||||
X509,
|
||||
X509Name,
|
||||
X509NameBuilder,
|
||||
},
|
||||
|
@ -94,27 +89,6 @@ pub fn gen_key(algorithm: Algorithm, strength: Strength) -> Result<PKey<Private>
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn is_matching(cert: X509, daydiff: u32, sans: Vec<San>) -> bool {
|
||||
let now = Asn1Time::days_from_now(daydiff).unwrap();
|
||||
if cert.not_after().compare(&now).is_ok_and(|order| order.is_le()) {
|
||||
return false;
|
||||
}
|
||||
let alt_names = match cert.subject_alt_names() {
|
||||
None => return false,
|
||||
Some(x) => x,
|
||||
};
|
||||
let mut cert_san = HashSet::<San>::new();
|
||||
for san in alt_names {
|
||||
cert_san.insert(san.into());
|
||||
}
|
||||
let mut config_san = HashSet::with_capacity(sans.len());
|
||||
for san in sans {
|
||||
config_san.insert(san);
|
||||
}
|
||||
config_san.difference(&cert_san).count() == 0
|
||||
}
|
||||
|
||||
pub fn string_to_cn(name: String) -> X509Name {
|
||||
let mut builder = X509NameBuilder::new().unwrap();
|
||||
builder.append_entry_by_nid(Nid::COMMONNAME, &name).unwrap();
|
||||
|
|
Laden …
Tabelle hinzufügen
In neuem Issue referenzieren