ReportedIP Agent for Linux
The agent is a single static binary that does two jobs on a Linux server: it keeps the community blacklist in the kernel firewall, and it reports the attackers it finds in that server's own logs. Optionally it also blocks those local finds itself. It replaces the hand-written sync script documented on this site, and it takes over the work that fail2ban used to do.
linux/amd64 and linux/arm64.
Each server needs a licence, one is included
with Professional and three with Business; see Server licences.
Licences are added and removed under Agent Servers in your
account.
What it does
Community blacklist into the kernel
Five lists, one per exposed service (ssh, mail, web, ftp, edge), plus a sixth,
group, with what the other servers in your account group reported, fetched as often as your
licence allows and swapped into an ipset or an nftables set atomically. A list that came back
empty or shorter than min_entries is never applied, so a failed fetch leaves the
previous set in place. The group list is the exception: it has no minimum, and an empty group
list empties its set.
Local attacks recognised and reported
Fourteen source types, eighteen event sources, each with its own threshold. The agent tails the logs the server already writes, counts hits per address and reports the address once its threshold is reached. No log line, no user name and no URL leaves the machine.
Local finds blocked
On after the installation (ban.enabled: true). An address the agent caught
itself goes into a kernel set with a timeout, and a repeat offender is banned for longer each
time. Set it to false for a host that is only meant to report.
Where to go next
- Installation: the terms of use, the checks, every question, the flags and variables of a rollout, a host without systemd.
- Installation examples: Ansible and cloud-init, a golden image, control panels, a mail or database host, LXC containers and cloud firewalls.
- Configuration: every key of config.yaml, the thresholds, the ban ladder, the mail, and when a change takes effect.
- Detection and rules: the source types, port scans from the kernel log, adding a log the installer missed, what leaves the machine, and the rule files.
- Blocking: the two kinds of set, how a ban begins and ends, the chain in the kernel, and lifting a ban.
- Groups: fleet-wide bans shared across your servers and WordPress sites, the tariff limits, the whitelist and the webhook.
- Running it: commands, exit codes, the units, monitoring, the reputation of the host's own address, and the troubleshooting table.
- Licences: a licence per server, the report budget, groups from Professional, and what runs without one.
- Replacing fail2ban: the migration in three steps, and the way back.
Requirements
| Needed | Why, and what happens without it |
|---|---|
A packet filter you can write to: ipset with iptables, or nftables | apt install ipset iptables or apt install nftables, on the RHEL family dnf instead. Without one the host reports and does not block, which is a supported installation. ip6tables is used when it is there, and without it IPv6 bans are recorded but not enforced. |
| systemd | The one hard requirement of install, because it writes and enables three units. Checked by reading /run/systemd/system, not by the presence of systemctl. The binary itself does not depend on it: watch for the detection plus a sync from cron is a working host, set up by hand. |
| Root | It writes firewall rules and reads logs that are not world-readable. There is no reduced mode that runs as a normal user. |
| An API key from your account | Used twice during the installation, once to download the licensed build and once to register the host. One key per host is the recommendation; several hosts on one key is allowed and costs nothing extra, because a host is identified by its install ID. |
curl or wget, plus sha256sum | Only for install.sh, for the download and the checksum. It refuses to install when the checksum tool is missing. |
Nothing else. The binary is statically linked and built without cgo, so it brings no interpreter, no shared library, no package repository and no cron helper of its own. It was developed and measured on Debian 12 (bookworm) on arm64 with ISPConfig, nginx, Postfix, pure-ftpd and bind.
ipset or nftables, and what auto decides
Both backends do the same job. What matters is which one the host already uses, because two packet filters writing the same INPUT chain is how an afternoon disappears.
backend | What happens |
|---|---|
auto (default) | ipset is preferred when both ipset and iptables are on the host, otherwise nft is used. The preference exists because CSF and the published firewall guide use that tool chain, so a host with existing rules keeps them. |
ipset | Sets in ipset, rules in the rip-blacklist chain of iptables and ip6tables. Fails with a clear message when either tool is missing, rather than falling back. |
nftables | Sets and rules in the native table inet reportedip. Fails when nft is missing. |
Only a binary that exists counts, never a version string: iptables --version on a host
with the nft backend says something that looks right and means nothing. Once a host has synced, the
chosen backend is recorded, and a later change needs
reportedip-agent sync --migrate-backend instead of silently building a second set of
rules.
Check the host first
doctor is the command to run before installing. It changes nothing at all: no
file, no directory, no rule, no set. Everything it reports it read.
reportedip-agent doctor
| Section | What it answers |
|---|---|
system | The distribution as its own os-release names it, the kernel, the platform, whether systemd is really running, the time zone that logs without an offset are read in, and the free space where the state directory will live. |
ssh | Which unit runs sshd, and the port. With ssh.socket the port comes from the listening process, because sshd -T still answers 22 while the real port is in the socket unit. doctor says which of the two applied. |
firewall | Where ipset, iptables, ip6tables and nft are, which backend that resolves to, and whether the local ban set carries a per-entry timeout. That last point decides whether the kernel expires a ban on its own. |
panel | ISPConfig, Plesk, cPanel or DirectAdmin. Only the ISPConfig per-site log layout is known to this version; the others are named rather than guessed, because a wrong glob would have the agent read byte counters instead of access logs. |
sources | Every configured source resolved to real files, how many there are and how many are readable, the largest one with an estimated line count, and a note about files that are there and empty. On the largest panel host measured, the web globs resolve to 196 files. It also names a log this host writes that no source reads, which is what a service installed after the agent looks like, and that finding is worth exit 1. |
mail | Whether an alert could actually leave this host. Of eighteen hosts measured, fourteen could deliver mail, two had sendmail with a stopped MTA, and two had no MTA at all. |
verdict | The summary, and it maps onto the exit code. |
Reading the verdict
| Exit | Verdict | What it means |
|---|---|---|
0 | everything the agent needs is here | Install. |
1 | runs on this host, with the limits above | Every limit is printed by name. A missing firewall backend means this host reports and does not block, which is a valid installation. A source that resolves to nothing is worth fixing first. |
2 | cannot run on this host | Not a Linux host, or a host that can neither block nor read a single log. Fix the cause before installing. |
Run doctor again after the installation. With a config in place it reports the configured
sources instead of the detected ones, and it adds the clock drift per source that the daemon
measured. A source whose log stamps every line more than a minute away from the system clock has a
counting window that never fills, and that is the one fault which looks exactly like "the detector
does not work".
Quick install
Three commands on a host that has none of this yet. Run them in this order and the agent is reporting, the community lists are in the kernel, and nothing has been dropped that you did not agree to.
# 1. Install, as root. At a terminal it shows the terms of use and
# takes a yes, then asks for your key, then three short questions;
# an Enter answers drop and local bans on.
curl -fsSL https://reportedip.com/agent/install.sh | sh
# For automation, and for fifty hosts, nothing is asked: the terms
# are accepted up front and the key comes from the environment.
curl -fsSL https://reportedip.com/agent/install.sh | REPORTEDIP_ACCEPT_TERMS=1 REPORTEDIP_KEY="YOUR_API_KEY" sh
# 2. The first sync builds the chain and the sets. The installation
# itself touches no firewall rule.
reportedip-agent sync
# 3. See that it ran, and read what would be dropped before it is.
reportedip-agent status
reportedip-agent doctor
reportedip-agent test /var/log/auth.log
What each one leaves behind: the first places the binary in /usr/local/bin, writes
/etc/reportedip-agent/config.yaml from what it found on this host and installs the
three systemd units. The second creates the firewall chain and the sets, which the installation
deliberately does not touch, and fills them. The third is the proof: status shows an
entry count per list and the licence state of this host, doctor names anything missing,
and test replays your own log to show which address would have crossed which threshold.
The rules deny from the first sync on, because that is what the installation writes, so the
whitelist is the thing to get right before that sync and not two days after it. Answer
log instead for a host you want to read a journal on first. The
installation page is the same
installation explained in full, and that detail is the reason it exists.
The first hour, in order
Each step answers a question the next one depends on. In a different order a working installation looks broken.
reportedip-agent doctor. Before anything else, because it is the only command that changes nothing.reportedip-agent sync. The first run builds the chain, creates the sets and downloads the configured lists, one after another with a short pause between them.reportedip-agent status. Check that every configured list has a plausible IPv4 count, that the chain saysok, that the whitelist holds the addresses you expect, and thataccountshows your role and the licence state of this host.- Fix the whitelist. The installer whitelisted the address your SSH session came from, which is a guess. Replace it with the range you administer from, and add your monitoring, your backup host and your office range.
- Read
statusagain after a day. The packet counters of the chain say how much the rules actually stopped, andban listsays which addresses this host caught in its own logs. - Only on a host you answered
logfor: the rules are fully built and they match, they only log instead of dropping, so the kernel log shows exactly whatdropwould have cut off. Read one journal, then setmode: dropand runsync, which rebuilds the rules.
reportedip-agent doctor
reportedip-agent sync
reportedip-agent status
reportedip-agent whitelist add 203.0.113.0/24 "management"
reportedip-agent whitelist add 198.51.100.7 "monitoring"
reportedip-agent whitelist list
log for gives you those two days as well, a host that took
the default does not.
Upgrades and release notes
The agent looks for a new release every six hours, during the sync run. It verifies the download
against an Ed25519 signature with the public key built into the binary, puts the running binary back if the
swap fails, and restarts the watch service itself. reportedip-agent update does the same on
demand, and update --check only reports what is published.
Running the install command again upgrades a host as well. A host below the minimum version the
service supports still updates itself; until it has, the agent warns about it in its log, in
status (exit 1) and in update --check.
Downgrades are refused in general, so a host that pulled a newer version does not go back by itself.
What the current release changed is public and needs no key:
curl -s https://reportedip.com/agent/whats-new
reportedip-agent update --check
Changes to the REST API itself, the response fields and status codes an integrator has to care about, are in the API changelog instead.
Recent releases
- 0.3.41:
--admin-iptakes a CIDR range for real. Up to 0.3.40 a range was cut down to its network address, so only that one address landed in the auto whitelist. Withbackend: autothe agent prefers ipset, and the installer notes now say so. - 0.3.40:
status --jsonfor monitoring: one document with schema 1 and the same judgement and exit code as the text, sent without a request to the API, and a broken configuration (exit 2) still prints a document.statusnow reports a stopped watch daemon as exit 1: the daemon leaves a heartbeat every 30 seconds, and one older than two minutes counts as stopped. Setup for common monitoring systems is under Monitoring. - 0.3.39: the port scan rule no longer bans FTP clients in passive mode. Every data connection goes to a port the server opens for that one transfer, the rule knew the listening ports only from the last sync, and so uploading a folder looked like a scan within seconds. The rule now asks the kernel whether a socket listens on the port at that moment (
-m socket --nowildcardwith iptables,socket transparentwith nftables), and the passive range of a running pure-ftpd, proftpd or vsftpd is read from its configuration and left out of the rule. A server without a configured range gets the ephemeral port range of the kernel left out instead. The chain is rebuilt once at the next sync. - 0.3.38:
installtakes the log level, as--log-level debug|info|warn|erroror asREPORTEDIP_LOG_LEVEL. The default is unchanged atwarn. A rollout that wants to read along on every host saysinfoonce instead of editing the config of every host afterwards, and a level the parser would refuse is refused before anything is written. - 0.3.37: a service that listens on loopback only is no longer treated as reachable. The port probe counted every listener in the kernel table whatever address it was bound to, so the stub resolver that holds
127.0.0.53:53on a normal Debian host looked like a name server, and a mail server that only takes mail from its own machine looked like one that takes it from the internet. A port that has a loopback socket next to a real one still counts. This also decides which ports the port scan rule treats as open. - 0.3.35:
doctornames a log this host writes that no source reads, and says where to add the source. Detection runs once, at install time, and that stays as it is, because a service stopped for maintenance must never switch a source off. The other direction had nobody watching it: a mail server installed on a host that had none before was not picked up, and the host looked healthy while that service went unwatched. The finding is worth exit 1. - 0.3.34:
installreads the web logs out of the web server's own configuration, the virtual host files underconf.dandsites-enabledincluded, instead of a list of default names. A real hosting setup names its logs after the site, so the default paths found the default log and missed the rest: on a measured host two further logs, one of them an access log with real addresses, would have gone unread. A log whose format masks the client address is still left out, and every log taken this way is named in one line at install time. - 0.3.32:
statusno longer calls a host degraded because its group list is empty, which is the normal state of a key that is in no group, anddoctorno longer claims there is no log source on a host whose sshd writes to the journal instead of to a file. - 0.3.29: a fresh install writes
log_level: warninstead ofinfo, so a new host stays quiet in the journal until something wants attention. The routine lines, a list updated, a report queued, a local ban placed, are unchanged and one word away. A host that is already installed keeps what its config file says. - 0.3.27: the whitelist of the group: addresses and prefixes a group in your account names are never blocked and never reported by any member. The sync reads it with the group list, writes it to
group-whitelistunder the state directory and applies it in the same run to the kernel whitelist set, the list filter and the reporting gate;whitelist listandstatusshow the entries with their notes. The whitelist of this host, the auto whitelist and the built-in layers stay as they are. - 0.3.26: a fast port scan stayed below its own threshold: the scan rule logged ten SYNs a minute with the kernel's default burst of five, so a scanner that probes a thousand ports in a few seconds left five lines and the rule needs ten. The rule now bursts twenty, the first twenty probes of a scan are logged at once, and the rate keeps the log quiet after them. The chain is rebuilt once.
- 0.3.25: a host whose key is in no group lost its chain on 0.3.24, because the group set is only created by its first feed and the rule that names it was refused. Every list set is now created before a rule names it. 0.3.24 was withdrawn.
- 0.3.24: the group list
groupin the setrip-groupon every port; the reputation of the address this host reports from as the health conditionreputation; port scan detection from the kernel log with the sourcescan. The chain is rebuilt once after this update and its packet counters start over.
Resource use
Measured on a Debian 12 arm64 host with seven log files under the agent: 16.6 MB of resident memory. There is no interpreter to start, no database and no cache directory to warm up, which is most of the reason it stays that small.
What the agent is not
It is not a malware scanner, not a web application firewall and not a replacement for Imunify360. It does not read your files, it does not inspect request bodies and it does not quarantine anything. It blocks and reports addresses, and it does that on a small, auditable surface. For protection at the level of a single request on a WordPress site, use the Hive plugin instead. The two run on the same machine without interfering with each other.
Last updated: · Maintained by the ReportedIP team