A big one: searchable long-term request history, a rebuilt admin portal, IPv6 range bans, and a round of reliability and security fixes.
New
- Request history on disk (
stats.archive) — hourly JSONL segments, gzipped and indexed when the hour closes, keptretention_days(default 30). The writer never blocks the proxy; IP lookups read only the hours an index says matter. - Admin portal rebuilt — overview with 1h/24h/7d/30d traffic, status mix and top IPs/paths; a Requests tab searchable by IP/CIDR, text, outcome, status and range; an IP panel with ban status, activity summary and latest requests; a ban dialog with durations; blacklist search/filter/sort, export/import (JSON or one-IP-per-line blocklists) and a whitelist view.
- IPv6 range bans — honeypot hits and temp-bans of an IPv6 client cover its
/64(ipv6_ban_prefix), and strikes are counted per range. - Temp-ban escalation —
temp_ban.escalate_aftermakes repeat offenders permanent. - Temp bans from the portal/API (
duration), live config reload (SIGHUP, Settings, orPOST /admin/reload), and Prometheus/metricson the admin port. - Request log records country (
Cf-Ipcountry) and user agent.
Fixed
- Concurrent bans could corrupt
blacklist.json, which then silently reloaded empty. Writes are now serialized and a corrupt file is moved aside, never overwritten. - An empty or
/honeypot banned every visitor; honeypots matching the site root are now rejected. - Whitelisting an already-banned IP didn't unban it; expired bans lingered in the list; an expiry race could delete a fresh permanent ban.
- Honeypots are matched case-insensitively and against the cleaned path (
//wp-admin,/x/../wp-admin), and unclean paths no longer get a 301 before the blacklist check. - IPs are canonicalized; tarpits release on shutdown; upstream connections are pooled.
- Admin login is rate-limited (429 +
Retry-After), request bodies are capped, and a password change signs out other sessions. - Permanent bans no longer serialize a bogus
0001-01-01expiry.
Upgrade notes
- Unknown config keys are now an error — check your config for typos before rolling out.
- An install still on admin/admin is locked to a set-a-new-password screen on first login; passwords need 8+ characters.
- IPv6 automatic bans now cover the /64 by default — set
"ipv6_ban_prefix": 128for the old single-address behaviour. /metricsis unauthenticated on the admin port; disable with"admin": {"disable_metrics": true}.- To use the archive, add
"stats": {"archive": {"dir": "/data/requests"}}and size the volume accordingly (the k3s manifest now requests 1Gi). admin.Handlernow takes anOptionsstruct (only relevant if you embed the package).
Image: ghcr.io/timothydodd/thorngate:v0.12.0 (amd64 + arm64).
