This commit is contained in:
2022-01-02 17:15:08 +08:00
parent af1ab9ad6c
commit c601f31bca
5 changed files with 77 additions and 27 deletions

2
Cargo.lock generated
View File

@@ -4,7 +4,7 @@ version = 3
[[package]]
name = "acme-client"
version = "1.0.0"
version = "1.0.1"
dependencies = [
"acme-lib",
"async-std",

View File

@@ -1,6 +1,6 @@
[package]
name = "acme-client"
version = "1.0.0"
version = "1.0.1"
authors = ["Hatter Jiang <jht5945@gmail.com>"]
edition = "2018"
description = "Acme auto challenge client, acme-client can issue certificates from Let's encrypt"

View File

@@ -5,7 +5,7 @@ ACME Client in Rust
Acme client help:
```shell
$ acme-client --help
acme-client 0.5.0
acme-client 1.0.1
Hatter Jiang <jht5945@gmail.com>
Acme auto challenge client, acme-client can issue certificates from Let's encrypt
@@ -13,23 +13,25 @@ USAGE:
acme-client [FLAGS] [OPTIONS]
FLAGS:
--check Check cert config
-h, --help Prints help information
--hide-logo Hide logo
--skip-verify-ip Skip verify public ip
-v, --verbose Verbose
-V, --version Print version
--check Check cert config
-h, --help Prints help information
--hide-logo Hide logo
-K, --skip-verify-certificate Skip verify certificate
-k, --skip-verify-ip Skip verify public ip
-v, --verbose Verbose
-V, --version Print version
OPTIONS:
-a, --algo <algo> Pki algo [default: ec384]
-c, --config <config> Cert config
--dir <dir> Account key dir [default: acme_dir]
-d, --domain <domain>... Domains
--email <email> Contract email
-m, --mode <mode> Mode [default: prod]
-p, --port <port> Http port [default: 80]
--timeout <timeout> Timeout (ms) [default: 5000]
-t, --type <type> Type http or dns [default: http]
-a, --algo <algo> Pki algo [default: ec384]
--cert-dir <cert-dir> Certificate dir
-c, --config <config> Cert config
--dir <dir> Account key dir [default: acme_dir]
-d, --domain <domain>... Domains
--email <email> Contract email
-m, --mode <mode> Mode [default: prod]
-p, --port <port> Http port [default: 80]
--timeout <timeout> Timeout (ms) [default: 5000]
-t, --type <type> Type http or dns [default: http]
```
签发一张证书示例
@@ -39,6 +41,9 @@ OPTIONS:
使用参数 `--config` 时的配置文件示例:
```json
{
"port": 18342,
"triggerAfterUpdate": ["/usr/local/nginx/nginx", "-s", "reload"],
"notifyToken": "dingtalk:access_token?sec_token",
"certItems": [{
"path": "dir_cryptofan_org",
"dnsNames": ["cryptofan.org", "www.cryptofan.org"]

View File

@@ -79,6 +79,7 @@ async fn main() -> tide::Result<()> {
.arg(Arg::with_name("check").long("check").help("Check cert config"))
.arg(Arg::with_name("hide-logo").long("hide-logo").help("Hide logo"))
.arg(Arg::with_name("skip-verify-ip").short("k").long("skip-verify-ip").help("Skip verify public ip"))
.arg(Arg::with_name("skip-verify-certificate").short("K").long("skip-verify-certificate").help("Skip verify certificate"))
.get_matches();
if matches.is_present("verbose") {
@@ -99,7 +100,8 @@ async fn main() -> tide::Result<()> {
let local_public_ip = if skip_verify_ip {
None
} else {
Some(get_local_public_ip().unwrap_or_else(|e| {
let skip_verify_certificate = matches.is_present("skip-verify-certificate");
Some(get_local_public_ip(skip_verify_certificate).unwrap_or_else(|e| {
failure!("Get local public ip failed: {}", e);
exit(1);
}))
@@ -309,18 +311,18 @@ async fn main() -> tide::Result<()> {
match run_command_and_wait(&mut cmd) {
Ok(_) => {
success!("Restart nginx success");
dingtalk_message.push_str("\n\nrestart nginx success");
dingtalk_message.push_str(&format!("\n\ntrigger after update success: {:?}", cmd));
}
Err(err) => {
failure!("Restart nginx failed: {:?}", err);
dingtalk_message.push_str(&format!("\n\nrestart nginx failed: {:?}", err));
dingtalk_message.push_str(&format!("\n\ntrigger after update failed: {:?}, message: {:?}", cmd, err));
}
}
} else {
warning!("No trigger after update is configed but is empty");
warning!("No trigger after update is configured but is empty");
}
} else {
warning!("No trigger after update configed");
warning!("No trigger after update configured");
}
}

View File

@@ -12,8 +12,51 @@ pub struct PublicIpResponse {
pub user_agent: Option<String>,
}
pub fn get_local_public_ip() -> XResult<String> {
let response = opt_result!(reqwest::blocking::get("https://hatter.ink/ip/ip.jsonp"), "Get local public ip failed: {}");
pub fn get_local_public_ip(skip_verify_certificate: bool) -> XResult<String> {
let mut client_builder = reqwest::blocking::Client::builder();
if skip_verify_certificate {
warning!("Skip verify certificate is turned on");
client_builder = client_builder.danger_accept_invalid_certs(true);
}
let root_certificate_isrg_x_pem = r#"-----BEGIN CERTIFICATE-----
MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC
ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL
wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D
LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK
4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5
bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y
sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ
Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4
FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc
SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql
PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND
TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1
c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx
+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB
ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu
b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E
U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu
MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC
5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW
9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG
WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O
he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC
Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5
-----END CERTIFICATE-----"#;
match reqwest::Certificate::from_pem(root_certificate_isrg_x_pem.as_bytes()) {
Err(err) => { warning!("Add ISRG X1 root failed: {}", err); },
Ok(certificate) => {
client_builder = client_builder.add_root_certificate(certificate);
},
}
let client = opt_result!(client_builder.build(), "Build http client failed: {}");
let response = opt_result!(client.get("https://hatter.ink/ip/ip.jsonp").send(), "Get local public ip failed: {}");
let response_text = opt_result!(response.text(), "Get local public ip failed: {}");
debugging!("Get local public ip response: {}", response_text);
let response_json: PublicIpResponse = opt_result!(deser_hjson::from_str(&response_text), "Parse get public ip response failed: {}");
@@ -32,6 +75,6 @@ pub fn resolve_first_ipv4(resolver: &Resolver, domain: &str) -> XResult<Option<S
#[test]
fn test() {
println!("{:?}", resolve_first_ipv4(&get_resolver().unwrap(),"hatter.ink"));
println!("{:?}", get_local_public_ip());
println!("{:?}", resolve_first_ipv4(&get_resolver().unwrap(), "hatter.ink"));
println!("{:?}", get_local_public_ip(false));
}