feat: config.rs

This commit is contained in:
2021-05-01 14:05:05 +08:00
parent 0a6e09fa96
commit c2a845ee2e
4 changed files with 150 additions and 34 deletions

11
Cargo.lock generated
View File

@@ -7,8 +7,10 @@ dependencies = [
"acme-lib",
"async-std",
"clap",
"deser-hjson",
"lazy_static",
"rust_util",
"serde",
"tide",
]
@@ -644,6 +646,15 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
[[package]]
name = "deser-hjson"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d995b60ff81bc6af01a98f0bf5db70a7418a1ac8bd74ada633968f388139da5e"
dependencies = [
"serde",
]
[[package]]
name = "digest"
version = "0.9.0"

View File

@@ -14,3 +14,5 @@ rust_util = "0.6"
acme-lib = "0.8"
tide = "0.16"
async-std = { version = "1.8", features = ["attributes"] }
serde = { version = "1.0", features = ["derive"] }
deser-hjson = "0.1"

113
src/config.rs Normal file
View File

@@ -0,0 +1,113 @@
use serde::{Deserialize, Serialize};
use rust_util::util_file;
use rust_util::XResult;
use std::fs;
use std::fs::File;
use std::io::Read;
use acme_lib::DirectoryUrl;
const CERT_NAME: &str = "cert.pem";
const KEY_NAME: &str = "key.pem";
#[derive(Debug, Clone, Copy)]
pub enum AcmeAlgo {
Ec256,
Ec384,
Rsa(u32),
}
impl AcmeAlgo {
pub fn parse(s: &str) -> XResult<AcmeAlgo> {
match s {
"ec256" => Ok(AcmeAlgo::Ec256),
"ec384" => Ok(AcmeAlgo::Ec384),
"rsa2048" => Ok(AcmeAlgo::Rsa(2048)),
"rsa3072" => Ok(AcmeAlgo::Rsa(3072)),
"rsa4096" => Ok(AcmeAlgo::Rsa(4096)),
_ => simple_error!("Unknown algo: {}", s),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum AcmeMode {
Prod,
Test,
}
impl AcmeMode {
pub fn parse(s: &str) -> XResult<AcmeMode> {
match s {
"prod" => Ok(AcmeMode::Prod),
"test" => Ok(AcmeMode::Test),
_ => simple_error!("Unknown mode: {}", s),
}
}
pub fn directory_url(&self) -> DirectoryUrl {
match self {
AcmeMode::Prod => DirectoryUrl::LetsEncrypt,
AcmeMode::Test => DirectoryUrl::LetsEncryptStaging,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AcmeConfig {
pub email: String,
pub dir: String,
pub auth_timeout: Option<u64>,
pub csr_timeout: Option<u64>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CertConfigItem {
pub path: String,
pub algo: 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 AcmeConfig {
pub fn get_auth_timeout(&self) -> u64 {
self.auth_timeout.unwrap_or(5_000)
}
pub fn get_csr_timeout(&self) -> u64 {
self.csr_timeout.unwrap_or(5_000)
}
}
impl CertConfigItem {
pub fn fill_dns_names(&mut self) -> XResult<()> {
// TODO
Ok(())
}
}
pub fn load_acme_config(file: Option<&str>, load_default: bool) -> XResult<AcmeConfig> {
if let Some(file) = file {
let s = opt_result!(util_file::read_file_content(file), "Read file: {}, failed: {}", file);
return Ok(opt_result!(deser_hjson::from_str(&s), "Parse acme config file: {}, failed: {}", file));
}
if load_default {
let default_config = util_file::read_config(None, &[
"~/acme_config.json".to_string(),
"/etc/acme_config.json".to_string(),
]);
if let Some(default_config) = default_config {
let s = opt_result!(fs::read_to_string(default_config.clone()), "Read file: {:?}, failed: {}", default_config);
return Ok(opt_result!(deser_hjson::from_str(&s), "Parse acme config file: {:?}, failed: {}", default_config));
}
}
simple_error!("Acme config file not found!")
}

View File

@@ -3,17 +3,22 @@ extern crate lazy_static;
#[macro_use]
extern crate rust_util;
mod config;
use rust_util::XResult;
use acme_lib::{DirectoryUrl, Directory};
use acme_lib::{create_p384_key, create_p256_key, create_rsa_key};
use acme_lib::persist::FilePersist;
use clap::{App, Arg};
use std::fs;
use std::sync::RwLock;
use std::collections::BTreeMap;
use tide::Request;
use std::process::exit;
use std::time::Duration;
use async_std::channel::Sender;
use config::AcmeAlgo;
use config::AcmeMode;
const NAME: &str = env!("CARGO_PKG_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -24,26 +29,13 @@ lazy_static! {
static ref TOKEN_MAP: RwLock<BTreeMap<String, String>> = RwLock::new(BTreeMap::new());
}
#[derive(Debug, Clone, Copy)]
enum Algo {
Ec256,
Ec384,
Rsa(u32),
}
#[derive(Debug, Clone, Copy)]
enum Mode {
Prod,
Test,
}
#[derive(Debug)]
struct AcmeRequest<'a> {
contract_email: &'a str,
primary_name: &'a str,
alt_names: &'a [&'a str],
algo: Algo,
mode: Mode,
algo: AcmeAlgo,
mode: AcmeMode,
dir: &'a str,
timeout: u64,
}
@@ -103,21 +95,22 @@ async fn main() -> tide::Result<()> {
}
};
let algo = match matches.value_of("algo") {
Some("ec256") => Algo::Ec256,
Some("ec384") => Algo::Ec384,
Some("rsa2048") => Algo::Rsa(2048),
Some("rsa3072") => Algo::Rsa(3072),
Some("rsa4096") => Algo::Rsa(4096),
Some(a) => AcmeAlgo::parse(a).unwrap_or_else(|e| {
failure!("{}", e);
exit(1);
}),
_ => {
failure!("Algo is not assigned, or wrong, should be: ec256, ec384, rsa2048, rsa3073 or rsa4096.");
failure!("Algo is not assigned, should be: ec256, ec384, rsa2048, rsa3073 or rsa4096.");
exit(1);
}
};
let mode = match matches.value_of("mode") {
Some("prod") => Mode::Prod,
Some("test") => Mode::Test,
Some(m) => AcmeMode::parse(m).unwrap_or_else(|e| {
failure!("{}", e);
exit(1);
}),
_ => {
failure!("Mode is not assigned, or wrong, should be: prod or test");
failure!("AcmeMode is not assigned, should be: prod or test");
exit(1);
}
};
@@ -155,12 +148,9 @@ async fn main() -> tide::Result<()> {
fn request_domains(acme_request: AcmeRequest) -> XResult<()> {
information!("Acme mode: {:?}", acme_request.mode);
let url = match acme_request.mode {
Mode::Prod => DirectoryUrl::LetsEncrypt,
Mode::Test => DirectoryUrl::LetsEncryptStaging,
};
let url = acme_request.mode.directory_url();
information!("Acme dir: {}", acme_request.dir);
std::fs::create_dir(acme_request.dir).ok();
fs::create_dir_all(acme_request.dir).ok();
let persist = FilePersist::new(acme_request.dir);
let dir = opt_result!(Directory::from_url(persist, url), "Create directory from url failed: {}");
let acc = opt_result!(dir.account(acme_request.contract_email), "Directory set account failed: {}");
@@ -194,16 +184,16 @@ fn request_domains(acme_request: AcmeRequest) -> XResult<()> {
information!("Generate private key, type: {:?}", acme_request.algo);
let pkey_pri = match acme_request.algo {
Algo::Ec256 => create_p256_key(),
Algo::Ec384 => create_p384_key(),
Algo::Rsa(bits) => create_rsa_key(bits),
AcmeAlgo::Ec256 => create_p256_key(),
AcmeAlgo::Ec384 => create_p384_key(),
AcmeAlgo::Rsa(bits) => create_rsa_key(bits),
};
information!("Created private key: {:?}", pkey_pri);
let ord_cert = opt_result!( ord_csr.finalize_pkey(pkey_pri, acme_request.timeout), "Submit CSR failed: {}");
let cert = opt_result!( ord_cert.download_and_save_cert(), "Download and save certificate failed: {}");
information!("Created certificate: {:?}", cert);
information!("Certificate key: {}", cert.private_key());
information!("Certificate pem: {}", cert.certificate());
Ok(())
}