commit f58886b89e126c4b3e7880457b8a0c17c8a243f0 Author: Daniel M Date: Wed Mar 23 23:00:22 2022 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f06b652 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +certs \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6b0fabe --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1121 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "acme2" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13f31afe167edf3e2aa5da3464f660ad3c7418d6a72a585acf0eadd3c64a9eb0" +dependencies = [ + "base64", + "hyper", + "openssl", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-futures", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "encoding_rs" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" + +[[package]] +name = "futures-sink" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" + +[[package]] +name = "futures-task" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" + +[[package]] +name = "futures-util" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "h2" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9de88456263e249e241fcd211d3954e2c9b0ef7ccfc235a444eb367cae3689" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.1", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 0.4.8", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +dependencies = [ + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "le-easy-certs" +version = "0.1.0" +dependencies = [ + "acme2", + "env_logger", + "hyper", + "log", + "serde", + "serde_derive", + "thiserror", + "tokio", + "toml", +] + +[[package]] +name = "libc" +version = "0.2.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + +[[package]] +name = "openssl" +version = "0.10.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-sys" +version = "0.9.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "reqwest" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-rustls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142" +dependencies = [ + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa 0.4.8", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "socket2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..774aa33 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "le-easy-certs" +version = "0.1.0" +edition = "2021" + +[dependencies] +acme2 = "0.5" +env_logger = "0.9" +hyper = { version = "0.14", features = [ "server" ] } +log = "0.4" +serde = "1.0" +serde_derive = "1.0" +thiserror = "1.0" +tokio = { version = "1", features = [ "macros", "rt", "rt-multi-thread", "fs", "parking_lot" ] } +toml = "0.5" diff --git a/src/certs.rs b/src/certs.rs new file mode 100644 index 0000000..f7b5306 --- /dev/null +++ b/src/certs.rs @@ -0,0 +1,263 @@ +use crate::{config::ConfigCert, http::ChallengeManager}; +use acme2::{ + gen_rsa_private_key, + openssl::{asn1::Asn1Time, pkey::PKey, x509::X509}, + Account, AccountBuilder, AuthorizationStatus, ChallengeStatus, Csr, Directory, + DirectoryBuilder, OrderBuilder, OrderStatus, +}; +use log::{debug, info}; +use std::{sync::Arc, time::Duration, fmt::Display}; +use tokio::fs; + +#[derive(thiserror::Error, Debug)] +pub enum ReqErr { + #[error("Invalid endpoint")] + InvaidEndpoint, + #[error("Account error")] + Account, + #[error("Order building error")] + OrderBuild, + #[error("Get Authorizations error")] + Authorizations, + #[error("Challenge error")] + Challenge, + #[error("Validation error")] + Validation, + #[error("Order error")] + Order, + #[error("Finalizing error")] + Finalizing, + #[error("No certificate chain error")] + NoChain, +} + +/// Convenience extension for X509 information in certs +pub trait CertExt { + /// Number of days until the certificate expires. This will be negative when the cert has + /// expired already + fn expires_in_days(&self) -> i32; + /// Get a list of all domain names for which this cert is valid + fn dns_names(&self) -> Vec; +} + +/// The ACME API endpoint that should be used. This can be the Letsencrypt production or tesing, as +/// well as any custom endpoint +#[derive(Debug, Clone)] +pub enum AcmeApiEndpoint { + /// Letsencrypt Staging API (testing) + LetsEncryptStaging, + /// Letsencrypt Production API (valid certs) + LetsEncryptProduction, + /// Custom ACME API specified via URL + Custom(String), +} + +#[derive(Debug)] +pub struct CertRequester { + endpoint: AcmeApiEndpoint, + challenge_mgr: ChallengeManager, + conf: ConfigCert, +} + +impl AcmeApiEndpoint { + /// Get the actual URL for the endpoint as string + pub fn url(&self) -> String { + match self { + AcmeApiEndpoint::LetsEncryptStaging => { + "https://acme-staging-v02.api.letsencrypt.org/directory".to_string() + } + AcmeApiEndpoint::LetsEncryptProduction => { + "https://acme-v02.api.letsencrypt.org/directory".to_string() + } + AcmeApiEndpoint::Custom(url) => url.to_string(), + } + } +} + +impl Display for AcmeApiEndpoint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AcmeApiEndpoint::LetsEncryptStaging => write!(f, "LetsEncryptStaging"), + AcmeApiEndpoint::LetsEncryptProduction => write!(f, "LetsEncryptProduction"), + AcmeApiEndpoint::Custom(url) => write!(f, "Custom({url})"), + } + } +} + +impl CertRequester { + pub fn new( + endpoint: AcmeApiEndpoint, + conf: ConfigCert, + challenge_mgr: ChallengeManager, + ) -> Self { + Self { + endpoint, + challenge_mgr, + conf, + } + } + + async fn load_or_create_account( + &self, + dir: Arc, + ) -> Result, Box> { + let mut builder = AccountBuilder::new(dir); + + let acc = match fs::read(&self.conf.account_file).await { + Ok(pem) => { + let key = PKey::private_key_from_pem(&pem)?; + builder.private_key(key).build().await? + } + Err(_) => { + println!("Creating new account!"); + let acc = builder.terms_of_service_agreed(true).build().await?; + let key = acc.private_key().private_key_to_pem_pkcs8()?; + fs::write(&self.conf.account_file, &key).await?; + acc + } + }; + + Ok(acc) + } + + pub async fn request_certs(&self) -> Result<(), ReqErr> { + let dir = DirectoryBuilder::new(self.endpoint.url()) + .build() + .await + .map_err(|_| ReqErr::InvaidEndpoint)?; + + let account = self + .load_or_create_account(dir) + .await + .map_err(|_| ReqErr::Account)?; + + let order = self + .conf + .domains + .iter() + .fold(&mut OrderBuilder::new(account), |acc, domain| { + acc.add_dns_identifier(domain.to_string()) + }) + .build() + .await + .map_err(|_| ReqErr::OrderBuild)?; + + let authorizations = order + .authorizations() + .await + .map_err(|_| ReqErr::Authorizations)?; + + info!("Creating challenges"); + for auth in authorizations { + let challenge = auth.get_challenge("http-01").ok_or(ReqErr::Challenge)?; + + let token = challenge + .token + .as_ref() + .ok_or(ReqErr::Challenge)? + .to_string(); + + let key_auth = challenge + .key_authorization() + .ok() + .flatten() + .ok_or(ReqErr::Challenge)?; + + debug!("Interting challenge in manager: {token} => {key_auth}"); + self.challenge_mgr.insert(token.clone(), key_auth).await; + + challenge.validate().await.map_err(|_| ReqErr::Validation)?; + + let challenge = challenge.wait_done(Duration::from_secs(5), 3).await.map_err(|_| ReqErr::Validation)?; + if !matches!(challenge.status, ChallengeStatus::Valid) { + return Err(ReqErr::Validation); + } + + self.challenge_mgr.remove(&token).await; + + debug!("Wait for authorization"); + let auth = auth.wait_done(Duration::from_secs(5), 3).await.map_err(|_| ReqErr::Validation)?; + if !matches!(auth.status, AuthorizationStatus::Valid) { + return Err(ReqErr::Validation); + } + } + + info!("Waiting for order"); + let order = order.wait_ready(Duration::from_secs(10), 5).await.map_err(|_| ReqErr::Order)?; + if !matches!(order.status, OrderStatus::Ready) { + return Err(ReqErr::Order); + } + + let pkey = gen_rsa_private_key(4096).unwrap(); + + info!("Finalizing certificate"); + let order = order.finalize(Csr::Automatic(pkey.clone())).await.map_err(|_| ReqErr::Finalizing)?; + let order = order.wait_done(Duration::from_secs(5), 3).await.map_err(|_| ReqErr::Finalizing)?; + if !matches!(order.status, OrderStatus::Valid) { + return Err(ReqErr::Finalizing); + } + + let certs = order.certificate().await.map_err(|_| ReqErr::Finalizing)?.ok_or(ReqErr::Finalizing)?; + + if certs.len() <= 1 { + return Err(ReqErr::NoChain); + } + + info!("The new certificate expires in {}", certs[0].expires_in_days()); + + let x = certs + .into_iter() + .map(|it| it.to_pem().unwrap()) + .flatten() + .chain(pkey.private_key_to_pem_pkcs8().unwrap().into_iter()) + .collect::>(); + + tokio::fs::write(&self.conf.fullchain_file, x).await.unwrap(); + + Ok(()) + } +} + +impl CertExt for X509 { + fn expires_in_days(&self) -> i32 { + let now = Asn1Time::days_from_now(0).unwrap(); + let diff = now.diff(self.not_after()).unwrap(); + diff.days + } + + fn dns_names(&self) -> Vec { + let mut names = Vec::new(); + + if let Some(alt_names) = self.subject_alt_names() { + names.extend( + alt_names + .into_iter() + .filter_map(|it| it.dnsname().map(String::from)), + ); + } + + names + } +} + +pub async fn load_cert_from_fullchain(path: &str) -> Option { + const START: &str = "-----BEGIN CERTIFICATE-----"; + const END: &str = "-----END CERTIFICATE-----"; + + let fullchain = tokio::fs::read_to_string(path).await.ok()?; + + let mut fullchain = fullchain.as_str(); + while let (Some(start), Some(end)) = (fullchain.find(START), fullchain.find(END)) { + let pem = &fullchain[start..end + END.len()]; + fullchain = &fullchain[end + END.len()..]; + + let cert = X509::from_pem(pem.as_bytes()).unwrap(); + if cert.dns_names().is_empty() { + continue; + } + + return Some(cert); + } + + None +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..55f20a7 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,34 @@ +use serde_derive::Deserialize; +use std::collections::HashMap; + +#[derive(Debug, Deserialize)] +pub struct Config { + pub http: ConfigHttp, + pub certs: HashMap, +} + +/// Config for the http server used to serve the challenges +#[derive(Debug, Deserialize)] +pub struct ConfigHttp { + /// Binding IP address + pub ip: String, + /// Binding port + pub port: u16, +} + +/// Configuration for a single certificate +#[derive(Debug, Deserialize)] +pub struct ConfigCert { + /// Path to the account file to use for this certificate request + pub account_file: String, + /// Path to the fullchain certificate file that will be created or updated + pub fullchain_file: String, + /// List of domain names to include in the certificate + pub domains: Vec, + /// Renew the certificate this many days before expiration + pub renew_days: i32, + /// Optional custom endpoint. If no enpoint is specified, the production letsencrypt endpoint + /// is used. "LetsEncryptStaging" and "LetsEncryptProduction" will use the corresponding + /// letsencrypt endpoints. Everything else has to be an actual URL specifying the endpoint. + pub endpoint: Option, +} diff --git a/src/http.rs b/src/http.rs new file mode 100644 index 0000000..a1ccff0 --- /dev/null +++ b/src/http.rs @@ -0,0 +1,114 @@ +use std::{sync::Arc, collections::HashMap, net::{SocketAddr, AddrParseError}, convert::Infallible, future::Future}; +use hyper::{Server, service::{make_service_fn, service_fn}, Request, Body, Response, Method}; +use log::info; +use tokio::sync::{RwLock, oneshot}; + +#[derive(Debug, Clone)] +pub struct ChallengeManager { + challenges: Arc>>, +} + +/// Http server to host the challenges +pub struct ChallengeServer { + mgr: ChallengeManager, + sock_addr: SocketAddr, +} + +impl ChallengeManager { + pub fn new() -> Self { + let challenges = Arc::new(RwLock::new(HashMap::new())); + Self { challenges } + } + + pub async fn insert(&self, token: String, key: String) { + self.challenges.write().await.insert(token, key); + } + + pub async fn get(&self, token: &str) -> Option { + self.challenges.read().await.get(token).map(String::to_string) + } + + pub async fn remove(&self, token: &str) { + self.challenges.write().await.remove(token); + } +} + +impl ChallengeServer { + pub fn new(bind_ip: &str, bind_port: u16) -> Result { + let mgr = ChallengeManager::new(); + let sock_addr = format!("{}:{}", bind_ip, bind_port).parse()?; + + Ok(Self { mgr, sock_addr }) + } + + pub fn clone_challenge_mgr(&self) -> ChallengeManager { + self.mgr.clone() + } + + pub async fn start(&self) -> Result<(impl Future, oneshot::Sender<()>), hyper::Error> { + let mgr = self.clone_challenge_mgr(); + let make_service = make_service_fn(move |_con| { + let mgr = mgr.clone(); + async move { + let mgr = mgr.clone(); + Ok::<_, Infallible>(service_fn(move |req: Request| { + let mgr = mgr.clone(); + async move { Ok::<_, Infallible>(Self::serve_challenges(req, mgr).await) } + })) + } + }); + let srv = Server::bind(&self.sock_addr).serve(make_service); + + let (tx, rx) = oneshot::channel::<()>(); + + let srv = srv.with_graceful_shutdown(async move { + let _ = rx.await; + }); + Ok((srv, tx)) + } + + async fn serve_challenges(req: Request, mgr: ChallengeManager) -> Response { + info!("New http request: {}", req.uri()); + + if !matches!(req.method(), &Method::GET) { + info!("Request is not GET -> Reject"); + + return Response::builder() + .status(405) + .body(String::new().into()) + .unwrap(); + } + + let challenge_prefix = "/.well-known/acme-challenge/"; + + let path = req.uri().path(); + if !path.starts_with(challenge_prefix) { + info!("Request is not for /.well-known -> Reject"); + + return Response::builder() + .status(404) + .body(String::new().into()) + .unwrap(); + } + + let tok = &path[challenge_prefix.len()..]; + info!("Requested token: {}", tok); + + match mgr.get(tok).await { + Some(auth) => { + info!("Answering Request = {auth}"); + Response::builder() + .status(200) + .body(auth.to_string().into()) + .unwrap() + } + None => { + info!("No matching challenge"); + Response::builder() + .status(404) + .body(String::new().into()) + .unwrap() + } + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c0b9782 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,81 @@ +use log::{debug, error, info}; + +use crate::{ + certs::{load_cert_from_fullchain, AcmeApiEndpoint, CertExt, CertRequester}, + config::Config, + http::ChallengeServer, +}; + +mod certs; +mod config; +mod http; + +#[tokio::main] +async fn main() -> Result<(), Box> { + env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "info")); + + // Config path evaluation order: + // - first cli argument + // - LE_CONF environment variable + // ./le-conf.toml + let config_path = std::env::args() + .skip(1) + .next() + .unwrap_or_else(|| std::env::var("LE_CONF").unwrap_or("./le-conf.toml".to_string())); + + info!("Loading config file '{}'", config_path); + let s_conf = tokio::fs::read_to_string(&config_path) + .await + .expect(&format!("Failed to load config file: {}", config_path)); + let conf: Config = toml::from_str(&s_conf)?; + + debug!("Config file: {:#?}", &conf); + + // Create the http server for serving the challenges + let srv = ChallengeServer::new(&conf.http.ip, conf.http.port)?; + let mgr = srv.clone_challenge_mgr(); + + // Start the server + info!("Starting http server: {}:{}", &conf.http.ip, conf.http.port); + tokio::spawn(async move { + srv.start().await.unwrap().0.await; + }); + + // See what certs are requested in the config file + for (name, conf) in conf.certs { + // Check if the cert needs to be created / renewed + let should_renew = load_cert_from_fullchain(&conf.fullchain_file) + .await + .map(|cert| { + let expires_in_days = cert.expires_in_days(); + let dns_names = cert.dns_names(); + expires_in_days <= conf.renew_days || dns_names != conf.domains + }) + .unwrap_or(true); + + if should_renew { + let endpoint = conf + .endpoint + .as_ref() + .map(|ep| match ep.as_ref() { + "LetsEncryptProduction" => AcmeApiEndpoint::LetsEncryptProduction, + "LetsEncryptStaging" => AcmeApiEndpoint::LetsEncryptStaging, + url => AcmeApiEndpoint::Custom(url.to_string()), + }) + .unwrap_or(AcmeApiEndpoint::LetsEncryptProduction); + + info!("Certificate {name} needs to be renewed. Using endpoint: {endpoint}"); + + let requester = CertRequester::new(endpoint, conf, mgr.clone()); + if let Err(e) = requester.request_certs().await { + error!("Certificate request for {name} failed: {e}"); + } + } else { + info!("Certificate {name} does not need to be renewed"); + } + } + + info!("All done. Shutting down"); + + Ok(()) +}