204 lines
9.3 KiB
HTML
204 lines
9.3 KiB
HTML
{{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 5–50 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}}
|