Seperated Code into different Files and functions Part 2

Dieser Commit ist enthalten in:
Sebastian Tobie 2025-05-15 08:35:09 +02:00
Ursprung dd026c901f
Commit bd3f0ad2ac
6 geänderte Dateien mit 225 neuen und 220 gelöschten Zeilen

Datei anzeigen

@ -5,17 +5,22 @@
pub(crate) mod consts; pub(crate) mod consts;
pub(crate) mod macros; pub(crate) mod macros;
pub(crate) mod prelude;
pub(crate) mod process; pub(crate) mod process;
pub(crate) mod types; pub(crate) mod types;
pub(crate) mod utils; pub(crate) mod utils;
use crate::{ use crate::{
consts::*, consts::*,
macros::match_error, prelude::*,
process::{
process_accounts,
process_site,
services,
},
types::{ types::{
config,
config::{ config::{
CA, GeneralConfig,
SiteConfig, SiteConfig,
}, },
structs::{ structs::{
@ -23,14 +28,8 @@ use crate::{
ProcessorArgs, ProcessorArgs,
}, },
}, },
utils::prefix_emails,
};
use acme2_eab::{
Account,
AccountBuilder,
Directory,
DirectoryBuilder,
}; };
use acme2_eab::Directory;
use async_scoped::TokioScope; use async_scoped::TokioScope;
use clap::Parser; use clap::Parser;
use env_logger::init as log_init; use env_logger::init as log_init;
@ -43,14 +42,7 @@ use openssl::{
Private, Private,
}, },
}; };
use process::{ use reqwest::tls::Version;
process_site,
services,
};
use reqwest::{
Client,
tls::Version,
};
use std::{ use std::{
collections::{ collections::{
HashMap, HashMap,
@ -69,20 +61,13 @@ use tokio::{
create_dir_all, create_dir_all,
read_dir, read_dir,
}, },
io::{ io::AsyncReadExt,
AsyncReadExt,
AsyncWriteExt,
},
sync::Mutex, sync::Mutex,
}; };
use tokio_stream::{ use tokio_stream::{
StreamExt, StreamExt,
wrappers::ReadDirStream, wrappers::ReadDirStream,
}; };
use types::{
config::GeneralConfig,
traits::FromFile as _,
};
fn default_client() -> reqwest::Client { fn default_client() -> reqwest::Client {
@ -105,107 +90,6 @@ async fn load_privkey(path: PathBuf) -> Result<PKey<Private>, ()> {
} }
} }
async fn process_accounts(
name: &String,
ca: &CA,
directories: &mut HashMap<String, Arc<Directory>>,
accounts: &mut HashMap<String, Arc<Account>>,
client: &Client,
accountpath: PathBuf,
) {
let directory = match directories.get(&ca.directory) {
Some(directory) => directory.to_owned(),
None => {
match DirectoryBuilder::new(ca.directory.clone()).http_client(client.clone()).build().await {
Ok(dir) => {
directories.insert(ca.directory.clone(), Arc::clone(&dir));
dir
},
Err(error) => {
error!("Failed to initialize directory for ca {name}: {error}");
return;
},
}
},
};
let mut ac = AccountBuilder::new(Arc::clone(&directory));
match ca.email_addresses.clone() {
Some(addr) => {
ac.contact(prefix_emails(addr));
},
None => {
ac.contact(Vec::new());
debug!("No Email address given")
},
}
let accountkey = accountpath.join("file.pem").with_file_name(name.clone());
let mut accountkeyfile = None;
if accountkey.exists() {
if let Ok(key) = load_privkey(accountkey).await {
ac.private_key(key);
}
} else {
info!("Registering for the CA {}", name.clone());
accountkeyfile = match FILE_MODE_WRITE.open(accountkey).await {
Ok(file) => Some(file),
Err(error) => {
error!("Failed to open the file for the accountkey: {error}");
return;
},
}
}
if let Some(meta) = &directory.meta {
// Collecting the errors about the metadata before annoying the admin about errors at different stages
let mut errors = false;
if let Some(tos) = &meta.terms_of_service {
if !ca.tos_accepted {
error!("Terms of Services were not agreed into: {tos}");
errors = true;
} else {
ac.terms_of_service_agreed(true);
}
}
if meta.external_account_required.unwrap_or(false) {
if let Some(eab) = &ca.eab {
match eab.key() {
Ok(private) => {
trace!("EAB Key info: Type={:?} Bits={}, Security-Bits={}", private.id(), private.bits(), private.security_bits());
ac.external_account_binding(eab.token.clone(), private);
},
Err(error) => {
error!("{error}");
errors = true;
},
}
} else {
error!("eab_token and/or eab_key are unset, but the CA requires those.");
errors = true;
}
} else if ca.eab.is_some() {
warn!("The CA doesn't need EAB Tokens but they were configured")
}
if errors {
return;
}
}
let account = match ac.build().await {
Ok(account) => {
accounts.insert(name.clone(), Arc::clone(&account));
account
},
Err(error) => {
error!("Failed to get/create account: {error}");
return;
},
};
if let Some(mut keyfile) = accountkeyfile {
let keydata = match_error!(account.private_key().private_key_to_pem_pkcs8()=>Err(error)-> "Failed to convert the private key to an pem: {error}");
if let Err(error) = keyfile.write(keydata.as_slice()).await {
error!("Failed to write the accountkey: {error}");
}
}
}
async fn racme(flags: Arguments) { async fn racme(flags: Arguments) {
let client = default_client(); let client = default_client();
let systemd_access = daemon::booted(); let systemd_access = daemon::booted();

8
src/prelude.rs Normale Datei
Datei anzeigen

@ -0,0 +1,8 @@
pub(crate) use crate::{
macros::*,
types::traits::{
FromFile as _,
MatchAlgorithm as _,
MatchX509 as _,
},
};

Datei anzeigen

@ -1,8 +1,12 @@
use std::{ use std::{
collections::HashSet, collections::{
HashMap,
HashSet,
},
fs::Permissions, fs::Permissions,
os::unix::fs::PermissionsExt, os::unix::fs::PermissionsExt,
path::PathBuf, path::PathBuf,
sync::Arc,
}; };
use crate::{ use crate::{
@ -13,25 +17,31 @@ use crate::{
WAIT_TIME, WAIT_TIME,
}, },
load_privkey, load_privkey,
macros::match_error, prelude::*,
types::{ types::{
config::Dns, config::{
CA,
Dns,
},
cryptography::Algorithm, cryptography::Algorithm,
structs::{ structs::{
ProcessorArgs, ProcessorArgs,
San, San,
}, },
traits::{
MatchAlgorithm as _,
MatchX509,
},
}, },
utils::gen_key, utils::{
gen_key,
prefix_emails,
},
}; };
use acme2_eab::{ use acme2_eab::{
Account,
AccountBuilder,
Authorization, Authorization,
ChallengeStatus, ChallengeStatus,
Csr, Csr,
Directory,
DirectoryBuilder,
Identifier, Identifier,
OrderBuilder, OrderBuilder,
OrderStatus, OrderStatus,
@ -54,6 +64,7 @@ use openssl::{
}, },
}, },
}; };
use reqwest::Client;
use tokio::{ use tokio::{
fs::{ fs::{
create_dir_all, create_dir_all,
@ -86,6 +97,107 @@ fn gen_stack(args: &ProcessorArgs, context: X509v3Context) -> Stack<X509Extensio
stack stack
} }
pub async fn process_accounts(
name: &String,
ca: &CA,
directories: &mut HashMap<String, Arc<Directory>>,
accounts: &mut HashMap<String, Arc<Account>>,
client: &Client,
accountpath: PathBuf,
) {
let directory = match directories.get(&ca.directory) {
Some(directory) => directory.to_owned(),
None => {
match DirectoryBuilder::new(ca.directory.clone()).http_client(client.clone()).build().await {
Ok(dir) => {
directories.insert(ca.directory.clone(), Arc::clone(&dir));
dir
},
Err(error) => {
error!("Failed to initialize directory for ca {name}: {error}");
return;
},
}
},
};
let mut ac = AccountBuilder::new(Arc::clone(&directory));
match ca.email_addresses.clone() {
Some(addr) => {
ac.contact(prefix_emails(addr));
},
None => {
ac.contact(Vec::new());
debug!("No Email address given")
},
}
let accountkey = accountpath.join("file.pem").with_file_name(name.clone());
let mut accountkeyfile = None;
if accountkey.exists() {
if let Ok(key) = load_privkey(accountkey).await {
ac.private_key(key);
}
} else {
info!("Registering for the CA {}", name.clone());
accountkeyfile = match FILE_MODE_WRITE.open(accountkey).await {
Ok(file) => Some(file),
Err(error) => {
error!("Failed to open the file for the accountkey: {error}");
return;
},
}
}
if let Some(meta) = &directory.meta {
// Collecting the errors about the metadata before annoying the admin about errors at different stages
let mut errors = false;
if let Some(tos) = &meta.terms_of_service {
if !ca.tos_accepted {
error!("Terms of Services were not agreed into: {tos}");
errors = true;
} else {
ac.terms_of_service_agreed(true);
}
}
if meta.external_account_required.unwrap_or(false) {
if let Some(eab) = &ca.eab {
match eab.key() {
Ok(private) => {
trace!("EAB Key info: Type={:?} Bits={}, Security-Bits={}", private.id(), private.bits(), private.security_bits());
ac.external_account_binding(eab.token.clone(), private);
},
Err(error) => {
error!("{error}");
errors = true;
},
}
} else {
error!("eab_token and/or eab_key are unset, but the CA requires those.");
errors = true;
}
} else if ca.eab.is_some() {
warn!("The CA doesn't need EAB Tokens but they were configured")
}
if errors {
return;
}
}
let account = match ac.build().await {
Ok(account) => {
accounts.insert(name.clone(), Arc::clone(&account));
account
},
Err(error) => {
error!("Failed to get/create account: {error}");
return;
},
};
if let Some(mut keyfile) = accountkeyfile {
let keydata = match_error!(account.private_key().private_key_to_pem_pkcs8()=>Err(error)-> "Failed to convert the private key to an pem: {error}");
if let Err(error) = keyfile.write(keydata.as_slice()).await {
error!("Failed to write the accountkey: {error}");
}
}
}
pub async fn process_site(args: ProcessorArgs<'_>) { pub async fn process_site(args: ProcessorArgs<'_>) {
let mut cert_renew = false; let mut cert_renew = false;
@ -98,31 +210,34 @@ pub async fn process_site(args: ProcessorArgs<'_>) {
}; };
cert_renew = true; cert_renew = true;
} }
let private_key_file = directory.join("privkey.pem");
let mut private_key; let mut private_key;
let mut write_pkey = false; // Private key block
if !private_key_file.exists() { {
cert_renew = true; let private_key_file = directory.join("privkey.pem");
write_pkey = true; let mut write_pkey = false;
private_key = match_error!(gen_key(args.algorithm(), args.strength())=>Err(error)-> "Aborting processing the site due to problem with the certificate generation: {error}"); if !private_key_file.exists() {
} else if let Ok(key) = load_privkey(private_key_file.clone()).await {
private_key = key;
if !private_key.matches(args.algorithm(), args.strength()) {
info!("Algorithm for the private key has changed, updating the key");
cert_renew = true; cert_renew = true;
write_pkey = true; write_pkey = true;
private_key = match_error!(gen_key(args.algorithm(), args.strength())=>Err(error)-> "Aborting processing the site due to problem with the certificate generation: {error}"); 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 !private_key.matches(args.algorithm(), args.strength()) {
info!("Algorithm for the private key has changed, updating the key");
cert_renew = true;
write_pkey = true;
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 {
error!("Failed to parse the private key. Renewing the private key.");
write_pkey = true;
cert_renew = true;
private_key = match_error!(gen_key(args.algorithm(), args.strength())=>Err(error)-> "Aborting processing the site due to problem with the certificate generation: {error}");
}
if write_pkey {
let pkey = private_key.private_key_to_pem_pkcs8().unwrap();
let mut file = match_error!(FILE_MODE_WRITE.open(private_key_file.clone()).await=>Err(error)-> "Failed to write new private key: {error}");
match_error!(file.write_all(&pkey).await=>Err(error)->"Failed to write new private key: {error}");
} }
} else {
error!("Failed to parse the private key. Renewing the private key.");
write_pkey = true;
cert_renew = true;
private_key = match_error!(gen_key(args.algorithm(), args.strength())=>Err(error)-> "Aborting processing the site due to problem with the certificate generation: {error}");
}
if write_pkey {
let pkey = private_key.private_key_to_pem_pkcs8().unwrap();
let mut file = match_error!(FILE_MODE_WRITE.open(private_key_file.clone()).await=>Err(error)-> "Failed to write new private key: {error}");
match_error!(file.write_all(&pkey).await=>Err(error)->"Failed to write new private key: {error}");
} }
let pubkey_filename = directory.join("pubkey.pem"); let pubkey_filename = directory.join("pubkey.pem");
if pubkey_filename.exists() { if pubkey_filename.exists() {
@ -151,60 +266,65 @@ pub async fn process_site(args: ProcessorArgs<'_>) {
info!("Site {} doesn't need an update for the certificate.", args.name()); info!("Site {} doesn't need an update for the certificate.", args.name());
return; return;
} }
info!("Renewing Certificate for site {}", args.name()); // Requesting a new cert
let mut builder = OrderBuilder::new(args.account()); let mut order;
builder.set_identifiers(args.san().iter().map(|s| s.to_owned().into()).collect::<Vec<Identifier>>()); {
let mut order = match_error!(builder.build().await=>Err(error)-> "Failed order the certificate: {error}"); info!("Renewing Certificate for site {}", args.name());
let authorizations = match_error!(order.authorizations().await=>Err(error)-> "Failed to get the authorizations: {error}"); let mut builder = OrderBuilder::new(args.account());
let (_, result) = tokio::join! { builder.set_identifiers(args.san().iter().map(|s| s.to_owned().into()).collect::<Vec<Identifier>>());
unsafe { order = match_error!(builder.build().await=>Err(error)-> "Failed order the certificate: {error}");
TokioScope::scope_and_collect(|scope|{ let authorizations = match_error!(order.authorizations().await=>Err(error)-> "Failed to get the authorizations: {error}");
for auth in authorizations { let (_, result) = tokio::join! {
scope.spawn(process_auth(auth, args.challenge_dir(), args.dnsserver())); unsafe {
} TokioScope::scope_and_collect(|scope|{
}) for auth in authorizations {
}, scope.spawn(process_auth(auth, args.challenge_dir(), args.dnsserver()));
order.wait_ready(WAIT_TIME, ATTEMPTS), }
}; })
order = match_error!(result=>Err(error)-> "Failed to process order: {error}"); },
if order.status == OrderStatus::Invalid { order.wait_ready(WAIT_TIME, ATTEMPTS),
error!("Failed the Order, check the logs for more information"); };
return; order = match_error!(result=>Err(error)-> "Failed to process order: {error}");
if order.status == OrderStatus::Invalid {
error!("Failed the Order, check the logs for more information");
return;
}
let mut csr = X509Req::builder().unwrap();
if let Err(error) = csr.set_pubkey(&private_key) {
error!("failed to add the public key: {error}");
return;
}
let _ = csr.add_extensions(&gen_stack(&args, csr.x509v3_context(None)));
if let Err(error) = csr.sign(
&private_key,
match args.algorithm() {
Algorithm::Rsa => MessageDigest::sha3_512(),
_ => MessageDigest::null(),
},
) {
error!("Failed to sign Request: {error}");
return;
}
order = match_error!(order.finalize(Csr::Custom(csr.build())).await=>Err(error)-> "Failed to finalize the order: {error}");
order = match_error!(order.wait_done(WAIT_TIME, ATTEMPTS).await=>Err(error)-> "Failed to finalize the order: {error}");
if order.status != OrderStatus::Valid {
error!("Failed to complete the order: check the logs for more information");
return;
}
let certs = order.certificate().await.unwrap().unwrap();
debug!("Received {} certificates.", certs.len());
let mut pubkey_file = match_error!(FILE_MODE_WRITE.open(pubkey_filename).await=>Err(error)-> "Failed to open the file for the publickey: {error}");
match_error!(pubkey_file.write_all(&certs[0].to_pem().unwrap()).await=>Err(error)-> "Failed to write the publickey: {error}");
let mut fullchain =
match_error!(FILE_MODE_WRITE.open(directory.join("fullchain.pem")).await=>Err(error)-> "failed to open the fullchain.pem: {error}");
for cert in certs.clone() {
let _ = fullchain.write_all(&cert.to_pem().unwrap()).await;
}
let mut bundle = match_error!(FILE_MODE_WRITE.open(directory.join("bundle.pem")).await=>Err(error)-> "failed to open the bundle.pem: {error}");
let _ = bundle.write_all(&private_key.private_key_to_pem_pkcs8().unwrap()).await;
let _ = bundle.write_all(&certs[0].to_pem().unwrap()).await;
info!("Processing of {} successful", args.name());
} }
let mut csr = X509Req::builder().unwrap();
if let Err(error) = csr.set_pubkey(&private_key) {
error!("failed to add the public key: {error}");
return;
}
let _ = csr.add_extensions(&gen_stack(&args, csr.x509v3_context(None)));
if let Err(error) = csr.sign(
&private_key,
match args.algorithm() {
Algorithm::Rsa => MessageDigest::sha3_512(),
_ => MessageDigest::null(),
},
) {
error!("Failed to sign Request: {error}");
return;
}
order = match_error!(order.finalize(Csr::Custom(csr.build())).await=>Err(error)-> "Failed to finalize the order: {error}");
order = match_error!(order.wait_done(WAIT_TIME, ATTEMPTS).await=>Err(error)-> "Failed to finalize the order: {error}");
if order.status != OrderStatus::Valid {
error!("Failed to complete the order: check the logs for more information");
return;
}
let certs = order.certificate().await.unwrap().unwrap();
debug!("Received {} certificates.", certs.len());
let mut pubkey_file = match_error!(FILE_MODE_WRITE.open(pubkey_filename).await=>Err(error)-> "Failed to open the file for the publickey: {error}");
match_error!(pubkey_file.write_all(&certs[0].to_pem().unwrap()).await=>Err(error)-> "Failed to write the publickey: {error}");
let mut fullchain = match_error!(FILE_MODE_WRITE.open(directory.join("fullchain.pem")).await=>Err(error)-> "failed to open the fullchain.pem: {error}");
for cert in certs.clone() {
let _ = fullchain.write_all(&cert.to_pem().unwrap()).await;
}
let mut bundle = match_error!(FILE_MODE_WRITE.open(directory.join("bundle.pem")).await=>Err(error)-> "failed to open the bundle.pem: {error}");
let _ = bundle.write_all(&private_key.private_key_to_pem_pkcs8().unwrap()).await;
let _ = bundle.write_all(&certs[0].to_pem().unwrap()).await;
info!("Processing of {} successful", args.name());
let mut services = args.reload_list().await; let mut services = args.reload_list().await;
for service in &args.reload_services() { for service in &args.reload_services() {
services.insert(service.to_owned()); services.insert(service.to_owned());

Datei anzeigen

@ -1,8 +1,5 @@
use crate::{ use crate::{
macros::{ prelude::*,
DefDer,
match_error,
},
types::{ types::{
VString, VString,
cryptography::{ cryptography::{

Datei anzeigen

@ -1,7 +1,7 @@
use macro_rules_attribute::macro_rules_derive; use macro_rules_attribute::macro_rules_derive;
use serde::Deserialize; use serde::Deserialize;
use crate::macros::DefDer; use crate::prelude::*;
#[macro_rules_derive(DefDer)] #[macro_rules_derive(DefDer)]

Datei anzeigen

@ -11,14 +11,10 @@ use macro_rules_attribute::macro_rules_derive;
use tokio::sync::MutexGuard; use tokio::sync::MutexGuard;
use crate::{ use crate::{
config::SiteConfig, prelude::*,
macros::{
DefDer,
Hashable,
attr_function,
},
types::{ types::{
SafeSet, SafeSet,
config::SiteConfig,
cryptography::{ cryptography::{
Algorithm, Algorithm,
Strength, Strength,
@ -26,7 +22,7 @@ use crate::{
}, },
}; };
use super::config::Dns; use crate::types::config::Dns;
#[macro_rules_derive(DefDer)] #[macro_rules_derive(DefDer)]