Files
2026-08-16 21:18:45 -05:00

204 lines
9.3 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{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}}