Agent ReportedIP pour Linux
L'agent est un unique binaire statique qui remplit deux tâches sur un serveur Linux : il maintient la Community Blacklist dans le pare-feu du noyau et il signale les attaquants qu'il trouve dans les journaux de ce serveur. En option, il bloque aussi lui-même ces découvertes locales. Il remplace le script de synchronisation écrit à la main que documente ce site, et il reprend le travail que faisait fail2ban.
linux/amd64 et linux/arm64. Chaque serveur a besoin d'une licence, une
est incluse dans Professional et trois dans Business, voir
Licences serveur. Les licences s'ajoutent et se retirent sous
Agent Servers dans votre
compte.
Ce qu'il fait
La Community Blacklist dans le noyau
Cinq listes, une par service exposé (ssh, mail, web, ftp, edge), récupérées aussi souvent que votre forfait le permet et basculées de façon atomique dans un ipset ou un ensemble nftables. Une liste revenue vide ou d'une longueur invraisemblable n'est jamais appliquée : un téléchargement raté laisse donc l'ensemble précédent en place au lieu d'ouvrir la porte.
Attaques locales reconnues et signalées
L'agent suit les journaux que le serveur écrit déjà, compte les occurrences par adresse face à un seuil propre à cette source, et signale l'adresse dès que le seuil est atteint. Aucune ligne de journal, aucun nom d'utilisateur et aucune URL ne quitte la machine.
Découvertes locales bloquées, si vous le souhaitez
Désactivé après l'installation. Une fois activé, une adresse que l'agent a lui-même attrapée est bannie dans un ensemble du noyau avec un délai d'expiration, et un récidiviste est banni plus longtemps chaque fois.
Prérequis
Quatre choses, et seule la dernière doit venir de nous.
- Un noyau Linux avec un filtre de paquets accessible en écriture. Soit
ipsetaveciptables, soitnftables. Sur un hôte Debian ou Ubuntu, c'estapt install ipset iptablesouapt install nftables; dans la famille RHEL,dnf install ipset iptablesoudnf install nftables. L'agent utilise aussiip6tablesquand il est présent ; sans cet outil, les bannissements IPv6 sont enregistrés mais pas appliqués. - systemd pour la minuterie et le service de surveillance. Sur un hôte sans systemd, l'agent fonctionne quand même, avec la synchronisation depuis cron et le démon depuis votre propre système d'init, et la section ci-dessous explique comment.
- Root. Il écrit des règles de pare-feu et lit des journaux qui ne sont pas lisibles par tous, et les deux l'exigent. Il n'existe pas de mode réduit pour un utilisateur ordinaire.
- Une clé API depuis votre compte. La clé sert deux fois pendant l'installation, une fois pour télécharger la version sous licence et une fois pour enregistrer l'hôte. Une clé par hôte est la recommandation ; plusieurs hôtes sur une même clé sont autorisés et ne coûtent rien de plus, car un hôte est identifié par son identifiant d'installation et non par la clé.
Rien d'autre. Le binaire est lié statiquement et compilé sans cgo, il n'apporte donc ni interpréteur,
ni bibliothèque, ni dépôt de paquets, ni utilitaire cron qui lui soit propre. Pour le téléchargement
et la somme de contrôle, install.sh a besoin de curl ou wget
ainsi que de sha256sum, et sans l'outil de vérification il n'installe rien. L'agent a
été développé et mesuré sur Debian 12 (bookworm) en arm64 avec ISPConfig, nginx, Postfix, pure-ftpd
et bind.
ipset ou nftables, et ce que décide auto
Les deux moteurs font le même travail, et sur un hôte neuf aucun n'est meilleur. Ce qui compte, c'est celui que l'hôte utilise déjà, car deux filtres de paquets qui écrivent la même chaîne INPUT sont la façon dont un après-midi disparaît.
backend | Ce qui se passe |
|---|---|
auto (défaut) | ipset est préféré quand ipset et iptables sont tous deux présents, sinon nft est utilisé. Cette préférence existe parce que CSF et le guide de pare-feu publié emploient cette chaîne d'outils : un hôte avec des règles existantes les conserve ainsi. |
ipset | Ensembles dans ipset, règles dans la chaîne rip-blacklist d'iptables et ip6tables. Si l'un des outils manque, l'agent s'arrête avec un message clair plutôt que de se rabattre ailleurs. |
nftables | Ensembles et règles dans la table native inet reportedip. S'arrête si nft manque. |
Aucune vérification de version n'intervient dans ce choix. Seul compte un binaire réellement présent,
car iptables --version sur un hôte doté du moteur nft répond quelque chose qui paraît
juste et ne signifie rien. Après la première synchronisation, le moteur retenu est consigné, et un
changement ultérieur exige reportedip-agent sync --migrate-backend au lieu de construire
en silence un second jeu de règles.
Vérifier l'hôte d'abord
doctor est la commande à lancer avant l'installation. Elle ne change rien du
tout : aucun fichier, aucun répertoire, aucune règle, aucun ensemble. Tout ce qu'elle rapporte, elle
l'a lu.
reportedip-agent doctor
Elle répond à six questions, dans cet ordre :
- system : la distribution telle que son propre
os-releasela nomme, le noyau, la plateforme, si systemd tourne vraiment (lu dans/run/systemd/system, et non déduit de la présence desystemctl), le fuseau horaire dans lequel sont lus les journaux sans décalage, et l'espace libre là où se trouvera le répertoire d'état. - ssh : quelle unité fait tourner sshd, et le port. Le cas intéressant est
ssh.socket: avec l'activation par socket,sshd -Trépond encore 22 alors que le vrai port est dans l'unité socket. Le port est alors pris sur le processus à l'écoute. doctor indique lequel des deux chemins a servi, car celui qui voit un mauvais port dans la configuration regarde ici en premier. - firewall : où se trouvent
ipset,iptables,ip6tablesetnft, quel moteur en découle, et si l'ensemble des bannissements locaux porte un délai d'expiration par entrée. Ce dernier point décide si le noyau fait expirer un bannissement tout seul, et c'est précisément ce qui évite qu'un agent planté laisse un blocage permanent. - panel : ISPConfig, Plesk, cPanel ou DirectAdmin. Seule la disposition des journaux par site d'ISPConfig est connue de cette version. Les autres sont nommés plutôt que devinés, car un motif erroné ferait lire à l'agent des compteurs d'octets au lieu de journaux d'accès.
- sources : chaque source configurée, résolue en fichiers réels, avec leur nombre et le nombre de fichiers lisibles, le plus grand d'entre eux avec une estimation du nombre de lignes, et une note sur les fichiers présents mais vides. Sur le plus gros hôte de panel mesuré, le motif web se résout en 196 fichiers, et qui installe l'agent là devrait le savoir avant plutôt qu'après.
- mail : si une alerte pourrait réellement quitter cet hôte. Sur dix-huit hôtes
mesurés, quatorze pouvaient livrer du courrier, deux avaient
sendmailavec un MTA arrêté, et deux n'avaient aucun MTA. Sans cette section, quelqu'un attend un courrier qui n'arrive jamais.
Lire le verdict
Le dernier bloc s'appelle verdict et correspond au code de sortie : une sonde de
supervision n'a donc aucune sortie à analyser.
| Sortie | Verdict | Signification |
|---|---|---|
0 | tout ce dont l'agent a besoin est là | Installer. |
1 | fonctionne sur cet hôte, avec les limites indiquées | Chaque limite est nommée dans la sortie. Un moteur de pare-feu absent signifie que cet hôte signale et ne bloque pas, et c'est une installation valide. Une source qui ne se résout sur rien mérite d'être corrigée d'abord. |
2 | ne peut pas fonctionner sur cet hôte | Pas un hôte Linux, ou un hôte qui ne peut ni bloquer ni lire un seul journal. Corrigez la cause avant d'installer. |
Relancez doctor après l'installation. Avec une configuration en place, il ne devine plus
et rapporte les sources configurées au lieu des sources détectées, et il ajoute la dérive d'horloge
par source mesurée par le démon. Une source dont le journal horodate chaque ligne à plus d'une minute
de l'horloge système a une fenêtre de comptage qui ne se remplit jamais, et c'est le seul défaut qui
ressemble exactement à « le détecteur ne marche pas ».
Installation
Une commande, en root. Elle lit la version actuelle dans les métadonnées publiques, télécharge avec
votre clé la version compilée pour votre architecture, la vérifie contre SHA256SUMS,
l'installe dans /usr/local/bin/reportedip-agent puis configure l'hôte.
curl -fsSL https://reportedip.com/agent/install.sh | REPORTEDIP_KEY=<YOUR-KEY> sh
La clé passe par l'environnement parce qu'elle sert deux fois, une fois pour le téléchargement sous
licence et une fois pour enregistrer l'hôte, et la coller deux fois est la façon dont une clé finit
sous une forme erronée dans un historique de shell. Le script est volontairement du
sh POSIX : il doit tourner sur des hôtes où bash n'est pas le shell par défaut. Il
s'arrête avec une raison nommée et n'installe rien s'il n'est pas lancé en root, si la clé manque, si
la plateforme n'est pas Linux, si l'architecture n'est ni amd64 ni arm64, si ni curl ni
wget n'existe, et si sha256sum manque.
Les deux voies de téléchargement sont volontairement séparées. Les métadonnées de version sont récupérées sans la clé, les fichiers sous licence avec la clé, de sorte que la clé ne part jamais vers une cible de redirection qui ne sert que des métadonnées publiques. Dans le fichier de sommes, seule la ligne de votre architecture est vérifiée, car le fichier liste aussi l'autre.
Ce que fait vraiment l'installateur
Tout ce qui suit est idempotent. La même commande sur un hôte qui possède déjà l'agent met à jour le binaire et laisse la configuration exactement telle qu'elle était.
- Lit
https://reportedip.com/agent/latestsans la clé et en extrait la version. Ce fichier est le même JSON que l'agent interroge lui-même pour les mises à jour. - Télécharge
reportedip-agent_linux_<arch>etSHA256SUMSavec l'en-têteX-Keydans un répertoire temporaire supprimé sur chaque chemin de sortie. - Vérifie la somme de contrôle et n'installe rien en cas d'écart.
- Installe le binaire en mode 0755 dans
/usr/local/bin/reportedip-agentet affiche la version qu'il vient de poser. - Redémarre
reportedip-agent.servicesi cette unité est active. C'est important lors d'une mise à jour : le démon de surveillance continuerait sinon à exécuter l'ancien code jusqu'au prochain redémarrage de la machine. L'unité de synchronisation est un oneshot et prend le nouveau binaire d'elle-même au démarrage suivant. - Exécute la configuration de l'hôte, sauf si
REPORTEDIP_NO_SETUP=1est défini ou si/etc/reportedip-agent/config.yamlexiste déjà. Dans le second cas, il le dit et laisse la configuration tranquille.
La configuration de l'hôte est l'étape qui écrit des fichiers, et c'est la même chose que
reportedip-agent install --key. Dans l'ordre :
- Crée
/etc/reportedip-agentet/var/lib/reportedip-agent, le second en mode 0700, et corrige le mode d'un répertoire déjà présent. - Détecte le port SSH depuis
sshd -T, depuisSSH_CONNECTIONet depuis les processus sshd à l'écoute, et prend l'union des trois. S'il ne trouve rien, il s'arrête et demande--ssh-portplutôt que de supposer 22. - Décide lesquelles des cinq listes du flux sont configurées.
sshetedgesont toujours actives.mail,webetftps'ajoutent quand une unité correspondante est active ou qu'un port correspondant écoute : un hôte sans serveur web n'obtient donc pas de liste web. - Détecte les sources de journaux et les écrit explicitement dans la configuration. La détection a lieu une fois, à l'installation, et le résultat reste dans le fichier : un service arrêté pour une maintenance ne doit jamais désactiver une source en silence.
- Écrit
/etc/reportedip-agent/config.yamlen mode 0600 avecmode: logetban.enabled: false. Une configuration existante n'est jamais écrasée, et--keyest alors ignorée avec une note invitant à modifierapi_keydans le fichier. - Écrit l'auto-liste blanche dans le répertoire d'état : l'adresse d'où venait votre session SSH, plus chaque adresse d'interface de l'hôte. L'adresse de session d'une exécution antérieure est conservée, afin qu'une seconde exécution depuis une autre session ne retire pas l'adresse sur laquelle la première comptait. Les adresses d'interface sont redéterminées à chaque exécution, pour qu'une adresse supprimée ne traîne pas.
- Génère l'identifiant d'installation, un UUID v4 aléatoire dans
/var/lib/reportedip-agent/install-id. C'est désormais l'identité de ce serveur face à l'API. Le nom d'hôte n'est jamais utilisé ni transmis. - Avertit de deux choses qu'il ne peut pas corriger pour vous : une table nftables
inet reportediplaissée par le script shell documenté alors que le moteur retenu est ipset, et un nginx qui lit les en-têtes Cloudflare sansset_real_ip_from. La source web compterait alors les adresses de Cloudflare au lieu de celles du visiteur. - Écrit les trois unités systemd dans
/etc/systemd/system, exécutedaemon-reload, activereportedip-agent-sync.service, puis active et démarrereportedip-agent-sync.timeretreportedip-agent.service. - Appelle
verify-keyavec la configuration fraîche et affiche votre rôle ainsi que les signalements consommés aujourd'hui face à la limite quotidienne. Un 401 ou un 403 ici signifie que la clé est fausse, et c'est le seul défaut à corriger tout de suite.
INPUT. Tout cela est créé par le premier
reportedip-agent sync, et c'est pourquoi l'installateur termine en vous disant de le
lancer. Jusque-là, l'hôte est exactement comme il était, et une installation contre laquelle vous
vous décidez coûte un rm de deux répertoires.
Les variables d'environnement de install.sh
| Variable | Défaut | À quoi elle sert |
|---|---|---|
REPORTEDIP_KEY | aucune, obligatoire | Votre clé API. Sans elle, le script s'arrête avant de télécharger quoi que ce soit. |
REPORTEDIP_VERSION | la version courante | Fixe une version, pour un déploiement progressif ou pour réinstaller une version précise. Les anciens répertoires de version restent sur le point de distribution exactement pour cela. |
REPORTEDIP_PREFIX | /usr/local/bin | Où va le binaire. Si vous le changez, les unités systemd doivent suivre, car elles nomment le chemin absolu. |
REPORTEDIP_BASE | https://reportedip.com/agent | La base de téléchargement. Pour un miroir interne qui sert latest, SHA256SUMS, SHA256SUMS.sig et les deux binaires. La vérification de signature s'y applique de la même façon. |
REPORTEDIP_NO_SETUP | non définie | 1 pose le binaire et s'arrête. Rien n'est détecté, aucune configuration n'est écrite et aucune unité n'est installée. C'est la variable d'une image de référence, d'une couche de conteneur ou d'une exécution de gestion de configuration qui possède elle-même le fichier de configuration. |
# Binary only, no host setup: for an image or for Ansible
curl -fsSL https://reportedip.com/agent/install.sh \
| REPORTEDIP_KEY=<YOUR-KEY> REPORTEDIP_NO_SETUP=1 sh
# Later, on the running host, or from your playbook:
reportedip-agent install --key "<YOUR-KEY>"
Installation à la main
L'étape de configuration est une commande à part et accepte trois options. Lancez-la quand
REPORTEDIP_NO_SETUP=1 n'a posé que le binaire, ou quand la détection a deviné quelque
chose que vous voulez corriger.
reportedip-agent install --key "<YOUR-KEY>"
# The detection could not find the SSH port (socket activation,
# a non-standard unit, a port from an include file):
reportedip-agent install --key "<YOUR-KEY>" --ssh-port 2222
# The session address is not where you administer this host from
# (a jump host, a console, a serial connection):
reportedip-agent install --key "<YOUR-KEY>" --admin-ip 203.0.113.0/24
--admin-ip accepte une adresse unique ou une plage CIDR et remplace entièrement la
supposition. Cela vaut la peine dès que la session SSH n'est pas représentative, car l'adresse devinée
est ce qui se dresse entre vous et votre propre règle de blocage au premier jour. Toute l'exécution
d'installation dispose d'un délai de cinq minutes : un nft ou un systemctl
figé se termine donc par une erreur et non par un processus que vous devez interrompre.
Un hôte sans systemd
Rien dans l'agent ne dépend de systemd. Les unités sont un confort, et doctor le dit
plutôt que d'échouer quand elles ne sont pas utilisables. Deux choses doivent être mises en place à la
main sur un tel hôte.
# 1. The feed sync, from cron. Every 15 minutes is safe on any
# plan: the agent skips a fetch its own plan interval does
# not allow yet, so nothing is requested too often.
*/15 * * * * root /usr/local/bin/reportedip-agent sync >/dev/null 2>&1
@reboot root sleep 120 && /usr/local/bin/reportedip-agent sync
# 2. The watch daemon, from your init system. It runs in the
# foreground, logs to stderr and stops on SIGTERM.
/usr/local/bin/reportedip-agent watch
L'entrée pour le redémarrage n'est pas optionnelle. Les délais d'expiration d'ipset ne survivent pas à
un redémarrage, et les ensembles eux-mêmes non plus : sans synchronisation après le démarrage, l'hôte
remonte sans règles et sans bannissements restaurés. Sur un hôte systemd, ce chemin est couvert par
reportedip-agent-sync.service, activé pour multi-user.target, qui tourne
après chaque service de pare-feu et sans décalage aléatoire. Sur un hôte sans journald, laissez
log_file défini, car stderr n'y mène peut-être nulle part.
La première heure, dans l'ordre
L'ordre ci-dessous n'est pas une suggestion. Chaque étape répond à une question dont dépend la suivante, et un autre ordre est la façon dont une installation qui fonctionne a l'air cassée.
reportedip-agent doctor. Avant tout le reste, parce que c'est la seule commande qui ne change rien. Ce qu'il nomme comme limite est une limite que vous redécouvrirez sinon comme symptôme deux jours plus tard.reportedip-agent sync. La première exécution construit la chaîne, crée les ensembles et télécharge les cinq listes. Cela prend un moment : les listes sont récupérées l'une après l'autre avec une courte pause entre elles.reportedip-agent status. Il y a maintenant quelque chose à regarder. Vérifiez que chaque liste configurée a un nombre d'adresses IPv4 plausible, que la chaîne indiqueok, que la liste blanche contient les adresses attendues, et queaccountmontre votre rôle et l'état de licence de cet hôte.- Corriger la liste blanche. L'installateur y a mis l'adresse d'où venait votre session SSH. C'est une supposition. Remplacez-la par la plage depuis laquelle vous administrez réellement, et ajoutez au passage votre supervision, votre hôte de sauvegarde et la plage de votre bureau.
- Attendre, puis relire
status. Un jour suffit, deux valent mieux. Enmode: log, les règles sont entièrement construites et elles s'appliquent, elles journalisent seulement au lieu de rejeter. Le journal du noyau vous montre donc exactement ce quedropaurait coupé. - Passer à
mode: droppuis lancersync, qui reconstruit les règles. - Seulement ensuite, envisager
ban.enabled: trueet redémarrer ensuite le service de surveillance. C'est le second interrupteur, et il concerne vos propres journaux plutôt que la liste communautaire.
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
mode: log, et c'est voulu. Rien n'est
rejeté avant que vous ne le décidiez, ce qui vous laisse deux jours pour remarquer la plage qui
manque encore dans la liste blanche. La liste blanche d'abord, drop ensuite :
l'ordre inverse marche aussi, jusqu'à la seule fois où il ne marche pas.
Configuration
Un fichier, /etc/reportedip-agent/config.yaml, appartenant à root et en mode 0600.
L'agent refuse de démarrer quand il est lisible par le groupe ou par les autres, et il le dit avec la
commande chmod qui corrige, car ce fichier contient votre clé API. L'état vit sous
/var/lib/reportedip-agent : positions de lecture, file d'attente des signalements,
mémoire de déduplication, registre des bannissements, état de santé et état de mise à jour.
Le fichier écrit par l'installateur convient déjà à l'hôte. La plupart des installations changent deux
choses dans leur première semaine, la valeur de mode et le fait que le blocage local soit
actif ou non, et plus rien ensuite.
window_minutes qui serait ignorée sans bruit vous laisserait croire à un seuil que
vous n'avez pas. Toutes les clés valides sont listées ci-dessous, et une clé qui n'y figure pas
n'existe pas. Vérifiez le fichier après chaque modification avant de compter sur lui :
reportedip-agent status se termine avec le code 2 et nomme la clé quand le fichier
n'est pas lisible.
Chaque clé de config.yaml
Rien ici n'est obligatoire sauf api_key. Chaque autre clé a la valeur par défaut
indiquée, et une clé que vous omettez conserve cette valeur.
| Clé | Défaut | Effet |
|---|---|---|
api_key | aucune | Votre clé API. La seule clé sans valeur par défaut. REPLACE_ME compte comme absente. |
api_url | https://reportedip.com/wp-json/reportedip/v2 | La base REST. Doit être une URL https absolue ; le http simple n'est admis que sur la boucle locale, pour un proxy local. |
update_url | déduite de api_url | D'où viennent les binaires de l'agent. Sans indication, ce sont le schéma et l'hôte de api_url avec le chemin /agent. À définir pour un miroir, qui doit lui aussi être en https. |
auto_update | true | Chercher une version plus récente pendant la synchronisation et l'installer. À passer à false quand la version de cet hôte est décidée ailleurs, par exemple par votre propre paquet ou par un déploiement canari. reportedip-agent update continue de fonctionner à la main, et status continue d'indiquer le retard de l'hôte. |
log_level | info | info ou debug. Rien d'autre. |
log_file | /var/log/reportedip-agent.log | Le journal propre de l'agent, en plus de stderr, pour que journald garde tout ce qu'il a aujourd'hui. Vide désactive le fichier. Seules la synchronisation et le démon de surveillance y écrivent. |
log_max_mb | 10 | Taille à laquelle l'agent effectue sa rotation, vérifiée à chaque écriture. Plage 1 à 1024. |
log_keep | 3 | Générations conservées, plage 0 à 20. Avec les valeurs par défaut, le pire cas sur le disque est de 40 Mo. Aucun fragment logrotate n'est nécessaire, et aucun ne gêne non plus. |
backend | auto | auto, ipset ou nftables. Voir plus haut. |
mode | log | log, drop ou off. S'applique aux règles, donc aux listes du flux et aux bannissements locaux. |
confidence | 90 | Le seuil du flux, de 1 à 100. Plus bas signifie une liste plus longue et davantage de cas limites. Le serveur annonce un plancher pour votre forfait, 75 à partir de Professional et 90 sur Contributor, et la plus haute des deux valeurs l'emporte. |
limit | 50000 | Nombre maximal d'adresses par requête de liste, de 1 à 50000. |
lists | ssh, edge | Quelles listes du flux sont actives. ssh et edge sont toujours actives et ne peuvent pas être désactivées. lists.ssh.ports est la seule option par liste ; les autres listes ont des jeux de ports fixes. |
ports | depuis le vrai processus à l'écoute | Sous lists.ssh : les ports sur lesquels s'applique la règle ssh. L'installateur les écrit d'après le processus détecté. Une liste vide fait s'appliquer la règle à tous les ports. |
min_entries | ssh 1000, mail 500, web 500, ftp 500, edge 1000 | Le plancher de plausibilité par liste : en dessous de ce nombre d'adresses, la liste n'est pas basculée. Au moins 1, car un minimum de zéro laisserait passer une liste vide en production. |
whitelist_file | /etc/reportedip-agent/whitelist.conf | Votre liste « jamais bloquer, jamais signaler ». Une adresse ou un CIDR par ligne, # commence un commentaire. Une ligne cassée est ignorée avec un avertissement et ne désactive jamais la protection. |
sources | une entrée sshd | Les sources de journaux. Un fichier avec une clé sources décide seul : une entrée de votre part doit donc se placer à côté de celles de l'installateur et non à leur place. |
type | aucun | Dans une source : l'un des treize types de source. Un type inconnu est une erreur qui énumère les types valides. |
path | aucun | Dans une source : un fichier de journal. Définissez path ou glob, jamais les deux. Sans aucun des deux, l'agent choisit le canal lui-même, journald ou le fichier par défaut de la distribution. |
glob | aucun | Dans une source : un motif sur plusieurs fichiers, réévalué toutes les dix minutes. Au plus cinq segments avec joker. |
exclude | aucun | Dans une source : des motifs qui retirent des fichiers attrapés par un motif. Motifs de type shell, pas d'expressions régulières. Un motif sans barre oblique correspond au seul nom de fichier ; avec une barre oblique il doit couvrir le chemin complet. Chaque motif qui ne correspond à rien est signalé au démarrage. |
poll_minutes | 5 | Dans une source : uniquement pour imunify360, qui interroge une commande au lieu de lire un fichier. Plage 1 à 60. Sur tout autre type, c'est une erreur. |
thresholds | vide | Remplace la table interne par source d'événement. Voir la section suivante. |
hits, window_minutes | voir la table ci-dessous | Dans une entrée thresholds : le couple qui signifie « autant d'occurrences en autant de minutes ». |
min_hits, window_minutes | 5, 10 | Le couple global pour chaque source sans entrée propre dans la table interne. |
web_min_hits, web_window_minutes | 50, 120 | Le même couple pour les journaux d'accès web, qui ont besoin de volume plutôt que d'un code de statut. |
dedup_hours | 6 | La même adresse est signalée au plus une fois par fenêtre de cette durée. |
queue_max | 5000 | Fichiers de signalement en attente. Au-delà, les plus anciens sont supprimés. |
disk_min_mb | 200 | Mébioctets libres exigés là où se trouve le répertoire d'état. En dessous, aucun nouveau signalement n'est mis en file tandis que la détection et le blocage continuent. 0 désactive la limite. |
jail_categories | table interne | Correspondance supplémentaire entre un nom de jail fail2ban et des identifiants de Threat Categories, de 1 à 58. Pertinent seulement tant que fail2ban est encore une source. |
ban | désactivé | Le bloc des bannissements locaux, avec enabled, time_minutes, max_time_minutes, escalate et memory_hours. Voir Blocage. |
notify | aucun courrier | Le bloc courrier, avec email, cooldown_hours et le sous-bloc smtp (host, port, from, user, password, starttls). |
Un fichier complet, avec chaque bloc utilisé par un hôte normal :
# /etc/reportedip-agent/config.yaml root:root 0600
api_key: "YOUR_API_KEY"
api_url: https://reportedip.com/wp-json/reportedip/v2
log_level: info # info | debug
log_file: /var/log/reportedip-agent.log
log_max_mb: 10
log_keep: 3
auto_update: true
backend: auto # auto | ipset | nftables
mode: log # log | drop | off
confidence: 90 # the feed threshold
limit: 50000
lists: # ssh and edge are always on
ssh:
ports: [22] # written by install from the real listener
edge: {}
mail: {}
web: {}
min_entries: # below this many addresses a list is not applied
ssh: 1000
mail: 500
web: 500
ftp: 500
edge: 1000
whitelist_file: /etc/reportedip-agent/whitelist.conf
min_hits: 5 # the global detection pair
window_minutes: 10
web_min_hits: 50 # web access logs use their own
web_window_minutes: 120
dedup_hours: 6
queue_max: 5000
disk_min_mb: 200
sources: # detected at install time
- type: sshd
- type: web
glob: /var/www/*/log/access.log
- type: web-error
path: /var/log/nginx/error.log
- type: postfix
path: /var/log/mail.log
- type: dovecot
path: /var/log/mail.log
thresholds: {} # per source overrides, see below
ban: # block local finds, not only the feed
enabled: false
time_minutes: 30
max_time_minutes: 10080
escalate: 4
memory_hours: 24
notify:
email: "" # empty means no mail
cooldown_hours: 24
Limites de détection, par source
Un seuil est un couple : combien d'occurrences, en combien de minutes. C'est la même idée que
maxretry et findtime d'un jail fail2ban, et les valeurs ci-dessous
proviennent de la configuration de jails de l'hôte sur lequel tout cela a été mesuré. Désactiver
fail2ban ne change donc pas la sensibilité d'un service.
Trois niveaux décident du couple qui s'applique à une source d'événement, et le premier qui a une
réponse gagne : une entrée explicite sous thresholds, puis la table interne, puis le
couple global.
| Source d'événement | Seuil | Origine |
|---|---|---|
sshd | 5 en 10 min | min_hits / window_minutes |
web-error | 5 en 10 min | min_hits / window_minutes |
modsec | 5 en 10 min | min_hits / window_minutes |
exim | 5 en 10 min | min_hits / window_minutes |
web | 50 en 120 min | web_min_hits / web_window_minutes |
web-app | 20 en 60 min | interne |
postfix-sasl | 3 en 60 min | interne, mesuré |
postfix-reject | 5 en 60 min | interne, rejets 5xx seulement |
postfix-amavis | 3 en 60 min | interne |
dovecot | 10 en 60 min | interne, mesuré |
ftp | 20 en 60 min | interne |
named | 20 en 30 min | interne |
panel | 5 en 60 min | interne |
fail2ban, csf, imunify360 | aucun seuil | Cette source a déjà compté : une ligne est donc un signalement. |
Pour changer une source et rien d'autre, nommez-la sous thresholds. La clé est la source
d'événement de la table ci-dessus, pas le type de source de sources, et un nom qui ne
figure pas dans cette liste est une erreur plutôt qu'un réglage qui ne s'applique jamais. Les deux
nombres doivent valoir au moins 1.
# A recursive resolver that sees a lot of refused queries:
# fewer minutes, more hits, and nothing else changes.
thresholds:
named:
hits: 40
window_minutes: 5
# A mail server whose customers keep mistyping passwords:
# SASL a little more forgiving, Dovecot untouched.
thresholds:
postfix-sasl:
hits: 6
window_minutes: 60
Un seuil plus haut rend l'agent plus discret et laisse passer les attaquants lents. Un seuil plus bas est la direction qui coûte quelque chose : en dessous d'environ trois occurrences par heure sur une source mail ou web, vous finirez par signaler un client dont le mot de passe est faux dans son logiciel de messagerie, et un signalement est public au sein de la communauté. Le couple web est haut exactement pour cette raison, car un WordPress ordinaire répond à un mot de passe faux par un HTTP 200 et le détecteur ne peut pas s'appuyer sur le code de statut.
Une autre grandeur n'est pas configurable. Une adresse avec un historique de bannissements a besoin de moins d'occurrences qu'une adresse inconnue, et c'est la même pondération que celle du jail recidive : un bannissement antérieur compte une ligne double, deux la comptent triple, puis cinq et neuf fois. Le poids est ramené au seuil de la source d'événement concernée : un dixième bannissement ne transforme donc pas une seule ligne en signalement.
Limites de bannissement
Quatre valeurs, toutes dans le bloc ban, toutes dans l'unité nommée par la clé. Elles ne
concernent que les adresses que l'agent a trouvées dans vos propres journaux. La liste communautaire
n'a aucune durée de bannissement, elle est intégralement remplacée à chaque passage.
| Clé | Défaut | Signification, et ce qui arrive aux extrêmes |
|---|---|---|
enabled | false | Si les découvertes locales sont bloquées du tout. false est un hôte qui signale et ne bloque pas. Un ban add manuel fonctionne dans les deux cas. |
time_minutes | 30 | Le premier bannissement d'une adresse. Au moins 1. Des valeurs très courtes font de l'escalade le seul élément qui compte ; des valeurs très longues signifient qu'un faux positif reste longtemps dans l'ensemble. |
max_time_minutes | 10080 (7 jours) | Le plafond de l'escalade. Ne doit pas être inférieur à time_minutes, sinon le plafond raccourcirait le premier bannissement, et l'agent refuse un tel fichier. L'abaisser plus tard raccourcit aussi les bannissements en cours : la synchronisation suivante les restaure ramenés au plafond du jour. |
escalate | 4 | Le multiplicateur par récidive dans la fenêtre de mémoire. Au moins 1, et 1 signifie que chaque bannissement dure time_minutes et qu'il n'y a aucune escalade. |
memory_hours | 24 | Combien de temps un bannissement compte pour l'escalade suivante. Au moins 1. Un enregistrement reste en plus pour le triple de la durée de son dernier bannissement quand c'est plus long, et c'est précisément ce qui rend le plafond atteignable sans que des bannissements courts fassent grossir le fichier. |
# Careful: a short first ban, a low cap, a long memory.
ban:
enabled: true
time_minutes: 10
max_time_minutes: 1440 # one day
escalate: 3
memory_hours: 72
# Strict: a long first ban and a hard escalation.
ban:
enabled: true
time_minutes: 60
max_time_minutes: 43200 # thirty days
escalate: 6
memory_hours: 168 # a week
Limites de fonctionnement
Ces valeurs limitent ce que l'agent consomme lui-même. Ce ne sont pas des réglages de détection, et les deux que vous voudrez vraiment modifier un jour sont la taille de la file d'attente et la limite de disque.
| Clé | Défaut | Unité | Trop bas | Trop haut |
|---|---|---|---|---|
queue_max | 5000 | fichiers en attente | Des signalements sont perdus pendant une panne de l'API, les plus anciens d'abord. | Une longue panne laisse des milliers de petits fichiers à envoyer ensuite. |
dedup_hours | 6 | heures | Le même attaquant est signalé encore et encore et mange votre quota quotidien. | Une adresse qui attaque de nouveau la semaine prochaine est signalée tard. |
disk_min_mb | 200 | Mo libres | L'agent peut contribuer à remplir /var, ce qui emporte l'hôte entier. | Les signalements s'arrêtent sur un hôte parfaitement sain. 0 supprime la limite. |
log_max_mb | 10 | Mo | Une rotation à chaque deuxième écriture et un historique trop court pour enquêter. | Avec log_keep, c'est le pire cas sur le disque. |
log_keep | 3 | générations | 0 ne conserve aucun fichier rotaté. | Au maximum 20, et le coût disque est le produit des deux valeurs. |
cooldown_hours | 24 | heures | Au moins 1, et 1 signifie un courrier par synchronisation pour un état qui reste ouvert. | Un problème survenu ce matin est signalé demain. |
poll_minutes | 5 | minutes | Une commande lancée trop souvent sur un hôte chargé. | Au maximum 60, et les découvertes arrivent d'autant plus tard. |
limit | 50000 | adresses par liste | Une liste tronquée : les pires adresses sont là, la suite non. | 50000 est le maximum servi par l'API. |
min_entries | 1000 / 500 | adresses | Une liste d'une brièveté invraisemblable passe en production et l'hôte est moins protégé qu'il ne le croit. | Une liste réellement petite est refusée durablement, et l'ensemble reste à sa génération précédente. |
Sous la limite de disque, l'agent ne met plus de nouveau signalement en file, consigne l'état et
l'envoie par courrier, et continue de détecter et de bloquer. Cet ordre a une raison : un
/var rempli est pire qu'un signalement perdu, et protéger un serveur en le cassant n'est
pas une protection.
Un courrier quand quelque chose ne va pas
Un notify.email vide est la valeur par défaut et signifie que cet hôte n'envoie rien. Ce
qui est signalé est un état et non un événement : un flux qui échoue chaque heure est donc un message
et non vingt-quatre. L'agent suit huit états (feed, api_key, chain, disk, source, update, queue,
license), retient quand chacun a été signalé pour la dernière fois afin qu'un redémarrage ne
recommence pas depuis le début, et envoie une levée d'alerte quand un état est terminé.
# A host with a local MTA: postfix, exim or msmtp. The agent
# uses "sendmail -t -i" and no password lives in a file.
notify:
email: "ops@example.org"
cooldown_hours: 24
# A host without an MTA: SMTP, and starttls stays on.
notify:
email: "ops@example.org"
cooldown_hours: 12
smtp:
host: mail.example.org
port: 587
from: agent@example.org
user: agent@example.org
password: "..."
starttls: true
Avec starttls: true, le courrier est refusé plutôt qu'envoyé en clair si le serveur
n'offre pas STARTTLS. Un courrier porte le nom de l'état, la version, une phrase générique avec des
nombres et la commande pour aller voir. Jamais une ligne de journal, jamais une adresse, jamais une
charge utile. Lancez reportedip-agent doctor après cette configuration : la section
mail dit si un courrier pourrait réellement quitter l'hôte, et c'est une autre question
que celle de savoir si la configuration se relit.
Quand une modification prend effet
L'agent lit sa configuration au démarrage et ne surveille pas le fichier, et il n'existe pas de signal de rechargement. La commande nécessaire dépend de la partie que vous avez touchée.
| Modifié | Ce qui le rend effectif |
|---|---|
mode, lists, min_entries, confidence, limit, backend | reportedip-agent sync. La synchronisation construit la chaîne et les règles : un nouveau mode est donc actif à sa fin. |
thresholds, min_hits, window_minutes, web_min_hits, web_window_minutes, dedup_hours, ban, notify, log_* | systemctl restart reportedip-agent.service. C'est ce que lit le démon de surveillance. |
Une source ajoutée à sources | systemctl restart reportedip-agent.service. |
Une source retirée de sources | Un arrêt et un démarrage, pas un redémarrage. Sur un hôte en production, un systemctl restart n'a pas suffi et le démon a continué de lire le fichier retiré : systemctl stop reportedip-agent.service && systemctl start reportedip-agent.service. |
Le contenu de whitelist_file | Rien, si vous utilisez reportedip-agent whitelist add : cela écrit le fichier et l'ensemble du noyau en une étape et prend effet aussitôt. Modifier le fichier à la main demande un sync. |
api_key, api_url, auto_update, update_url | Rien pour le prochain sync, qui est un oneshot et relit le fichier. Redémarrez le service de surveillance pour que le chemin de signalement reprenne aussi la clé. |
# The safe sequence after any edit
reportedip-agent status >/dev/null && echo "config parses"
reportedip-agent sync
systemctl restart reportedip-agent.service
systemctl status reportedip-agent.service --no-pager
Le flux communautaire
C'est la partie que la page Blocage au niveau du réseau décrit sous forme de script shell. L'agent fait la même chose, avec les parties qu'on rate facilement en l'écrivant soi-même déjà réglées : une requête conditionnelle par liste pour qu'une liste inchangée ne coûte rien, une vérification de taille avant que quoi que ce soit ne passe en production, une bascule atomique pour qu'il n'y ait jamais de fenêtre avec un ensemble vide, et des règles liées aux ports pour qu'un attaquant web n'enferme personne hors de SSH.
La correspondance des catégories est la même que sur cette page. Lesquelles des cinq listes sont actives dépend de ce qui écoute réellement, et il n'existe pas de liste unifiée, car un ensemble par service est ce qui rend la règle liée aux ports possible.
| Liste | Ensemble du noyau | Ports concernés par la règle |
|---|---|---|
ssh | rip-ssh, rip-ssh-v6 | depuis lists.ssh.ports |
mail | rip-mail, rip-mail-v6 | 25, 465, 587, 110, 995, 143, 993 |
web | rip-web, rip-web-v6 | 80, 443 |
ftp | rip-ftp, rip-ftp-v6 | 21 |
edge | rip-edge, rip-edge-v6 | tous les ports, volontairement |
La fréquence de récupération du flux dépend de votre forfait, et c'est le serveur qui en décide, pas l'agent. Sur un serveur sous licence, l'intervalle est de 15 minutes avec une confiance minimale de 75 à partir de Professional, et d'une heure avec une confiance minimale de 90 sur Contributor. Business et Enterprise se comportent comme Professional. L'agent demande à chaque passage quelles valeurs s'appliquent à cet hôte et suit la réponse : une montée de forfait prend donc effet sans que personne ne modifie un fichier. Il n'existe aucune clé de configuration pour l'intervalle, et une clé que vous inventeriez pour cela arrête l'agent avec le code 2.
Deux règles s'ajoutent par-dessus. Une liste récupérée il y a moins de 15 minutes n'est pas récupérée
de nouveau, car c'est la durée pendant laquelle le serveur la garde en cache. Lancer sync
à la main est donc toujours permis et jamais nuisible. Et chaque récupération est conditionnelle : une
liste inchangée répond 304 et coûte une requête sans transfert, et c'est pourquoi un intervalle court
est économique des deux côtés.
Ce que l'agent trouve dans vos journaux
Chaque source a son propre détecteur et son propre seuil, car cinq connexions SSH échouées en dix
minutes et cinquante requêtes web suspectes en deux heures sont la même affirmation sur un attaquant
et pas le même nombre. La rotation, copytruncate et un journal qui disparaît un moment
sont pris en charge, et un fichier illisible produit un avertissement et non un par lecture.
Les treize types de source
| Type | Chemin typique | Ce qui compte comme occurrence |
|---|---|---|
sshd | journald, sinon /var/log/auth.log ou /var/log/secure | Mots de passe faux, utilisateurs invalides, clés refusées pour un utilisateur qui n'existe pas, limites de tentatives dépassées, et les échecs avant l'authentification qui caractérisent les scanners : aucune chaîne d'identification, une mauvaise version de protocole, des données inutilisables dans l'échange de bannière. Une clé refusée pour un utilisateur valide ne compte volontairement pas, car un administrateur avec cinq clés en écrit quatre par connexion réussie. Une connexion réussie efface les échecs de cette adresse. |
web | /var/log/nginx/access.log ou un motif par site | Les POST de connexion, comptés quel que soit le code de statut, et les chemins qu'aucun client légitime ne demande. Tout le reste d'un journal d'accès est ignoré, car c'est la source qui a de vrais clients derrière elle. |
web-error | /var/log/nginx/error.log, /var/log/apache2/error.log | Les requêtes que le serveur web a lui-même refusées, les échecs d'authentification HTTP basique, et les lignes ModSecurity de gravité critique quand elles arrivent ici plutôt que dans un journal d'audit. Les lignes de limitation de débit ne comptent volontairement pas : une limite se déclenche aussi sur un vrai navigateur avec vingt onglets. Les messages de négociation TLS et de PHP disent quelque chose du serveur et rien du client. |
web-app | les mêmes journaux d'accès | Tourne après web sur les mêmes lignes et couvre les attaques au niveau applicatif : énumération d'utilisateurs WordPress, sondage des plugins et du cœur, chemins de connexion et d'exploitation de Drupal. Un POST de connexion est compté une fois, par web. |
postfix | /var/log/mail.log, /var/log/maillog | Trois sources d'événement distinctes issues d'un seul fichier : les échecs d'authentification SASL, les rejets NOQUEUE qui sont réellement des 5xx (un 450 est du greylisting et ne compte pas), et les blocages amavis. Un courrier livré n'est jamais une occurrence. |
dovecot | le même journal de courrier | Les échecs de connexion IMAP et POP3, un par ligne, quoi que dise « N attempts ». L'adresse distante vient du champ rip=, jamais de lip=, qui est l'adresse propre du serveur. Une connexion interrompue sans tentative d'authentification n'est pas une occurrence : c'est un scan TLS, et aucun mot de passe n'a été essayé. |
exim | /var/log/exim4/mainlog, /var/log/exim/main.log | Échecs d'authentification et expéditeurs rejetés. Non vérifié face à un hôte réel : les motifs viennent du format de journal documenté. |
ftp | /var/log/syslog, /var/log/messages | Échecs d'authentification de pure-ftpd, proftpd et vsftpd. Tous trois journalisent via syslog, et chacun écrit le pair différemment, donc trois motifs sont utilisés. pure-ftpd est mesuré : 494 lignes sur 494 d'un hôte de production avaient une seule et même forme. |
named | le même syslog | Seule une requête entrante que bind a refusée. Le grand piège est le sens inverse : connection refused resolving est le résolveur de cet hôte qui n'atteint pas le serveur de noms de quelqu'un d'autre, et 90 pour cent des lignes mesurées étaient cela. Un détecteur sans cette exception signale les serveurs de noms d'autrui comme attaquants. |
panel | /var/log/ispconfig/auth.log | Connexions échouées au panneau ISPConfig. Le fichier fait 0 octet sur chaque hôte mesuré, et ce n'est pas un défaut : le panneau n'y écrit tout simplement rien. doctor le signale au bout d'une semaine. |
modsec | /var/log/apache2/modsec_audit.log | Un événement par transaction dont le verdict porte une gravité critique. C'est le seul détecteur avec état, car une transaction couvre plusieurs lignes. Rien de la requête ne voyage dans l'événement : ni l'identifiant de règle, ni la partie correspondante, ni l'URI. |
csf | /var/log/lfd.log | Ce que CSF a réellement bloqué, jamais ce qu'il a seulement détecté. Aucun second seuil par-dessus : lfd a compté avant que l'agent ne voie la ligne, un blocage est donc un signalement. |
fail2ban | /var/log/fail2ban.log, sinon le journal de l'unité | Seulement les actions de bannissement. Un Restore Ban n'est jamais signalé, car c'est ce qu'un redémarrage de fail2ban rejoue depuis sa propre base et non une nouvelle attaque. Une ligne Found d'un filtre n'est pas signalée non plus : le jail n'a pas encore décidé. |
imunify360 | interroge imunify360-agent | Des incidents issus du CLI, associés à des catégories par préfixe de nom de règle. Expérimental et non vérifié face à une installation sous licence ; le décodeur ignore ce qu'il ne peut pas lire plutôt que de transformer une réponse inattendue en source morte. |
Ajouter une source que l'installateur n'a pas trouvée
Une source que l'installateur n'a pas trouvée est simplement absente de la configuration, et l'ajouter
est une entrée avec un chemin ou un motif. Comme un fichier avec un bloc sources remplace
entièrement la liste par défaut, votre entrée va à côté des entrées existantes et non dans un second
bloc.
sources:
- type: sshd
- type: web
path: /var/log/nginx/access.log
# A second web server on a different path
- type: web
path: /var/log/caddy/access.log
# Every site of a panel host, rescanned every ten minutes.
# At most five wildcard segments.
- type: web
glob: /var/www/clients/*/web*/log/access.log
exclude:
# This site reports to the API on its own, through Hive or a
# honeypot. Without the exclude the host sends every address
# twice and pays twice out of the daily quota.
- /var/log/ispconfig/httpd/honeypot.example.com/*
# Exim on a host the installer saw as a Postfix machine
- type: exim
path: /var/log/exim4/mainlog
Deux chemins qui pointent sur le même fichier ne posent pas de problème et n'ont pas besoin d'exclude :
ISPConfig publie chaque journal d'accès deux fois, sous /var/www et sous
/var/log/ispconfig, et l'agent lit un tel fichier une seule fois. Avant de faire confiance
à une nouvelle entrée, pointez reportedip-agent test sur le fichier, redémarrez ensuite le
service de surveillance, puis regardez status : la section sources montre les
occurrences par source, et une source avec files= à zéro ne se résout sur rien du tout.
Ce qui quitte la machine
Un signalement contient l'adresse, les identifiants des Threat Categories et une phrase générée. C'est tout. Aucune ligne de journal, aucun nom d'utilisateur, aucun corps de requête, aucun User-Agent et aucune URL n'est jamais transmis : un signalement ne peut donc pas divulguer vos clients, vos chemins ou vos identifiants, même par accident.
Dans la version actuelle, l'agent n'envoie aucun nom d'hôte. Un serveur s'identifie auprès de l'API avec un UUID aléatoire généré à l'installation, et c'est l'identité que vous voyez dans votre compte. Vous lui donnez une étiquette là si vous voulez le reconnaître ; l'étiquette reste dans le compte et n'est jamais déduite de la machine.
Quatre choses ne sont jamais signalées et jamais bloquées, et c'est une frontière dans le code plutôt qu'un réglage : les plages privées et réservées, la boucle locale, chaque adresse des interfaces de ce serveur avec sa passerelle par défaut, et tout ce qui figure dans votre liste blanche. L'adresse du client SSH relevée à l'installation se trouve dans l'auto-liste blanche et appartient donc au dernier groupe.
Blocage
L'agent écrit deux choses très différentes dans le noyau, et qui vient de fail2ban les confond généralement la première semaine. Elles ont des origines différentes, des durées de vie différentes et des interrupteurs différents, il vaut donc la peine de les séparer une fois pour de bon.
Deux sortes d'ensembles, deux durées de vie
| Community Blacklist | Découvertes locales | |
|---|---|---|
| Ensembles | rip-ssh, rip-mail, rip-web, rip-ftp, rip-edge, et un ensemble -v6 pour chacun | rip-local et rip-local-v6 |
| D'où viennent les adresses | De chaque déclarant de la communauté, filtrées par votre confidence | Uniquement des journaux de cet hôte |
| Comment une entrée arrive | L'ensemble entier est remplacé à chaque passage du flux | Une adresse à la fois, quand un seuil est atteint |
| Comment une entrée disparaît | À la bascule suivante, quand le flux ne la liste plus | Le noyau la fait expirer quand son propre délai est écoulé |
| Délai par entrée | aucun | oui, c'est toute la conception |
| Interrupteur | mode | ban.enabled, et mode par-dessus |
| Nécessite une licence | oui, c'est la partie payée | non |
La bascule du flux ne touche jamais rip-local. Les ensembles du flux sont remplacés
intégralement et ne portent pas de délai ; les bannissements locaux arrivent un par un et expirent
d'eux-mêmes. Cette séparation explique pourquoi une panne du flux ne peut pas lever vos bannissements
locaux, et pourquoi un bannissement local ne survit pas en orphelin après la reconstruction de
l'ensemble du flux.
Comment un bannissement local commence et comment il finit
Un bannissement commence quand le compteur d'un détecteur pour une adresse dépasse le seuil de sa source d'événement. Ce qui se passe alors, dans l'ordre :
- La liste blanche est vérifiée en premier, avant même que l'on demande autre chose. Une adresse en
liste blanche ne laisse aucune trace : vous ne trouverez donc jamais votre propre plage
d'administration dans
reportedip-agent ban listen vous demandant si elle a été bloquée. La vérification indique quelle couche a répondu : une plage réservée interne, une des adresses propres de cet hôte, ou votre fichier. - Le registre des bannissements,
/var/lib/reportedip-agent/bans.json, est interrogé pour la durée. Il retient l'adresse, la date d'expiration, le nombre de bannissements tombés dans la fenêtre de mémoire, le début du dernier, la source et les catégories. Le fichier est écrit avant l'entrée dans le noyau : un plantage juste après l'écriture dans le noyau ne peut donc pas perdre l'enregistrement qui lève le bannissement. - L'adresse entre dans
rip-localavec cette durée comme délai par entrée. Le noyau la fait expirer. Il n'y a pas de minuterie de nettoyage, pas de tâche cron et pas de file de débannissement, et c'est pour cela qu'un agent planté ou supprimé ne laisse aucun blocage permanent. - Une ligne part dans le journal avec l'adresse, la durée, la source, le nombre de bannissements de cette adresse dans la fenêtre, l'écart entre la ligne de journal et l'entrée dans le noyau, et les deux commandes qui annulent tout cela.
Un bannissement en cours n'est jamais raccourci. Quand une nouvelle occurrence arrive pour une adresse
déjà bannie, rien ne se passe : ce n'est pas non plus une escalade, car l'escalade compte les
bannissements et non les occurrences, exactement comme le jail recidive qu'elle remplace. Une adresse
déjà bloquée ne peut pas être bloquée une seconde fois. Cette règle garde aussi la période de
transition honnête, quand l'agent voit une attaque deux fois, une fois comme ligne brute de
auth.log et une fois comme ligne de bannissement de fail2ban.log.
Une ligne de journal rejouée ne produit pas de bannissement non plus. Une occurrence dont l'horodatage n'est pas plus récent que le dernier bannissement de cette adresse est une relecture après un arrêt brutal et non une nouvelle infraction, et le chemin de signalement a la même protection dans son fichier de déduplication.
L'état survit à plus qu'un redémarrage. Chaque synchronisation compare bans.json à ce que
le noyau détient réellement et remet ce qui manque, avec le temps restant :
- Après un redémarrage de la machine, c'est la totalité, car ni un délai ipset ni
l'ensemble lui-même ne survit à un redémarrage. C'est pourquoi le service de synchronisation est
activé pour
multi-user.targetet tourne après chaque service de pare-feu. - En fonctionnement normal, c'est ce qui a emporté l'ensemble : un
firewall-cmd --reload, uncsf -r, unipset flush. Les entrées que le noyau détient encore ne sont pas touchées, l'écart est donc une partie d'un intervalle de synchronisation et non le reste du bannissement. Le journal dit explicitement quand des entrées manquaient sous des bannissements actifs, car cela signifie que quelque chose a modifié l'ensemble sous l'agent. - Un enregistrement expiré ne revient jamais. L'attaquant d'hier n'est pas une raison de couper quelqu'un aujourd'hui.
- La liste blanche est revérifiée à chaque restauration. Elle peut avoir grandi pendant que l'hôte était arrêté, et une adresse que vous avez mise en liste blanche hier ne doit pas être rebloquée par un enregistrement de la veille.
L'escalade, avec de vrais chiffres
Le premier bannissement dure time_minutes. Chaque récidive dans la fenêtre de mémoire le
multiplie par escalate, et max_time_minutes le plafonne. Avec les valeurs
par défaut (30 minutes, multiplicateur 4, plafond d'une semaine), une adresse qui revient sans cesse
suit ce chemin :
| Bannissement | Calcul | Durée |
|---|---|---|
| 1er | time_minutes | 30 minutes |
| 2e | 30 × 4 | 2 heures |
| 3e | 120 × 4 | 8 heures |
| 4e | 480 × 4 | 32 heures |
| 5e | 1920 × 4 | 128 heures, cinq jours et huit heures |
| 6e et suivants | ce serait 512 heures, plafonné | 7 jours, la valeur de max_time_minutes |
Le calcul concret. Un attaquant frappe votre port SSH lundi à 09:00 et atteint cinq échecs en dix minutes : à 09:07 il écope donc d'un bannissement de 30 minutes qui expire à 09:37. Il revient à 11:00 et est banni de nouveau, cette fois deux heures, car le premier bannissement est encore dans la mémoire de 24 heures. La troisième tentative à 14:00 coûte huit heures et le mène jusqu'à 22:00. La quatrième, tôt le mardi, coûte 32 heures. À partir de là, l'enregistrement est conservé pour le triple de la durée du dernier bannissement plutôt que pour les 24 heures habituelles, et c'est précisément ce qui rend la cinquième étape atteignable : un long bannissement vaut une longue mémoire, un bannissement de 30 minutes seulement la journée configurée, et le fichier de bannissements ne grossit donc pas à cause de bannissements courts.
Deux réglages changent la forme de cette courbe. escalate: 1 désactive complètement
l'escalade et donne la même durée à chaque bannissement. Un memory_hours court par
rapport à time_minutes signifie qu'une adresse redevient primo-délinquante après une
journée de pause, et c'est parfois exactement ce qu'il faut pour un serveur de courrier avec de vrais
clients.
Abaisser max_time_minutes s'applique aussi aux bannissements déjà en cours. La
synchronisation suivante les restaure ramenés au plafond du jour, car qui abaisse le plafond après un
incident attend que les bannissements en cours suivent plutôt que de rester une semaine dans le noyau.
La chaîne dans le noyau
Avec le moteur ipset, l'agent possède exactement une chaîne, rip-blacklist, et un saut
vers elle. La chaîne est vidée et reconstruite à chaque synchronisation, son contenu est donc toujours
ce que dit la configuration, et le saut n'est inséré à la position 1 de INPUT que s'il
n'y est pas déjà.
# What a sync builds, in this order:
iptables -N rip-blacklist
iptables -F rip-blacklist
iptables -A rip-blacklist -m set --match-set rip-whitelist src -j RETURN
iptables -A rip-blacklist -m set --match-set rip-local src -j DROP
iptables -A rip-blacklist -p tcp --dport 22 \
-m set --match-set rip-ssh src -j DROP
iptables -A rip-blacklist -p tcp -m multiport --dports 25,465,587,110,995,143,993 \
-m set --match-set rip-mail src -j DROP
iptables -A rip-blacklist -p tcp -m multiport --dports 80,443 \
-m set --match-set rip-web src -j DROP
iptables -A rip-blacklist -m set --match-set rip-edge src -j DROP
iptables -I INPUT 1 -j rip-blacklist
La liste blanche est la première règle et elle renvoie, elle n'est pas la dernière
règle. Un RETURN avant tout le reste signifie qu'une adresse en liste blanche
quitte la chaîne avant qu'aucun ensemble ne soit consulté : elle passe donc au-dessus de chaque
bannissement, y compris un bannissement déjà présent dans rip-local. C'est ce qui fait de
whitelist add une véritable sortie de secours, sans synchronisation et sans redémarrage.
Une liste blanche placée à la fin serait une liste d'exceptions qui arrivent trop tard, et sur un hôte
où vous vous êtes enfermé dehors, « trop tard » est tout le problème.
La règle des bannissements locaux n'est liée à aucun port, car un hôte qui a attaqué
votre serveur de courrier n'a rien à faire sur le port 22 non plus. Les cinq règles du flux sont liées
aux ports : une adresse de la liste web est donc rejetée sur 80 et 443 et atteint toujours SSH. C'est
voulu : une adresse partagée, un NAT d'opérateur ou un proxy compromis dans la liste web ne doit pas
enfermer un administrateur hors de la machine. Seul edge s'applique à tous les ports, et
c'est ce que signifie la catégorie qui est derrière.
Avec le moteur nftables, la forme est la même en natif : une table inet reportedip, une
chaîne accrochée à input avec la priorité -10 et la politique accept, d'abord les deux
règles return de la liste blanche, puis rip-local, puis les listes du flux
avec leurs ports. Les ensembles de liste blanche sont des ensembles d'intervalles pour pouvoir
contenir des plages CIDR ; les ensembles locaux portent l'option de délai, que les deux outils exigent
sur l'ensemble avant qu'un élément puisse porter un délai.
En mode: log, les mêmes règles sont construites, et le verdict est une ligne de journal
à débit limité (six par minute, avec le préfixe rip-<liste>:) au lieu de
DROP. Tout le reste est identique, et c'est pourquoi le mode journal est une vraie
répétition et non une simulation.
mode et ban.enabled sont deux interrupteurs
C'est le malentendu le plus fréquent, il a donc droit à son propre paragraphe. mode décide
de ce que les règles font. ban.enabled décide si vos propres découvertes
deviennent un jour une entrée. Qui ne met que mode: drop applique la liste communautaire
et n'obtient rien de ses propres découvertes.
mode | ban.enabled | Liste communautaire | Vos propres découvertes |
|---|---|---|---|
log | false | S'applique et journalise, rien n'est rejeté | Signalées à la communauté, pas bloquées localement |
log | true | S'applique et journalise | Enregistrées dans rip-local et visibles dans ban list, journalisées au lieu d'être rejetées |
drop | false | Rejetée | Seulement signalées. C'est un état permanent parfaitement sensé. |
drop | true | Rejetée | Rejetées aussi, avec escalade. La configuration complète. |
off | les deux | Rien dans le chemin des paquets : le saut est retiré et la chaîne reste avec ses règles | Enregistrées dans le registre, sans effet |
off est l'interrupteur d'urgence et il est honnête là-dessus. Il retire ce qui accroche la
chaîne au chemin des paquets, jusqu'à cinq fois pour attraper les sauts en double, et s'arrête
bruyamment si un saut survit, car dire « off » et continuer de rejeter est pire qu'une erreur. Les
ensembles gardent leurs entrées : revenir en arrière ne demande donc aucun téléchargement complet.
Un bannissement manuel ignore volontairement les deux interrupteurs :
reportedip-agent ban add fonctionne même avec ban.enabled: false, car c'est
un acte délibéré d'un opérateur, comme l'était fail2ban-client set banip. La liste
blanche continue de s'appliquer, et sur un hôte sans moteur de pare-feu la commande refuse.
Passer de log à drop
# 1. What would have been dropped? The rules already match in
# log mode, so ask the kernel log.
journalctl -k --since "24 hours ago" | grep -c "rip-"
journalctl -k --since "24 hours ago" | grep -o "rip-[a-z]*:" | sort | uniq -c
# 2. Is anything of yours in there? Check every source address
# against your whitelist before you switch.
reportedip-agent whitelist list
# 3. Switch, and rebuild the rules.
sed -i "s/^mode: log/mode: drop/" /etc/reportedip-agent/config.yaml
reportedip-agent sync
reportedip-agent status
# 4. Only now the second switch, if you want it.
# ban.enabled: true, then restart the daemon.
systemctl restart reportedip-agent.service
reportedip-agent ban list
Si l'étape 1 renvoie un nombre à quatre chiffres, c'est normal sur un hôte exposé et c'est la liste communautaire qui travaille. Ce qui compte n'est pas le nombre mais de savoir si une adresse de ce journal vous appartient. Deux jours en mode journal suffisent pour le découvrir, et la réponse coûte ici beaucoup moins cher qu'après la bascule.
Regarder ce qui est vraiment dans le noyau
reportedip-agent status est le résumé, et il lit à la fois les fichiers d'état et le
noyau en cours afin que les deux puissent être comparés. Là où ils se contredisent, le noyau est la
vérité, et status le dit : un enregistrement sans entrée dans le noyau est un bannissement qui n'a pas
d'effet.
# The agent's own view
reportedip-agent status
reportedip-agent ban list
# ipset backend, straight from the kernel
ipset list -t rip-ssh # header and entry count only
ipset list rip-local # entries, each with its timeout
iptables -S rip-blacklist
iptables -L INPUT -n --line-numbers | head
# nftables backend
nft list table inet reportedip
nft list set inet reportedip rip-local
Dans ban list, la colonne kernel est ce que le noyau a encore au compteur et
record ce qu'attend bans.json. Un tiret sous kernel à côté d'un
enregistrement actif signifie que le bannissement n'a pas d'effet et que la synchronisation suivante
le restaurera. Une ligne dont l'enregistrement dit none n'existe que dans le noyau : donc
quelqu'un a utilisé ipset ou nft à la main, ou le registre a été perdu. Elle
expirera quand même, mais aucun redémarrage ne la ramènera.
Lever un bannissement, et tout démonter
reportedip-agent whitelist add <adresse> agit
donc immédiatement et passe au-dessus de chaque bannissement, y compris un bannissement déjà dans
l'ensemble. Elle écrit le fichier et l'ensemble du noyau en une étape et ne demande ni
synchronisation ni redémarrage.
# Lift one ban and forget its history. The next detection starts
# from a first offence rather than escalating on top of a verdict
# you disagreed with.
reportedip-agent unban 203.0.113.45
# Never ban and never report this address or range, from now on.
reportedip-agent whitelist add 203.0.113.0/24 "customer office"
# Ban by hand. Works even with ban.enabled: false. A time given
# here is the time, not the first step of an escalation.
reportedip-agent ban add 203.0.113.45
reportedip-agent ban add 203.0.113.45 1440
# Stop blocking without uninstalling: no jump, chain and sets kept.
sed -i "s/^mode: .*/mode: off/" /etc/reportedip-agent/config.yaml
reportedip-agent sync
# Remove the agent from the packet path completely (ipset backend).
iptables -D INPUT -j rip-blacklist; iptables -F rip-blacklist; iptables -X rip-blacklist
ip6tables -D INPUT -j rip-blacklist; ip6tables -F rip-blacklist; ip6tables -X rip-blacklist
for s in $(ipset list -n | grep "^rip-"); do ipset destroy "$s"; done
# nftables backend
nft delete table inet reportedip
unban et whitelist add prennent tous deux le verrou de synchronisation :
l'étape de restauration d'une synchronisation en cours ne peut donc pas remettre ce que vous êtes en
train de lever. Retirer une entrée du fichier de liste blanche est la seule opération qui n'est pas
immédiate : elle prend effet à la reconstruction de la synchronisation suivante, et retirer une
exception n'a jamais besoin d'être instantané.
rip-. iptables-restore abandonne tout le fichier dès qu'il
tombe sur un ensemble inconnu : une ligne oubliée dans /etc/iptables/rules.v4 peut
donc vous enlever tout le pare-feu au prochain démarrage. L'agent reconstruit sa chaîne à chaque
démarrage et n'a besoin de rien d'enregistré. status avertit quand il trouve
rip- dans rules.v4, rules.v6,
/etc/sysconfig/iptables, ip6tables, /etc/nftables.conf ou
/etc/nftables.d.
Commandes
| Commande | Effet | Sortie |
|---|---|---|
install [--key K] [--admin-ip IP] [--ssh-port N] | Détecte les services, écrit la configuration, l'auto-liste blanche et l'identifiant d'installation, installe et active les unités et vérifie la clé. N'écrase jamais une configuration existante. En root uniquement. | 0, 1 si la clé est refusée, 2 en cas de problème de configuration |
sync | Un passage du flux : reconstruire l'ensemble de liste blanche, récupérer chaque liste de façon conditionnelle, basculer ce qui a passé la vérification de taille, reconstruire la chaîne, restaurer les bannissements locaux manquants, faire le ménage, chercher une mise à jour toutes les six heures. C'est ce que lance la minuterie, et c'est ce qui tourne au démarrage. | 0, 1 dégradé, également 0 si une autre synchronisation détient le verrou |
watch | Le démon. Suit chaque source, compte, signale et bannit si c'est activé. Se termine sur SIGTERM. | 1 si le verrou de file est encore détenu après une minute, 2 en cas de problème de configuration |
report-queue | Envoie une fois les signalements en attente. Utile après une panne de l'API, ou sur un hôte où le démon ne tourne pas. | 0, 1 dégradé |
status | Version, moteur, mode, outils, nombre et dernier succès par liste, liste blanche, contrôle de la chaîne, positions de lecture et occurrences par source, file et expéditeur, compte et licence, disque, bannissements locaux, états ouverts. | 0 sain, 1 dégradé, 2 configuration |
doctor | Ce que l'hôte offre et ce qui manque. Ne change rien, fonctionne sans configuration. | 0, 1 avec limites, 2 impossible |
test <fichier>... [--type T] [--lines] | Fait passer les détecteurs sur de vrais fichiers de journaux. Ne signale rien, ne bannit rien, n'écrit rien. | 0, 2 en cas de problème de fichier ou de type |
ban list | add <ip> [minutes] | rm <ip> | Montre les bannissements locaux face à ce que le noyau détient réellement, en pose un à la main, en lève un. | 0, 1 en cas d'échec du noyau, 2 si un argument est faux |
unban <ip> | La même chose que ban rm, sous le mot qu'un opérateur tape quand ça brûle. | comme ci-dessus |
whitelist add <ip|cidr> [commentaire] | list | rm <ip|cidr> [--auto] | Gère la liste « jamais bloquer, jamais signaler ». add écrit le fichier et l'ensemble du noyau d'un coup. list affiche votre fichier et l'auto-liste blanche. --auto retire une entrée écrite par l'installateur. | 0, 1 si le fichier a changé et pas l'ensemble, 2 si l'adresse est fausse |
update [--check] | Vérifie le point de distribution, remplace ce binaire puis redémarre le service de surveillance. --check ne fait que rapporter. C'est la seule commande qui lit la configuration de façon souple : elle fonctionne donc aussi sur un hôte dont la configuration est plus récente que le binaire. | 0, 1 si la vérification ou l'installation a échoué |
housekeeping | Retire les propres restes de l'agent et affiche les chiffres. La synchronisation fait la même chose en silence. | 0, 2 en cas de problème de configuration ou d'état |
version | Affiche la version et rien d'autre. | 0 |
help | La liste des commandes, avec les chemins de configuration et d'état. | 0 |
test, au lieu de fail2ban-regex
test fait passer la chaîne de détecteurs sur un fichier de journal que vous avez déjà,
avec les seuils et la liste blanche de cet hôte, et affiche quelles adresses auraient dépassé un seuil
et à quel endroit du fichier. Il ne signale rien, ne bloque rien et n'écrit rien : il est donc sans
danger sur un hôte de production et c'est la réponse honnête à la question de savoir si cela aurait
attrapé l'attaque de la semaine dernière.
# A configured source: the type is taken from the config
reportedip-agent test /var/log/auth.log
# Any other file: name the type yourself
reportedip-agent test --type postfix /var/log/mail.log.1
# A rotated log and its successor as one stream, in this order,
# so a window that spans the rotation counts like the daemon saw it.
# A .gz is read inflated.
reportedip-agent test /var/log/mail.log.2.gz /var/log/mail.log.1 /var/log/mail.log
# Every single hit, with line number, address and timestamp
reportedip-agent test --lines /var/log/auth.log
La ligne de résumé indique les lignes lues, combien ont correspondu, combien ne portaient pas d'horodatage utilisable et combien ont été ignorées comme trop longues. Suit une ligne par adresse avec ses occurrences, ses remises à zéro, si elle aurait été bannie et combien de fois, l'heure de la première occurrence et le seuil appliqué. Une adresse figurant dans votre liste blanche est affichée avec la couche qui a répondu et marquée comme jamais bannie et jamais signalée, ce qui fait de cette commande le moyen le plus rapide de prouver qu'une entrée de liste blanche fonctionne vraiment.
Deux choses que test ne fait pas. Il ne devine aucun format : sans --type, le
fichier doit être l'un des chemins de source configurés, sinon il le dit et énumère les types valides.
Et il n'applique pas la pondération issue de l'historique de bannissements : le démon a donc besoin de
moins d'occurrences pour une adresse déjà bannie que ne le suggère cette relecture.
Exploitation
- Une minuterie au rythme de votre forfait, avec dispersion.
reportedip-agent-sync.timerse déclenche quelques minutes après son activation puis de façon répétée dans l'intervalle fourni par votre forfait, avec un décalage aléatoire mais fixe, pour qu'une flotte n'atteigne pas l'API à la même seconde. Cet intervalle vient du serveur, voir Le flux communautaire. Le chemin de démarrage est séparé :reportedip-agent-sync.serviceest activé pourmulti-user.targetet tourne aprèsnetwork-online.targetet après chaque service de pare-feu, sans décalage aléatoire, car un hôte ne doit pas rester un quart d'heure sans protection après un redémarrage. - Un service de surveillance qui s'arrête quand il le faut.
reportedip-agent.serviceredémarre après une erreur au bout de dix secondes, au plus cinq fois en cinq minutes, et jamais sur le code de sortie 2. Un hôte mal configuré reste arrêté avec une raison lisible au lieu d'être relancé sans fin. Les deux unités tournent avecNoNewPrivileges,ProtectHome,PrivateTmp, une personnalité verrouillée et un ensemble restreint de familles d'adresses. - Son propre journal, sa propre rotation. L'agent écrit sur stderr, que journald
reprend, et dans son propre fichier quand
log_fileest défini. Il effectue lui-même la rotation de ce fichier àlog_max_mbet conservelog_keepgénérations. Il ne dépend pas de logrotate et ne remplit pas le journal. - Un courrier quand quelque chose ne va pas, un quand c'est réglé. Par état et non par événement, avec un délai d'attente : un flux cassé produit donc un message et non un par heure.
- Une limite de disque. Sous
disk_min_mb, l'agent ne met plus de nouveau signalement en file et le dit, tandis que la détection et le blocage continuent. housekeeping. Anciens fichiers de file, entrées de déduplication expirées, anciennes positions de lecture de fichiers qui n'existent plus, fichiers temporaires et de verrou restants, enregistrements de bannissement expirés qui n'alimentent plus aucune escalade, et le binaire précédent dès qu'il a un mois. Tourne à chaque synchronisation, et à la main quand vous voulez voir les chiffres.- Mise à jour automatique toutes les six heures. Une nouvelle version est vérifiée avant installation contre une signature Ed25519 avec la clé publique compilée dans le binaire : un téléchargement altéré échoue donc sur votre machine au lieu d'être jugé fiable parce qu'il est arrivé par HTTPS. Le binaire précédent reste disponible pour un retour arrière et est retiré par le ménage après trente jours.
systemctl list-timers reportedip-agent-sync.timer
systemctl status reportedip-agent.service --no-pager
journalctl -u reportedip-agent.service -n 50 --no-pager
journalctl -u reportedip-agent-sync.service -n 50 --no-pager
tail -n 100 /var/log/reportedip-agent.log
Codes de sortie
| Code | Signification | Que faire |
|---|---|---|
0 | Sain | Rien. |
1 | Dégradé, mais une nouvelle tentative suivra | Lire la sortie. Une nouvelle tentative peut très bien régler la chose. Un état d'erreur ouvert dans le registre de santé colore le code de sortie même si l'exécution elle-même est passée, car un code 0 à côté d'une erreur consignée n'est pas un signal pour une supervision. |
2 | Non reproductible sans un humain | La configuration, les droits de fichier, un outil manquant, un verrou détenu par un autre processus. Lancer doctor. systemd ne relance pas le service de surveillance sur un 2. |
reportedip-agent status est ainsi utilisable directement comme sonde de supervision, sans
script d'encapsulation et sans analyse de sortie.
Mises à jour et notes de version
L'agent cherche une nouvelle version toutes les six heures, pendant la synchronisation. Il vérifie le
téléchargement contre une signature Ed25519 avec la clé publique intégrée au binaire, garde la version
précédente pour un retour arrière et redémarre lui-même le service de surveillance.
reportedip-agent update fait la même chose à la demande, et update --check ne
rapporte que ce qui est publié.
Relancer la commande d'installation met également à jour un hôte, et c'est la voie à suivre quand une version relève la version minimale : l'agent refuse alors la mise à jour automatique et le dit, car il a besoin d'un humain. Les retours à une version antérieure sont refusés en général, un hôte qui a récupéré une version plus récente ne revient donc pas en arrière de lui-même.
Un cas de mise à jour vaut la peine d'être connu à l'avance. L'analyseur de configuration est strict
sur les clés inconnues : un binaire plus ancien ne peut donc pas lire une configuration qui porte déjà
un bloc plus récent. Pour un client, l'ordre est inoffensif, car l'agent se met à jour d'abord et une
nouvelle clé n'apparaît que lorsque quelqu'un l'ajoute. Si vous vous retrouvez malgré tout avec un
binaire plus ancien que sa configuration, update est la sortie : c'est la seule commande
qui lit le fichier de façon souple, et elle nomme les clés qu'elle ne connaît pas.
Ce que la version actuelle a changé est public et ne demande aucune clé :
curl -s https://reportedip.com/agent/whats-new
reportedip-agent update --check
Les modifications de la REST API elle-même, donc les champs de réponse et les codes de statut qui concernent une intégration, figurent plutôt dans le Changelog de l'API.
Licences serveur
L'agent est sous licence par serveur. Les serveurs forment leur propre réserve et sont comptés séparément des domaines du plugin Hive : une installation de l'agent ne vous coûte donc jamais un domaine.
| Forfait | Serveurs inclus | Serveurs supplémentaires |
|---|---|---|
| Free | aucun | Non disponible |
| Contributor | aucun | Non disponible |
| Professional | 1 | Autant que voulu |
| Business | 3 | Autant que voulu |
| Enterprise | selon contrat | selon contrat |
Une licence supplémentaire coûte 4,90 € par mois ou 49 € par an et par serveur, TVA incluse, et devient moins chère par serveur à mesure que le nombre augmente : 4,90 € pour les quatre premiers, 3,90 € à partir du cinquième, 2,90 € à partir du dixième, 1,90 € à partir du vingt-cinquième et 1,40 € à partir du cinquantième. Le multiplicateur de volume de Business augmente votre nombre de domaines et non vos serveurs inclus, car les serveurs sont la partie payée séparément.
Vous réglez vous-même la quantité sous Agent Servers dans votre compte. Un hôte y apparaît dès que son agent appelle l'API pour la première fois, avec son identifiant d'installation, sa version et la date à laquelle il a été entendu pour la dernière fois. Si le compte a une licence libre, l'hôte la prend aussitôt. S'il n'en a aucune, la ligne le dit et un bouton en ajoute une. Il n'y a rien à enregistrer au préalable et rien à copier dans un fichier : l'hôte détient déjà une clé API de votre compte, et c'est une preuve plus forte que n'importe quel fichier de vérification.
L'identité est l'identifiant d'installation, jamais le nom d'hôte et jamais la clé. Changer la clé API sur un hôte ne crée pas une seconde licence, et renommer la machine ne change rien. Si vous avez plus d'agents que de licences, les hôtes les plus anciens gardent la leur. Une licence serveur résiliée fonctionne encore sept jours.
Ce qui se passe sans licence
Une seule chose s'arrête : le flux n'est plus récupéré. Tout le reste continue, et c'est voulu. Un serveur ne doit pas devenir sans protection parce qu'une facture est restée en attente, et un hôte sans licence n'est pas un hôte sans défense. Il ne reçoit simplement plus la liste communautaire.
| Fonction | Sans licence |
|---|---|
| Téléchargement du flux | S'arrête. C'est la partie qui est payée. |
| La liste déjà présente dans le noyau | Reste. Elle n'est jamais vidée et continue de bloquer. |
| Détection locale | Continue. Les treize sources, tous les seuils. |
| Blocage local | Continue, escalade comprise, restauration après un redémarrage comprise. |
| Signalements | Continuent, dans la limite quotidienne de votre forfait. |
| Liste blanche, chaîne, ménage, rotation des journaux | Continuent. |
| Mise à jour automatique | Continue. Un agent obsolète sur un hôte impayé est notre risque, pas notre moyen de pression. |
status | Affiche l'état de licence, la raison, l'âge de la liste et ce qu'il faut faire. |
Le service n'est pas arrêté, les ensembles ne sont pas vidés, les bannissements locaux ne sont pas levés, et il n'y a aucune publicité dans le journal. Un compte Free peut donc utiliser l'agent comme simple déclarant avec une protection locale complète, et c'est un usage légitime.
Remplacer fail2ban
L'agent ne se place pas à côté de fail2ban, il prend la relève. fail2ban reste disponible comme une source parmi d'autres : un hôte qui le fait encore tourner continue donc de voir ses bannissements signalés. Mais un hôte sans fail2ban ne perd absolument rien : pour chaque type d'attaque qui arrivait auparavant par un jail, un détecteur lit maintenant le journal d'origine.
La comparaison a été mesurée et non affirmée. Sur onze jours sur un hôte de production, l'agent a trouvé 187 des 189 adresses que fail2ban avait bannies sur la même machine. Les deux manquantes étaient des adresses de test injectées à la main. En plus de cela, il a signalé 78 adresses supplémentaires qui attaquaient réellement et étaient restées sous les seuils de fail2ban, et pour le FTP il a fait exact, trois sur trois.
Deux différences pratiques. L'escalade pour les récidivistes vient de l'historique de bannissements
propre à l'agent et non d'un jail qui lit le journal d'un autre jail, et
reportedip-agent test <fichier> prend la place de fail2ban-regex.
Vérifier d'abord si quelque chose possède déjà ces noms
Cela a coûté un après-midi sur une flotte de production, c'est donc à lire avant tout le reste. Un
script de pare-feu écrit à la main, du genre que ce site documentait autrefois, peut employer
exactement les noms que l'agent emploie. Trouvés sur le terrain : les ensembles
rip-whitelist, rip-ssh, rip-mail, rip-web,
rip-ftp et rip-edge, plus une chaîne rip-blacklist accrochée
par -A INPUT -j rip-blacklist, reconstruite chaque heure par une tâche cron, et presque
caractère pour caractère la chaîne que l'agent construit lui-même.
Sur un tel hôte, l'ordre habituel est faux dans les deux sens. La première synchronisation de l'agent
reconstruit la chaîne et remplace les règles DROP existantes par des règles
LOG, ce qui laisse l'hôte sans protection jusqu'à la prochaine exécution de l'ancienne
tâche cron, soit jusqu'à une heure plus tard. Et quand cette tâche s'exécute, elle reconstruit la
chaîne à sa façon et retire au passage la règle rip-local de l'agent. Deux programmes,
une chaîne, et chacun défait le travail de l'autre toutes les heures.
Vérifiez donc avant la première synchronisation, et si les noms entrent en collision, ne vous arrêtez
pas à mode: log sur cet hôte. Installez l'agent, ce qui ne touche aucune règle, reprenez
l'ancienne liste blanche, désactivez l'ancienne tâche cron, et lancez seulement ensuite la première
synchronisation avec mode: drop déjà défini.
# 1. Does something already own these names?
ipset list -n | grep "^rip-"
iptables -S INPUT | grep rip-
iptables -S rip-blacklist 2>/dev/null | head
crontab -l | grep -iE "ipset|blacklist|reportedip"
ls -l /etc/cron.d/ /etc/cron.hourly/ 2>/dev/null
# 2. If yes, take the old whitelist over FIRST. The feed can be
# downloaded again; that list cannot.
ipset list rip-whitelist | sed -n "/^Members/,\$p" | tail -n +2 > /tmp/rip-wl
ipset list rip-whitelist-v6 | sed -n "/^Members/,\$p" | tail -n +2 >> /tmp/rip-wl
wc -l /tmp/rip-wl
while read -r a; do
[ -n "$a" ] && reportedip-agent whitelist add "$a" "imported from the old script"
done < /tmp/rip-wl
reportedip-agent whitelist list | wc -l
# 3. Disable the old job, then hand the chain over in one step.
crontab -l | grep -v blacklist | crontab - # or: rm /etc/cron.d/<job>
sed -i "s/^mode: log/mode: drop/" /etc/reportedip-agent/config.yaml
reportedip-agent sync
reportedip-agent status
reportedip-agent whitelist list.
Faire tourner les deux un moment
Faire tourner les deux est sans danger et c'est la façon sensée de migrer. L'agent n'écrit jamais dans
/etc/fail2ban, et fail2ban ne sait rien des ensembles rip- : les deux
utilisent donc des chaînes séparées et des états séparés. Deux règles qui rejettent la même adresse
coûtent une comparaison de paquet.
La seule chose à éviter est de signaler le même événement deux fois. Si vous gardez l' action fail2ban qui envoie les bannissements par HTTP et que vous configurez fail2ban comme source de l'agent, chaque bannissement quitte l'hôte deux fois et est payé deux fois sur votre quota quotidien. Prenez l'agent ou l'action.
# What did fail2ban ban, and what does the agent make of the
# same logs? Same file, same window, two verdicts.
fail2ban-client status sshd
reportedip-agent test /var/log/auth.log
# Per jail, then the agent over the file behind it
for j in $(fail2ban-client status | sed -n "s/.*Jail list:\t*//p" | tr -d " " | tr "," " "); do
echo "== $j"; fail2ban-client status "$j" | grep -E "Total banned|Currently banned"
done
reportedip-agent status | sed -n "/^sources:/,/^queue:/p"
Désactiver fail2ban définitivement
Faites-le une fois que vous avez comparé les deux pendant quelques jours et que la liste de sources de l'agent couvre chaque jail que vous aviez.
# 1. Stop it and keep it stopped.
systemctl stop fail2ban
systemctl disable fail2ban
# 2. Remove the fail2ban source from the agent config, then stop
# and start the daemon. A restart is not enough for a removed
# source: the daemon keeps tailing the old file.
$EDITOR /etc/reportedip-agent/config.yaml
systemctl stop reportedip-agent.service
systemctl start reportedip-agent.service
# 3. Check what fail2ban left in the kernel. Stopping it does not
# always clean up: a jail that once used an iptables action
# leaves old-style f2b- chains behind.
nft list tables | grep f2b || echo "no f2b table"
iptables -S | grep "f2b-" || echo "no f2b chain"
ipset list -n | grep f2b || echo "no f2b set"
# 4. Remove the leftovers from the RUNNING ruleset.
iptables -D INPUT -j f2b-sshd
iptables -F f2b-sshd && iptables -X f2b-sshd
# 5. And from the SAVED one, or they come back at the next boot.
grep -n "f2b" /etc/iptables/rules.v4 /etc/iptables/rules.v6
netfilter-persistent save
# 6. Confirm the agent is happy without it.
reportedip-agent status | grep -i fail2ban
reportedip-agent status; echo "exit $?"
f2b- seulement du jeu de règles en cours, netfilter-persistent les
restaure à chaque démarrage depuis /etc/iptables/rules.v4 et depuis
/etc/iptables/rules.v6. Elles reviennent vides, donc elles ne rejettent rien et
paraissent inoffensives, et dans un an elles seront encore là à embrouiller la personne suivante
qui lira le jeu de règles. Examinez les deux fichiers : sur un hôte, les restes n'étaient que
dans le fichier v6, et qui ne cherche que dans rules.v4 passe à côté. D'abord
nettoyer le jeu de règles en cours, puis l'enregistrer, et vérifier après un redémarrage.
Un retour arrière est une commande et prend quelques secondes, car l'agent n'a jamais touché à
/etc/fail2ban : systemctl enable --now fail2ban ramène chaque jail et sa
propre table, et l'agent n'en est pas affecté. Mesuré sur un hôte en production, les onze jails étaient
revenus en huit secondes.
Consommation de ressources
Mesuré sur un hôte Debian 12 arm64 avec sept fichiers de journaux suivis par l'agent : 0,085 % d'un cœur et 16,6 Mo de mémoire résidente. Il n'y a pas d'interpréteur à lancer, pas de base de données et pas de répertoire de cache à réchauffer, et c'est l'essentiel de l'explication de ces petits chiffres.
Sur le disque, les fichiers propres de l'agent sont bornés par la configuration et non par l'espoir :
log_max_mb fois log_keep plus le fichier courant pour le journal,
queue_max petits fichiers pour la file, et un registre de bannissements qui ne garde que
ce dont une escalade a encore besoin. Le répertoire d'état d'un hôte chargé reste bien en dessous de
cent mégaoctets, et c'est pourquoi la limite de disque par défaut de 200 Mo ne se déclenche jamais sur
une machine saine.
Ce que l'agent n'est pas
Ce n'est pas un antivirus, pas un pare-feu applicatif web et pas un remplacement d'Imunify360. Il ne lit pas vos fichiers, n'examine pas les corps de requête et ne met rien en quarantaine. Il bloque et signale des adresses, et il le fait sur une surface petite et vérifiable. Pour une protection au niveau d'une requête unique sur un site WordPress, prenez plutôt le plugin Hive. Les deux tournent sur la même machine sans se gêner.
Dépannage
Un symptôme, une cause probable, une commande. reportedip-agent doctor et
reportedip-agent status répondent ensemble à la plupart de ces cas, et ce sont les deux
choses que le support demande en premier.
| Symptôme | Cause | Commande |
|---|---|---|
| Ne démarre pas et nomme son fichier de configuration | Le fichier est lisible par le groupe ou par les autres, et il contient votre clé API. | chmod 0600 /etc/reportedip-agent/config.yaml && chown root:root /etc/reportedip-agent/config.yaml |
| Code 2 avec « field ... not found in type » | Une clé inconnue dans la configuration. L'analyseur est strict volontairement. | Retirer ou corriger la clé nommée. La liste des clés valides figure plus haut. |
| Chaque commande se termine par un code 2 après un retour arrière | Le binaire est plus ancien que la configuration et ne peut pas lire un bloc plus récent. | reportedip-agent update, la seule commande qui lit le fichier de façon souple et nomme les clés inconnues. |
| Les ensembles existent mais sont vides | Soit le flux a été refusé, soit la liste a échoué à la vérification de taille. | reportedip-agent status dit lequel des deux, et la ligne de la liste porte la dernière erreur. |
status indique unlicensed | Le compte n'a aucune licence libre pour cet hôte. | En ajouter une sous Agent Servers. La liste dans le noyau continue de travailler pendant ce temps. |
| Rien n'est jamais signalé | La source qui aurait réagi n'a jamais été détectée, ou son seuil n'est pas atteint. | reportedip-agent test /chemin/vers/journal, puis reportedip-agent status et lire la section sources. |
| Rien n'est jamais bloqué, bien que des signalements partent | ban.enabled est encore à false, ou mode est encore log. Deux interrupteurs. | reportedip-agent ban list dit lequel des deux dans ses dernières lignes. |
| Aucun état de source du tout | Le démon de surveillance n'a jamais tourné. | systemctl enable --now reportedip-agent.service |
Une source affiche files=0 | Le chemin ou le motif ne se résout sur rien sur cet hôte. | reportedip-agent doctor nomme la source et le motif essayé. |
| Une source de journal est signalée illisible | Le fichier n'est pas lisible même pour root. Souvent un panneau de contrôle qui met le mode 000 à la rotation. | ls -l sur le chemin, et corriger la rotation qui a produit cela. |
| Une source a des occurrences mais ne signale jamais | Ses horodatages sont à plus d'une minute de l'horloge système, la fenêtre de comptage ne se remplit donc jamais. | reportedip-agent doctor, les lignes de dérive sous sources. Corriger le fuseau horaire de ce journal. |
| Votre propre adresse a été bannie | Elle n'était pas en liste blanche. | reportedip-agent whitelist add <adresse>. Effet immédiat, car la règle de liste blanche est avant la règle de blocage. |
Un bannissement est dans bans.json mais pas dans le noyau | Un redémarrage sans synchronisation, ou un rechargement de pare-feu qui a emporté l'ensemble. | reportedip-agent sync le restaure avec le temps restant. |
Une entrée du noyau avec l'enregistrement none | Quelqu'un a banni à la main avec ipset ou nft, ou le registre a été perdu. Elle expire, mais aucun redémarrage ne la ramène. | reportedip-agent ban list |
| Un ensemble existe avec un mauvais type ou sans délai | Un reste du script shell documenté ou d'un autre outil. | ipset destroy <ensemble> puis reportedip-agent sync, qui le recrée correctement. |
Les deux chaînes d'outils ont des objets rip- | Le moteur a été changé sans migration. | reportedip-agent sync --migrate-backend, et status affiche les commandes exactes de suppression pour l'autre côté. |
| Le pare-feu a disparu après un redémarrage | Un jeu de règles enregistré référence un ensemble rip-, et iptables-restore abandonne tout le fichier sur un ensemble inconnu. | grep -n rip- /etc/iptables/rules.v4 /etc/iptables/rules.v6 et retirer ces lignes. L'agent n'a besoin de rien d'enregistré. |
| Une source retirée continue d'être lue | Un systemctl restart ne suffit pas pour une source retirée. | systemctl stop reportedip-agent.service && systemctl start reportedip-agent.service |
| Les signalements s'arrêtent, la détection continue | La limite de disque a été atteinte. | Libérer de l'espace, puis reportedip-agent housekeeping. |
| HTTP 429 sur les signalements | La limite quotidienne de signalements de votre forfait. | Les limites figurent sous Authentification. |
| La file d'attente ne cesse de grandir | L'expéditeur est en pause après une erreur, avec un délai progressif. | reportedip-agent status montre la pause et la raison ; reportedip-agent report-queue envoie un passage à la main. |
| Chaque occurrence web est la même poignée d'adresses | nginx voit un proxy ou Cloudflare et non le visiteur. | Configurer set_real_ip_from. reportedip-agent install avertit précisément de ce cas. |
| « more addresses than the counter tracks at once » | Un balayage plus large que le compteur par source. Pas un défaut. | Rien. Un balayage aussi large est un cas pour la liste communautaire et non pour des bannissements locaux. |
status dit que l'hôte est en retard de version | La mise à jour automatique ne passe pas, ou auto_update est désactivée. | reportedip-agent update --check, puis reportedip-agent update. |
Dernière mise à jour: · Maintenu par l’équipe ReportedIP