diff --git a/acmed/Cargo.toml b/acmed/Cargo.toml index a13461d..092268f 100644 --- a/acmed/Cargo.toml +++ b/acmed/Cargo.toml @@ -20,6 +20,7 @@ http_req = "0.5" log = "0.4" nom = "5.0" openssl-sys = "0.9" +reqwest = { version = "0.10", features = ["blocking"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" toml = "0.5" diff --git a/acmed/src/acme_proto.rs b/acmed/src/acme_proto.rs index 6e98341..992ac66 100644 --- a/acmed/src/acme_proto.rs +++ b/acmed/src/acme_proto.rs @@ -3,6 +3,7 @@ use crate::acme_proto::structs::{ ApiError, Authorization, AuthorizationStatus, NewOrder, Order, OrderStatus, }; use crate::certificate::Certificate; +use crate::endpoint::Endpoint; use crate::jws::encode_kid; use crate::storage; use acme_common::crypto::Csr; @@ -66,7 +67,7 @@ macro_rules! set_empty_data_builder { }; } -pub fn request_certificate(cert: &Certificate, root_certs: &[String]) -> Result<(), Error> { +pub fn request_certificate(cert: &Certificate, root_certs: &[String], endpoint: &mut Endpoint) -> Result<(), Error> { let domains = cert .domains .iter() @@ -75,13 +76,13 @@ pub fn request_certificate(cert: &Certificate, root_certs: &[String]) -> Result< let mut hook_datas = vec![]; // 1. Get the directory - let directory = http::get_directory(cert, root_certs, &cert.remote_url)?; + let directory = http::get_directory(cert, root_certs, &endpoint.url)?; // 2. Get a first nonce let nonce = http::get_nonce(cert, root_certs, &directory.new_nonce)?; // 3. Get or create the account - let (account, nonce) = AccountManager::new(cert, &directory, &nonce, root_certs)?; + let (account, nonce) = AccountManager::new(cert, &directory, endpoint, &nonce, root_certs)?; // 4. Create a new order let new_order = NewOrder::new(&domains); diff --git a/acmed/src/acme_proto/account.rs b/acmed/src/acme_proto/account.rs index 4068264..0c9abbf 100644 --- a/acmed/src/acme_proto/account.rs +++ b/acmed/src/acme_proto/account.rs @@ -1,6 +1,7 @@ use crate::acme_proto::http; use crate::acme_proto::structs::{Account, AccountResponse, Directory}; use crate::certificate::Certificate; +use crate::endpoint::Endpoint; use crate::jws::algorithms::SignatureAlgorithm; use crate::jws::encode_jwk; use crate::storage; @@ -18,12 +19,13 @@ impl AccountManager { pub fn new( cert: &Certificate, directory: &Directory, + endpoint: &Endpoint, nonce: &str, root_certs: &[String], ) -> Result<(Self, String), Error> { // TODO: store the key id (account url) let key_pair = storage::get_account_keypair(cert)?; - let account = Account::new(cert); + let account = Account::new(cert, endpoint); let account = serde_json::to_string(&account)?; let data_builder = |n: &str| encode_jwk(&key_pair, account.as_bytes(), &directory.new_account, n); diff --git a/acmed/src/acme_proto/structs/account.rs b/acmed/src/acme_proto/structs/account.rs index 841e7a9..a869177 100644 --- a/acmed/src/acme_proto/structs/account.rs +++ b/acmed/src/acme_proto/structs/account.rs @@ -1,4 +1,5 @@ use crate::certificate::Certificate; +use crate::endpoint::Endpoint; use acme_common::error::Error; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -12,10 +13,10 @@ pub struct Account { } impl Account { - pub fn new(cert: &Certificate) -> Self { + pub fn new(cert: &Certificate, endpoint: &Endpoint) -> Self { Account { contact: vec![format!("mailto:{}", cert.account.email)], - terms_of_service_agreed: cert.tos_agreed, + terms_of_service_agreed: endpoint.tos_agreed, only_return_existing: false, } } diff --git a/acmed/src/certificate.rs b/acmed/src/certificate.rs index 09e78c7..de3d1e9 100644 --- a/acmed/src/certificate.rs +++ b/acmed/src/certificate.rs @@ -47,8 +47,7 @@ pub struct Certificate { pub domains: Vec, pub algo: Algorithm, pub kp_reuse: bool, - pub remote_url: String, - pub tos_agreed: bool, + pub endpoint_name: String, pub hooks: Vec, pub account_directory: String, pub crt_directory: String, diff --git a/acmed/src/config.rs b/acmed/src/config.rs index e76273c..f436c53 100644 --- a/acmed/src/config.rs +++ b/acmed/src/config.rs @@ -184,13 +184,14 @@ pub struct Endpoint { } impl Endpoint { - fn is_used(&self, config: &Config) -> bool { - for crt in config.certificate.iter() { - if crt.endpoint == self.name { - return true; - } - } - false + fn to_generic(&self, _cnf: &Config) -> Result { + // TODO: include rate limits using `cnf.get_rate_limit()` + let ep = crate::endpoint::Endpoint { + name: self.name.to_owned(), + url: self.url.to_owned(), + tos_agreed: self.tos_agreed, + }; + Ok(ep) } } @@ -334,30 +335,16 @@ impl Certificate { crt_directory.to_string() } - fn get_endpoint(&self, cnf: &Config) -> Result { + pub fn get_endpoint(&self, cnf: &Config) -> Result { for endpoint in cnf.endpoint.iter() { if endpoint.name == self.endpoint { - return Ok(endpoint.clone()); + let ep = endpoint.to_generic(cnf)?; + return Ok(ep); } } Err(format!("{}: unknown endpoint.", self.endpoint).into()) } - pub fn get_remote_url(&self, cnf: &Config) -> Result { - let ep = self.get_endpoint(cnf)?; - Ok(ep.url) - } - - pub fn get_endpoint_name(&self, cnf: &Config) -> Result { - let ep = self.get_endpoint(cnf)?; - Ok(ep.name) - } - - pub fn get_tos_agreement(&self, cnf: &Config) -> Result { - let ep = self.get_endpoint(cnf)?; - Ok(ep.tos_agreed) - } - pub fn get_hooks(&self, cnf: &Config) -> Result, Error> { let mut res = vec![]; for name in self.hooks.iter() { diff --git a/acmed/src/endpoint.rs b/acmed/src/endpoint.rs new file mode 100644 index 0000000..9d8ef4a --- /dev/null +++ b/acmed/src/endpoint.rs @@ -0,0 +1,6 @@ +pub struct Endpoint { + pub name: String, + pub url: String, + pub tos_agreed: bool, + // TODO: rate limits +} diff --git a/acmed/src/main.rs b/acmed/src/main.rs index 3ea922c..9f7e4d1 100644 --- a/acmed/src/main.rs +++ b/acmed/src/main.rs @@ -6,6 +6,7 @@ use log::error; mod acme_proto; mod certificate; mod config; +mod endpoint; mod hooks; mod jws; mod main_event_loop; @@ -120,7 +121,7 @@ fn main() { ); let config_file = matches.value_of("config").unwrap_or(DEFAULT_CONFIG_FILE); - let srv = match MainEventLoop::new(&config_file, &root_certs) { + let mut srv = match MainEventLoop::new(&config_file, &root_certs) { Ok(s) => s, Err(e) => { error!("{}", e); diff --git a/acmed/src/main_event_loop.rs b/acmed/src/main_event_loop.rs index a9a312f..b8f1bc4 100644 --- a/acmed/src/main_event_loop.rs +++ b/acmed/src/main_event_loop.rs @@ -2,12 +2,14 @@ use crate::acme_proto::account::init_account; use crate::acme_proto::request_certificate; use crate::certificate::Certificate; use crate::config; +use crate::endpoint::Endpoint; use acme_common::error::Error; +use std::collections::HashMap; use std::thread; use std::time::Duration; -fn renew_certificate(crt: &Certificate, root_certs: &[String]) { - let (status, is_success) = match request_certificate(crt, root_certs) { +fn renew_certificate(crt: &Certificate, root_certs: &[String], endpoint: &mut Endpoint) { + let (status, is_success) = match request_certificate(crt, root_certs, endpoint) { Ok(_) => ("Success.".to_string(), true), Err(e) => { let e = e.prefix("Unable to renew the certificate"); @@ -27,6 +29,7 @@ fn renew_certificate(crt: &Certificate, root_certs: &[String]) { pub struct MainEventLoop { certs: Vec, root_certs: Vec, + endpoints: HashMap, } impl MainEventLoop { @@ -34,15 +37,15 @@ impl MainEventLoop { let cnf = config::from_file(config_file)?; let mut certs = Vec::new(); + let mut endpoints = HashMap::new(); for (i, crt) in cnf.certificate.iter().enumerate() { - let ep_name = crt.get_endpoint_name(&cnf)?; + let endpoint = crt.get_endpoint(&cnf)?; let cert = Certificate { account: crt.get_account(&cnf)?, domains: crt.get_domains()?, algo: crt.get_algorithm()?, kp_reuse: crt.get_kp_reuse(), - remote_url: crt.get_remote_url(&cnf)?, - tos_agreed: crt.get_tos_agreement(&cnf)?, + endpoint_name: endpoint.name.clone(), hooks: crt.get_hooks(&cnf)?, account_directory: cnf.get_account_dir(), crt_directory: crt.get_crt_dir(&cnf), @@ -57,6 +60,9 @@ impl MainEventLoop { env: crt.env.to_owned(), id: i + 1, }; + if ! endpoints.contains_key(&endpoint.name) { + endpoints.insert(endpoint.name.clone(), endpoint); + } init_account(&cert)?; certs.push(cert); } @@ -64,23 +70,30 @@ impl MainEventLoop { Ok(MainEventLoop { certs, root_certs: root_certs.iter().map(|v| (*v).to_string()).collect(), + endpoints, }) } - pub fn run(&self) { + pub fn run(&mut self) { loop { self.renew_certificates(); thread::sleep(Duration::from_secs(crate::DEFAULT_SLEEP_TIME)); } } - fn renew_certificates(&self) { + fn renew_certificates(&mut self) { for crt in self.certs.iter() { match crt.should_renew() { Ok(true) => { let root_certs = self.root_certs.clone(); - let cert = (*crt).clone(); - renew_certificate(&cert, &root_certs); + match self.endpoints.get_mut(&crt.endpoint_name) { + Some(mut endpoint) => { + renew_certificate(&crt, &root_certs, &mut endpoint); + }, + None => { + crt.warn(&format!("{}: Endpoint not found", &crt.endpoint_name)); + } + }; } Ok(false) => {} Err(e) => {