aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml1
-rw-r--r--src/cert_store.rs46
3 files changed, 57 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a094835..c05bdc0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2146,6 +2146,7 @@ dependencies = [
"tokio 1.14.0",
"tokio-rustls",
"tokio-util",
+ "uuid",
]
[[package]]
@@ -2234,6 +2235,15 @@ dependencies = [
]
[[package]]
+name = "uuid"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 9ed93c0..0d1374f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -34,3 +34,4 @@ rcgen = "0.8"
accept-encoding-fork = "0.2.0-alpha.3"
async-compression = { version = "0.3", features = ["tokio", "gzip", "zstd", "deflate", "brotli"] }
tokio-util = { version = "0.6", features = ["io"] }
+uuid = { version = "0.8.2", features = ["v4"] }
diff --git a/src/cert_store.rs b/src/cert_store.rs
index c272759..d561605 100644
--- a/src/cert_store.rs
+++ b/src/cert_store.rs
@@ -205,6 +205,12 @@ impl CertStore {
bail!("Lock is already taken, not renewing for now.");
}
+ // ---- Accessibility check ----
+ // We don't want to ask Let's encrypt for a domain that
+ // is not configured to point here. This can happen with wildcards: someone can send
+ // a fake SNI to a domain that is not ours. We have to detect it here.
+ self.check_domain_accessibility(domain, &session).await?;
+
// ---- Do let's encrypt stuff ----
let dir = Directory::from_url(DirectoryUrl::LetsEncrypt)?;
@@ -287,6 +293,46 @@ impl CertStore {
Ok(())
}
+ async fn check_domain_accessibility(&self, domain: &str, session: &str) -> Result<()> {
+ // Returns Ok(()) only if domain is a correct domain name that
+ // redirects to this server
+ let self_challenge_id = uuid::Uuid::new_v4().to_string();
+ let self_challenge_key = format!("challenge/{}", self_challenge_id);
+ let self_challenge_resp = uuid::Uuid::new_v4().to_string();
+
+ self.consul
+ .acquire(
+ &self_challenge_key,
+ self_challenge_resp.as_bytes().to_vec().into(),
+ session,
+ )
+ .await?;
+
+ let httpcli = reqwest::Client::new();
+ let chall_url = format!(
+ "http://{}/.well-known/acme-challenge/{}",
+ domain, self_challenge_id
+ );
+
+ for i in 1..=4 {
+ tokio::time::sleep(Duration::from_secs(2)).await;
+ info!("({}) Accessibility check {}/4", domain, i);
+
+ let httpresp = httpcli.get(&chall_url).send().await?;
+ if httpresp.status() == reqwest::StatusCode::OK
+ && httpresp.bytes().await? == self_challenge_resp.as_bytes()
+ {
+ // Challenge successfully validated
+ info!("({}) Accessibility check successfull", domain);
+ return Ok(());
+ }
+
+ tokio::time::sleep(Duration::from_secs(2)).await;
+ }
+
+ bail!("Unable to validate self-challenge for domain accessibility check");
+ }
+
fn gen_self_signed_certificate(&self, domain: &str) -> Result<Arc<Cert>> {
let subject_alt_names = vec![domain.to_string(), "localhost".to_string()];
let cert = rcgen::generate_simple_self_signed(subject_alt_names)?;