Fix DNS Management: CAA management deletes too third-party CAA entries

This commit is contained in:
Maurus Decimus
2026-07-12 15:48:08 +02:00
parent 8d54d0b44c
commit b5c703e4e3
2 changed files with 48 additions and 6 deletions

View File

@@ -6,7 +6,7 @@
use crate::task_manager::TaskResult;
use common::Server;
use dns_update::{DnsRecord, DnsRecordType, Error as DnsUpdateError};
use dns_update::{CAARecord, DnsRecord, DnsRecordType, Error as DnsUpdateError, KeyValue};
use registry::schema::structs::{
DnsManagement, Domain, Task, TaskDnsManagement, TaskDomainManagement, TaskStatus,
};
@@ -73,11 +73,16 @@ async fn dns_management(server: &Server, task: &TaskDnsManagement) -> trc::Resul
let mut errors = String::new();
for ((name, record_type), mut recs) in by_owner {
if matches!(record_type, DnsRecordType::TXT) && !is_owned_txt_name(&name) {
let preserve_unrelated = match record_type {
DnsRecordType::TXT => !is_owned_txt_name(&name),
DnsRecordType::CAA => true,
_ => false,
};
if preserve_unrelated {
match dns_updater.list_rrset(origin, &name, record_type).await {
Ok(existing) => {
for existing_rec in existing {
if !recs.iter().any(|new| same_txt_family(new, &existing_rec)) {
if !recs.iter().any(|new| same_rrset_family(new, &existing_rec)) {
recs.push(existing_rec);
}
}
@@ -138,6 +143,14 @@ async fn dns_management(server: &Server, task: &TaskDnsManagement) -> trc::Resul
}
}
fn same_rrset_family(a: &DnsRecord, b: &DnsRecord) -> bool {
match (a, b) {
(DnsRecord::TXT(_), DnsRecord::TXT(_)) => same_txt_family(a, b),
(DnsRecord::CAA(_), DnsRecord::CAA(_)) => same_caa_family(a, b),
_ => false,
}
}
fn same_txt_family(a: &DnsRecord, b: &DnsRecord) -> bool {
match (a, b) {
(DnsRecord::TXT(va), DnsRecord::TXT(vb)) => match (txt_family(va), txt_family(vb)) {
@@ -148,10 +161,38 @@ fn same_txt_family(a: &DnsRecord, b: &DnsRecord) -> bool {
}
}
fn same_caa_family(a: &DnsRecord, b: &DnsRecord) -> bool {
match (a, b) {
(DnsRecord::CAA(ca), DnsRecord::CAA(cb)) => match (ca, cb) {
(CAARecord::Issue { options: oa, .. }, CAARecord::Issue { options: ob, .. })
| (
CAARecord::IssueWild { options: oa, .. },
CAARecord::IssueWild { options: ob, .. },
) => match (caa_account_uri(oa), caa_account_uri(ob)) {
(Some(ua), Some(ub)) => ua.eq_ignore_ascii_case(ub),
_ => false,
},
(CAARecord::Iodef { url: ua, .. }, CAARecord::Iodef { url: ub, .. }) => {
ua.eq_ignore_ascii_case(ub)
}
_ => false,
},
_ => false,
}
}
fn caa_account_uri(options: &[KeyValue]) -> Option<&str> {
options
.iter()
.find(|kv| kv.key.eq_ignore_ascii_case("accounturi"))
.map(|kv| kv.value.as_str())
}
fn txt_family(value: &str) -> Option<&str> {
let rest = value.trim_start().strip_prefix("v=")?;
let end = rest.find([';', ' ']).unwrap_or(rest.len());
Some(&rest[..end])
value.trim_start().strip_prefix("v=").map(|rest| {
rest.split_once([';', ' '])
.map_or(rest, |(family, _)| family)
})
}
fn is_owned_txt_name(name: &str) -> bool {