feat: init commit
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
.idea/
|
||||
__temp_dir/
|
||||
# ---> Rust
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
|
||||
16
Cargo.toml
Normal file
16
Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "acme-client"
|
||||
version = "0.1.0"
|
||||
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "Acme auto challenge client"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
clap = "2.33.3"
|
||||
rust_util = "0.6"
|
||||
acme-lib = "0.8.1"
|
||||
tide = "0.16.0"
|
||||
async-std = { version = "1.8.0", features = ["attributes"] }
|
||||
10
justfile
Normal file
10
justfile
Normal file
@@ -0,0 +1,10 @@
|
||||
_:
|
||||
@just --list
|
||||
|
||||
# cross build x86-64 musl debug
|
||||
cross-build-x64-debug:
|
||||
cross build --target x86_64-unknown-linux-musl
|
||||
|
||||
# cross build x86-64 musl release
|
||||
cross-build-x64:
|
||||
cross build --target x86_64-unknown-linux-musl --release
|
||||
135
src/main.rs
Normal file
135
src/main.rs
Normal file
@@ -0,0 +1,135 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate rust_util;
|
||||
|
||||
use rust_util::XResult;
|
||||
use acme_lib::{DirectoryUrl, Directory, create_p384_key};
|
||||
use acme_lib::persist::FilePersist;
|
||||
use clap::{App, Arg};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::collections::BTreeMap;
|
||||
use tide::Request;
|
||||
|
||||
const NAME: &str = env!("CARGO_PKG_NAME");
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
const DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
|
||||
|
||||
lazy_static! {
|
||||
static ref TOKEN_MAP: Arc<RwLock<BTreeMap<String, String>>> = Arc::new(RwLock::new(BTreeMap::new()));
|
||||
}
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() -> tide::Result<()> {
|
||||
let matches = App::new(NAME)
|
||||
.version(VERSION)
|
||||
.about(DESCRIPTION)
|
||||
.arg(Arg::with_name("version").short("V").long("version").help("Print version"))
|
||||
.arg(Arg::with_name("verbose").short("v").long("verbose").help("Verbose"))
|
||||
.arg(Arg::with_name("type").short("t").long("type").default_value("http").takes_value(true).help("Type http or dns"))
|
||||
.arg(Arg::with_name("port").short("p").long("port").default_value("80").takes_value(true).help("Http port"))
|
||||
.arg(Arg::with_name("domain").short("d").long("domain").multiple(true).takes_value(true).help("Domains"))
|
||||
.arg(Arg::with_name("email").long("email").takes_value(true).help("Contract email"))
|
||||
.get_matches();
|
||||
|
||||
if matches.is_present("version") {
|
||||
information!("{} v{}", NAME, VERSION);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let email = match matches.value_of("email") {
|
||||
Some(email) => email,
|
||||
None => {
|
||||
failure!("Email is not assigned.");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let _type = matches.value_of("type").expect("Failed to get type");
|
||||
let port: u16 = matches.value_of("port").expect("Failed to get port").parse().expect("Failed to parse port");
|
||||
|
||||
let domains_val = match matches.values_of("domain") {
|
||||
Some(domain_val) => domain_val,
|
||||
None => {
|
||||
failure!("Domains is not assigned.");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let domains: Vec<&str> = domains_val.collect::<Vec<_>>();
|
||||
information!("Domains: {:?}", domains);
|
||||
|
||||
async_std::task::spawn(async move {
|
||||
information!("Listen at 0.0.0.0:{}", port);
|
||||
let mut app = tide::new();
|
||||
app.at("/.well-known/acme-challenge/:token").get(|req: Request<()>| async move {
|
||||
let token = match req.param("token") {
|
||||
Ok(token) => token,
|
||||
Err(e) => {
|
||||
warning!("Cannot get token from url, error: {}", e);
|
||||
return Ok("400 - bad request".to_string());
|
||||
}
|
||||
};
|
||||
let auth_token = { TOKEN_MAP.read().unwrap().get(token).cloned() };
|
||||
match auth_token {
|
||||
Some(auth_token) => {
|
||||
information!("Request acme challenge: {} -> {}", token, auth_token);
|
||||
Ok(auth_token)
|
||||
}
|
||||
None => {
|
||||
warning!("Request acme challenge not found: {}", token);
|
||||
Ok("404 - not found".to_string())
|
||||
}
|
||||
}
|
||||
});
|
||||
if let Err(e) = app.listen(&format!("0.0.0.0:{}", port)).await {
|
||||
failure!("Failed to listen 0.0.0.0:{}, error: {}", port, e);
|
||||
}
|
||||
});
|
||||
|
||||
let primary_name = domains[0];
|
||||
let alt_names: Vec<&str> = domains.into_iter().skip(1).collect();
|
||||
request_domains(email, primary_name, &alt_names).expect("Request domain failed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn request_domains(contract_email: &str, primary_name: &str, alt_names: &[&str]) -> XResult<()> {
|
||||
let url = DirectoryUrl::LetsEncrypt;
|
||||
std::fs::create_dir("__temp_dir").expect("Create temp dir failed!");
|
||||
let persist = FilePersist::new("__temp_dir");
|
||||
let dir = Directory::from_url(persist, url)?;
|
||||
let acc = dir.account(contract_email)?;
|
||||
let mut ord_new = acc.new_order(primary_name, alt_names)?;
|
||||
|
||||
let ord_csr = loop {
|
||||
if let Some(ord_csr) = ord_new.confirm_validations() {
|
||||
break ord_csr;
|
||||
}
|
||||
|
||||
let auths = ord_new.authorizations()?;
|
||||
for auth in &auths {
|
||||
let chall = auth.http_challenge();
|
||||
let token = chall.http_token();
|
||||
// let path = format!(".well-known/acme-challenge/{}", token);
|
||||
let proof = chall.http_proof();
|
||||
|
||||
information!("Add acme challenge: {} -> {}",token, proof);
|
||||
TOKEN_MAP.write().unwrap().insert(token.to_string(), proof);
|
||||
|
||||
chall.validate(5000)?;
|
||||
}
|
||||
|
||||
ord_new.refresh()?;
|
||||
};
|
||||
|
||||
let pkey_pri = create_p384_key();
|
||||
println!("{:?}", pkey_pri);
|
||||
|
||||
let ord_cert = ord_csr.finalize_pkey(pkey_pri, 5000)?;
|
||||
let cert = ord_cert.download_and_save_cert()?;
|
||||
|
||||
println!("{:?}", cert);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user