feat: cert config
This commit is contained in:
118
src/config.rs
118
src/config.rs
@@ -5,14 +5,20 @@ use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use acme_lib::DirectoryUrl;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use crate::x509;
|
||||
use crate::x509::{X509PublicKeyAlgo, X509EcPublicKeyAlgo, X509Certificate};
|
||||
use std::time::{SystemTime, Duration};
|
||||
|
||||
const CERT_NAME: &str = "cert.pem";
|
||||
const KEY_NAME: &str = "key.pem";
|
||||
pub const CERT_NAME: &str = "cert.pem";
|
||||
pub const KEY_NAME: &str = "key.pem";
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum AcmeAlgo {
|
||||
Ec256,
|
||||
Ec384,
|
||||
Ec521,
|
||||
Rsa(u32),
|
||||
}
|
||||
|
||||
@@ -31,6 +37,7 @@ impl AcmeAlgo {
|
||||
match s {
|
||||
"ec256" => Ok(AcmeAlgo::Ec256),
|
||||
"ec384" => Ok(AcmeAlgo::Ec384),
|
||||
"ec521" => Ok(AcmeAlgo::Ec521),
|
||||
"rsa2048" => Ok(AcmeAlgo::Rsa(2048)),
|
||||
"rsa3072" => Ok(AcmeAlgo::Rsa(3072)),
|
||||
"rsa4096" => Ok(AcmeAlgo::Rsa(4096)),
|
||||
@@ -79,22 +86,7 @@ pub struct AcmeConfig {
|
||||
pub dir: String,
|
||||
pub auth_timeout: Option<u64>,
|
||||
pub csr_timeout: Option<u64>,
|
||||
pub concurrent: Option<u32>, // ?
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CertConfigItem {
|
||||
pub path: String,
|
||||
pub algo: Option<String>,
|
||||
pub common_name: Option<String>,
|
||||
pub dns_names: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CertConfig {
|
||||
pub cert_items: Vec<CertConfigItem>,
|
||||
pub concurrent: Option<u32>,
|
||||
}
|
||||
|
||||
impl AcmeConfig {
|
||||
@@ -111,10 +103,94 @@ impl AcmeConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CertConfigItem {
|
||||
pub path: String,
|
||||
pub algo: Option<String>,
|
||||
pub public_key_algo: Option<X509PublicKeyAlgo>,
|
||||
pub common_name: Option<String>,
|
||||
pub dns_names: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CertConfig {
|
||||
pub cert_items: Vec<CertConfigItem>,
|
||||
}
|
||||
|
||||
impl CertConfig {
|
||||
pub fn filter_cert_config_items(self, valid_days: i32) -> Self {
|
||||
let mut cert_items = vec![];
|
||||
|
||||
let valid_days_secs = valid_days as i64 * 24 * 3600;
|
||||
let secs_from_unix_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() as i64;
|
||||
for item in &self.cert_items {
|
||||
let mut item2 = item.clone();
|
||||
match item2.fill_dns_names() {
|
||||
Ok(Some(x509_certificate)) => {
|
||||
if x509_certificate.certificate_not_after >= (valid_days_secs + secs_from_unix_epoch) {
|
||||
information!("Certificate: {} is valid: {} days", item.path,
|
||||
(x509_certificate.certificate_not_after - secs_from_unix_epoch)/valid_days_secs
|
||||
)
|
||||
} else {
|
||||
cert_items.push(item2);
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
if fs::read_dir(&item.path).is_err() {
|
||||
information!("Create path: {}", item.path);
|
||||
fs::create_dir_all(&item.path).ok();
|
||||
}
|
||||
cert_items.push(item2);
|
||||
}
|
||||
Err(e) => warning!("Certificate: {}, parse error: {}", item.path, e),
|
||||
}
|
||||
}
|
||||
|
||||
Self { cert_items }
|
||||
}
|
||||
|
||||
pub fn load(config_fn: &str) -> XResult<Self> {
|
||||
let config_content = opt_result!(fs::read_to_string(config_fn), "Load config: {}, failed: {}", config_fn);
|
||||
let config: CertConfig = opt_result!(deser_hjson::from_str(&config_content), "Parse config: {}, failed: {}", config_fn);
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
impl CertConfigItem {
|
||||
pub fn fill_dns_names(&mut self) -> XResult<()> {
|
||||
// TODO
|
||||
Ok(())
|
||||
pub fn fill_dns_names(&mut self) -> XResult<Option<X509Certificate>> {
|
||||
if self.common_name.is_none() || self.dns_names.is_none() {
|
||||
if self.path.is_empty() {
|
||||
return simple_error!("Cert config item common name and path both empty");
|
||||
}
|
||||
let path_buff = opt_result!(PathBuf::from_str(&self.path), "Path: {}, failed: {}", self.path);
|
||||
let cert_path_buff = path_buff.join(CERT_NAME);
|
||||
let pem = opt_result!(fs::read_to_string(cert_path_buff.clone()), "Read file: {:?}, failed: {}", cert_path_buff);
|
||||
let x509_certificate = opt_result!(x509::parse_x509(&format!("{}/{}", self.path, CERT_NAME), &pem), "Parse x509: {}/{}, faield: {}", self.path, CERT_NAME);
|
||||
self.common_name = Some(x509_certificate.common_name.clone());
|
||||
self.dns_names = Some(x509_certificate.alt_names.clone());
|
||||
self.algo = None;
|
||||
self.public_key_algo = Some(x509_certificate.public_key_algo.clone());
|
||||
Ok(Some(x509_certificate))
|
||||
} else {
|
||||
if self.path.is_empty() {
|
||||
return simple_error!("Cert config item path is empty");
|
||||
}
|
||||
if self.public_key_algo.is_none() {
|
||||
self.public_key_algo = match &self.algo {
|
||||
None => Some(X509PublicKeyAlgo::Rsa(2048)),
|
||||
Some(algo) => match AcmeAlgo::parse(&algo) {
|
||||
Ok(AcmeAlgo::Rsa(bit_length)) => Some(X509PublicKeyAlgo::Rsa(bit_length)),
|
||||
Ok(AcmeAlgo::Ec256) => Some(X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp256r1)),
|
||||
Ok(AcmeAlgo::Ec384) => Some(X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp384r1)),
|
||||
Ok(AcmeAlgo::Ec521) => Some(X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp521r1)),
|
||||
Err(_) => return simple_error!("Unknown algo: {}", algo),
|
||||
},
|
||||
};
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user