From eb8716f3d45de7c4e62b8145eeaa36291c9a1986 Mon Sep 17 00:00:00 2001 From: Johan Thomsen Date: Wed, 16 Oct 2024 14:37:04 +0200 Subject: [PATCH] trust-dns --- Cargo.toml | 2 +- src/dns/mod.rs | 6 +-- src/issuer.rs | 111 ++++++++++++++++++++++++------------------------- 3 files changed, 59 insertions(+), 60 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0c07482..ba40e16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ acme-lib = { git = 'https://github.com/DBCDK/acme-lib', branch = 'dbc-fork' } regex = "1" lazy_static = "1" walkdir = "2" -trust-dns-resolver = "0" +trust-dns-resolver = { version = "0", features = ["tokio-runtime"] } env_logger = "0" prometheus_exporter_base = { version = "=1.4.0", features = ["hyper_server"] } tokio = { version = "1", features = [ "full" ] } diff --git a/src/dns/mod.rs b/src/dns/mod.rs index 655f2df..bb4ddc3 100644 --- a/src/dns/mod.rs +++ b/src/dns/mod.rs @@ -8,7 +8,7 @@ use std::convert::From; use crate::log; use crate::common::{CertSpec, DNSName, SpecError}; use crate::config::Zone; -use self::trust_dns_resolver::Resolver; +use self::trust_dns_resolver::TokioAsyncResolver; use self::trust_dns_resolver::error::{ResolveError,ResolveErrorKind}; use std::string::String; @@ -77,9 +77,9 @@ pub fn delete(config: &FaytheConfig, spec: &CertSpec) -> Result<(), DNSError> { Ok(()) } -pub fn query(resolver: &Resolver, host: &DNSName, proof: &String) -> Result<(), DNSError> { +pub async fn query(resolver: &TokioAsyncResolver, host: &DNSName, proof: &String) -> Result<(), DNSError> { let challenge_host = challenge_host(host, None); - match resolver.txt_lookup(&challenge_host) { + match resolver.txt_lookup(&challenge_host).await { Ok(res) => { let trim_chars: &[_] = &['"', '\n']; res.iter().find(|record_set| diff --git a/src/issuer.rs b/src/issuer.rs index de4b7b0..d2bcd81 100644 --- a/src/issuer.rs +++ b/src/issuer.rs @@ -17,8 +17,11 @@ use std::prelude::v1::Vec; use serde_json::json; use std::convert::TryFrom; -use trust_dns_resolver::Resolver; -use std::sync::RwLock; +use trust_dns_resolver::{AsyncResolver, TokioAsyncResolver}; +use trust_dns_resolver::config::{ResolverConfig, NameServerConfigGroup, ResolverOpts}; +use trust_dns_resolver::error::ResolveErrorKind; +use std::net::IpAddr; +use tokio::sync::RwLock; use std::fmt::Debug; use crate::dns::DNSError; @@ -30,7 +33,8 @@ use chrono::Utc; pub async fn process(faythe_config: FaytheConfig, mut rx: Receiver) { let mut queue: VecDeque = VecDeque::new(); - RESOLVERS.with(|r| r.write().unwrap().inner = init_resolvers(&faythe_config)); + let resolvers = init_resolvers(&faythe_config).await.unwrap(); + RESOLVERS.write().await.inner = resolvers; log::info("processing-started"); loop { @@ -65,7 +69,7 @@ pub async fn process(faythe_config: FaytheConfig, mut rx: Receiver) { async fn check_queue(queue: &mut VecDeque) -> Result<(), IssuerError> { match queue.pop_front() { Some(mut order) => { - match validate_challenge(&order) { + match validate_challenge(&order).await { Ok(_) => { order.inner.refresh()?; if order.inner.is_validated() { @@ -103,28 +107,28 @@ async fn check_queue(queue: &mut VecDeque) -> Result<(), IssuerError } } -fn validate_challenge(order: &IssueOrder) -> Result<(), IssuerError> { - +async fn validate_challenge(order: &IssueOrder) -> Result<(), IssuerError> { for a in &order.authorizations { let domain = DNSName::try_from(&String::from(a.domain_name()))?; let challenge = a.dns_challenge(); let proof = challenge.dns_proof(); let log_data = json!({ "domain": &domain, "proof": &proof }); - RESOLVERS.with(|r| -> Result<(), DNSError> { + { + let resolvers = RESOLVERS.read().await; + // TODO: Proper retry logic log::info("Validating internally after 20s"); log::data("Validating auth_dns_servers internally", &log_data); for d in &order.auth_dns_servers { - dns::query(r.read().unwrap().get(&d).unwrap(), &domain, &proof)?; + dns::query(resolvers.get(&d).unwrap(), &domain, &proof).await?; } log::data("Validating val_dns_servers internally", &log_data); for d in &order.val_dns_servers { - dns::query(r.read().unwrap().get(&d).unwrap(), &domain, &proof)?; + dns::query(resolvers.get(&d).unwrap(), &domain, &proof).await?; } - Ok(()) - })?; + } log::data("Asking LE to validate", &log_data); challenge.validate(5000)?; } @@ -250,66 +254,61 @@ impl std::convert::From for ResolverError<'_> { } } -thread_local! { - static RESOLVERS: RwLock = RwLock::new(Resolvers{ +lazy_static! { + static ref RESOLVERS: RwLock = RwLock::new(Resolvers{ inner: HashMap::with_capacity(0) }); } struct Resolvers { - inner: HashMap + inner: HashMap } impl Resolvers { - fn get(&self, server: &String) -> Option<&Resolver> { + fn get(&self, server: &String) -> Option<&TokioAsyncResolver> { self.inner.get(server) } } -fn init_resolvers<'l>(config: &FaytheConfig) -> HashMap { - use trust_dns_resolver::config::{ResolverConfig, NameServerConfigGroup, ResolverOpts}; - use trust_dns_resolver::error::ResolveErrorKind; - use std::net::IpAddr; - +async fn init_resolvers<'l>(config: &FaytheConfig) -> Result, ResolverError> { let mut resolvers = HashMap::new(); - let create_resolvers = |server: &String, resolvers: &mut HashMap| { - - match || -> Result<(), ResolverError> { - let system_resolver = Resolver::from_system_conf().or(Err(ResolverError::SystemResolveConf))?; - - //try-parse what's in the config file as an ip-address, if that fails, assume it's a hostname that can be looked up - let ip: IpAddr = match server.parse() { - Ok(ip) => Ok(ip), - Err(_) => { - match system_resolver.lookup_ip(server) { - Ok(res) => res.iter().next().ok_or(ResolverError::NoIpsForResolversFound(server)), // grabbing the first A record only for now - Err(err) => { - Err(match err.kind() { - ResolveErrorKind::NoRecordsFound { .. } => ResolverError::NoIpsForResolversFound(server), - _ => ResolverError::Other - }) - } - } - } - }?; + for z in &config.zones { + let server = &z.1.auth_dns_server; + resolvers.insert(server.clone(), create_resolvers(&server).await?); + } + for s in &config.val_dns_servers { + resolvers.insert(s.to_string(), create_resolvers(&s).await?); + } + Ok(resolvers) +} - let mut conf = ResolverConfig::new(); - for c in &*NameServerConfigGroup::from_ips_clear(&[ip.to_owned()], 53, true) { - conf.add_name_server(c.to_owned()); +async fn create_resolvers<'a>(server: &'a String) -> Result> { + + let system_resolver = AsyncResolver::tokio_from_system_conf().or(Err(ResolverError::SystemResolveConf))?; + + //try-parse what's in the config file as an ip-address, if that fails, assume it's a hostname that can be looked up + let ip: IpAddr = match server.parse() { + Ok(ip) => Ok(ip), + Err(_) => { + match system_resolver.lookup_ip(server).await { + Ok(res) => res.iter().next().ok_or(ResolverError::NoIpsForResolversFound(server)), // grabbing the first A record only for now + Err(err) => { + Err(match err.kind() { + ResolveErrorKind::NoRecordsFound { .. } => ResolverError::NoIpsForResolversFound(server), + _ => ResolverError::Other + }) + } } - let mut opts = ResolverOpts::default(); - // Never believe NXDOMAIN for more than 1 minute - opts.negative_max_ttl = Some(Duration::new(60,0)); - resolvers.insert(server.to_owned(), Resolver::new(conf, opts).unwrap()); - Ok(()) - }() { - Err(e) => { log::error(format!("failed to init resolver for server: {}", &server).as_str(), &e); } - _ => {} - }; - }; + } + }?; - config.zones.iter().for_each(|(_, z)| create_resolvers(&z.auth_dns_server, &mut resolvers)); - config.val_dns_servers.iter().for_each(|s| create_resolvers(&s, &mut resolvers)); - resolvers + let mut conf = ResolverConfig::new(); + for c in &*NameServerConfigGroup::from_ips_clear(&[ip.to_owned()], 53, true) { + conf.add_name_server(c.to_owned()); + } + let mut opts = ResolverOpts::default(); + // Never believe NXDOMAIN for more than 1 minute + opts.negative_max_ttl = Some(Duration::new(60,0)); + Ok(AsyncResolver::tokio(conf, opts)) }