diff --git a/Cargo.lock b/Cargo.lock index 0615a73..7a41d90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "acme-client" -version = "1.0.0" +version = "1.0.1" dependencies = [ "acme-lib", "async-std", diff --git a/Cargo.toml b/Cargo.toml index 6010e26..515d90c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "acme-client" -version = "1.0.0" +version = "1.0.1" authors = ["Hatter Jiang "] edition = "2018" description = "Acme auto challenge client, acme-client can issue certificates from Let's encrypt" diff --git a/README.md b/README.md index 53965b0..ea56312 100644 --- a/README.md +++ b/README.md @@ -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 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 Pki algo [default: ec384] - -c, --config Cert config - --dir Account key dir [default: acme_dir] - -d, --domain ... Domains - --email Contract email - -m, --mode Mode [default: prod] - -p, --port Http port [default: 80] - --timeout Timeout (ms) [default: 5000] - -t, --type Type http or dns [default: http] + -a, --algo Pki algo [default: ec384] + --cert-dir Certificate dir + -c, --config Cert config + --dir Account key dir [default: acme_dir] + -d, --domain ... Domains + --email Contract email + -m, --mode Mode [default: prod] + -p, --port Http port [default: 80] + --timeout Timeout (ms) [default: 5000] + -t, --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"] diff --git a/src/main.rs b/src/main.rs index 633a054..c841170 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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"); } } diff --git a/src/network.rs b/src/network.rs index dde92c0..4f36cfb 100644 --- a/src/network.rs +++ b/src/network.rs @@ -12,8 +12,51 @@ pub struct PublicIpResponse { pub user_agent: Option, } -pub fn get_local_public_ip() -> XResult { - 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 { + 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