2026-02-11 09:53:46 +02:00

225 lines
8.0 KiB
Go

package checks
import (
"fmt"
"github.com/DeBrosOfficial/network/pkg/inspector"
)
func init() {
inspector.RegisterChecker("dns", CheckDNS)
}
const dnsSub = "dns"
// CheckDNS runs all DNS/CoreDNS health checks against cluster data.
func CheckDNS(data *inspector.ClusterData) []inspector.CheckResult {
var results []inspector.CheckResult
for _, nd := range data.Nodes {
if nd.DNS == nil {
continue
}
results = append(results, checkDNSPerNode(nd)...)
}
results = append(results, checkDNSCrossNode(data)...)
return results
}
func checkDNSPerNode(nd *inspector.NodeData) []inspector.CheckResult {
var r []inspector.CheckResult
dns := nd.DNS
node := nd.Node.Name()
// 4.1 CoreDNS service running
if dns.CoreDNSActive {
r = append(r, inspector.Pass("dns.coredns_active", "CoreDNS service active", dnsSub, node,
"coredns is active", inspector.Critical))
} else {
r = append(r, inspector.Fail("dns.coredns_active", "CoreDNS service active", dnsSub, node,
"coredns is not active", inspector.Critical))
return r
}
// 4.47 Caddy service running
if dns.CaddyActive {
r = append(r, inspector.Pass("dns.caddy_active", "Caddy service active", dnsSub, node,
"caddy is active", inspector.Critical))
} else {
r = append(r, inspector.Fail("dns.caddy_active", "Caddy service active", dnsSub, node,
"caddy is not active", inspector.Critical))
}
// 4.8 DNS port 53 bound
if dns.Port53Bound {
r = append(r, inspector.Pass("dns.port_53", "DNS port 53 bound", dnsSub, node,
"UDP 53 is listening", inspector.Critical))
} else {
r = append(r, inspector.Fail("dns.port_53", "DNS port 53 bound", dnsSub, node,
"UDP 53 is NOT listening", inspector.Critical))
}
// 4.10 HTTP port 80
if dns.Port80Bound {
r = append(r, inspector.Pass("dns.port_80", "HTTP port 80 bound", dnsSub, node,
"TCP 80 is listening", inspector.High))
} else {
r = append(r, inspector.Warn("dns.port_80", "HTTP port 80 bound", dnsSub, node,
"TCP 80 is NOT listening", inspector.High))
}
// 4.11 HTTPS port 443
if dns.Port443Bound {
r = append(r, inspector.Pass("dns.port_443", "HTTPS port 443 bound", dnsSub, node,
"TCP 443 is listening", inspector.Critical))
} else {
r = append(r, inspector.Fail("dns.port_443", "HTTPS port 443 bound", dnsSub, node,
"TCP 443 is NOT listening", inspector.Critical))
}
// 4.3 CoreDNS memory
if dns.CoreDNSMemMB > 0 {
if dns.CoreDNSMemMB < 100 {
r = append(r, inspector.Pass("dns.coredns_memory", "CoreDNS memory healthy", dnsSub, node,
fmt.Sprintf("RSS=%dMB", dns.CoreDNSMemMB), inspector.Medium))
} else if dns.CoreDNSMemMB < 200 {
r = append(r, inspector.Warn("dns.coredns_memory", "CoreDNS memory healthy", dnsSub, node,
fmt.Sprintf("RSS=%dMB (elevated)", dns.CoreDNSMemMB), inspector.Medium))
} else {
r = append(r, inspector.Fail("dns.coredns_memory", "CoreDNS memory healthy", dnsSub, node,
fmt.Sprintf("RSS=%dMB (high)", dns.CoreDNSMemMB), inspector.High))
}
}
// 4.4 CoreDNS restart count
if dns.CoreDNSRestarts == 0 {
r = append(r, inspector.Pass("dns.coredns_restarts", "CoreDNS low restart count", dnsSub, node,
"NRestarts=0", inspector.High))
} else if dns.CoreDNSRestarts <= 3 {
r = append(r, inspector.Warn("dns.coredns_restarts", "CoreDNS low restart count", dnsSub, node,
fmt.Sprintf("NRestarts=%d", dns.CoreDNSRestarts), inspector.High))
} else {
r = append(r, inspector.Fail("dns.coredns_restarts", "CoreDNS low restart count", dnsSub, node,
fmt.Sprintf("NRestarts=%d (crash-looping?)", dns.CoreDNSRestarts), inspector.High))
}
// 4.7 CoreDNS log error rate
if dns.LogErrors == 0 {
r = append(r, inspector.Pass("dns.coredns_log_errors", "No recent CoreDNS errors", dnsSub, node,
"0 errors in last 5 minutes", inspector.High))
} else if dns.LogErrors < 5 {
r = append(r, inspector.Warn("dns.coredns_log_errors", "No recent CoreDNS errors", dnsSub, node,
fmt.Sprintf("%d errors in last 5 minutes", dns.LogErrors), inspector.High))
} else {
r = append(r, inspector.Fail("dns.coredns_log_errors", "No recent CoreDNS errors", dnsSub, node,
fmt.Sprintf("%d errors in last 5 minutes", dns.LogErrors), inspector.High))
}
// 4.14 Corefile exists
if dns.CorefileExists {
r = append(r, inspector.Pass("dns.corefile_exists", "Corefile exists", dnsSub, node,
"/etc/coredns/Corefile present", inspector.High))
} else {
r = append(r, inspector.Fail("dns.corefile_exists", "Corefile exists", dnsSub, node,
"/etc/coredns/Corefile NOT found", inspector.High))
}
// 4.20 SOA resolution
if dns.SOAResolves {
r = append(r, inspector.Pass("dns.soa_resolves", "SOA record resolves", dnsSub, node,
"dig SOA returned result", inspector.Critical))
} else {
r = append(r, inspector.Fail("dns.soa_resolves", "SOA record resolves", dnsSub, node,
"dig SOA returned no result", inspector.Critical))
}
// 4.21 NS records resolve
if dns.NSResolves {
r = append(r, inspector.Pass("dns.ns_resolves", "NS records resolve", dnsSub, node,
fmt.Sprintf("%d NS records returned", dns.NSRecordCount), inspector.Critical))
} else {
r = append(r, inspector.Fail("dns.ns_resolves", "NS records resolve", dnsSub, node,
"dig NS returned no results", inspector.Critical))
}
// 4.23 Wildcard DNS resolution
if dns.WildcardResolves {
r = append(r, inspector.Pass("dns.wildcard_resolves", "Wildcard DNS resolves", dnsSub, node,
"test-wildcard.<domain> returned IP", inspector.Critical))
} else {
r = append(r, inspector.Fail("dns.wildcard_resolves", "Wildcard DNS resolves", dnsSub, node,
"test-wildcard.<domain> returned no IP", inspector.Critical))
}
// 4.24 Base domain A record
if dns.BaseAResolves {
r = append(r, inspector.Pass("dns.base_a_resolves", "Base domain A record resolves", dnsSub, node,
"<domain> A record returned IP", inspector.High))
} else {
r = append(r, inspector.Warn("dns.base_a_resolves", "Base domain A record resolves", dnsSub, node,
"<domain> A record returned no IP", inspector.High))
}
// 4.50 TLS certificate - base domain
if dns.BaseTLSDaysLeft >= 0 {
if dns.BaseTLSDaysLeft > 30 {
r = append(r, inspector.Pass("dns.tls_base", "Base domain TLS cert valid", dnsSub, node,
fmt.Sprintf("%d days until expiry", dns.BaseTLSDaysLeft), inspector.Critical))
} else if dns.BaseTLSDaysLeft > 7 {
r = append(r, inspector.Warn("dns.tls_base", "Base domain TLS cert valid", dnsSub, node,
fmt.Sprintf("%d days until expiry (expiring soon)", dns.BaseTLSDaysLeft), inspector.Critical))
} else {
r = append(r, inspector.Fail("dns.tls_base", "Base domain TLS cert valid", dnsSub, node,
fmt.Sprintf("%d days until expiry (CRITICAL)", dns.BaseTLSDaysLeft), inspector.Critical))
}
}
// 4.51 TLS certificate - wildcard
if dns.WildTLSDaysLeft >= 0 {
if dns.WildTLSDaysLeft > 30 {
r = append(r, inspector.Pass("dns.tls_wildcard", "Wildcard TLS cert valid", dnsSub, node,
fmt.Sprintf("%d days until expiry", dns.WildTLSDaysLeft), inspector.Critical))
} else if dns.WildTLSDaysLeft > 7 {
r = append(r, inspector.Warn("dns.tls_wildcard", "Wildcard TLS cert valid", dnsSub, node,
fmt.Sprintf("%d days until expiry (expiring soon)", dns.WildTLSDaysLeft), inspector.Critical))
} else {
r = append(r, inspector.Fail("dns.tls_wildcard", "Wildcard TLS cert valid", dnsSub, node,
fmt.Sprintf("%d days until expiry (CRITICAL)", dns.WildTLSDaysLeft), inspector.Critical))
}
}
return r
}
func checkDNSCrossNode(data *inspector.ClusterData) []inspector.CheckResult {
var r []inspector.CheckResult
activeCount := 0
totalNS := 0
for _, nd := range data.Nodes {
if nd.DNS == nil {
continue
}
totalNS++
if nd.DNS.CoreDNSActive {
activeCount++
}
}
if totalNS == 0 {
return r
}
if activeCount == totalNS {
r = append(r, inspector.Pass("dns.all_ns_active", "All nameservers running CoreDNS", dnsSub, "",
fmt.Sprintf("%d/%d nameservers active", activeCount, totalNS), inspector.Critical))
} else {
r = append(r, inspector.Fail("dns.all_ns_active", "All nameservers running CoreDNS", dnsSub, "",
fmt.Sprintf("%d/%d nameservers active", activeCount, totalNS), inspector.Critical))
}
return r
}