Implement basic domain accesibility check before asking for certificate

This commit is contained in:
Alex 2021-12-14 11:31:22 +01:00
parent 7488d8e907
commit d13066b12b
No known key found for this signature in database
GPG Key ID: EDABF9711E244EB1
3 changed files with 57 additions and 0 deletions

10
Cargo.lock generated
View File

@ -2146,6 +2146,7 @@ dependencies = [
"tokio 1.14.0",
"tokio-rustls",
"tokio-util",
"uuid",
]
[[package]]
@ -2233,6 +2234,15 @@ dependencies = [
"percent-encoding",
]
[[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"

View File

@ -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"] }

View File

@ -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)?;