a
This commit is contained in:
198
src/acme.rs
Normal file
198
src/acme.rs
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
use std::sync::RwLock;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fs;
|
||||||
|
use acme_lib::{create_p256_key, create_p384_key, create_rsa_key};
|
||||||
|
use acme_lib::persist::FilePersist;
|
||||||
|
use acme_lib::Directory;
|
||||||
|
use aliyun_openapi_core_rust_sdk::RPClient;
|
||||||
|
use rust_util::XResult;
|
||||||
|
use crate::util::parse_dns_record;
|
||||||
|
use crate::network::{get_resolver, resolve_first_ipv4};
|
||||||
|
use crate::ali_dns::{add_txt_dns_record, build_dns_client, delete_dns_record, list_dns, simple_parse_aliyun_supplier};
|
||||||
|
use crate::config::{AcmeChallenge, AcmeMode};
|
||||||
|
use crate::x509::{X509PublicKeyAlgo, X509EcPublicKeyAlgo};
|
||||||
|
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref TOKEN_MAP: RwLock<BTreeMap<String, String>> = RwLock::new(BTreeMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct AcmeRequest<'a> {
|
||||||
|
pub challenge: AcmeChallenge,
|
||||||
|
// issue, single acme request can only process one supplier
|
||||||
|
pub credential_supplier: Option<&'a str>,
|
||||||
|
pub allow_interact: bool,
|
||||||
|
pub contract_email: &'a str,
|
||||||
|
pub primary_name: &'a str,
|
||||||
|
pub alt_names: &'a [&'a str],
|
||||||
|
pub algo: X509PublicKeyAlgo,
|
||||||
|
pub mode: AcmeMode,
|
||||||
|
pub account_dir: &'a str,
|
||||||
|
pub timeout: u64,
|
||||||
|
pub local_public_ip: Option<&'a str>,
|
||||||
|
pub key_file: Option<String>,
|
||||||
|
pub cert_file: Option<String>,
|
||||||
|
pub outputs_file: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request_acme_certificate(acme_request: AcmeRequest, dns_cleaned_domains: &mut Vec<String>) -> XResult<()> {
|
||||||
|
if let Some(local_public_ip) = acme_request.local_public_ip {
|
||||||
|
let mut all_domains = vec![acme_request.primary_name.to_string()];
|
||||||
|
for alt_name in acme_request.alt_names {
|
||||||
|
all_domains.push(alt_name.to_string());
|
||||||
|
}
|
||||||
|
information!("Checking domain dns records, domains: {:?}", all_domains);
|
||||||
|
let resolver = opt_result!(get_resolver(), "Get resolver failed: {}");
|
||||||
|
|
||||||
|
if acme_request.challenge == AcmeChallenge::Http {
|
||||||
|
for domain in &all_domains {
|
||||||
|
debugging!("Checking domain: {}", domain);
|
||||||
|
let ipv4 = opt_result!(resolve_first_ipv4(&resolver, domain), "{}");
|
||||||
|
match ipv4 {
|
||||||
|
None => return simple_error!("Resolve domain ip failed: {}", domain),
|
||||||
|
Some(ipv4) => if local_public_ip != ipv4 {
|
||||||
|
return simple_error!("Check domain ip: {}, mis-match, local: {} vs domain: {}", domain, local_public_ip, ipv4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
information!("Acme mode: {:?}", acme_request.mode);
|
||||||
|
let url = acme_request.mode.directory_url();
|
||||||
|
let persist = FilePersist::new(acme_request.account_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: {}");
|
||||||
|
let mut ord_new = opt_result!( acc.new_order(acme_request.primary_name, acme_request.alt_names), "Create order failed: {}");
|
||||||
|
let ali_yun_client: Option<RPClient> = match acme_request.credential_supplier {
|
||||||
|
Some(credential_supplier) => Some(build_dns_client(
|
||||||
|
&opt_result!(simple_parse_aliyun_supplier(credential_supplier), "Parse credential supplier failed: {}"))),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut order_csr_index = 0;
|
||||||
|
let ord_csr = loop {
|
||||||
|
if let Some(ord_csr) = ord_new.confirm_validations() {
|
||||||
|
debugging!("Valid acme certificate http challenge success");
|
||||||
|
break ord_csr;
|
||||||
|
}
|
||||||
|
|
||||||
|
information!("Loop for acme challenge auth, #{}", order_csr_index);
|
||||||
|
order_csr_index += 1;
|
||||||
|
|
||||||
|
debugging!("Start acme certificate http challenge");
|
||||||
|
let auths = opt_result!(ord_new.authorizations(), "Order auth failed: {}");
|
||||||
|
for auth in &auths {
|
||||||
|
match acme_request.challenge {
|
||||||
|
AcmeChallenge::Http => {
|
||||||
|
let chall = auth.http_challenge();
|
||||||
|
let token = chall.http_token();
|
||||||
|
let proof = chall.http_proof();
|
||||||
|
|
||||||
|
{
|
||||||
|
information!("Add acme http challenge: {} -> {}",token, proof);
|
||||||
|
TOKEN_MAP.write().unwrap().insert(token.to_string(), proof);
|
||||||
|
}
|
||||||
|
debugging!("Valid acme certificate http challenge");
|
||||||
|
opt_result!(chall.validate(acme_request.timeout), "Validate http challenge failed: {}");
|
||||||
|
}
|
||||||
|
AcmeChallenge::Dns => {
|
||||||
|
let chall = auth.dns_challenge();
|
||||||
|
let record = format!("_acme-challenge.{}.", auth.domain_name());
|
||||||
|
let proof = chall.dns_proof();
|
||||||
|
information!("Add acme dns challenge: {} -> {}", record, proof);
|
||||||
|
|
||||||
|
let rr_and_domain = opt_result!(parse_dns_record(&record), "Parse record to rr&domain failed: {}");
|
||||||
|
|
||||||
|
if !dns_cleaned_domains.contains(&rr_and_domain.1) {
|
||||||
|
information!("Clearing domain: {}", &rr_and_domain.1);
|
||||||
|
dns_cleaned_domains.push(rr_and_domain.1.clone());
|
||||||
|
ali_yun_client.as_ref().map(|client| {
|
||||||
|
match list_dns(client, &rr_and_domain.1) {
|
||||||
|
Err(e) => warning!("List dns for: {}, failed: {}", &rr_and_domain.1, e),
|
||||||
|
Ok(Err(e)) => warning!("List dns for: {}, failed: {:?}", &rr_and_domain.1, e),
|
||||||
|
Ok(Ok(s)) => {
|
||||||
|
for r in &s.domain_records.record {
|
||||||
|
let rr = &r.rr;
|
||||||
|
if rr == "_acme-challenge" || rr.starts_with("_acme-challenge.") {
|
||||||
|
match delete_dns_record(client, &r.record_id) {
|
||||||
|
Err(e) => warning!("Delete dns: {}.{}, failed: {}", r.rr, r.domain_name, e),
|
||||||
|
Ok(Err(e)) => warning!("Delete dns: {}.{}, failed: {:?}", r.rr, r.domain_name, e),
|
||||||
|
Ok(Ok(_)) => success!("Delete dns: {}.{}", r.rr, r.domain_name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
match &ali_yun_client {
|
||||||
|
Some(client) => {
|
||||||
|
let add_txt_dns_result = opt_result!(add_txt_dns_record(client, &rr_and_domain.1, &rr_and_domain.0, &proof), "Add DNS TXT record failed: {}");
|
||||||
|
match add_txt_dns_result {
|
||||||
|
Ok(s) => success!("Add dns txt record successes: {}", s.record_id),
|
||||||
|
Err(e) => return simple_error!("Add dns txt record failed: {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => if acme_request.allow_interact {
|
||||||
|
let mut line = String::new();
|
||||||
|
information!("You need to config dns manually, press enter to continue...");
|
||||||
|
let _ = std::io::stdin().read_line(&mut line).unwrap();
|
||||||
|
} else {
|
||||||
|
return simple_error!("Interact is not allowed, --allow-interact to allow interact");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debugging!("Valid acme certificate dns challenge");
|
||||||
|
opt_result!(chall.validate(acme_request.timeout), "Validate dns challenge failed: {}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debugging!("Refresh acme certificate order");
|
||||||
|
opt_result!(ord_new.refresh(), "Refresh order failed: {}");
|
||||||
|
};
|
||||||
|
|
||||||
|
information!("Generate private key, key type: {:?}", acme_request.algo);
|
||||||
|
let pkey_pri = match acme_request.algo {
|
||||||
|
X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp256r1) => create_p256_key(),
|
||||||
|
X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp384r1) => create_p384_key(),
|
||||||
|
X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp521r1) => return simple_error!("Algo ec521 is not supported"),
|
||||||
|
X509PublicKeyAlgo::Rsa(bits) => create_rsa_key(bits),
|
||||||
|
};
|
||||||
|
|
||||||
|
debugging!("Invoking csr finalize pkey");
|
||||||
|
let ord_cert = opt_result!( ord_csr.finalize_pkey(pkey_pri, acme_request.timeout), "Submit CSR failed: {}");
|
||||||
|
debugging!("Downloading and save cert");
|
||||||
|
let cert = opt_result!( ord_cert.download_and_save_cert(), "Download and save certificate failed: {}");
|
||||||
|
|
||||||
|
if let (Some(cert_file), Some(key_file)) = (&acme_request.cert_file, &acme_request.key_file) {
|
||||||
|
debugging!("Certificate key: {}", cert.private_key());
|
||||||
|
debugging!("Certificate pem: {}", cert.certificate());
|
||||||
|
information!("Write file: {}", cert_file);
|
||||||
|
if let Err(e) = fs::write(cert_file, cert.certificate()) {
|
||||||
|
failure!("Write file: {}, failed: {}", cert_file, e);
|
||||||
|
}
|
||||||
|
information!("Write file: {}", key_file);
|
||||||
|
if let Err(e) = fs::write(key_file, cert.private_key()) {
|
||||||
|
failure!("Write file: {}, failed: {}", key_file, e);
|
||||||
|
}
|
||||||
|
success!("Write files success: {} and {}", cert_file, key_file);
|
||||||
|
} else if let Some(outputs_file) = &acme_request.outputs_file {
|
||||||
|
let mut outputs = String::new();
|
||||||
|
outputs.push_str("private key:\n");
|
||||||
|
outputs.push_str(cert.private_key());
|
||||||
|
outputs.push_str("\n\ncertificates:\n");
|
||||||
|
outputs.push_str(cert.certificate());
|
||||||
|
if let Err(e) = fs::write(outputs_file, outputs) {
|
||||||
|
failure!("Write file: {}, failed: {}", outputs_file, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
information!("Certificate key: {}", cert.private_key());
|
||||||
|
information!("Certificate pem: {}", cert.certificate());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
203
src/main.rs
203
src/main.rs
@@ -3,6 +3,7 @@ extern crate lazy_static;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rust_util;
|
extern crate rust_util;
|
||||||
|
|
||||||
|
mod acme;
|
||||||
mod util;
|
mod util;
|
||||||
mod config;
|
mod config;
|
||||||
mod x509;
|
mod x509;
|
||||||
@@ -13,15 +14,9 @@ mod ali_dns;
|
|||||||
// mod simple_thread_pool;
|
// mod simple_thread_pool;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use rust_util::XResult;
|
|
||||||
use acme_lib::Directory;
|
|
||||||
use acme_lib::{create_p384_key, create_p256_key, create_rsa_key};
|
|
||||||
use acme_lib::persist::FilePersist;
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::RwLock;
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use tide::Request;
|
use tide::Request;
|
||||||
use std::process::{Command, exit};
|
use std::process::{Command, exit};
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
@@ -29,45 +24,20 @@ use async_std::task;
|
|||||||
use async_std::channel;
|
use async_std::channel;
|
||||||
use async_std::channel::Sender;
|
use async_std::channel::Sender;
|
||||||
use config::AcmeMode;
|
use config::AcmeMode;
|
||||||
use crate::config::{AcmeChallenge, CertConfig, CERT_NAME, KEY_NAME};
|
|
||||||
use crate::x509::{X509PublicKeyAlgo, X509EcPublicKeyAlgo};
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use aliyun_openapi_core_rust_sdk::RPClient;
|
|
||||||
use rust_util::util_cmd::run_command_and_wait;
|
use rust_util::util_cmd::run_command_and_wait;
|
||||||
use crate::ali_dns::{add_txt_dns_record, build_dns_client, delete_dns_record, list_dns, simple_parse_aliyun_supplier};
|
use crate::config::{AcmeChallenge, CertConfig, CERT_NAME, KEY_NAME};
|
||||||
|
use crate::x509::{X509PublicKeyAlgo};
|
||||||
use crate::dingtalk::send_dingtalk_message;
|
use crate::dingtalk::send_dingtalk_message;
|
||||||
use crate::network::{get_local_public_ip, get_resolver, resolve_first_ipv4};
|
|
||||||
use crate::statics::{AcmeStatics, AcmeStatus};
|
use crate::statics::{AcmeStatics, AcmeStatus};
|
||||||
use crate::util::parse_dns_record;
|
use crate::acme::{AcmeRequest, request_acme_certificate};
|
||||||
|
use crate::network::get_local_public_ip;
|
||||||
|
|
||||||
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");
|
||||||
const AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
const AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
||||||
const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
|
const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref TOKEN_MAP: RwLock<BTreeMap<String, String>> = RwLock::new(BTreeMap::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
struct AcmeRequest<'a> {
|
|
||||||
challenge: AcmeChallenge,
|
|
||||||
// issue, single acme request can only process one supplier
|
|
||||||
credential_supplier: Option<&'a str>,
|
|
||||||
allow_interact: bool,
|
|
||||||
contract_email: &'a str,
|
|
||||||
primary_name: &'a str,
|
|
||||||
alt_names: &'a [&'a str],
|
|
||||||
algo: X509PublicKeyAlgo,
|
|
||||||
mode: AcmeMode,
|
|
||||||
account_dir: &'a str,
|
|
||||||
timeout: u64,
|
|
||||||
local_public_ip: Option<&'a str>,
|
|
||||||
key_file: Option<String>,
|
|
||||||
cert_file: Option<String>,
|
|
||||||
outputs_file: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn main() -> tide::Result<()> {
|
async fn main() -> tide::Result<()> {
|
||||||
let matches = App::new(NAME)
|
let matches = App::new(NAME)
|
||||||
@@ -432,167 +402,6 @@ fn check_cert_config(cert_config: &CertConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_acme_certificate(acme_request: AcmeRequest, dns_cleaned_domains: &mut Vec<String>) -> XResult<()> {
|
|
||||||
if let Some(local_public_ip) = acme_request.local_public_ip {
|
|
||||||
let mut all_domains = vec![acme_request.primary_name.to_string()];
|
|
||||||
for alt_name in acme_request.alt_names {
|
|
||||||
all_domains.push(alt_name.to_string());
|
|
||||||
}
|
|
||||||
information!("Checking domain dns records, domains: {:?}", all_domains);
|
|
||||||
let resolver = opt_result!(get_resolver(), "Get resolver failed: {}");
|
|
||||||
|
|
||||||
if acme_request.challenge == AcmeChallenge::Http {
|
|
||||||
for domain in &all_domains {
|
|
||||||
debugging!("Checking domain: {}", domain);
|
|
||||||
let ipv4 = opt_result!(resolve_first_ipv4(&resolver, domain), "{}");
|
|
||||||
match ipv4 {
|
|
||||||
None => return simple_error!("Resolve domain ip failed: {}", domain),
|
|
||||||
Some(ipv4) => if local_public_ip != ipv4 {
|
|
||||||
return simple_error!("Check domain ip: {}, mis-match, local: {} vs domain: {}", domain, local_public_ip, ipv4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
information!("Acme mode: {:?}", acme_request.mode);
|
|
||||||
let url = acme_request.mode.directory_url();
|
|
||||||
let persist = FilePersist::new(acme_request.account_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: {}");
|
|
||||||
let mut ord_new = opt_result!( acc.new_order(acme_request.primary_name, acme_request.alt_names), "Create order failed: {}");
|
|
||||||
let ali_yun_client: Option<RPClient> = match acme_request.credential_supplier {
|
|
||||||
Some(credential_supplier) => Some(build_dns_client(
|
|
||||||
&opt_result!(simple_parse_aliyun_supplier(credential_supplier), "Parse credential supplier failed: {}"))),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut order_csr_index = 0;
|
|
||||||
let ord_csr = loop {
|
|
||||||
if let Some(ord_csr) = ord_new.confirm_validations() {
|
|
||||||
debugging!("Valid acme certificate http challenge success");
|
|
||||||
break ord_csr;
|
|
||||||
}
|
|
||||||
|
|
||||||
information!("Loop for acme challenge auth, #{}", order_csr_index);
|
|
||||||
order_csr_index += 1;
|
|
||||||
|
|
||||||
debugging!("Start acme certificate http challenge");
|
|
||||||
let auths = opt_result!(ord_new.authorizations(), "Order auth failed: {}");
|
|
||||||
for auth in &auths {
|
|
||||||
match acme_request.challenge {
|
|
||||||
AcmeChallenge::Http => {
|
|
||||||
let chall = auth.http_challenge();
|
|
||||||
let token = chall.http_token();
|
|
||||||
let proof = chall.http_proof();
|
|
||||||
|
|
||||||
{
|
|
||||||
information!("Add acme http challenge: {} -> {}",token, proof);
|
|
||||||
TOKEN_MAP.write().unwrap().insert(token.to_string(), proof);
|
|
||||||
}
|
|
||||||
debugging!("Valid acme certificate http challenge");
|
|
||||||
opt_result!(chall.validate(acme_request.timeout), "Validate http challenge failed: {}");
|
|
||||||
}
|
|
||||||
AcmeChallenge::Dns => {
|
|
||||||
let chall = auth.dns_challenge();
|
|
||||||
let record = format!("_acme-challenge.{}.", auth.domain_name());
|
|
||||||
let proof = chall.dns_proof();
|
|
||||||
information!("Add acme dns challenge: {} -> {}", record, proof);
|
|
||||||
|
|
||||||
let rr_and_domain = opt_result!(parse_dns_record(&record), "Parse record to rr&domain failed: {}");
|
|
||||||
|
|
||||||
if !dns_cleaned_domains.contains(&rr_and_domain.1) {
|
|
||||||
information!("Clearing domain: {}", &rr_and_domain.1);
|
|
||||||
dns_cleaned_domains.push(rr_and_domain.1.clone());
|
|
||||||
ali_yun_client.as_ref().map(|client| {
|
|
||||||
match list_dns(client, &rr_and_domain.1) {
|
|
||||||
Err(e) => warning!("List dns for: {}, failed: {}", &rr_and_domain.1, e),
|
|
||||||
Ok(Err(e)) => warning!("List dns for: {}, failed: {:?}", &rr_and_domain.1, e),
|
|
||||||
Ok(Ok(s)) => {
|
|
||||||
for r in &s.domain_records.record {
|
|
||||||
let rr = &r.rr;
|
|
||||||
if rr == "_acme-challenge" || rr.starts_with("_acme-challenge.") {
|
|
||||||
match delete_dns_record(client, &r.record_id) {
|
|
||||||
Err(e) => warning!("Delete dns: {}.{}, failed: {}", r.rr, r.domain_name, e),
|
|
||||||
Ok(Err(e)) => warning!("Delete dns: {}.{}, failed: {:?}", r.rr, r.domain_name, e),
|
|
||||||
Ok(Ok(_)) => success!("Delete dns: {}.{}", r.rr, r.domain_name),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
match &ali_yun_client {
|
|
||||||
Some(client) => {
|
|
||||||
let add_txt_dns_result = opt_result!(add_txt_dns_record(client, &rr_and_domain.1, &rr_and_domain.0, &proof), "Add DNS TXT record failed: {}");
|
|
||||||
match add_txt_dns_result {
|
|
||||||
Ok(s) => success!("Add dns txt record successes: {}", s.record_id),
|
|
||||||
Err(e) => return simple_error!("Add dns txt record failed: {:?}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => if acme_request.allow_interact {
|
|
||||||
let mut line = String::new();
|
|
||||||
information!("You need to config dns manually, press enter to continue...");
|
|
||||||
let _ = std::io::stdin().read_line(&mut line).unwrap();
|
|
||||||
} else {
|
|
||||||
return simple_error!("Interact is not allowed, --allow-interact to allow interact");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debugging!("Valid acme certificate dns challenge");
|
|
||||||
opt_result!(chall.validate(acme_request.timeout), "Validate dns challenge failed: {}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debugging!("Refresh acme certificate order");
|
|
||||||
opt_result!(ord_new.refresh(), "Refresh order failed: {}");
|
|
||||||
};
|
|
||||||
|
|
||||||
information!("Generate private key, key type: {:?}", acme_request.algo);
|
|
||||||
let pkey_pri = match acme_request.algo {
|
|
||||||
X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp256r1) => create_p256_key(),
|
|
||||||
X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp384r1) => create_p384_key(),
|
|
||||||
X509PublicKeyAlgo::EcKey(X509EcPublicKeyAlgo::Secp521r1) => return simple_error!("Algo ec521 is not supported"),
|
|
||||||
X509PublicKeyAlgo::Rsa(bits) => create_rsa_key(bits),
|
|
||||||
};
|
|
||||||
|
|
||||||
debugging!("Invoking csr finalize pkey");
|
|
||||||
let ord_cert = opt_result!( ord_csr.finalize_pkey(pkey_pri, acme_request.timeout), "Submit CSR failed: {}");
|
|
||||||
debugging!("Downloading and save cert");
|
|
||||||
let cert = opt_result!( ord_cert.download_and_save_cert(), "Download and save certificate failed: {}");
|
|
||||||
|
|
||||||
if let (Some(cert_file), Some(key_file)) = (&acme_request.cert_file, &acme_request.key_file) {
|
|
||||||
debugging!("Certificate key: {}", cert.private_key());
|
|
||||||
debugging!("Certificate pem: {}", cert.certificate());
|
|
||||||
information!("Write file: {}", cert_file);
|
|
||||||
if let Err(e) = fs::write(cert_file, cert.certificate()) {
|
|
||||||
failure!("Write file: {}, failed: {}", cert_file, e);
|
|
||||||
}
|
|
||||||
information!("Write file: {}", key_file);
|
|
||||||
if let Err(e) = fs::write(key_file, cert.private_key()) {
|
|
||||||
failure!("Write file: {}, failed: {}", key_file, e);
|
|
||||||
}
|
|
||||||
success!("Write files success: {} and {}", cert_file, key_file);
|
|
||||||
} else if let Some(outputs_file) = &acme_request.outputs_file {
|
|
||||||
let mut outputs = String::new();
|
|
||||||
outputs.push_str("private key:\n");
|
|
||||||
outputs.push_str(cert.private_key());
|
|
||||||
outputs.push_str("\n\ncertificates:\n");
|
|
||||||
outputs.push_str(cert.certificate());
|
|
||||||
if let Err(e) = fs::write(outputs_file, outputs) {
|
|
||||||
failure!("Write file: {}, failed: {}", outputs_file, e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
information!("Certificate key: {}", cert.private_key());
|
|
||||||
information!("Certificate pem: {}", cert.certificate());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn startup_http_server(s: Sender<i32>, port: u16) {
|
fn startup_http_server(s: Sender<i32>, port: u16) {
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
information!("Listen at 0.0.0.0:{}", port);
|
information!("Listen at 0.0.0.0:{}", port);
|
||||||
@@ -610,7 +419,7 @@ fn startup_http_server(s: Sender<i32>, port: u16) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let peer = req.peer_addr().unwrap_or("none");
|
let peer = req.peer_addr().unwrap_or("none");
|
||||||
let auth_token = { TOKEN_MAP.read().unwrap().get(token).cloned() };
|
let auth_token = { crate::acme::TOKEN_MAP.read().unwrap().get(token).cloned() };
|
||||||
match auth_token {
|
match auth_token {
|
||||||
Some(auth_token) => {
|
Some(auth_token) => {
|
||||||
information!("Request acme challenge: {} -> {}, peer: {:?}", token, auth_token, peer);
|
information!("Request acme challenge: {} -> {}, peer: {:?}", token, auth_token, peer);
|
||||||
|
|||||||
Reference in New Issue
Block a user