Skip to main contentSkip to footer

Installation examples

The same installer on the hosts it usually meets: a rollout with Ansible or cloud-init, a golden image, a server with a control panel, a mail or database host, an LXC container, and a VPS behind a cloud firewall. Every example uses the flags and variables the installation page documents, nothing here is a second way in.

Unattended, on many hosts

Three things make an unattended install work, and every example below is built on them. The terms of use are accepted up front with REPORTEDIP_ACCEPT_TERMS=1, because there is nobody to type yes. The key travels in the environment and never as an argument, because /proc/<pid>/cmdline is readable by every local user and the environment is not. And the answers to the questions a terminal would ask are given as variables, so no host waits for an answer: REPORTEDIP_MODE, REPORTEDIP_BAN and REPORTEDIP_NOTIFY_EMAIL. Leave one out and the default of a fresh config applies, which is drop, local bans on, and no mail.

The run is idempotent. On a host that already has /etc/reportedip-agent/config.yaml the script places the binary and stops, and the configuration is left alone. Upgrades do not need the rollout at all: the agent updates itself every six hours over a signed release. The one thing to do after every install is the whitelist: the installer puts the address the session came from into it, and your management range is not there until you add it.

Ansible

A task list, not a role, because there is nothing to template: the installer probes the host itself. The key comes from the vault, the task is skipped on a host that has a config, and the whitelist task treats an address that is already there as unchanged, which is what the agent reports it as.

yaml
- name: Install the ReportedIP Agent
  ansible.builtin.shell: curl -fsSL https://reportedip.com/agent/install.sh | sh
  args:
    creates: /etc/reportedip-agent/config.yaml
  environment:
    REPORTEDIP_ACCEPT_TERMS: "1"
    REPORTEDIP_KEY: "{{ reportedip_api_key }}"
    REPORTEDIP_MODE: "drop"
    REPORTEDIP_BAN: "true"
    REPORTEDIP_NOTIFY_EMAIL: "ops@example.org"
  no_log: true

- name: Whitelist the management range
  ansible.builtin.command:
    argv: [reportedip-agent, whitelist, add, "{{ management_range }}", "management"]
  register: rip_whitelist
  changed_when: rip_whitelist.rc == 0
  failed_when: rip_whitelist.rc != 0 and "already in" not in (rip_whitelist.stderr ~ rip_whitelist.stdout)

- name: Confirm the host is healthy
  ansible.builtin.command: reportedip-agent status
  changed_when: false

no_log keeps the key out of the Ansible output and its logs; the installer itself keeps it out of the process list. A jump host that is not where you administer from needs --admin-ip, and that is an argument only: there is no REPORTEDIP_ADMIN_IP, and install.sh passes no arguments to the setup. Run the script with REPORTEDIP_NO_SETUP=1 and then reportedip-agent install --admin-ip 203.0.113.0/24 in a second task, with the same environment block.

cloud-init

The same line in runcmd. What differs is where the key lives: user data is readable from inside the instance by every process that can reach the metadata service, so a key placed there is a key every application on the host can read. Where that matters, fetch it at boot from a secret store and pass it on in the environment of the one command.

yaml
#cloud-config
runcmd:
  - [sh, -c, "curl -fsSL https://reportedip.com/agent/install.sh | REPORTEDIP_ACCEPT_TERMS=1 REPORTEDIP_KEY=YOUR_API_KEY REPORTEDIP_NOTIFY_EMAIL=ops@example.org sh"]
  - [reportedip-agent, whitelist, add, "203.0.113.0/24", "management"]
  - [reportedip-agent, sync]

A golden image

Place the binary in the image and nothing else. REPORTEDIP_NO_SETUP=1 downloads, verifies and installs the binary and stops there: no config, no units, no registration. The setup runs once at first boot, on the real host, because two things it writes must not be cloned: the install id, which is how the service tells one server from another, and the auto-whitelist, which carries the address of the session that ran it. An image that carries an install id makes every clone the same server in the registry, and a licence is then counted once for all of them and assigned to none of them reliably.

bash
# In the image build:
curl -fsSL https://reportedip.com/agent/install.sh | REPORTEDIP_ACCEPT_TERMS=1 REPORTEDIP_KEY=YOUR_API_KEY REPORTEDIP_NO_SETUP=1 sh

# At first boot of the clone, once:
REPORTEDIP_ACCEPT_TERMS=1 REPORTEDIP_KEY=YOUR_API_KEY reportedip-agent install --notify-email ops@example.org
reportedip-agent sync

Control panels

reportedip-agent doctor names the panel it finds under /usr/local: ISPConfig, Plesk, cPanel or DirectAdmin. What the installer configures beyond the name depends on whether the per-site log layout of that panel has been measured. Today that is ISPConfig; for the others the panel is named, the standard logs are found, and the per-domain logs are yours to add. It also lists the configured sources with the files they resolve to, and reportedip-agent test <log> shows what a log would produce before it is added.

ISPConfig

Measured on production hosts. The installer finds the per-site logs under /var/log/ispconfig/httpd/*/access.log and error.log as a glob, so a site added later is covered without a change, the panel login log /var/log/ispconfig/auth.log as the panel source, and the mail log, pure-ftpd (which ISPConfig installs as pure-ftpd-mysql) and fail2ban when they run. On one hosting host that came to 196 log files from 62 lines of configuration. The mail, web and ftp lists follow the active services and the ports that listen beyond loopback; ssh and edge are always on.

Plesk

Named by /usr/local/psa. The standard mail log is found; the per-domain web logs are not configured, because their layout was not measured. They usually live under /var/www/vhosts/system/<domain>/logs/ as access_log, plus proxy_access_log where nginx sits in front of Apache. Check with ls, then add one web source with a glob and one web-error source for the error logs. A panel firewall that rewrites the tables when its rules are applied does not remove the agent for good: the next sync run notices that the chain is gone and rebuilds it, and reportedip-agent sync does it now.

yaml
sources:
  - type: sshd
  - type: postfix
    path: /var/log/maillog
  - type: dovecot
    path: /var/log/maillog
  - type: web
    glob: "/var/www/vhosts/system/*/logs/access_log"
  - type: web
    glob: "/var/www/vhosts/system/*/logs/proxy_access_log"
  - type: web-error
    glob: "/var/www/vhosts/system/*/logs/error_log"

cPanel

Named by /usr/local/cpanel. Three things differ from a plain host. The per-domain logs are the domlogs, usually /var/log/apache2/domlogs/ with /usr/local/apache/domlogs pointing there; one web source with a glob covers them. The mail server is Exim and its log is /var/log/exim_mainlog, which is not one of the paths the installer looks for, so the exim source is added by hand. And where csf runs, /var/log/lfd.log is a source of its own, csf, and every block lfd makes is reported through it; Imunify360 has a polling source, marked experimental. Run test on each log first: a domlog with a custom format produces nothing, and that is better learned in a terminal than from an empty counter a week later.

yaml
sources:
  - type: sshd
  - type: web
    glob: "/var/log/apache2/domlogs/*"
    exclude: ["*-ssl_log", "*.bkup*"]
  - type: exim
    path: /var/log/exim_mainlog
  - type: csf
    path: /var/log/lfd.log

DirectAdmin

Named by /usr/local/directadmin. The per-domain logs are usually under /var/log/httpd/domains/, one <domain>.log and one <domain>.error.log each, and Exim writes /var/log/exim/mainlog, which again is not a path the installer tries. Two globs and one path cover the host.

A mail server, a database server

Mail

A host that runs Postfix and Dovecot needs nothing added. The installer sees the services or the listening ports and configures both sources on the one mail log, and the mail list lands on the fixed mail ports 25, 465, 587, 110, 995, 143 and 993. The thresholds are per event source and low on purpose, because a mail server sees few legitimate failures: three SASL failures in an hour, five rejected recipients, three amavis rejections, ten Dovecot failures. A host that runs Exim instead gets the exim source when the log is at one of the Debian or Red Hat paths. What leaves the machine is the same as everywhere: the address, the category ids, one generated sentence, and no line of the log.

Database

A database host that listens on nothing but SSH and the database port gets one source, sshd, and two lists that matter: ssh on the SSH port and edge, which matches every port and therefore also the database port. The agent reads no database log, so it will not see a brute force against the database itself; the answer to that is not on this page, it is a firewall rule that keeps the port off the internet. What the agent adds is that an address the network already knows as an attacker is dropped before its first connection attempt, on every port.

LXC containers and cloud firewalls

Proxmox LXC

The agent needs two things from a container: systemd, which an LXC container with a systemd distribution has, and a firewall backend it can use, which is the open question. Whether nft or ipset can create a set inside the container depends on how the container is privileged, and in an unprivileged container it usually cannot. The installer does not guess: the requirements block names the backend it found, and an install without one goes through as report-only, which doctor says in as many words. Run one sync after the install and read status: a chain that is there is the answer. Where it is not, blocking belongs on the Proxmox host, and the container keeps detecting and reporting.

A VPS behind a cloud firewall

A provider firewall filters before the packet reaches the host. What it drops never appears in a log, so the agent's counters and sets see only what the provider let through; that is not a fault, it is the layer order. Two things are worth doing on a VPS. Put the range you administer from into the whitelist right after the install, because the auto-whitelist holds only the address the session came from. And know the way back before you need it: the provider's console is out of band, it does not pass through the kernel sets, so a banned admin address is one reportedip-agent unban <address> away from the console, and every local ban expires by itself anyway.

Last updated: · Maintained by the ReportedIP team

Security Focused
GDPR Compliant
Made in Germany
Back to Docs