checker-alias/checker/rules_apex.go
Pierre-Olivier Mercier da6def100c
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
checker: report transient apex-lookup failures as Unknown, not Crit
apexLookupRule mapped every findApex failure to Crit, including transport
and resolver faults like "lookup nemunai.re on 127.0.0.11:53: server
misbehaving" — a flaky recursive resolver, not a broken delegation. That
made the check flap into Crit whenever the resolver hiccuped, the same
class of false negative the chain path already fixed.

Mark apex-lookup failures that stem from a transport/resolver fault
(resolveZoneNSAddrs net errors, recursiveExchange transport errors, and
SERVFAIL/REFUSED seen during the SOA walk) as transient via a typed
error, surface it as ApexLookupTransient, and have apexLookupRule report
Unknown for those. Definitive failures (NXDOMAIN-only walk, no resolvable
NS) still drive Crit.
2026-06-18 10:29:30 +09:00

102 lines
3.5 KiB
Go

package checker
import (
"context"
"fmt"
sdk "git.happydns.org/checker-sdk-go/checker"
)
type apexLookupRule struct{}
func (apexLookupRule) Name() string { return "apex_lookup" }
func (apexLookupRule) Description() string {
return "Verifies the zone apex (SOA) of the checked name can be located."
}
func (apexLookupRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, _ sdk.CheckerOptions) []sdk.CheckState {
data, errState := loadAlias(ctx, obs)
if errState != nil {
return errState
}
if data.Apex != "" {
return okState(data.Apex, fmt.Sprintf("apex %s located", data.Apex))
}
// A transport/resolver fault means the apex could not be observed, not that the
// delegation is broken. Report it as Unknown so an intermittent recursive-resolver
// glitch does not flap the check into Crit; only definitive evidence drives Crit.
status := sdk.StatusCrit
hint := "Check that the parent delegation exists and that the zone is published."
if data.ApexLookupTransient {
status = sdk.StatusUnknown
hint = "The zone apex could not be observed due to a resolver/transport fault; retry and check recursive-resolver reachability."
}
return []sdk.CheckState{withHint(sdk.CheckState{
Status: status,
Subject: data.Owner,
Message: fmt.Sprintf("could not locate zone apex: %s", data.ApexLookupError),
}, hint)}
}
type cnameAtApexRule struct{}
func (cnameAtApexRule) Name() string { return "cname_at_apex" }
func (cnameAtApexRule) Description() string {
return "Flags a CNAME at the zone apex, which conflicts with the SOA/NS records the apex must carry (RFC 1912 §2.4). Honours the shared allowApexCNAME option."
}
func (cnameAtApexRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState {
data, errState := loadAlias(ctx, obs)
if errState != nil {
return errState
}
if !apexKnown(data) {
return skipped("apex lookup failed")
}
if !data.OwnerIsApex {
return skipped("owner is not the zone apex")
}
if !data.ApexHasCNAME {
return okState(data.Apex, "no CNAME at apex")
}
status := sdk.StatusCrit
if allowApexCNAME(opts) {
status = sdk.StatusWarn
}
return []sdk.CheckState{withHint(sdk.CheckState{
Status: status,
Subject: data.Apex,
Message: fmt.Sprintf("CNAME at apex %s conflicts with SOA/NS (RFC 1912 §2.4)", data.Apex),
}, "Use the provider's ALIAS/ANAME flattening, an HTTP redirect, or move content to a sub-label such as www.")}
}
type apexFlatteningRule struct{}
func (apexFlatteningRule) Name() string { return "apex_flattening" }
func (apexFlatteningRule) Description() string {
return "Notes ALIAS/ANAME provider-side flattening (A/AAAA served at apex alongside SOA/NS)."
}
func (apexFlatteningRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState {
data, errState := loadAlias(ctx, obs)
if errState != nil {
return errState
}
if !apexKnown(data) {
return skipped("apex lookup failed")
}
if !data.OwnerIsApex {
return skipped("owner is not the zone apex")
}
if !data.ApexFlattening {
return okState(data.Apex, "no ALIAS/ANAME flattening detected")
}
if !recognizeApexFlattening(opts) {
return skipped("recognizeApexFlattening disabled")
}
return []sdk.CheckState{withHint(sdk.CheckState{
Status: sdk.StatusInfo,
Subject: data.Apex,
Message: fmt.Sprintf("apex %s serves A/AAAA directly (provider-side ALIAS/ANAME flattening)", data.Apex),
}, "Keep the upstream target's TTL in mind: apex A/AAAA will only update as fast as the provider re-flattens.")}
}