checker: report transient mid-chain and final rcodes as Unknown, not Crit/Warn
All checks were successful
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing

SERVFAIL/REFUSED from every auth server means the record could not be
observed, not that the zone published a negative answer. Mark such rcodes
transient on TermRcode terminations and final A/AAAA lookups so chainRcodeRule
reports Unknown instead of flapping the check into Crit/Warn; definitive
NXDOMAIN answers still drive Crit (mid-chain) and Warn (final).
This commit is contained in:
nemunaire 2026-06-18 11:22:00 +09:00
commit 65687ce375
4 changed files with 65 additions and 16 deletions

View file

@ -137,11 +137,15 @@ func (c *chainCtx) walk(ctx context.Context, name string) {
if r.Rcode != dns.RcodeSuccess {
rcode := rcodeText(r.Rcode)
// A SERVFAIL/REFUSED from every auth server means we could not observe
// the record, not that the zone published a negative answer; mark it
// transient so the rule reports Unknown instead of Crit.
c.data.ChainTerminated = ChainTermination{
Reason: TermRcode,
Subject: current,
Rcode: rcode,
Detail: fmt.Sprintf("server answered %s for %s", rcode, current),
Reason: TermRcode,
Subject: current,
Rcode: rcode,
Detail: fmt.Sprintf("server answered %s for %s", rcode, current),
Transient: isTransientRcode(r.Rcode),
}
c.data.FinalTarget = current
return
@ -280,8 +284,9 @@ func extractCNAME(r *dns.Msg, owner string) (target string, fromDNAME bool, ttl
func (c *chainCtx) resolveFinal(ctx context.Context, name string, servers []string) {
type result struct {
addrs []string
rcode string
addrs []string
rcode string
transient bool
}
query := func(qtype uint16) result {
@ -301,6 +306,7 @@ func (c *chainCtx) resolveFinal(ctx context.Context, name string, servers []stri
var res result
if r.Rcode != dns.RcodeSuccess {
res.rcode = rcodeText(r.Rcode)
res.transient = isTransientRcode(r.Rcode)
}
for _, rr := range r.Answer {
switch v := rr.(type) {
@ -332,8 +338,10 @@ func (c *chainCtx) resolveFinal(ctx context.Context, name string, servers []stri
switch {
case aRes.rcode != "":
c.data.FinalRcode = aRes.rcode
c.data.FinalRcodeTransient = aRes.transient
case aaaaRes.rcode != "":
c.data.FinalRcode = aaaaRes.rcode
c.data.FinalRcodeTransient = aaaaRes.transient
}
}