Add DNS-aware whitelisting feature

Support for whitelisting SIP trunks and providers by hostname or SRV
record with automatic IP resolution and periodic refresh.

Features:
- Hostname resolution via A/AAAA records
- SRV record resolution (e.g., _sip._udp.provider.com)
- Configurable refresh interval (default 5m)
- Stale entry handling when DNS fails
- Admin API endpoints for DNS whitelist management
- Caddyfile directives: whitelist_hosts, whitelist_srv, dns_refresh

This allows whitelisting by provider name rather than tracking
constantly-changing IP addresses.
This commit is contained in:
Ryan Malloy 2025-12-08 00:46:43 -07:00
parent 46a47ce2c6
commit 5cf34eb3c0
8 changed files with 2383 additions and 11 deletions

View file

@ -57,6 +57,10 @@ func (h *AdminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request, next ca
return h.handleBans(w, r)
case strings.HasSuffix(path, "/stats"):
return h.handleStats(w, r)
case strings.HasSuffix(path, "/dns-whitelist"):
return h.handleDNSWhitelist(w, r)
case strings.HasSuffix(path, "/dns-whitelist/refresh"):
return h.handleDNSWhitelistRefresh(w, r)
case strings.Contains(path, "/unban/"):
return h.handleUnban(w, r, path)
case strings.Contains(path, "/ban/"):
@ -131,6 +135,50 @@ func (h *AdminHandler) handleUnban(w http.ResponseWriter, r *http.Request, path
return nil
}
// handleDNSWhitelist returns DNS whitelist entries and stats
func (h *AdminHandler) handleDNSWhitelist(w http.ResponseWriter, r *http.Request) error {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return nil
}
entries := h.guardian.GetDNSWhitelistEntries()
stats := h.guardian.GetStats()
response := map[string]interface{}{
"entries": entries,
"count": len(entries),
}
// Add DNS-specific stats if available
if dnsStats, ok := stats["dns_whitelist"]; ok {
response["stats"] = dnsStats
}
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(response)
}
// handleDNSWhitelistRefresh forces an immediate DNS refresh
func (h *AdminHandler) handleDNSWhitelistRefresh(w http.ResponseWriter, r *http.Request) error {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return nil
}
h.guardian.RefreshDNSWhitelist()
// Get updated entries
entries := h.guardian.GetDNSWhitelistEntries()
w.Header().Set("Content-Type", "application/json")
return json.NewEncoder(w).Encode(map[string]interface{}{
"success": true,
"message": "DNS whitelist refreshed",
"count": len(entries),
})
}
// handleBan manually adds an IP to the ban list
func (h *AdminHandler) handleBan(w http.ResponseWriter, r *http.Request, path string) error {
if r.Method != http.MethodPost {