Browse Source

Add an independent endpoint structure

Having the data relative to the endpoint in the certificate structure
makes no sense. There is no way to share it across certificates which is
sometimes needed. Also, having the other part of the endpoint data
(mostly nonce and rate limit) in different places makes it difficult to
maintain.
Hence, the endpoint structure has been created. For now it is quite
simple and does not handle every aspects of the endpoint, but this will
be implemented in the future.
pull/31/head
Rodolphe Breard 5 years ago
parent
commit
b288f2b32c
  1. 1
      acmed/Cargo.toml
  2. 7
      acmed/src/acme_proto.rs
  3. 4
      acmed/src/acme_proto/account.rs
  4. 5
      acmed/src/acme_proto/structs/account.rs
  5. 3
      acmed/src/certificate.rs
  6. 35
      acmed/src/config.rs
  7. 6
      acmed/src/endpoint.rs
  8. 3
      acmed/src/main.rs
  9. 31
      acmed/src/main_event_loop.rs

1
acmed/Cargo.toml

@ -20,6 +20,7 @@ http_req = "0.5"
log = "0.4" log = "0.4"
nom = "5.0" nom = "5.0"
openssl-sys = "0.9" openssl-sys = "0.9"
reqwest = { version = "0.10", features = ["blocking"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
toml = "0.5" toml = "0.5"

7
acmed/src/acme_proto.rs

@ -3,6 +3,7 @@ use crate::acme_proto::structs::{
ApiError, Authorization, AuthorizationStatus, NewOrder, Order, OrderStatus, ApiError, Authorization, AuthorizationStatus, NewOrder, Order, OrderStatus,
}; };
use crate::certificate::Certificate; use crate::certificate::Certificate;
use crate::endpoint::Endpoint;
use crate::jws::encode_kid; use crate::jws::encode_kid;
use crate::storage; use crate::storage;
use acme_common::crypto::Csr; 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 let domains = cert
.domains .domains
.iter() .iter()
@ -75,13 +76,13 @@ pub fn request_certificate(cert: &Certificate, root_certs: &[String]) -> Result<
let mut hook_datas = vec![]; let mut hook_datas = vec![];
// 1. Get the directory // 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 // 2. Get a first nonce
let nonce = http::get_nonce(cert, root_certs, &directory.new_nonce)?; let nonce = http::get_nonce(cert, root_certs, &directory.new_nonce)?;
// 3. Get or create the account // 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 // 4. Create a new order
let new_order = NewOrder::new(&domains); let new_order = NewOrder::new(&domains);

4
acmed/src/acme_proto/account.rs

@ -1,6 +1,7 @@
use crate::acme_proto::http; use crate::acme_proto::http;
use crate::acme_proto::structs::{Account, AccountResponse, Directory}; use crate::acme_proto::structs::{Account, AccountResponse, Directory};
use crate::certificate::Certificate; use crate::certificate::Certificate;
use crate::endpoint::Endpoint;
use crate::jws::algorithms::SignatureAlgorithm; use crate::jws::algorithms::SignatureAlgorithm;
use crate::jws::encode_jwk; use crate::jws::encode_jwk;
use crate::storage; use crate::storage;
@ -18,12 +19,13 @@ impl AccountManager {
pub fn new( pub fn new(
cert: &Certificate, cert: &Certificate,
directory: &Directory, directory: &Directory,
endpoint: &Endpoint,
nonce: &str, nonce: &str,
root_certs: &[String], root_certs: &[String],
) -> Result<(Self, String), Error> { ) -> Result<(Self, String), Error> {
// TODO: store the key id (account url) // TODO: store the key id (account url)
let key_pair = storage::get_account_keypair(cert)?; 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 account = serde_json::to_string(&account)?;
let data_builder = let data_builder =
|n: &str| encode_jwk(&key_pair, account.as_bytes(), &directory.new_account, n); |n: &str| encode_jwk(&key_pair, account.as_bytes(), &directory.new_account, n);

5
acmed/src/acme_proto/structs/account.rs

@ -1,4 +1,5 @@
use crate::certificate::Certificate; use crate::certificate::Certificate;
use crate::endpoint::Endpoint;
use acme_common::error::Error; use acme_common::error::Error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::str::FromStr; use std::str::FromStr;
@ -12,10 +13,10 @@ pub struct Account {
} }
impl Account { impl Account {
pub fn new(cert: &Certificate) -> Self {
pub fn new(cert: &Certificate, endpoint: &Endpoint) -> Self {
Account { Account {
contact: vec![format!("mailto:{}", cert.account.email)], 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, only_return_existing: false,
} }
} }

3
acmed/src/certificate.rs

@ -47,8 +47,7 @@ pub struct Certificate {
pub domains: Vec<Domain>, pub domains: Vec<Domain>,
pub algo: Algorithm, pub algo: Algorithm,
pub kp_reuse: bool, pub kp_reuse: bool,
pub remote_url: String,
pub tos_agreed: bool,
pub endpoint_name: String,
pub hooks: Vec<Hook>, pub hooks: Vec<Hook>,
pub account_directory: String, pub account_directory: String,
pub crt_directory: String, pub crt_directory: String,

35
acmed/src/config.rs

@ -184,13 +184,14 @@ pub struct Endpoint {
} }
impl 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<crate::endpoint::Endpoint, Error> {
// 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() crt_directory.to_string()
} }
fn get_endpoint(&self, cnf: &Config) -> Result<Endpoint, Error> {
pub fn get_endpoint(&self, cnf: &Config) -> Result<crate::endpoint::Endpoint, Error> {
for endpoint in cnf.endpoint.iter() { for endpoint in cnf.endpoint.iter() {
if endpoint.name == self.endpoint { 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()) Err(format!("{}: unknown endpoint.", self.endpoint).into())
} }
pub fn get_remote_url(&self, cnf: &Config) -> Result<String, Error> {
let ep = self.get_endpoint(cnf)?;
Ok(ep.url)
}
pub fn get_endpoint_name(&self, cnf: &Config) -> Result<String, Error> {
let ep = self.get_endpoint(cnf)?;
Ok(ep.name)
}
pub fn get_tos_agreement(&self, cnf: &Config) -> Result<bool, Error> {
let ep = self.get_endpoint(cnf)?;
Ok(ep.tos_agreed)
}
pub fn get_hooks(&self, cnf: &Config) -> Result<Vec<hooks::Hook>, Error> { pub fn get_hooks(&self, cnf: &Config) -> Result<Vec<hooks::Hook>, Error> {
let mut res = vec![]; let mut res = vec![];
for name in self.hooks.iter() { for name in self.hooks.iter() {

6
acmed/src/endpoint.rs

@ -0,0 +1,6 @@
pub struct Endpoint {
pub name: String,
pub url: String,
pub tos_agreed: bool,
// TODO: rate limits
}

3
acmed/src/main.rs

@ -6,6 +6,7 @@ use log::error;
mod acme_proto; mod acme_proto;
mod certificate; mod certificate;
mod config; mod config;
mod endpoint;
mod hooks; mod hooks;
mod jws; mod jws;
mod main_event_loop; mod main_event_loop;
@ -120,7 +121,7 @@ fn main() {
); );
let config_file = matches.value_of("config").unwrap_or(DEFAULT_CONFIG_FILE); 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, Ok(s) => s,
Err(e) => { Err(e) => {
error!("{}", e); error!("{}", e);

31
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::acme_proto::request_certificate;
use crate::certificate::Certificate; use crate::certificate::Certificate;
use crate::config; use crate::config;
use crate::endpoint::Endpoint;
use acme_common::error::Error; use acme_common::error::Error;
use std::collections::HashMap;
use std::thread; use std::thread;
use std::time::Duration; 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), Ok(_) => ("Success.".to_string(), true),
Err(e) => { Err(e) => {
let e = e.prefix("Unable to renew the certificate"); let e = e.prefix("Unable to renew the certificate");
@ -27,6 +29,7 @@ fn renew_certificate(crt: &Certificate, root_certs: &[String]) {
pub struct MainEventLoop { pub struct MainEventLoop {
certs: Vec<Certificate>, certs: Vec<Certificate>,
root_certs: Vec<String>, root_certs: Vec<String>,
endpoints: HashMap<String, Endpoint>,
} }
impl MainEventLoop { impl MainEventLoop {
@ -34,15 +37,15 @@ impl MainEventLoop {
let cnf = config::from_file(config_file)?; let cnf = config::from_file(config_file)?;
let mut certs = Vec::new(); let mut certs = Vec::new();
let mut endpoints = HashMap::new();
for (i, crt) in cnf.certificate.iter().enumerate() { 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 { let cert = Certificate {
account: crt.get_account(&cnf)?, account: crt.get_account(&cnf)?,
domains: crt.get_domains()?, domains: crt.get_domains()?,
algo: crt.get_algorithm()?, algo: crt.get_algorithm()?,
kp_reuse: crt.get_kp_reuse(), 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)?, hooks: crt.get_hooks(&cnf)?,
account_directory: cnf.get_account_dir(), account_directory: cnf.get_account_dir(),
crt_directory: crt.get_crt_dir(&cnf), crt_directory: crt.get_crt_dir(&cnf),
@ -57,6 +60,9 @@ impl MainEventLoop {
env: crt.env.to_owned(), env: crt.env.to_owned(),
id: i + 1, id: i + 1,
}; };
if ! endpoints.contains_key(&endpoint.name) {
endpoints.insert(endpoint.name.clone(), endpoint);
}
init_account(&cert)?; init_account(&cert)?;
certs.push(cert); certs.push(cert);
} }
@ -64,23 +70,30 @@ impl MainEventLoop {
Ok(MainEventLoop { Ok(MainEventLoop {
certs, certs,
root_certs: root_certs.iter().map(|v| (*v).to_string()).collect(), root_certs: root_certs.iter().map(|v| (*v).to_string()).collect(),
endpoints,
}) })
} }
pub fn run(&self) {
pub fn run(&mut self) {
loop { loop {
self.renew_certificates(); self.renew_certificates();
thread::sleep(Duration::from_secs(crate::DEFAULT_SLEEP_TIME)); thread::sleep(Duration::from_secs(crate::DEFAULT_SLEEP_TIME));
} }
} }
fn renew_certificates(&self) {
fn renew_certificates(&mut self) {
for crt in self.certs.iter() { for crt in self.certs.iter() {
match crt.should_renew() { match crt.should_renew() {
Ok(true) => { Ok(true) => {
let root_certs = self.root_certs.clone(); 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) => {} Ok(false) => {}
Err(e) => { Err(e) => {

Loading…
Cancel
Save