Skip to main contentSkip to footer

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.

Current version: 0.3.41 for 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

1

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.

2

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.

3

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

NeededWhy, and what happens without it
A packet filter you can write to: ipset with iptables, or nftablesapt 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.
systemdThe 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.
RootIt 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 accountUsed 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 sha256sumOnly 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.

backendWhat 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.
ipsetSets 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.
nftablesSets 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.

bash
reportedip-agent doctor
SectionWhat it answers
systemThe 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.
sshWhich 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.
firewallWhere 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.
panelISPConfig, 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.
sourcesEvery 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.
mailWhether 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.
verdictThe summary, and it maps onto the exit code.

Reading the verdict

ExitVerdictWhat it means
0everything the agent needs is hereInstall.
1runs on this host, with the limits aboveEvery 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.
2cannot run on this hostNot 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.

bash
# 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.

  1. reportedip-agent doctor. Before anything else, because it is the only command that changes nothing.
  2. 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.
  3. reportedip-agent status. Check that every configured list has a plausible IPv4 count, that the chain says ok, that the whitelist holds the addresses you expect, and that account shows your role and the licence state of this host.
  4. 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.
  5. Read status again after a day. The packet counters of the chain say how much the rules actually stopped, and ban list says which addresses this host caught in its own logs.
  6. Only on a host you answered log for: the rules are fully built and they match, they only log instead of dropping, so the kernel log shows exactly what drop would have cut off. Read one journal, then set mode: drop and run sync, which rebuilds the rules.
bash
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
The installation denies from its first sync on, which is why the whitelist is step four and not step six. Do it before that sync and not two days after it: the range you administer from, your monitoring and your backup host belong in there while nothing is blocked yet. A host you answered 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:

bash
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-ip takes 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. With backend: auto the agent prefers ipset, and the installer notes now say so.
  • 0.3.40: status --json for 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. status now 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 --nowildcard with iptables, socket transparent with 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: install takes the log level, as --log-level debug|info|warn|error or as REPORTEDIP_LOG_LEVEL. The default is unchanged at warn. A rollout that wants to read along on every host says info once 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:53 on 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: doctor names 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: install reads the web logs out of the web server's own configuration, the virtual host files under conf.d and sites-enabled included, 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: status no longer calls a host degraded because its group list is empty, which is the normal state of a key that is in no group, and doctor no 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: warn instead of info, 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-whitelist under the state directory and applies it in the same run to the kernel whitelist set, the list filter and the reporting gate; whitelist list and status show 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 group in the set rip-group on every port; the reputation of the address this host reports from as the health condition reputation; port scan detection from the kernel log with the source scan. 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

Security Focused
GDPR Compliant
Made in Germany
Back to Docs