diff --git a/crates/common/src/network/dns/records.rs b/crates/common/src/network/dns/records.rs index 33a06723..dccf1630 100644 --- a/crates/common/src/network/dns/records.rs +++ b/crates/common/src/network/dns/records.rs @@ -15,7 +15,7 @@ use dns_update::{ }; use inbuxa_features::relay::dns as relay_dns; use registry::schema::{ - enums::{DnsRecordType, ServiceProtocol}, + enums::{AcmeChallengeType, DnsRecordType, ServiceProtocol}, prelude::{ObjectType, Property}, structs::{AcmeProvider, CertificateManagement, DkimSignature, DnsManagement, Domain}, }; @@ -334,18 +334,12 @@ impl Server { } // ACME DNS-PERSIST-01 validation record - records.push(NamedDnsRecord { - name: format!("_validation-persist.{domain_name}."), - record: DnsRecord::TXT(format!( - "{provider_name}; accounturi={}{}", - provider.account_uri, - if props.subject_alternative_names.is_empty() { - "; policy=wildcard" - } else { - "" - } - )), - }); + records.push(persist_validation_record( + domain_name, + provider_name, + &provider.account_uri, + props.subject_alternative_names.is_empty(), + )); } } DnsRecordType::Tlsa => { @@ -446,6 +440,26 @@ impl Server { } } + // inbuxa: DNS-PERSIST-01 can't validate without this record, and CAA + // is optional. A domain that doesn't publish CAA still gets it (#292). + if !record_types.contains(&DnsRecordType::Caa) + && let CertificateManagement::Automatic(props) = &domain.certificate_management + && let Some(provider) = self + .registry() + .object::(props.acme_provider_id) + .await? + && matches!(provider.challenge_type, AcmeChallengeType::DnsPersist01) + && let Ok(provider_url) = Url::parse(&provider.directory) + && let Some(provider_name) = provider_domain(&provider_url) + { + records.push(persist_validation_record( + domain_name, + provider_name, + &provider.account_uri, + props.subject_alternative_names.is_empty(), + )); + } + Ok(records) } @@ -508,6 +522,21 @@ impl Pacc { #[inline(always)] #[allow(unused)] +fn persist_validation_record( + domain_name: &str, + provider_name: &str, + account_uri: &str, + wildcard: bool, +) -> NamedDnsRecord { + NamedDnsRecord { + name: format!("_validation-persist.{domain_name}."), + record: DnsRecord::TXT(format!( + "{provider_name}; accounturi={account_uri}{}", + if wildcard { "; policy=wildcard" } else { "" } + )), + } +} + fn provider_domain(url: &Url) -> Option<&str> { #[cfg(feature = "test_mode")] { diff --git a/crates/registry/src/schema/structs_impl.rs b/crates/registry/src/schema/structs_impl.rs index e501961c..c196e2b6 100644 --- a/crates/registry/src/schema/structs_impl.rs +++ b/crates/registry/src/schema/structs_impl.rs @@ -10041,7 +10041,6 @@ impl Default for DnsManagementProperties { DnsRecordType::Srv, DnsRecordType::MtaSts, DnsRecordType::TlsRpt, - DnsRecordType::Caa, DnsRecordType::AutoConfig, DnsRecordType::AutoConfigLegacy, DnsRecordType::AutoDiscover, diff --git a/crates/services/src/task_manager/dns.rs b/crates/services/src/task_manager/dns.rs index e403dd5f..1fb7cf1e 100644 --- a/crates/services/src/task_manager/dns.rs +++ b/crates/services/src/task_manager/dns.rs @@ -312,6 +312,15 @@ mod tests { use super::*; use dns_update::MXRecord; + // #292: a CAA record binds every name under the domain to this server's + // ACME account, so it has to be asked for. + #[test] + fn caa_is_off_by_default() { + let defaults = registry::schema::structs::DnsManagementProperties::default(); + assert!(!defaults.publish_records.contains(&RecordType::Caa)); + assert!(defaults.publish_records.contains(&RecordType::Dkim)); + } + fn mx(hosts: &[&str]) -> Vec { hosts .iter() diff --git a/resources/schema/schema.json.gz b/resources/schema/schema.json.gz index b5f76768..884cb64f 100644 Binary files a/resources/schema/schema.json.gz and b/resources/schema/schema.json.gz differ diff --git a/resources/schema/schema.json.sha256 b/resources/schema/schema.json.sha256 index b8ca8c92..1e5ddbde 100644 --- a/resources/schema/schema.json.sha256 +++ b/resources/schema/schema.json.sha256 @@ -1 +1 @@ -HXMSYoJ437ZjHoG2tQpfBeT-LXUJ_7ZoXHyOVr7623w \ No newline at end of file +UmAfWXIO8ueR4c-UCZwM3YFjQxdHLGSRXpcC8dbuvws \ No newline at end of file diff --git a/tests/src/automation/acme.rs b/tests/src/automation/acme.rs index f7b580d8..bf67ba07 100644 --- a/tests/src/automation/acme.rs +++ b/tests/src/automation/acme.rs @@ -424,7 +424,7 @@ pub async fn test(test: &TestServer) { dkim_management: DkimManagement::Manual, dns_management: DnsManagement::Automatic(DnsManagementProperties { dns_server_id: pebble_dns_id, - publish_records: Map::new(vec![DnsRecordType::Caa]), + publish_records: Map::new(vec![DnsRecordType::TlsRpt]), ..Default::default() }), ..Default::default()