Fail2Ban integration
Drop brute-forcers at the firewall — enable the shipped filter + jail and verify it.
MeshHold's built-in lockout refuses a brute-forcer at the login
(15 bad attempts per IP → a 15-minute 429). Fail2Ban
goes one step further: it watches the log and drops the offender's
packets at the firewall, so they can't even reach the listener. This
page sets it up.
The Linux .deb / .rpm ship a ready-made filter and jail; on other
platforms (or a from-source build) you can drop the same two files in by
hand.
What the package installs
Installing the MeshHold package places two files (and reloads fail2ban if it's already present):
/etc/fail2ban/filter.d/meshhold.conf— matches the daemon'sauth login failedlog line and extracts the client IP./etc/fail2ban/jail.d/meshhold.conf— the jail, shipped disabled so installing MeshHold can never lock you out of your own box. It's a conffile, so your edits survive package upgrades.
The filter reads the systemd journal, so it needs a Fail2Ban built with journal support — the default on modern distros.
Enable it
- Make sure Fail2Ban is installed:
sh
# Debian / Ubuntu
sudo apt install fail2ban
# RHEL / Fedora / Alma / Rocky
sudo dnf install fail2ban
- Edit
/etc/fail2ban/jail.d/meshhold.confand flipenabled:
ini
[meshhold]
enabled = true
backend = systemd
filter = meshhold
maxretry = 15
findtime = 15m
bantime = 1h
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
port = 8080
Trim ignoreip to the ranges you actually trust, and change port if
you moved api.listen_addr off 8080.
- Reload and verify:
sh
sudo systemctl reload fail2ban
sudo fail2ban-client status meshhold
That last command should report the jail as active with 0 currently
banned. Drive a few failed logins from a throwaway IP and watch the
banned count climb.
How it matches
Under the packaged systemd unit the daemon logs in JSON
(--log-format=json), so a failed login lands in the journal as:
{"level":"warn","ts":...,"msg":"auth login failed",
"client_ip":"203.0.113.5","path":"/api/v1/auth/login"}
The shipped filter keys off the stable auth login failed message and
captures client_ip:
[Definition]
failregex = "msg":"auth login failed".*?"client_ip":"<HOST>"
ignoreregex =
journalmatch = _SYSTEMD_UNIT=meshhold.service
A separate auth login lockout line is emitted when MeshHold's own
per-IP cap trips, if you'd like to alert on that too.
Get the client IP right
Fail2Ban bans whatever IP the daemon logged. By default MeshHold logs the
real TCP peer and ignores X-Forwarded-For, which is what you want for a
directly-exposed node. If you run MeshHold behind a reverse proxy, set
api.trusted_proxies (see Security & hardening) so the
logged IP is the real client and not your proxy — otherwise Fail2Ban will
happily ban your own proxy and lock everyone out.
Manual install (non-package builds)
If you didn't install from the .deb/.rpm, create the two files
yourself with the contents shown above
(/etc/fail2ban/filter.d/meshhold.conf and
/etc/fail2ban/jail.d/meshhold.conf), make sure the daemon logs to the
journal with --log-format=json, then systemctl reload fail2ban.
Tuning
- maxretry / findtime mirror MeshHold's own lockout (15 / 15m) so a ban only fires once an IP has clearly blown past the in-app cap. Lower them for a stricter posture.
- bantime — bump to
1dor use Fail2Ban'sbantime.incrementfor escalating bans on repeat offenders. - action — the default bans via your firewall backend
(iptables / nftables). To also be notified, swap in an
action_mwl-style action.