Reference

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's auth login failed log 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

  1. Make sure Fail2Ban is installed:

sh # Debian / Ubuntu sudo apt install fail2ban # RHEL / Fedora / Alma / Rocky sudo dnf install fail2ban

  1. Edit /etc/fail2ban/jail.d/meshhold.conf and flip enabled:

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.

  1. 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 1d or use Fail2Ban's bantime.increment for 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.