feat: config.rs
This commit is contained in:
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -7,8 +7,10 @@ dependencies = [
|
|||||||
"acme-lib",
|
"acme-lib",
|
||||||
"async-std",
|
"async-std",
|
||||||
"clap",
|
"clap",
|
||||||
|
"deser-hjson",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"rust_util",
|
"rust_util",
|
||||||
|
"serde",
|
||||||
"tide",
|
"tide",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -644,6 +646,15 @@ version = "2.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
|
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]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
|||||||
@@ -14,3 +14,5 @@ rust_util = "0.6"
|
|||||||
acme-lib = "0.8"
|
acme-lib = "0.8"
|
||||||
tide = "0.16"
|
tide = "0.16"
|
||||||
async-std = { version = "1.8", features = ["attributes"] }
|
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]
|
#[macro_use]
|
||||||
extern crate rust_util;
|
extern crate rust_util;
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
|
||||||
use rust_util::XResult;
|
use rust_util::XResult;
|
||||||
use acme_lib::{DirectoryUrl, Directory};
|
use acme_lib::{DirectoryUrl, Directory};
|
||||||
use acme_lib::{create_p384_key, create_p256_key, create_rsa_key};
|
use acme_lib::{create_p384_key, create_p256_key, create_rsa_key};
|
||||||
use acme_lib::persist::FilePersist;
|
use acme_lib::persist::FilePersist;
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
use std::fs;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use tide::Request;
|
use tide::Request;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use async_std::channel::Sender;
|
use async_std::channel::Sender;
|
||||||
|
use config::AcmeAlgo;
|
||||||
|
use config::AcmeMode;
|
||||||
|
|
||||||
const NAME: &str = env!("CARGO_PKG_NAME");
|
const NAME: &str = env!("CARGO_PKG_NAME");
|
||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
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());
|
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)]
|
#[derive(Debug)]
|
||||||
struct AcmeRequest<'a> {
|
struct AcmeRequest<'a> {
|
||||||
contract_email: &'a str,
|
contract_email: &'a str,
|
||||||
primary_name: &'a str,
|
primary_name: &'a str,
|
||||||
alt_names: &'a [&'a str],
|
alt_names: &'a [&'a str],
|
||||||
algo: Algo,
|
algo: AcmeAlgo,
|
||||||
mode: Mode,
|
mode: AcmeMode,
|
||||||
dir: &'a str,
|
dir: &'a str,
|
||||||
timeout: u64,
|
timeout: u64,
|
||||||
}
|
}
|
||||||
@@ -103,21 +95,22 @@ async fn main() -> tide::Result<()> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let algo = match matches.value_of("algo") {
|
let algo = match matches.value_of("algo") {
|
||||||
Some("ec256") => Algo::Ec256,
|
Some(a) => AcmeAlgo::parse(a).unwrap_or_else(|e| {
|
||||||
Some("ec384") => Algo::Ec384,
|
failure!("{}", e);
|
||||||
Some("rsa2048") => Algo::Rsa(2048),
|
exit(1);
|
||||||
Some("rsa3072") => Algo::Rsa(3072),
|
}),
|
||||||
Some("rsa4096") => Algo::Rsa(4096),
|
|
||||||
_ => {
|
_ => {
|
||||||
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);
|
exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mode = match matches.value_of("mode") {
|
let mode = match matches.value_of("mode") {
|
||||||
Some("prod") => Mode::Prod,
|
Some(m) => AcmeMode::parse(m).unwrap_or_else(|e| {
|
||||||
Some("test") => Mode::Test,
|
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);
|
exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -155,12 +148,9 @@ async fn main() -> tide::Result<()> {
|
|||||||
|
|
||||||
fn request_domains(acme_request: AcmeRequest) -> XResult<()> {
|
fn request_domains(acme_request: AcmeRequest) -> XResult<()> {
|
||||||
information!("Acme mode: {:?}", acme_request.mode);
|
information!("Acme mode: {:?}", acme_request.mode);
|
||||||
let url = match acme_request.mode {
|
let url = acme_request.mode.directory_url();
|
||||||
Mode::Prod => DirectoryUrl::LetsEncrypt,
|
|
||||||
Mode::Test => DirectoryUrl::LetsEncryptStaging,
|
|
||||||
};
|
|
||||||
information!("Acme dir: {}", acme_request.dir);
|
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 persist = FilePersist::new(acme_request.dir);
|
||||||
let dir = opt_result!(Directory::from_url(persist, url), "Create directory from url failed: {}");
|
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: {}");
|
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);
|
information!("Generate private key, type: {:?}", acme_request.algo);
|
||||||
let pkey_pri = match acme_request.algo {
|
let pkey_pri = match acme_request.algo {
|
||||||
Algo::Ec256 => create_p256_key(),
|
AcmeAlgo::Ec256 => create_p256_key(),
|
||||||
Algo::Ec384 => create_p384_key(),
|
AcmeAlgo::Ec384 => create_p384_key(),
|
||||||
Algo::Rsa(bits) => create_rsa_key(bits),
|
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 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: {}");
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user