fix(dns): reconciliar reescrevia os mesmos registos a cada passagem

Duas coisas apanhadas ao correr o reconcile contra a zona real depois de a
rota passar a funcionar.

1) Nao era idempotente: 0 criados, 4 corrigidos, 11 iguais -- e outra vez 4
   corrigidos na passagem seguinte, para sempre, com o temporizador a fazer o
   mesmo em ciclo. Os 4 eram os SRV. A Cloudflare devolve o content de um SRV
   SEM a prioridade ("1 993 host" para um registo guardado como "0 1 993
   host") e guarda-a em data. O adaptador so recorria a data quando o content
   vinha vazio, portanto lia sempre um valor diferente do da spec.
   Para SRV e TLSA a data passa a ser a autoridade.

2) A spec de cada dominio incluia o A e o AAAA do mail host. Um segundo
   dominio servido pelo mesmo servidor aponta o MX para um host que nao lhe
   pertence, por isso reconciliar o cienciaembarcada.com.br tentava escrever
   mail.lucasrguerra.dev.br -- zona alheia. A Cloudflare respondia "an
   identical record already exists" e a consola mostrava dois erros para um
   dominio que estava correcto. O mesmo valia para o TLSA, que tambem tem o
   nome do mail host.
   Os registos de endereco passam a ser publicados so pela zona que contem o
   host; o MX e os SRV, que apontam para ele, continuam a ser de cada dominio.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lucas RayanandClaude Opus 5 committed 2026-08-24 08:34:14 -03:00
1 parent 9ce5fcafaa
commit 47c3700380
7 files changed
+153 -20

No files matched your search

@@ -130,12 +130,15 @@ func (a *CloudflareAdapter) ListRecords(ctx context.Context, zoneID string) ([]e
if r.Proxied != nil {
rec.Proxied = *r.Proxied
}
// Cloudflare returns SRV and TLSA with content already rendered,
// but older zones and some responses only populate data; fall back
// to it so a structured record is not seen as an empty value and
// rewritten on every reconcile.
if rec.Value == "" && len(r.Data) > 0 {
rec.Value = renderStructuredValue(r.Type, r.Data)
// For SRV and TLSA the structured data is the authority, not the
// rendered content. Cloudflare renders SRV content WITHOUT the
// priority - a record stored as "0 1 993 host" reads back as
// "1 993 host" - so trusting content made every SRV differ from
// the spec and be rewritten on every single reconcile.
if len(r.Data) > 0 {
if rendered := renderStructuredValue(r.Type, r.Data); rendered != "" {
rec.Value = rendered
}
}
out = append(out, rec)
}