feat: config.rs
This commit is contained in:
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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
113
src/config.rs
Normal 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!")
|
||||
}
|
||||
58
src/main.rs
58
src/main.rs
@@ -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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user