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