238 lines
8.2 KiB
Go
238 lines
8.2 KiB
Go
package checker
|
|
|
|
import (
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
// DeprecatedSeverity grades a deprecated record family.
|
|
//
|
|
// "Critical" reflects record types whose continued use breaks DNSSEC
|
|
// validation or modern resolvers (KEY/SIG/NXT, replaced by the DNSSEC-bis
|
|
// triplet, RFC 3755). "Warning" covers types that still parse but have a
|
|
// long-standing replacement (SPF→TXT, A6→AAAA, …). "Info" is reserved for
|
|
// experimental types nobody implements anymore (NULL, NSAP, …): present in
|
|
// a zone is harmless but pointless.
|
|
type DeprecatedSeverity int
|
|
|
|
const (
|
|
SeverityInfo DeprecatedSeverity = iota
|
|
SeverityWarn
|
|
SeverityCrit
|
|
)
|
|
|
|
func (s DeprecatedSeverity) String() string {
|
|
switch s {
|
|
case SeverityCrit:
|
|
return "crit"
|
|
case SeverityWarn:
|
|
return "warn"
|
|
default:
|
|
return "info"
|
|
}
|
|
}
|
|
|
|
// DeprecationInfo describes one deprecated RR type.
|
|
type DeprecationInfo struct {
|
|
Reason string
|
|
|
|
// Replacement is the modern record type to use instead, or "" when the
|
|
// type has no replacement (just remove the record).
|
|
Replacement string
|
|
|
|
// HowToFix is the actionable instruction shown in the HTML report.
|
|
// Phrased as a direct imperative so the user can act without context
|
|
// switching to the relevant RFC.
|
|
HowToFix string
|
|
|
|
Severity DeprecatedSeverity
|
|
}
|
|
|
|
// deprecatedTypes is the source of truth for what counts as legacy.
|
|
//
|
|
// Numeric keys (instead of dns.TypeXxx) are used for types miekg/dns does
|
|
// not export as named constants; they remain valid wire types and may
|
|
// well show up in zones imported from BIND or older tooling.
|
|
var deprecatedTypes = map[uint16]DeprecationInfo{
|
|
// --- DNSSEC predecessors (RFC 3755, RFC 4033 family) -----------------
|
|
dns.TypeKEY: {
|
|
Reason: "RFC 3755 obsoleted KEY in favour of DNSKEY",
|
|
Replacement: "DNSKEY",
|
|
HowToFix: "Re-sign the zone with a DNSSEC implementation that emits DNSKEY/RRSIG/NSEC records, then remove the KEY entries.",
|
|
Severity: SeverityCrit,
|
|
},
|
|
dns.TypeSIG: {
|
|
Reason: "RFC 3755 obsoleted SIG in favour of RRSIG",
|
|
Replacement: "RRSIG",
|
|
HowToFix: "SIG records are not validated by modern resolvers. Drop them; RRSIG records are produced automatically when the zone is DNSSEC-signed.",
|
|
Severity: SeverityCrit,
|
|
},
|
|
dns.TypeNXT: {
|
|
Reason: "RFC 3755 obsoleted NXT in favour of NSEC",
|
|
Replacement: "NSEC",
|
|
HowToFix: "NXT predates DNSSEC-bis and is not understood by current validators. Re-sign the zone to produce NSEC (or NSEC3) records and remove NXT.",
|
|
Severity: SeverityCrit,
|
|
},
|
|
|
|
// --- Replaced by a clear modern equivalent ---------------------------
|
|
dns.TypeSPF: {
|
|
Reason: "RFC 7208 §3.1 deprecated the SPF record type; publish SPF policy in TXT only",
|
|
Replacement: "TXT",
|
|
HowToFix: "Publish the SPF policy as a TXT record (`v=spf1 …`) at the same owner name, then delete the SPF-typed record. Some receivers ignore SPF-typed records entirely.",
|
|
Severity: SeverityWarn,
|
|
},
|
|
38: { // A6
|
|
Reason: "RFC 6563 moved A6 to historic status",
|
|
Replacement: "AAAA",
|
|
HowToFix: "Replace each A6 record with an equivalent AAAA record carrying the full IPv6 address.",
|
|
Severity: SeverityWarn,
|
|
},
|
|
dns.TypeMD: {
|
|
Reason: "RFC 973 obsoleted MD in 1986; use MX",
|
|
Replacement: "MX",
|
|
HowToFix: "Translate the mail-destination into an MX record (preference + exchange host) and delete the MD record.",
|
|
Severity: SeverityWarn,
|
|
},
|
|
dns.TypeMF: {
|
|
Reason: "RFC 973 obsoleted MF in 1986; use MX",
|
|
Replacement: "MX",
|
|
HowToFix: "Translate the mail-forwarder into an MX record (preference + exchange host) and delete the MF record.",
|
|
Severity: SeverityWarn,
|
|
},
|
|
dns.TypeGPOS: {
|
|
Reason: "RFC 1712 superseded GPOS with LOC",
|
|
Replacement: "LOC",
|
|
HowToFix: "If geolocation is genuinely needed, publish a LOC record instead. Otherwise delete the GPOS record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
|
|
// --- Privacy/info-leak deprecations ----------------------------------
|
|
dns.TypeMB: {
|
|
Reason: "RFC 2505/RFC 1035 §3.3: experimental, unused; replaced by MX",
|
|
Replacement: "MX",
|
|
HowToFix: "Delete the MB record; route mailbox traffic via MX.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
dns.TypeMG: {
|
|
Reason: "RFC 1035 §3.3: experimental mail-group record, never widely deployed",
|
|
Replacement: "",
|
|
HowToFix: "Delete the MG record; mail-group semantics now belong on the SMTP layer.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
dns.TypeMR: {
|
|
Reason: "RFC 1035 §3.3: experimental mail-rename record, never widely deployed",
|
|
Replacement: "",
|
|
HowToFix: "Delete the MR record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
dns.TypeMINFO: {
|
|
Reason: "RFC 1035 §3.3: experimental mailbox-info record, never widely deployed",
|
|
Replacement: "",
|
|
HowToFix: "Delete the MINFO record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
dns.TypeNULL: {
|
|
Reason: "RFC 1035 §3.3.10: experimental, must not appear in master files",
|
|
Replacement: "",
|
|
HowToFix: "Delete the NULL record. If it is used as a private channel, switch to TXT or a dedicated underscore label.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
11: { // WKS
|
|
Reason: "RFC 1123 §6.1.3.6 discouraged WKS; modern stacks ignore it",
|
|
Replacement: "",
|
|
HowToFix: "Delete the WKS record. Service availability belongs in SRV, ALPN, or HTTPS/SVCB records, not WKS.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
|
|
// --- Historical address families (no live deployment) ----------------
|
|
22: { // NSAP
|
|
Reason: "RFC 1706 historical: OSI/CLNP addressing, no current deployment",
|
|
Replacement: "",
|
|
HowToFix: "Delete the NSAP record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
dns.TypeNSAPPTR: {
|
|
Reason: "RFC 1706 historical: OSI reverse mapping, no current deployment",
|
|
Replacement: "",
|
|
HowToFix: "Delete the NSAP-PTR record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
dns.TypeX25: {
|
|
Reason: "RFC 1183 historical: X.25 addressing, no current deployment",
|
|
Replacement: "",
|
|
HowToFix: "Delete the X25 record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
dns.TypeISDN: {
|
|
Reason: "RFC 1183 historical: ISDN addressing, no current deployment",
|
|
Replacement: "",
|
|
HowToFix: "Delete the ISDN record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
dns.TypeRT: {
|
|
Reason: "RFC 1183 historical: route-through, superseded by direct routing",
|
|
Replacement: "",
|
|
HowToFix: "Delete the RT record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
dns.TypeATMA: {
|
|
Reason: "ATM Forum AF-SAA-0069 historical: ATM addressing, no current deployment",
|
|
Replacement: "",
|
|
HowToFix: "Delete the ATMA record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
31: { // EID
|
|
Reason: "Nimrod EID: never deployed beyond the experiment",
|
|
Replacement: "",
|
|
HowToFix: "Delete the EID record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
32: { // NIMLOC
|
|
Reason: "Nimrod NIMLOC: never deployed beyond the experiment",
|
|
Replacement: "",
|
|
HowToFix: "Delete the NIMLOC record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
40: { // SINK
|
|
Reason: "draft-eastlake-kitchen-sink: never standardised",
|
|
Replacement: "",
|
|
HowToFix: "Delete the SINK record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
56: { // NINFO
|
|
Reason: "draft-reid-dnsext-zs: never standardised",
|
|
Replacement: "TXT",
|
|
HowToFix: "If you need free-form zone metadata, use a TXT record at the apex with a clearly scoped prefix.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
57: { // RKEY
|
|
Reason: "draft-reid-dnsext-rkey: never standardised",
|
|
Replacement: "",
|
|
HowToFix: "Delete the RKEY record.",
|
|
Severity: SeverityInfo,
|
|
},
|
|
}
|
|
|
|
// extraTypeNames covers the deprecated record types that miekg/dns does
|
|
// not list in TypeToString (WKS, NSAP, A6, SINK). Without this fallback,
|
|
// typeLabel would return "TYPEnnn" for them and the report would lose the
|
|
// human-friendly name.
|
|
var extraTypeNames = map[uint16]string{
|
|
11: "WKS",
|
|
22: "NSAP",
|
|
38: "A6",
|
|
40: "SINK",
|
|
}
|
|
|
|
// typeLabel returns the textual record type name. dns.TypeToString covers
|
|
// the well-known set; for unknown rrtypes we fall back to RFC 3597 form
|
|
// ("TYPEnnn") so the report stays readable.
|
|
func typeLabel(rrtype uint16) string {
|
|
if name, ok := dns.TypeToString[rrtype]; ok {
|
|
return name
|
|
}
|
|
if name, ok := extraTypeNames[rrtype]; ok {
|
|
return name
|
|
}
|
|
// dns.Type stringer produces "TYPEnnn" for unknown types (RFC 3597).
|
|
return dns.Type(rrtype).String()
|
|
}
|