initial commit

This commit is contained in:
2026-08-16 21:18:45 -05:00
commit 1e05a01bcf
122 changed files with 29178 additions and 0 deletions
+203
View File
@@ -0,0 +1,203 @@
{{define "content"}}
{{$s := .Data.S}}
<h1 class="page-title mb-1">Settings</h1>
<p class="text-body-secondary mb-4">Server configuration, stored in the database.</p>
{{template "settingsnav" .}}
<form method="post" action="/settings/http">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<div class="row g-3">
<div class="col-12 col-xl-8">
<div class="card mb-3">
<div class="card-header">Management interface</div>
<div class="card-body">
<div class="form-section">
<div class="row g-3">
<div class="col-12 col-md-6">
<label class="form-label" for="httpListen">
Listen address {{template "restartbadge"}}
</label>
<input type="text" class="form-control mono" id="httpListen" name="listen"
value="{{$s.HTTP.Listen}}" placeholder="127.0.0.1:8080" required>
<div class="form-text">
Currently bound to <span class="mono">{{.Data.Bound}}</span>.
Binding to loopback and reaching it over SSH or a VPN keeps the
interface off the network entirely.
</div>
</div>
<div class="col-12 col-md-6">
<label class="form-label" for="baseURL">Base URL</label>
<input type="url" class="form-control" id="baseURL" name="base_url"
value="{{$s.HTTP.BaseURL}}" placeholder="https://dns.example.com">
<div class="form-text">
Only needed behind a reverse proxy, for building absolute links.
</div>
</div>
</div>
</div>
<div class="form-section">
<div class="form-section-title">Reverse proxy</div>
<label class="form-label" for="trustedProxies">Trusted proxies</label>
<textarea class="form-control list-input" id="trustedProxies" name="trusted_proxies" rows="3"
placeholder="10.0.0.5
172.18.0.0/16">{{joinLines $s.HTTP.TrustedProxies}}</textarea>
<div class="form-text">
<code>X-Forwarded-For</code> is honoured only when the request arrives from one of
these addresses. Leave empty when there is no proxy: trusting the header
unconditionally would let any client forge its own address and slip past the
sign-in rate limiter.
</div>
</div>
<div class="form-section">
<div class="form-section-title">Limits</div>
<div class="row g-3">
<div class="col-12 col-md-6">
<label class="form-label" for="maxUpload">Maximum upload size</label>
<div class="input-group">
<input type="number" class="form-control" id="maxUpload" name="max_upload_mb"
value="{{$s.HTTP.MaxUploadMB}}" min="1" max="4096">
<span class="input-group-text">MB</span>
</div>
<div class="form-text">
Applies to blocklist and zone file imports. Large public blocklists are
commonly 550 MB.
</div>
</div>
<div class="col-12 col-md-6">
<label class="form-label" for="httpRateLimit">Request rate limit</label>
<div class="input-group">
<input type="number" class="form-control" id="httpRateLimit" name="http_rate_limit"
value="{{$s.HTTP.RateLimitPerMin}}" min="10" max="1000000">
<span class="input-group-text">/min</span>
</div>
<div class="form-text">Per client address, excluding static assets.</div>
</div>
</div>
</div>
<div class="form-section">
<div class="form-section-title">Metrics</div>
<div class="form-check form-switch mb-2">
<input type="hidden" name="metrics_enabled" value="false">
<input class="form-check-input" type="checkbox" role="switch" id="metricsEnabled"
name="metrics_enabled" value="true" {{if $s.HTTP.MetricsEnabled}}checked{{end}}>
<label class="form-check-label" for="metricsEnabled">
Expose Prometheus metrics at <span class="mono">/metrics</span>
</label>
</div>
<div class="form-check form-switch">
<input type="hidden" name="metrics_public" value="false">
<input class="form-check-input" type="checkbox" role="switch" id="metricsPublic"
name="metrics_public" value="true" {{if $s.HTTP.MetricsPublic}}checked{{end}}>
<label class="form-check-label" for="metricsPublic">
Allow metrics without authentication
</label>
</div>
<div class="form-text">
Metrics reveal query volumes and cache behaviour. Leave authentication on
unless the endpoint is reachable only by your scraper.
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">DNS rate limiting</div>
<div class="card-body">
<div class="form-check form-switch mb-3">
<input type="hidden" name="dns_ratelimit_enabled" value="false">
<input class="form-check-input" type="checkbox" role="switch" id="rlEnabled"
name="dns_ratelimit_enabled" value="true" {{if $s.RateLimit.Enabled}}checked{{end}}>
<label class="form-check-label" for="rlEnabled">
Limit how fast a single client may query
</label>
</div>
<div class="form-text mb-3">
Clients over the limit are dropped without a reply. Answering would let an
attacker use this server to amplify traffic at a spoofed victim, which is the
abuse the limiter exists to prevent.
</div>
<div class="row g-3">
<div class="col-6 col-md-3">
<label class="form-label" for="dnsQPS">Queries per second</label>
<input type="number" class="form-control" id="dnsQPS" name="dns_qps"
value="{{$s.RateLimit.QPS}}" min="1" max="1000000">
</div>
<div class="col-6 col-md-3">
<label class="form-label" for="dnsBurst">Burst allowance</label>
<input type="number" class="form-control" id="dnsBurst" name="dns_burst"
value="{{$s.RateLimit.Burst}}" min="1" max="1000000">
<div class="form-text">Never below the rate</div>
</div>
<div class="col-12 col-md-6">
<label class="form-label" for="exemptNetworks">Exempt networks</label>
<textarea class="form-control list-input" id="exemptNetworks" name="exempt_networks" rows="3"
placeholder="127.0.0.0/8
10.0.0.10">{{joinLines $s.RateLimit.ExemptNetworks}}</textarea>
<div class="form-text">
Trusted infrastructure — a downstream forwarder or a busy mail server —
is never limited.
</div>
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">
<i class="bi bi-check-lg me-1"></i>Save web server settings
</button>
</div>
</div>
</div>
</div>
<div class="col-12 col-xl-4">
<div class="card mb-3">
<div class="card-header">Rate limiter activity</div>
<div class="card-body">
<dl class="row small mb-0">
<dt class="col-7 text-body-secondary">State</dt>
<dd class="col-5 text-end">
{{if .Data.RateLimit.Enabled}}<span class="badge text-bg-success">on</span>
{{else}}<span class="badge text-bg-secondary">off</span>{{end}}
</dd>
<dt class="col-7 text-body-secondary">Allowed</dt>
<dd class="col-5 text-end">{{num .Data.RateLimit.Allowed}}</dd>
<dt class="col-7 text-body-secondary">Denied</dt>
<dd class="col-5 text-end {{if gt .Data.RateLimit.Denied 0}}text-warning fw-semibold{{end}}">
{{num .Data.RateLimit.Denied}}
</dd>
<dt class="col-7 text-body-secondary">Tracked clients</dt>
<dd class="col-5 text-end">{{num .Data.RateLimit.TrackedClients}}</dd>
</dl>
</div>
</div>
<div class="card">
<div class="card-header">Observability endpoints</div>
<div class="card-body small">
<dl class="row mb-0">
<dt class="col-4 mono">/healthz</dt>
<dd class="col-8 text-body-secondary">Process liveness. Never touches the database.</dd>
<dt class="col-4 mono">/readyz</dt>
<dd class="col-8 text-body-secondary">Readiness: listeners up and database reachable.</dd>
<dt class="col-4 mono">/metrics</dt>
<dd class="col-8 text-body-secondary">
Prometheus exposition.
{{if not $s.HTTP.MetricsEnabled}}<span class="badge text-bg-secondary">disabled</span>
{{else if $s.HTTP.MetricsPublic}}<span class="badge text-bg-warning">public</span>
{{else}}<span class="badge text-bg-success">authenticated</span>{{end}}
</dd>
</dl>
</div>
</div>
</div>
</div>
</form>
{{end}}