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 macros;
pub(crate) mod prelude;
pub(crate) mod process;
pub(crate) mod types;
pub(crate) mod utils;
use crate::{
consts::*,
macros::match_error,
prelude::*,
process::{
process_accounts,
process_site,
services,
},
types::{
config,
config::{
CA,
GeneralConfig,
SiteConfig,
},
structs::{
@ -23,14 +28,8 @@ use crate::{
ProcessorArgs,
},
},
utils::prefix_emails,
};
use acme2_eab::{
Account,
AccountBuilder,
Directory,
DirectoryBuilder,
};
use acme2_eab::Directory;
use async_scoped::TokioScope;
use clap::Parser;
use env_logger::init as log_init;
@ -43,14 +42,7 @@ use openssl::{
Private,
},
};
use process::{
process_site,
services,
};
use reqwest::{
Client,
tls::Version,
};
use reqwest::tls::Version;
use std::{
collections::{
HashMap,
@ -69,20 +61,13 @@ use tokio::{
create_dir_all,
read_dir,
},
io::{
AsyncReadExt,
AsyncWriteExt,
},
io::AsyncReadExt,
sync::Mutex,
};
use tokio_stream::{
StreamExt,
wrappers::ReadDirStream,
};
use types::{
config::GeneralConfig,
traits::FromFile as _,
};
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) {
let client = default_client();
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::{
collections::HashSet,
collections::{
HashMap,
HashSet,
},
fs::Permissions,
os::unix::fs::PermissionsExt,
path::PathBuf,
sync::Arc,
};
use crate::{
@ -13,25 +17,31 @@ use crate::{
WAIT_TIME,
},
load_privkey,
macros::match_error,
prelude::*,
types::{
config::Dns,
config::{
CA,
Dns,
},
cryptography::Algorithm,
structs::{
ProcessorArgs,
San,
},
traits::{
MatchAlgorithm as _,
MatchX509,
},
},
utils::gen_key,
utils::{
gen_key,
prefix_emails,
},
};
use acme2_eab::{
Account,
AccountBuilder,
Authorization,
ChallengeStatus,
Csr,
Directory,
DirectoryBuilder,
Identifier,
OrderBuilder,
OrderStatus,
@ -54,6 +64,7 @@ use openssl::{
},
},
};
use reqwest::Client;
use tokio::{
fs::{
create_dir_all,
@ -86,6 +97,107 @@ fn gen_stack(args: &ProcessorArgs, context: X509v3Context) -> Stack<X509Extensio
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<'_>) {
let mut cert_renew = false;
@ -98,31 +210,34 @@ pub async fn process_site(args: ProcessorArgs<'_>) {
};
cert_renew = true;
}
let private_key_file = directory.join("privkey.pem");
let mut private_key;
let mut write_pkey = false;
if !private_key_file.exists() {
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 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");
// Private key block
{
let private_key_file = directory.join("privkey.pem");
let mut write_pkey = false;
if !private_key_file.exists() {
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 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");
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());
return;
}
info!("Renewing Certificate for site {}", args.name());
let mut builder = OrderBuilder::new(args.account());
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}");
let authorizations = match_error!(order.authorizations().await=>Err(error)-> "Failed to get the authorizations: {error}");
let (_, result) = tokio::join! {
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 {
error!("Failed the Order, check the logs for more information");
return;
// Requesting a new cert
let mut order;
{
info!("Renewing Certificate for site {}", args.name());
let mut builder = OrderBuilder::new(args.account());
builder.set_identifiers(args.san().iter().map(|s| s.to_owned().into()).collect::<Vec<Identifier>>());
order = match_error!(builder.build().await=>Err(error)-> "Failed order the certificate: {error}");
let authorizations = match_error!(order.authorizations().await=>Err(error)-> "Failed to get the authorizations: {error}");
let (_, result) = tokio::join! {
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 {
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;
for service in &args.reload_services() {
services.insert(service.to_owned());

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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