Add a customizable HTTP/HTTPS block page for sinkholed queries

Serves a page explaining why a domain was blocked instead of leaving a
sinkholed client with a dead connection. Binds its own HTTP/HTTPS
listeners with self-signed, per-hostname TLS certs generated on the
fly, re-evaluates the requesting client against the policy engine per
request, and renders an HTML template editable from Settings with a
live preview.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTKpGMQzpfDsvedu1hvSUf
This commit is contained in:
2026-08-17 00:24:00 -05:00
co-authored by Claude Sonnet 5
parent 566a5fd3ed
commit 4895c8fd1e
13 changed files with 948 additions and 16 deletions
+17 -3
View File
@@ -13,9 +13,12 @@ import (
// RestartRequired lists the settings that only take effect after a restart,
// because they control a bound socket.
var RestartRequired = map[string]string{
config.KeyDNSUDPListen: "DNS UDP listen address",
config.KeyDNSTCPListen: "DNS TCP listen address",
config.KeyHTTPListen: "Management HTTP listen address",
config.KeyDNSUDPListen: "DNS UDP listen address",
config.KeyDNSTCPListen: "DNS TCP listen address",
config.KeyHTTPListen: "Management HTTP listen address",
config.KeyBlockPageEnabled: "Block page enabled state",
config.KeyBlockPageHTTPListen: "Block page HTTP listen address",
config.KeyBlockPageHTTPSListen: "Block page HTTPS listen address",
}
// SettingsGroup names a page of the settings interface.
@@ -30,6 +33,7 @@ const (
GroupHTTP SettingsGroup = "http"
GroupBackup SettingsGroup = "backup"
GroupRateLimit SettingsGroup = "ratelimit"
GroupBlockPage SettingsGroup = "blockpage"
)
// SaveSettings validates and persists a complete settings object.
@@ -96,6 +100,16 @@ func (a *App) PendingRestart(ctx context.Context) []string {
pending = append(pending, fmt.Sprintf("DNS TCP address (listening on %s, configured as %s)",
tcp, current.DNS.TCPListen))
}
bpHTTP, bpHTTPS := a.BlockPage.ListenAddrs()
bpRunning := a.BlockPage.Running()
if current.BlockPage.Enabled != bpRunning {
pending = append(pending, "Block page enabled state has changed")
} else if bpRunning && (current.BlockPage.HTTPListen != bpHTTP || current.BlockPage.HTTPSListen != bpHTTPS) {
pending = append(pending, fmt.Sprintf(
"Block page address (listening on %s/%s, configured as %s/%s)",
bpHTTP, bpHTTPS, current.BlockPage.HTTPListen, current.BlockPage.HTTPSListen))
}
return pending
}