# Reference ## MQTT topics | Topic | Direction | Role | |---|---|---| | `fail2ban//ban` | node → master | ban/notice event published by this node | | `fail2ban//heartbeat` | node → master | periodic liveness proof (independent of any ban activity) | | `fail2ban//ack` | node → master | acknowledgement after executing a command received from the master | | `fail2ban//action` | master → node | command addressed specifically to this node | | `fail2ban/broadcast/action` | master → all nodes | command broadcast to all subscribed nodes | | `fail2ban/broadcast/roster` | master → all nodes | list of known nodes + global jail/country counters, periodic | `` in the topic segment is the mTLS client certificate's CN (enforced by the broker's ACL) — the node's internal identifier (`uuid.getnode()`) travels in the message body, not in the topic. ## Master commands | Command | Effect on the receiving node | Trigger | |---|---|---| | `SYNC_BAN` | Full ban (all ports) via a dedicated jail | Automatic: the master systematically publishes a `SYNC_BAN` for every ban it ingests | | `ESCALATE` | **Permanent** ban via a dedicated jail | Automatic: the master correlates — an IP independently banned on several distinct nodes within 24h triggers an escalation | | `BAN_ALLPORTS` | Full ban (all ports), same mechanism as `SYNC_BAN` | Manual (targeted human decision) | | `WHITELIST` | Added to `ignoreip` on all active jails + unbanned — in-memory, not persistent | Manual | | `RATE_LIMIT` | Throttle (10 req/s, burst 20) instead of a full block | Manual — edge case, likely false positives | | `NOTIFY_ONLY` | No network action, just a visible dashboard notification | Manual — observation mode | | `REPORT_ABUSE` | Report to AbuseIPDB — **not broadcast to nodes**, submitted directly by the master (one report per IP is enough) | Manual, dry-run until `ABUSEIPDB_ENABLED`/`ABUSEIPDB_API_KEY` are configured | Manual commands are triggered on the master, by hand: ```sh emitter/.venv/bin/python emitter/manage.py publish_command WHITELIST 203.0.113.5 emitter/.venv/bin/python emitter/manage.py publish_command BAN_ALLPORTS 203.0.113.5 emitter/.venv/bin/python emitter/manage.py publish_command RATE_LIMIT 203.0.113.5 emitter/.venv/bin/python emitter/manage.py publish_command REPORT_ABUSE 203.0.113.5 ``` Geolocation of banned IPs is automatic (asynchronous, on ingestion of each event); to retry the ones left without coordinates (API failure, private IP): ```sh emitter/.venv/bin/python emitter/manage.py retry_geolocation # all ungeolocated events emitter/.venv/bin/python emitter/manage.py retry_geolocation --ip # a specific IP ``` ## Custom fail2ban jails | Jail | Role | |---|---| | `emitter-admin-auth` | Dashboard `/admin/` lockout (django-axes) → network ban | | `master-sync` | Receiving a `SYNC_BAN`/`BAN_ALLPORTS` — never triggered by a log, only by manual injection from the master relay | | `master-ratelimit` | Receiving a `RATE_LIMIT` | | `master-escalate` | Receiving an `ESCALATE` — permanent ban | `emitter-admin-auth` follows a reusable template (`generic/fail2ban/jail.d/app-auth.conf.template` + `generic/fail2ban/filter.d/app-auth.conf`) to protect any other application exposed on the same server the same way, not just this dashboard.