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
57 lines
2.4 KiB
Go
57 lines
2.4 KiB
Go
package config
|
|
|
|
// DefaultBlockPageHTML is the block page shown for a blocked query answered
|
|
// with the sinkhole action, before an operator customises it. It is a
|
|
// self-contained document — no external stylesheets, fonts or scripts — since
|
|
// it must render for a client that is, by definition, cut off from the rest
|
|
// of the network. The placeholders are filled in per request; see the
|
|
// "Available placeholders" reference on the block page settings screen.
|
|
const DefaultBlockPageHTML = `<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Blocked — {{.Domain}}</title>
|
|
<style>
|
|
:root { color-scheme: light dark; }
|
|
* { box-sizing: border-box; }
|
|
body {
|
|
margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center;
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
background: #0f1115; color: #e6e8eb; padding: 24px;
|
|
}
|
|
.card {
|
|
max-width: 560px; width: 100%; background: #171a21; border: 1px solid #2a2e37;
|
|
border-radius: 12px; padding: 32px 36px; box-shadow: 0 10px 40px rgba(0,0,0,.35);
|
|
}
|
|
.icon {
|
|
width: 56px; height: 56px; border-radius: 50%; background: #3a1d1d; color: #ff6b6b;
|
|
display: flex; align-items: center; justify-content: center; font-size: 28px; margin-bottom: 20px;
|
|
}
|
|
h1 { font-size: 22px; margin: 0 0 6px; }
|
|
.domain { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: #ff8787; word-break: break-all; }
|
|
p { color: #aab0bb; line-height: 1.5; }
|
|
dl { display: grid; grid-template-columns: auto 1fr; gap: 6px 16px; margin: 20px 0 0; font-size: 14px; }
|
|
dt { color: #7c828d; }
|
|
dd { margin: 0; color: #e6e8eb; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
|
|
.footer { margin-top: 24px; font-size: 12px; color: #565c68; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="card">
|
|
<div class="icon">⚠</div>
|
|
<h1>This domain is blocked</h1>
|
|
<p><span class="domain">{{.Domain}}</span> was blocked by the network's DNS filtering policy.</p>
|
|
<dl>
|
|
<dt>Blocklist</dt><dd>{{.ListName}}</dd>
|
|
<dt>Matched rule</dt><dd>{{.MatchedDomain}}</dd>
|
|
<dt>Policy</dt><dd>{{.PolicyName}}</dd>
|
|
<dt>Network</dt><dd>{{.NetworkName}}</dd>
|
|
<dt>Your address</dt><dd>{{.ClientIP}}</dd>
|
|
</dl>
|
|
<div class="footer">Blocked at {{.Timestamp}} by VibeDNS</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
`
|