Bloquer avec l'agent Linux
Le flux communautaire dans le noyau et les adresses que cet hôte bannit lui-même : les deux sortes d'ensembles et leurs durées de vie, comment un bannissement local commence et finit, l'escalade avec de vrais chiffres, la chaîne dans le noyau, les deux interrupteurs mode et ban.enabled, et comment on lève un bannissement ou tout à la fois.
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.
| 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 |
group | rip-group, rip-group-v6 | tous les ports, voir ci-dessous |
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 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. La réponse est gardée en cache jusqu'à 24 heures, et seul un état
api_key, license ou reputation ouvert le fait redemander à
chaque passage : un changement de forfait arrive donc sur l'hôte en un jour au plus. 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.
La liste de groupe
Une clé dans un groupe reçoit une sixième liste, récupérée en json avec le flux communautaire.
Chaque adresse est rejetée sur tous les ports dans les ensembles rip-group et
rip-group-v6, quel que soit le service que cet hôte expose. Une clé sans groupe ne
change rien : l'ensemble reste vide, et status affiche group=none.
La liste blanche du groupe, si elle en porte une, est lue dans la même requête et écrite dans
/var/lib/reportedip-agent/group-whitelist, au format de whitelist.conf
avec chaque note en commentaire. Ne modifiez pas ce fichier à la main, la synchronisation suivante
le réécrit. Elle est appliquée dans le même passage aux trois endroits où une liste blanche agit :
l'ensemble de liste blanche du noyau, le filtre de chaque liste et le filtre de signalement du
service de surveillance, une couche de plus à côté de la whitelist.conf propre de cet hôte et de sa
liste blanche automatique. Une clé qui quitte son groupe perd le fichier à la synchronisation
suivante.
whitelist list et status affichent les entrées du groupe sur des lignes
marquées group:, avec leurs notes. status nomme aussi le groupe et sa
taille à côté de la ligne du compte et affiche le nombre d'entrées de la liste de groupe sous les
listes comme pour toute autre. Voir Groupes pour ce qu'est un groupe, les limites
par formule, la liste blanche et le webhook côté portail.
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étections locales | |
|---|---|---|
| Ensembles | rip-ssh, rip-mail, rip-web, rip-ftp, rip-edge, rip-group, et un ensemble -v6 pour chacun | rip-local et rip-local-v6 |
| D'où viennent les adresses | De chaque membre de la communauté qui signale, 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 d'expiration est écoulé |
| Délai d'expiration 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 |
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 d'expiration 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 la 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. Avec ban.enabled: true, 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 d'expiration 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 |
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. Une synchronisation reconstruit la chaîne dès que son empreinte (la configuration et la
chaîne telle que le noyau l'affiche) diffère, son contenu est donc toujours ce que dit la
configuration ; sinon elle laisse la chaîne en place, pour que les compteurs de paquets continuent
de compter. 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 -A rip-blacklist -m set --match-set rip-group src -j DROP
# Only with a scan source in the config: a SYN to a port this host
# does not listen on is logged, at most ten a minute. Listening ports
# and the passive range of a running FTP server return first, then any
# port a socket listens on at this very moment.
iptables -N rip-blacklist-scan
iptables -A rip-blacklist-scan -p tcp -m multiport --dports 21,22,25,80,443,40110:40210 -j RETURN
iptables -A rip-blacklist-scan -p tcp -m socket --nowildcard -j RETURN
iptables -A rip-blacklist-scan -m limit --limit 10/min --limit-burst 20 \
-j LOG --log-prefix "rip-scan: "
iptables -A rip-blacklist -p tcp --syn -j rip-blacklist-scan
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 règles de ssh,
mail, web et ftp sont liées aux ports : une adresse de la liste web est donc rejetée sur 80 et 443 et peut toujours atteindre 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. Seuls edge et group s'appliquent à tous les
ports : pour edge, c'est ce que signifie la catégorie qui est derrière, pour
group, c'est le sens même d'un bannissement partagé.
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 timeout, que les deux outils exigent
sur l'ensemble avant qu'un élément puisse porter un délai d'expiration.
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-<list>:) 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étections
deviennent un jour une entrée. Par défaut, les deux sont actifs (mode: drop,
ban.enabled: true). Avec ban.enabled: false, mode: drop
applique la liste communautaire et rien de vos propres détections.
mode | ban.enabled | Liste communautaire | Vos propres détections |
|---|---|---|---|
log | false | Correspondances journalisées, rien n'est rejeté | Signalées à la communauté, pas bloquées localement |
log | true | Correspondances journalisées | 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 | peu importe | Rien dans le chemin des paquets : le saut est retiré ; avec ipset la chaîne reste avec ses règles, avec nftables la chaîne est supprimée | 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 échoue
bruyamment si un saut survit, car dire « off » et continuer de rejeter est pire qu'une erreur. Avec
nftables, la chaîne elle-même est supprimée. Les ensembles gardent leurs entrées avec les deux
moteurs : 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
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 direct 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 la durée qui reste encore dans le noyau 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 <address> 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, the sets keep their entries.
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 de lui-même à 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 ou /etc/nftables.conf.
Il ne regarde pas dans /etc/nftables.d.
Dernière mise à jour: · Maintenu par l’équipe ReportedIP