checker-email-keys/checker/rules_dns.go

78 lines
2.6 KiB
Go

package checker
import (
"fmt"
"strings"
sdk "git.happydns.org/checker-sdk-go/checker"
)
// DNS-level rules: lookup outcome, record presence, service/DNS parity,
// DNSSEC authentication, and owner-name hash correctness. These apply
// to both OPENPGPKEY and SMIMEA records.
func checkDNSQueryFailed(d *EmailKeyData, _ sdk.CheckerOptions) []issue {
if d.DNSQueryError == "" {
return nil
}
return []issue{{
Severity: sdk.StatusCrit,
Message: d.DNSQueryError,
Hint: "Check that the zone is published at an authoritative server reachable from this checker.",
}}
}
func checkDNSNoRecord(d *EmailKeyData, _ sdk.CheckerOptions) []issue {
if d.DNSAnswerPresent == nil || *d.DNSAnswerPresent {
return nil
}
kind := "OPENPGPKEY"
if d.Kind == KindSMIMEA {
kind = "SMIMEA"
}
return []issue{{
Severity: sdk.StatusCrit,
Message: fmt.Sprintf("Authoritative DNS returned no %s record at %s.", kind, d.QueriedOwner),
Hint: "Ensure the record is present in the zone and that the zone has been loaded by the authoritative servers.",
}}
}
func checkDNSRecordMismatch(d *EmailKeyData, _ sdk.CheckerOptions) []issue {
if d.DNSRecordMatchesService == nil || *d.DNSRecordMatchesService {
return nil
}
return []issue{{
Severity: sdk.StatusWarn,
Message: "The record returned by DNS does not match the one declared in the service. The zone may not have been re-published since the last edit.",
Hint: "Propagate the zone to the authoritative servers, then wait for TTL/negative-cache expiry.",
}}
}
func checkDNSSECNotValidated(d *EmailKeyData, opts sdk.CheckerOptions) []issue {
if d.DNSSECSecure == nil || *d.DNSSECSecure {
return nil
}
sev := sdk.StatusWarn
if sdk.GetBoolOption(opts, OptionRequireDNSSEC, true) {
sev = sdk.StatusCrit
}
return []issue{{
Severity: sev,
Message: "The validating resolver did not set the AD flag: the record is not DNSSEC-authenticated, which defeats the whole DANE trust model.",
Hint: "Sign the zone with DNSSEC and publish the DS record at the parent so RFC 7929/8162 consumers can authenticate the key.",
}}
}
func checkOwnerHashMismatch(d *EmailKeyData, _ sdk.CheckerOptions) []issue {
if d.ExpectedOwnerPrefix == "" || d.ObservedOwnerPrefix == "" {
return nil
}
if strings.EqualFold(d.ObservedOwnerPrefix, d.ExpectedOwnerPrefix) {
return nil
}
return []issue{{
Severity: sdk.StatusCrit,
Message: fmt.Sprintf("Owner name prefix %q does not match SHA-256(%q)[:28]=%q.", d.ObservedOwnerPrefix, d.Username, d.ExpectedOwnerPrefix),
Hint: "Republish the record at the hash-derived name for the intended user, or update the Username field to match the record's owner name.",
}}
}