Skip to main contentSkip to footer

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.

Version actuelle : 0.3.8 pour 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

1

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.

2

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.

3

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 ipset avec iptables, soit nftables. Sur un hôte Debian ou Ubuntu, c'est apt install ipset iptables ou apt install nftables ; dans la famille RHEL, dnf install ipset iptables ou dnf install nftables. L'agent utilise aussi ip6tables quand 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.

backendCe 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.
ipsetEnsembles 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.
nftablesEnsembles 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.

bash
reportedip-agent doctor

Elle répond à six questions, dans cet ordre :

  • system : la distribution telle que son propre os-release la nomme, le noyau, la plateforme, si systemd tourne vraiment (lu dans /run/systemd/system, et non déduit de la présence de systemctl), 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 -T ré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, ip6tables et nft, 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 sendmail avec 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.

SortieVerdictSignification
0tout ce dont l'agent a besoin est làInstaller.
1fonctionne sur cet hôte, avec les limites indiquéesChaque 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.
2ne peut pas fonctionner sur cet hôtePas 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.

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

  1. Lit https://reportedip.com/agent/latest sans 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.
  2. Télécharge reportedip-agent_linux_<arch> et SHA256SUMS avec l'en-tête X-Key dans un répertoire temporaire supprimé sur chaque chemin de sortie.
  3. Vérifie la somme de contrôle et n'installe rien en cas d'écart.
  4. Installe le binaire en mode 0755 dans /usr/local/bin/reportedip-agent et affiche la version qu'il vient de poser.
  5. Redémarre reportedip-agent.service si 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.
  6. Exécute la configuration de l'hôte, sauf si REPORTEDIP_NO_SETUP=1 est défini ou si /etc/reportedip-agent/config.yaml existe 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-agent et /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, depuis SSH_CONNECTION et depuis les processus sshd à l'écoute, et prend l'union des trois. S'il ne trouve rien, il s'arrête et demande --ssh-port plutôt que de supposer 22.
  • Décide lesquelles des cinq listes du flux sont configurées. ssh et edge sont toujours actives. mail, web et ftp s'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.yaml en mode 0600 avec mode: log et ban.enabled: false. Une configuration existante n'est jamais écrasée, et --key est alors ignorée avec une note invitant à modifier api_key dans 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 reportedip laissée par le script shell documenté alors que le moteur retenu est ipset, et un nginx qui lit les en-têtes Cloudflare sans set_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écute daemon-reload, active reportedip-agent-sync.service, puis active et démarre reportedip-agent-sync.timer et reportedip-agent.service.
  • Appelle verify-key avec 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.
L'installation ne touche à aucune règle de pare-feu. Aucune chaîne, aucun ensemble, aucun saut vers 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

VariableDéfautÀ quoi elle sert
REPORTEDIP_KEYaucune, obligatoireVotre clé API. Sans elle, le script s'arrête avant de télécharger quoi que ce soit.
REPORTEDIP_VERSIONla version couranteFixe 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/binOù va le binaire. Si vous le changez, les unités systemd doivent suivre, car elles nomment le chemin absolu.
REPORTEDIP_BASEhttps://reportedip.com/agentLa 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_SETUPnon définie1 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.
bash
# 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.

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

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

  1. 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.
  2. 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.
  3. 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 indique ok, que la liste blanche contient les adresses attendues, et que account montre votre rôle et l'état de licence de cet hôte.
  4. 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.
  5. Attendre, puis relire status. Un jour suffit, deux valent mieux. En mode: 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 que drop aurait coupé.
  6. Passer à mode: drop puis lancer sync, qui reconstruit les règles.
  7. Seulement ensuite, envisager ban.enabled: true et redémarrer ensuite le service de surveillance. C'est le second interrupteur, et il concerne vos propres journaux plutôt que la liste communautaire.
bash
reportedip-agent doctor
reportedip-agent sync
reportedip-agent status
reportedip-agent whitelist add 203.0.113.0/24 "management"
reportedip-agent whitelist add 198.51.100.7   "monitoring"
reportedip-agent whitelist list
L'installation démarre en 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.

Une clé inconnue arrête l'agent avec le code 2. L'analyseur vérifie strictement les noms de champs : une faute de frappe est donc une erreur avec un numéro de ligne et non une valeur qui ne s'applique jamais en silence. C'est volontaire : une faute dans 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éfautEffet
api_keyaucuneVotre clé API. La seule clé sans valeur par défaut. REPLACE_ME compte comme absente.
api_urlhttps://reportedip.com/wp-json/reportedip/v2La base REST. Doit être une URL https absolue ; le http simple n'est admis que sur la boucle locale, pour un proxy local.
update_urldéduite de api_urlD'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_updatetrueChercher 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_levelinfoinfo ou debug. Rien d'autre.
log_file/var/log/reportedip-agent.logLe 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_mb10Taille à laquelle l'agent effectue sa rotation, vérifiée à chaque écriture. Plage 1 à 1024.
log_keep3Gé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.
backendautoauto, ipset ou nftables. Voir plus haut.
modeloglog, drop ou off. S'applique aux règles, donc aux listes du flux et aux bannissements locaux.
confidence90Le 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.
limit50000Nombre maximal d'adresses par requête de liste, de 1 à 50000.
listsssh, edgeQuelles 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.
portsdepuis le vrai processus à l'écouteSous 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_entriesssh 1000, mail 500, web 500, ftp 500, edge 1000Le 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.confVotre 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.
sourcesune entrée sshdLes 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.
typeaucunDans une source : l'un des treize types de source. Un type inconnu est une erreur qui énumère les types valides.
pathaucunDans 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.
globaucunDans une source : un motif sur plusieurs fichiers, réévalué toutes les dix minutes. Au plus cinq segments avec joker.
excludeaucunDans 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_minutes5Dans 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.
thresholdsvideRemplace la table interne par source d'événement. Voir la section suivante.
hits, window_minutesvoir la table ci-dessousDans une entrée thresholds : le couple qui signifie « autant d'occurrences en autant de minutes ».
min_hits, window_minutes5, 10Le couple global pour chaque source sans entrée propre dans la table interne.
web_min_hits, web_window_minutes50, 120Le même couple pour les journaux d'accès web, qui ont besoin de volume plutôt que d'un code de statut.
dedup_hours6La même adresse est signalée au plus une fois par fenêtre de cette durée.
queue_max5000Fichiers de signalement en attente. Au-delà, les plus anciens sont supprimés.
disk_min_mb200Mé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_categoriestable interneCorrespondance 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.
bandésactivéLe bloc des bannissements locaux, avec enabled, time_minutes, max_time_minutes, escalate et memory_hours. Voir Blocage.
notifyaucun courrierLe 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 :

yaml
# /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énementSeuilOrigine
sshd5 en 10 minmin_hits / window_minutes
web-error5 en 10 minmin_hits / window_minutes
modsec5 en 10 minmin_hits / window_minutes
exim5 en 10 minmin_hits / window_minutes
web50 en 120 minweb_min_hits / web_window_minutes
web-app20 en 60 mininterne
postfix-sasl3 en 60 mininterne, mesuré
postfix-reject5 en 60 mininterne, rejets 5xx seulement
postfix-amavis3 en 60 mininterne
dovecot10 en 60 mininterne, mesuré
ftp20 en 60 mininterne
named20 en 30 mininterne
panel5 en 60 mininterne
fail2ban, csf, imunify360aucun seuilCette 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.

yaml
# 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éfautSignification, et ce qui arrive aux extrêmes
enabledfalseSi 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_minutes30Le 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_minutes10080 (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.
escalate4Le 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_hours24Combien 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.
yaml
# 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éfautUnitéTrop basTrop haut
queue_max5000fichiers en attenteDes 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_hours6heuresLe 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_mb200Mo libresL'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_mb10MoUne 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_keep3générations0 ne conserve aucun fichier rotaté.Au maximum 20, et le coût disque est le produit des deux valeurs.
cooldown_hours24heuresAu 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_minutes5minutesUne commande lancée trop souvent sur un hôte chargé.Au maximum 60, et les découvertes arrivent d'autant plus tard.
limit50000adresses par listeUne liste tronquée : les pires adresses sont là, la suite non.50000 est le maximum servi par l'API.
min_entries1000 / 500adressesUne 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é.

yaml
# 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, backendreportedip-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 à sourcessystemctl restart reportedip-agent.service.
Une source retirée de sourcesUn 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_fileRien, 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_urlRien 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é.
bash
# 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.

ListeEnsemble du noyauPorts concernés par la règle
sshrip-ssh, rip-ssh-v6depuis lists.ssh.ports
mailrip-mail, rip-mail-v625, 465, 587, 110, 995, 143, 993
webrip-web, rip-web-v680, 443
ftprip-ftp, rip-ftp-v621
edgerip-edge, rip-edge-v6tous 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

TypeChemin typiqueCe qui compte comme occurrence
sshdjournald, sinon /var/log/auth.log ou /var/log/secureMots 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 siteLes 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.logLes 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-apples mêmes journaux d'accèsTourne 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/maillogTrois 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.
dovecotle même journal de courrierLes é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.
namedle même syslogSeule 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.logConnexions é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.logUn é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.logCe 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é.
imunify360interroge imunify360-agentDes 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.

yaml
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 BlacklistDécouvertes locales
Ensemblesrip-ssh, rip-mail, rip-web, rip-ftp, rip-edge, et un ensemble -v6 pour chacunrip-local et rip-local-v6
D'où viennent les adressesDe chaque déclarant de la communauté, filtrées par votre confidenceUniquement des journaux de cet hôte
Comment une entrée arriveL'ensemble entier est remplacé à chaque passage du fluxUne adresse à la fois, quand un seuil est atteint
Comment une entrée disparaîtÀ la bascule suivante, quand le flux ne la liste plusLe noyau la fait expirer quand son propre délai est écoulé
Délai par entréeaucunoui, c'est toute la conception
Interrupteurmodeban.enabled, et mode par-dessus
Nécessite une licenceoui, c'est la partie payéenon

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 :

  1. 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 list en 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.
  2. 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.
  3. L'adresse entre dans rip-local avec 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.
  4. 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.target et tourne après chaque service de pare-feu.
  • En fonctionnement normal, c'est ce qui a emporté l'ensemble : un firewall-cmd --reload, un csf -r, un ipset 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 :

BannissementCalculDurée
1ertime_minutes30 minutes
2e30 × 42 heures
3e120 × 48 heures
4e480 × 432 heures
5e1920 × 4128 heures, cinq jours et huit heures
6e et suivantsce 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à.

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

modeban.enabledListe communautaireVos propres découvertes
logfalseS'applique et journalise, rien n'est rejetéSignalées à la communauté, pas bloquées localement
logtrueS'applique et journaliseEnregistrées dans rip-local et visibles dans ban list, journalisées au lieu d'être rejetées
dropfalseRejetéeSeulement signalées. C'est un état permanent parfaitement sensé.
droptrueRejetéeRejetées aussi, avec escalade. La configuration complète.
offles deuxRien dans le chemin des paquets : le saut est retiré et la chaîne reste avec ses règlesEnregistré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

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

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

La sortie d'un enfermement est une seule commande. La règle de liste blanche est placée avant la règle de blocage, 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.
bash
# 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é.

Ne laissez jamais un jeu de règles enregistré référencer un ensemble 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

CommandeEffetSortie
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
syncUn 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
watchLe 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-queueEnvoie 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é
statusVersion, 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
doctorCe 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é
housekeepingRetire 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
versionAffiche la version et rien d'autre.0
helpLa 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.

bash
# 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.timer se 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.service est activé pour multi-user.target et tourne après network-online.target et 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.service redé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 avec NoNewPrivileges, 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_file est défini. Il effectue lui-même la rotation de ce fichier à log_max_mb et conserve log_keep gé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.
bash
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

CodeSignificationQue faire
0SainRien.
1Dégradé, mais une nouvelle tentative suivraLire 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.
2Non reproductible sans un humainLa 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é :

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

ForfaitServeurs inclusServeurs supplémentaires
FreeaucunNon disponible
ContributoraucunNon disponible
Professional1Autant que voulu
Business3Autant que voulu
Enterpriseselon contratselon 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.

FonctionSans licence
Téléchargement du fluxS'arrête. C'est la partie qui est payée.
La liste déjà présente dans le noyauReste. Elle n'est jamais vidée et continue de bloquer.
Détection localeContinue. Les treize sources, tous les seuils.
Blocage localContinue, escalade comprise, restauration après un redémarrage comprise.
SignalementsContinuent, dans la limite quotidienne de votre forfait.
Liste blanche, chaîne, ménage, rotation des journauxContinuent.
Mise à jour automatiqueContinue. Un agent obsolète sur un hôte impayé est notre risque, pas notre moyen de pression.
statusAffiche 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.

bash
# 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
L'étape 2 n'est pas optionnelle. Sur les hôtes examinés, la liste blanche de l'ancien script contenait 208 préfixes IPv4 et 61 préfixes IPv6 : robots de moteurs de recherche, Cloudflare et les propres machines de l'exploitant. Sans cet import, l'agent aurait signalé Googlebot, Cloudflare et la flotte elle-même dès son premier jour. Une liste blanche est le seul état qu'une migration ne peut reconstruire à partir de rien d'autre : reprenez-la donc avant de basculer quoi que ce soit, et relisez-la avant la première synchronisation avec 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.

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

bash
# 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 $?"
L'étape 5 est celle qu'on saute. Si vous supprimez les chaînes 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ômeCauseCommande
Ne démarre pas et nomme son fichier de configurationLe 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èreLe 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 videsSoit 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 unlicensedLe 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 partentban.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 toutLe démon de surveillance n'a jamais tourné.systemctl enable --now reportedip-agent.service
Une source affiche files=0Le 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 illisibleLe 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 jamaisSes 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é bannieElle 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 noyauUn 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 noneQuelqu'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élaiUn 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émarrageUn 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 lueUn 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 continueLa limite de disque a été atteinte.Libérer de l'espace, puis reportedip-agent housekeeping.
HTTP 429 sur les signalementsLa limite quotidienne de signalements de votre forfait.Les limites figurent sous Authentification.
La file d'attente ne cesse de grandirL'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'adressesnginx 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 versionLa 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

Security Focused
Conforme au RGPD
Made in Germany
Retour aux docs