mirror of https://github.com/breard-r/acmed.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
5.9 KiB
209 lines
5.9 KiB
use crate::account::Account;
|
|
use crate::acme_proto::request_certificate;
|
|
use crate::certificate::Certificate;
|
|
use crate::config;
|
|
use crate::endpoint::Endpoint;
|
|
use crate::hooks::HookType;
|
|
use crate::logs::HasLogger;
|
|
use crate::storage::FileManager;
|
|
use acme_common::error::Error;
|
|
use std::collections::HashMap;
|
|
use std::sync::{Arc, RwLock};
|
|
use std::thread;
|
|
use std::time::Duration;
|
|
|
|
type AccountSync = Arc<RwLock<Account>>;
|
|
type EndpointSync = Arc<RwLock<Endpoint>>;
|
|
|
|
fn renew_certificate(crt: &Certificate, endpoint: &mut Endpoint, account: &mut Account) {
|
|
let (status, is_success) = match request_certificate(crt, endpoint, account) {
|
|
Ok(_) => ("success".to_string(), true),
|
|
Err(e) => {
|
|
let e = e.prefix("unable to renew the certificate");
|
|
crt.warn(&e.message);
|
|
(e.message, false)
|
|
}
|
|
};
|
|
match crt.call_post_operation_hooks(&status, is_success) {
|
|
Ok(_) => {}
|
|
Err(e) => {
|
|
let e = e.prefix("post-operation hook error");
|
|
crt.warn(&e.message);
|
|
}
|
|
};
|
|
}
|
|
|
|
pub struct MainEventLoop {
|
|
certs: Vec<Certificate>,
|
|
accounts: HashMap<String, AccountSync>,
|
|
endpoints: HashMap<String, EndpointSync>,
|
|
}
|
|
|
|
impl MainEventLoop {
|
|
pub fn new(config_file: &str, root_certs: &[&str]) -> Result<Self, Error> {
|
|
let cnf = config::from_file(config_file)?;
|
|
let file_hooks = vec![
|
|
HookType::FilePreCreate,
|
|
HookType::FilePostCreate,
|
|
HookType::FilePreEdit,
|
|
HookType::FilePostEdit,
|
|
]
|
|
.into_iter()
|
|
.collect();
|
|
let cert_hooks = vec![
|
|
HookType::ChallengeHttp01,
|
|
HookType::ChallengeHttp01Clean,
|
|
HookType::ChallengeDns01,
|
|
HookType::ChallengeDns01Clean,
|
|
HookType::ChallengeTlsAlpn01,
|
|
HookType::ChallengeTlsAlpn01Clean,
|
|
HookType::PostOperation,
|
|
]
|
|
.into_iter()
|
|
.collect();
|
|
|
|
let mut accounts = HashMap::new();
|
|
for acc in cnf.account.iter() {
|
|
let fm = FileManager {
|
|
account_directory: cnf.get_account_dir(),
|
|
account_name: acc.name.clone(),
|
|
crt_name: String::new(),
|
|
crt_name_format: String::new(),
|
|
crt_directory: String::new(),
|
|
crt_key_type: String::new(),
|
|
cert_file_mode: cnf.get_cert_file_mode(),
|
|
cert_file_owner: cnf.get_cert_file_user(),
|
|
cert_file_group: cnf.get_cert_file_group(),
|
|
pk_file_mode: cnf.get_pk_file_mode(),
|
|
pk_file_owner: cnf.get_pk_file_user(),
|
|
pk_file_group: cnf.get_pk_file_group(),
|
|
hooks: acc
|
|
.get_hooks(&cnf)?
|
|
.iter()
|
|
.filter(|h| !h.hook_type.is_disjoint(&file_hooks))
|
|
.map(|e| e.to_owned())
|
|
.collect(),
|
|
env: acc.env.clone(),
|
|
};
|
|
let account = acc.to_generic(&fm)?;
|
|
accounts.insert(acc.name.clone(), account);
|
|
}
|
|
|
|
let mut certs: Vec<Certificate> = Vec::new();
|
|
let mut endpoints = HashMap::new();
|
|
for crt in cnf.certificate.iter() {
|
|
let endpoint = crt.get_endpoint(&cnf, root_certs)?;
|
|
let endpoint_name = endpoint.name.clone();
|
|
let crt_name = crt.get_crt_name()?;
|
|
let key_type = crt.get_key_type()?;
|
|
let hooks = crt.get_hooks(&cnf)?;
|
|
let fm = FileManager {
|
|
account_directory: cnf.get_account_dir(),
|
|
account_name: crt.account.clone(),
|
|
crt_name: crt_name.clone(),
|
|
crt_name_format: crt.get_crt_name_format(&cnf)?,
|
|
crt_directory: crt.get_crt_dir(&cnf),
|
|
crt_key_type: key_type.to_string(),
|
|
cert_file_mode: cnf.get_cert_file_mode(),
|
|
cert_file_owner: cnf.get_cert_file_user(),
|
|
cert_file_group: cnf.get_cert_file_group(),
|
|
pk_file_mode: cnf.get_pk_file_mode(),
|
|
pk_file_owner: cnf.get_pk_file_user(),
|
|
pk_file_group: cnf.get_pk_file_group(),
|
|
hooks: hooks
|
|
.iter()
|
|
.filter(|h| !h.hook_type.is_disjoint(&file_hooks))
|
|
.map(|e| e.to_owned())
|
|
.collect(),
|
|
env: crt.env.clone(),
|
|
};
|
|
let cert = Certificate {
|
|
account_name: crt.account.clone(),
|
|
identifiers: crt.get_identifiers()?,
|
|
subject_attributes: crt.subject_attributes.to_generic(),
|
|
key_type,
|
|
csr_digest: crt.get_csr_digest()?,
|
|
kp_reuse: crt.get_kp_reuse(),
|
|
endpoint_name: endpoint_name.clone(),
|
|
hooks: hooks
|
|
.iter()
|
|
.filter(|h| !h.hook_type.is_disjoint(&cert_hooks))
|
|
.map(|e| e.to_owned())
|
|
.collect(),
|
|
crt_name,
|
|
env: crt.env.to_owned(),
|
|
renew_delay: crt.get_renew_delay(&cnf)?,
|
|
file_manager: fm,
|
|
};
|
|
let crt_id = cert.get_id();
|
|
if certs.iter().any(|c| c.get_id() == crt_id) {
|
|
let msg = format!("{}: duplicate certificate id", crt_id);
|
|
return Err(msg.into());
|
|
}
|
|
match accounts.get_mut(&crt.account) {
|
|
Some(acc) => acc.add_endpoint_name(&endpoint_name),
|
|
None => {
|
|
let msg = format!("{}: account not found", &crt.account);
|
|
return Err(msg.into());
|
|
}
|
|
};
|
|
endpoints.entry(endpoint_name).or_insert(endpoint);
|
|
certs.push(cert);
|
|
}
|
|
|
|
Ok(MainEventLoop {
|
|
certs,
|
|
accounts: accounts
|
|
.iter()
|
|
.map(|(k, v)| (k.to_owned(), Arc::new(RwLock::new(v.to_owned()))))
|
|
.collect(),
|
|
endpoints: endpoints
|
|
.iter()
|
|
.map(|(k, v)| (k.to_owned(), Arc::new(RwLock::new(v.to_owned()))))
|
|
.collect(),
|
|
})
|
|
}
|
|
|
|
pub fn run(&mut self) {
|
|
loop {
|
|
self.renew_certificates();
|
|
thread::sleep(Duration::from_secs(crate::DEFAULT_SLEEP_TIME));
|
|
}
|
|
}
|
|
|
|
fn renew_certificates(&mut self) {
|
|
let mut handles = vec![];
|
|
for (ep_name, endpoint_lock) in self.endpoints.iter_mut() {
|
|
let mut certs_to_renew = vec![];
|
|
for crt in self.certs.iter() {
|
|
if crt.endpoint_name == *ep_name {
|
|
match crt.should_renew() {
|
|
Ok(true) => {
|
|
let crt_arc = Arc::new(crt.clone());
|
|
certs_to_renew.push(crt_arc);
|
|
}
|
|
Ok(false) => {}
|
|
Err(e) => {
|
|
crt.warn(&e.message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
let mut accounts_lock = self.accounts.clone();
|
|
let ep_lock = endpoint_lock.clone();
|
|
let handle = thread::spawn(move || {
|
|
let mut endpoint = ep_lock.write().unwrap();
|
|
for crt in certs_to_renew {
|
|
if let Some(acc_lock) = accounts_lock.get_mut(&crt.account_name) {
|
|
let mut account = acc_lock.write().unwrap();
|
|
renew_certificate(&crt, &mut endpoint, &mut account);
|
|
};
|
|
}
|
|
});
|
|
handles.push(handle);
|
|
}
|
|
for handle in handles {
|
|
let _ = handle.join();
|
|
}
|
|
}
|
|
}
|