Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
pratique:informatique:parefeu:reaction [21/03/2025 22:08] – [Mes bouts de stream et de config] Zatalyzpratique:informatique:parefeu:reaction [16/06/2026 12:25] (Version actuelle) Zatalyz
Ligne 7: Ligne 7:
 Et autres qualités que l'auteur explique bien.  Et autres qualités que l'auteur explique bien. 
  
-Faudra que je participe au wiki officiel mais en attendant je met mes idées en place ici. +Faudra que je participe au [[https://reaction.ppom.me/intro/|wiki officiel]] mais en attendant je met mes idées en place ici. 
  
 <WRAP center round tip 60%> <WRAP center round tip 60%>
 J'utilise la syntaxe jsonnet, qui est la plus complète et lisible à mes yeux. Ma config se met dans ''/etc/reaction/server.jsonnet'' J'utilise la syntaxe jsonnet, qui est la plus complète et lisible à mes yeux. Ma config se met dans ''/etc/reaction/server.jsonnet''
 </WRAP> </WRAP>
 +
 +===== Commandes de base =====
 +Commandes:
 +  * **''start''** : Démarre le démon Reaction
 +    * ''reaction start [OPTIONS] --config <CONFIG>''
 +    * Options possibles : ''-c'' (**Requis**), ''-l'', ''-s'', ''-h''
 +    * Par exemple, pour un fichier ''reaction start -c /etc/reaction/server.jsonnet''
 +    * Et un dossier : ''reaction start -c /etc/reaction/''
 +    * Dans le cas des dossiers, les seuls fichiers du dossier qui seront lus automatiquement
 +      * Doivent se terminer par les extensions ''.json'', ''.jsonnet'', ''.yml'' ou ''.yaml''.
 +      * Ne doivent pas démarrer par ''.'' ou ''_''.
 +  * **''show''** : Affiche les correspondances actuelles et les actions (= qui est banni)
 +    * ''reaction show [OPTIONS] [NAME=PATTERN]''
 +    * ''[NAME=PATTERN]'' : n'affiche que les éléments correspondant à name=PATTERN regex
 +    * Options possibles : ''-s'', ''-f'', ''-l'', ''-h''
 +  * **''flush''** : Retire une cible de Reaction (unban)
 +    * ''reaction flush [OPTIONS] [NAME=PATTERN]''
 +    * Options possibles : ''-s'', ''-f'', ''-l'', ''-h''
 +    * Débannir quelqu'un : ''reaction flush IP''
 +  * **''test-regex''** : Tester une regex
 +    * reaction test-regex --config <CONFIG> <REGEX> [LINE]
 +    * Arguments:
 +      * <REGEX>  Regex to test
 +      * [LINE]   Line to be tested
 +    * Options possibles : ''-c'' (**requis**), ''-h''
 +  * **''test-config''** : Tester une configuration
 +    * ''reaction test-config [OPTIONS] --config <CONFIG>''
 +    * Options possibles : ''-c'' (**requis**), ''-f'', ''-v'', ''-h''
 +  * **''help''**, ''-h'' : Affiche l'aide sur les commandes et sous-commandes
 +  * **''-V''**, ''--version'' : Affiche la version de reaction
 +
 +Pour les options possibles (suivant les commandes) : 
 +  * ''-c'', ''--config <CONFIG>'' : Fichier de configuration en format json, jsonnet ou yaml ; ou dossier contenant ces fichers. Requis.
 +  * ''-l'', ''--loglevel <LOGLEVEL>'' : Niveau de log minimum à afficher [default: INFO].
 +  * ''-s'', ''--socket <SOCKET>'' : chemin d'accès au socket de communication client-daemon [default: /run/reaction/reaction.sock].
 +  * ''-h'', ''--help'' : Affiche l'aide.
 +  * ''-f'', ''--format <FORMAT>'' : comment formater la sortie [default: yaml] [valeurs possibles : json, yaml].
 +  * ''-v'', ''--verbose'' : affiche plus d'infos en sortie.
 +
 +==== Les plus fréquement utilisées ====
 +
 +Voir qui est banni :
 +
 +  reaction show
 +
 +Débannir quelqu'un :
 +
 +  reaction flush IP
 +
 +Tester le dossier de configuration :
 +  reaction test-config -c /etc/reaction
 +
 +Démarrer (sans systemd) : 
 +  reaction start -c /etc/reaction/
 +
 +==== Base de donnée ====
 +
 +Par défaut les bases de données sont dans ''/var/lib/reaction/'' (''reaction-matches.db'' et ''reaction-flushes.db''). Les effacer remet tout à zéro.
 +
 +Si on lance avec la commande dans le terminal, ces deux fichiers sont là où on lance la commande (je crois, j'ai un doute... en tout cas j'étais dans /etc/reaction et ils ont popés là).
 +===== Concepts =====
 +  * Patterns : mots-clés utilisés ensuite au sein des expressions régulières pour "capturer" une info, typiquement les ip. Les types déjà renseignés : ipv4, ipv6, ip (qui inclue ipv4 et ipv6). On peut crée des patterns personnalisés en plus.
 +  * Streams : équivalent des jails de fail2ban. On va par exemple déclarer "ssh" et indiquer où les logs doivent être lu et ce qui doit s'y appliquer.
 +  * Filters : groupe d'expressions régulières à appliquer à un stream (par exemple, pour détecter les erreurs de connexion à ssh).
 +  * Trigger : conditions, quand l'expression régulière est trouvée, à remplir pour passer à la suite. Typiquement, ''retry'' (nombre d'occurence) et ''retryperiod'' (période où ce nombre sera considéré comme "trop").
 +  * Actions : ce qui est exécuté quand le filter est déclenché. Par exemple, bannir. Le cœur du logiciel !
 +  * Start/Stop : commandes qui seront exécuté au démarrage et à l'arrête de Reaction, par exemple créer les tables dans nftables et les enlever.
 +
 +La syntaxe est en jsonnet, ce qui permet d'écrire des variables et fonctions en plus. 
  
 ===== Installation et lancement automatique ===== ===== Installation et lancement automatique =====
-Pour l'installer, il y a un [[https://framagit.org/ppom/reaction/-/releases/|paquet debian]] mais pas dans les dépôts. Installer aussi Minisign pour vérifier l'intégrité. Ci-dessous en exemple mais la page des releases donne les bons numéros de release...+Pour l'installer, il y a un [[https://framagit.org/ppom/reaction/-/releases/|paquet debian]] mais pas dans les dépôts. Installer aussi Minisign pour vérifier l'intégrité. Ci-dessous en exemple mais [[https://framagit.org/ppom/reaction/-/releases|la page des releases]] donne les bons numéros de release (et les instructions)... J'utilise wget plutôt que curl, ce dernier ayant quelques soucis de sécurité (et puis wget est déjà de base sur mon serveur).
  
 <code>sudo apt install minisign <code>sudo apt install minisign
-wget https://static.ppom.me/reaction/releases/v1.4.1/reaction_1.4.1-1_amd64.deb \ +wget https://static.ppom.me/reaction/releases/v1.4.1/reaction_1.4.1-1_amd64.deb 
-     https://static.ppom.me/reaction/releases/v1.4.1/reaction_1.4.1-1_amd64.deb.minisig +wget https://static.ppom.me/reaction/releases/v1.4.1/reaction_1.4.1-1_amd64.deb.minisig 
-minisign -VP RWSpLTPfbvllNqRrXUgZzM7mFjLUA7PQioAItz80ag8uU4A2wtoT2DzX -m reaction_1.4.1-1_amd64.deb && +minisign -VP RWSpLTPfbvllNqRrXUgZzM7mFjLUA7PQioAItz80ag8uU4A2wtoT2DzX -m reaction_1.4.1-1_amd64.deb 
-  rm reaction_1.4.1-1_amd64.deb.minisig && +rm reaction_1.4.1-1_amd64.deb.minisig 
-  sudo apt install ./reaction_1.4.1-1_amd64.deb+sudo apt install ./reaction_1.4.1-1_amd64.deb 
 +rm reaction_1.4.1-1_amd64.deb
 </code> </code>
-   + 
-Ensuite on crée un fichier de config, on le modifie, et aussi un truc pour systemd.+Ensuite on crée le ou les fichiers de config, et on les bidouillent. Voir plus bas la/les confs.
   sudo mkdir /etc/reaction/   sudo mkdir /etc/reaction/
-  sudo nano /etc/reaction/reaction.jsonnet +  sudo nano /etc/reaction/server.jsonnet
-(voir plus bas la/les confs).+
  
-  sudo nano /etc/systemd/system/reaction.service+==== Démarrage automatique ==== 
 + 
 +<WRAP center round tip 100%> 
 +Avant de démarrer  automatiquement le service, c'est pas mal de tester sa configuration en lançant seulement la ligne de démarrage dans le terminal, ce qui casse à la première erreur : 
 +  sudo reaction start -c /etc/reaction/server.jsonnet 
 + 
 +On peut aussi vérifier la syntaxe avec : 
 +  sudo reaction test-config -c /etc/reaction 
 +</WRAP> 
 + 
 +Avec la version 2 il y a un à présent un service systemd déjà fourni. Il se trouve sur /lib/systemd/system/reaction@.service. 
 + 
 +Après installation et vérification, pour utiliser systemd : 
 + 
 +<code>sudo systemctl daemon-reload 
 +sudo systemctl enable --now reaction@reaction 
 +</code> 
 + 
 +Ce qui suit après le @ est le chemin vers la configuration dans /etc/ ; comme je lui fais lire le dossier, c'est donc juste "reaction" pour "/etc/reaction".  
 + 
 +<WRAP center round alert 100%> 
 +<WRAP center round info 100%> 
 +Je laisse la vieille doc "pour mémoire" ci-dessous mais ce n'est plus forcément utile.  
 +</WRAP> 
 + 
 +Je vais créer deux services : un pour Reaction proprement dit, un pour avertir en cas de plantage. Vu que j'ai eu des plantages muets, je lui dis de se relancer si ça lui arrive((Je précise que ça date des premières versions du logiciel, qui a bien évolué depuis.)) et sinon, j'ai une alerte. 
 + 
 + 
 +<code bash /etc/systemd/system/reaction.service> 
 +[Unit] 
 +Description=Reaction to ban bad ip 
 +After=network.target 
 +# Alerte si ça "fail" 
 +OnFailure=reaction-alert.service
  
-<code> 
 [Install] [Install]
 WantedBy=multi-user.target WantedBy=multi-user.target
 +
 [Service] [Service]
-ExecStart=/usr/bin/reaction start -c /etc/reaction/server.jsonnet+ExecStart=/usr/bin/reaction start -c /etc/reaction/
 StateDirectory=reaction StateDirectory=reaction
 RuntimeDirectory=reaction RuntimeDirectory=reaction
-WorkingDirectory=/var/lib/reaction+WorkingDirectory=/var/lib/reaction 
 Restart=on-failure Restart=on-failure
 RestartSec=3 RestartSec=3
 +# Anti-flood de redémarrage en boucle
 +StartLimitIntervalSec=400
 +StartLimitBurst=3
 </code> </code>
  
-Vu que j'ai eu des plantages muets, je lui dis de se relancer si ça lui arrive.+<code bash /etc/systemd/system/reaction-alert.service> 
 +[Unit] 
 +Description=Envoie une alerte si Reaction plante
  
-Et puis on lance +[Service] 
 +Type=oneshot 
 +ExecStart=/usr/local/bin/reaction-alert.sh 
 +</code> 
 + 
 +Et le script pour envoyer un mail (ça aurait pu être autre chose, mais j'aime bien les mails).  
 +<code> 
 +#!/bin/bash 
 +LOCKFILE="/tmp/reaction-alert.lock" 
 +# Délai en minutes, donc 3 h = 180 
 +DELAY=180 
 + 
 +# Si une alerte a déjà été envoyée il a moins de $DELAY, on quitte 
 +if [ -e "$LOCKFILE" ] && [ "$(find "$LOCKFILE" -mmin -lt $DELAY)" ]; then 
 +    logger -t reaction-alert "Notification de plantage de Reaction déjà envoyée récemment, aucune alerte renvoyée." 
 +    exit 0 
 +fi 
 + 
 +touch "$LOCKFILE" 
 + 
 +SUBJECT="Reaction a planté sur $(hostname)" 
 +TO="monemail@domaine.org" 
 +BODY="Le service Reaction sur $(hostname) semble avoir un souci.  
 +Arrêt à $(date). 
 +
 + 
 +echo "$BODY" | mail -s "$SUBJECT" "$TO" 
 +logger -t reaction-alert "Alerte envoyée par mail à $TO pour le plantage de Reaction" 
 + 
 +</code> 
 + 
 +On démarre le service 
 <code>sudo systemctl enable reaction.service <code>sudo systemctl enable reaction.service
 sudo service reaction start</code> sudo service reaction start</code>
- 
-<WRAP center round tip 60%> 
-Mais avant de démarrer le service, c'est pas mal de tester sa configuration en lançant seulement la ligne de démarrage dans le terminal, ce qui casse à la première erreur : 
-  sudo reaction start -c /etc/reaction/server.jsonnet 
 </WRAP> </WRAP>
  
-===== Commandes de base ===== 
-Voir qui est banni : 
-  reaction show 
  
-Débannir quelqu'un : 
-  reaction flush IP 
  
-Consulter l'aide : 
-  reaction --help 
  
  
-Par défaut les bases de données sont dans ''/var/lib/reaction/'' : ''reaction-matches.db'' et ''reaction-flushes.db''. Les effacer remet tout à zéro+===== Configuration ===== 
-===== Envoyer des mails quand il y a une action ===== +Pour la doc  complète, se référer à [[https://reaction.ppom.me/]]. Concernant ma configuration, je pars avec ces spécificités : 
-J'ai une commande qui va envoyer un mail via un script, en ayant en paramètre deux variables : ''ip'' (l'ip bannie) et ''rule'' (la raison du bannissement). +  * Utilisation de nftables 
-<code>local sendmail(ip,rule) = +  * Découpage de la configuration par services (pour faciliter certaines automatisations) 
-  mail: { +  * Analyse des ip bannies pour en déduire des plages et bannir ces dernières ?  
-    cmd: ['sh', '/root/scripts/sendmailreaction/mailreaction.sh', ip, rule],+  * Rapport (journalier ?) sur les ip bannies (histoire de pouvoir suivre ce qui se passe et la charge) ? 
 + 
 +Point côté syntaxe : dans les noms, on peut utiliser le tiret bas, mais pas de tiret classique, ça va faire des erreurs car jsonnet l'interprète... Donc "mon_filtre" est ok, pas "mon-filtre".  
 +==== Découpage des fichiers ==== 
 +Il s'agit de me simplifier un peu la lecture et la maintenance. Sur l'exemple ici, j'ai 3 types de fichiers 
 +  * Définitions ce qu'est l'action pour bannir/débannir, les périodes par défaut de bannissement... C'est le fichier ''_lib.jsonnet''. 
 +  * ''server.jsonnet'' qui contient les trucs de base : comment les ip sont formatées et ce qui se passe quand on démarre/arrête le service. 
 +  * Les fichiers de stream (''ssh.jsonnet'', ''web.jsonnet'', etc) qui vont lister, par service, quel fichier de log analyser et sur quelles regex on va bannir. 
 + 
 +On paramètre évidement le lancement de Reaction (avec service ou direct en ligne de commande) pour lire tout le contenu du dossier (ici /etc/reaction). 
 + 
 +=== server.jsonnet === 
 +Ici c'est assez classique, le seul point important étant que je tourne avec nftables. Il peut être utile de spécifier les ip à ne pas bannir avec Reaction, couplé avec un fichier [[pratique:informatique:parefeu:nftables|nftables]] complet (entre autre sur la vérification des ip locales)ça évite de se bannir son propre routeur ou en tant que sysadmin.  
 + 
 +Sur un pare-feu, l'ordre des règles a une importance((Même si la façon dont ça s'applique est parfois complexe à comprendre.)), aussi j'ai préféré directement ajouter les bonnes instructions dans [[https://port.numenaute.org/zatalyz/zscript-sysadmin/src/branch/main/files/nftables/nftables.d|mes fichiers nftables]] :  
 + 
 +<code bash nftables.conf_ou_autre> 
 +set reaction_v4 { 
 +    type ipv4_addr 
 +    flags interval 
 +
 +set reaction_v6 { 
 +    type ipv6_addr 
 +    flags interval 
 +
 +chain input_all { 
 + # Par défaut, on rejette tout à moins de suivre une des règles ci-dessous. 
 + type filter hook input priority 0; policy drop; 
 +  
 + # Vérifie si l'IP est bloquée (du plus long au plus court) 
 + # ... Durablement  
 + ip saddr @blocklist_v4 drop 
 + ip6 saddr @blocklist_v6 drop 
 + # ... Par Reaction 
 + ip saddr @reaction_v4 drop 
 + ip6 saddr @reaction_v6 drop 
 + # Ou dans la liste temporaire 
 + ip saddr @tempblock_v4 drop 
 + ip6 saddr @tempblock_v6 drop 
 +# [...] 
 +
 +</code> 
 + 
 +De ce fait, je ne crée pas les sets à la volée au démarrage de Reaction (l'action ''start'' ne déclenche rien), et c'est déjà au bon endroit dans le bon tableau.  
 + 
 +Les patterns des ip sont à présent dans le logiciel (depuis la version 2.0?ce qui rend certaines déclarations plus rapidesNotons aussi l'apparition de la gestion des masques de sous-réseau.  
 + 
 +<code json server.jsonnet> 
 +
 +  patterns: { 
 +    ip: { 
 +    // IPv4 et IPv6, et masque des ipv6. 
 +      type: 'ip', 
 +      ipv6mask: 64, 
 +      ignore: [ 
 +        // Ne pas bannir les ip des sysadmins et sur le local 
 +        // Ne pas oublier la virgule après chaque ip, pour l'énumération ;) 
 +        '127.0.0.1', 
 +        '::1', 
 +        // Sous-réseau d'une box 
 +        '192.168.1.0/24', 
 +        // Sous-réseau proxmox ? 
 +        //'10.0.0.0/8', 
 +        // Ip fixes de sysadmins et/ou des bastions à ajouter. 
 +      ], 
 +    }, 
 +    ipmask: { 
 +      // Uniquement utilisé pour les filtres les plus aggressifs, risque de trop ban sinon ! 
 +      type: 'ip', 
 +      // ipv4 : 24 = tout le dernier bit, soit toute ip du type a.b.c.* (tout est dans le joker). 
 +      ipv4mask: 24,  
 +      // ipv6 : 48 = ban des datacenters. 56 est un peu moins excessif. 64 est une norme de particulier. 
 +      ipv6mask: 56, 
 +      ignore: [ 
 +        '127.0.0.1', 
 +        '::1', 
 +        '192.168.1.0/24', 
 +        '82.65.53.240', 
 +        '2a01:e0a:26f:e650:aaa1:59ff:fe82:5187', 
 +      ]
 +    },
   },   },
 +
 +// nftables est directement paramétré avec des tables/sets pour Reaction, histoire que ce soit au bon endroit dans la chaine. 
 +  start: [],
 +  // Je flush le set au stop, quand même. Car Reaction les renvoie au redémarrage ; évite les doublons.
 +  stop: [
 +    ['nft', 'flush', 'set', 'inet', 'firewall', 'reaction_v4'],
 +    ['nft', 'flush', 'set', 'inet', 'firewall', 'reaction_v6'],
 +  ],
 +}
 +
 +</code>
 +
 +=== _lib.jsonnet : actions par défaut ===
 +Ce fichier sert à définir les actions telles que "banFor", qu'on configure évidement avec notre commande pour nftables((Cela m'a valu quelques erreurs en recopiant trop bêtement la doc officielle, qui est pour iptable...)). 
 +
 +On peut ainsi définir des actions par défaut : combien de temps on bannit, combien de lignes dans les logs avant de bannir, etc. Cela permet par exemple de définir pour tout le monde la même durée de bannissement et de la changer à un seul endroit si besoin. 
 +
 +Le fait que le fichier commence par "_" fait qu'il ne sera pas lu automatiquement au lancement de Reaction, il faudra l'appeler là où il est utile (dans les streams). Si on ne fais pas ça (s'il est noté "lib.jsonnet" par exemple), on aura l'erreur ''ERROR While reading ssh.jsonnet in /etc/reaction: variable is not defined: filter_default'' lors de l'execution.
 +
 +Mon "banFor" a une subtilité : il attends qu'on déclare un temps (celui avant de débannir) ET une raison. Cette info peut être récupérée dans un log ou envoyé dans un mail, ou autre (cf le script plus bas).
 +
 +Je n'utilise pas les outils "nft" fournis avec Reaction, au final la commande de base Unix fonctionne bien pour mes besoins.
 +
 +<code json _lib.jsonnet>
 +local banFor(time, name) = {
 +    ban_v4: {
 +        cmd: ['nft', 'add', 'element', 'inet', 'firewall', 'reaction_v4', '{ <ip> }'],
 +        ipv4only: true,
 +    },
 +    ban_v6: {
 +        cmd: ['nft', 'add', 'element', 'inet', 'firewall', 'reaction_v6', '{ <ip> }'],
 +        ipv6only: true,
 +    },
 +    unban_v4: {
 +        after: time,
 +        cmd: ['nft', 'delete', 'element', 'inet', 'firewall', 'reaction_v4', '{ <ip> }'],
 +        ipv4only: true,
 +    },
 +    unban_v6: {
 +        after: time,
 +        cmd: ['nft', 'delete', 'element', 'inet', 'firewall', 'reaction_v6', '{ <ip> }'],
 +        ipv6only: true,
 +    },
 +    log: {
 +        cmd: ['/etc/reaction/_ban.sh', '<ip>', name],
 +        oneshot: true,
 +    },
 }; };
 +
 +// retry et retryperiod sont quand il y a plusieurs tentatives autorisées
 +// juste mettre le banFor sinon... Le ban sera alors à la première tentative.
 +
 +// Filtre (et options) par défaut : ni trop doux, ni trop cruel.
 +local filter_default = {
 +  retry: 3,
 +  retryperiod: '1h',
 +  actions: banFor('48h', 'ban par défaut'),
 +};
 +
 +// Filtre doux : c'est peut-être légitime. Et peut-être pas. Ça délaye les attaques.
 +local filter_soft = {
 +  retry: 6,
 +  retryperiod: '1h',
 +  actions: banFor('30s', 'ban temporaire'),
 +};
 +
 +// Filtre violent : un seul essai, banni un mois.
 +local filter_hard = {
 +  actions: banFor('720h', 'banni un mois'),
 +};
 +
 +
 +// Exposer les définitions précédentes pour qu'elles soient accessibles depuis un autre fichier Jsonnet
 +{
 +  banFor: banFor,
 +  filter_default: filter_default,
 +  filter_soft: filter_soft,
 +  filter_hard: filter_hard,
 +}
 +
  
 </code> </code>
  
-Le script : +== Script bash de log, notif, etc == 
-<code>#!/bin/bash +Un script très basique pour "loguerles adresses bannis, avec la raison, appelé par l'action "banFor". Je compte me servir de ce genre de log pour alimenter par la suite : 
-# Envoyer un mail +  * les ips bloquées en longue durée (celles qui reviennent plusieurs fois via les filtres doux et par défaut : 6 fois c'est peut-être une erreur, 12 c'est de la maladresse, 24 c'est de l'acharnement...), 
-dossiermail="/root/scripts/sendmailreaction/mail" +  * les ips en bloc (ipmask). Ce n'est pas du tout paramétré pour le moment ! 
-titremail="$1 banni+Le format actuel de mon log n'est sans doute pas parfait pour ça, mais s'améliorera au fil des tests.
-# Création du fichier du corps du mail dans un fichier temporaire qui évite toute collision +
-corpmail="$(mktemp -p $dossiermail)" +
-listeip="/var/lib/reaction/reaction-matches.db"+
  
-Vérifier si l'adresse a déjà été banni pour éviter de flooder lorsqu'on relance +<code bash _ban.sh> 
-if grep -q $1 $listeip; then +#!/bin/bash 
-    exit +# Envoi d'un mail ou notif ou ce qu'on veut 
-else +Log des ip 
-    Message pour le corps du mail +echo "$1 banni car $2 // Date : $(date)" >> /var/log/reaction_ban.log 
-    echo "Méchant $1 $2 \n \n" >> $corpmail +</code>
-    # Ajouter les logs pour les détails +
-    journalctl --since yesterday | grep $1 >> $corpmail +
-    #Envoyer le mail +
-    cat $corpmail | mail -s "$titremail" root +
-    # Effacer le corps du crime pour éviter d'encombrer sans intérêt +
-    rm $corpmail +
-fi</code>+
  
-Penser évidement à créer les dossiers ''/root/scripts/sendmailreaction/'' et ''/root/scripts/sendmailreaction/mail'' avant de lancer le reste.+=== Streams === 
 +Je met ici une config très basique pour ssh (voir [[https://port.numenaute.org/zatalyz/zscript-sysadmin/src/branch/main/files/reaction|la forge]] pour plus de filtres), suffisante pour lister les "pièges" dans la syntaxe.
  
-Plus loin dans le fichier de configuration de Reaction, dans les streams, comment dire qu'il faut envoyer un mail (exemple sur ssh) : +<WRAP center round tip 100%> 
-<code>  streams: {+[[https://www.sshguard.net/|SSHguard]]((Empaqueté sur Debian)) est souvent plus adapté pour la protection de SSH, car il bannit sur des durées de plus en plus longues. Donc attention à ne pas gêner son fonctionnement avec Reaction. Ce qui n'empêche pas d'ajouter quelques pains bien violents à certains attaquants. Personnellement je n'ai aucune pitié avec ceux qui tentent de se connecter en rootet un banissement d'un mois est presque tendre ; je ne vois pas l'intérêt de les laisser essayer deux fois de suite. Aucun sysadmin suivant les bonnes pratiques ne pourrait faire l'erreur de tenter de se connecter en root.  
 +</WRAP> 
 + 
 +<code json ssh.jsonnet> 
 +local lib = import '_lib.jsonnet'; 
 +
 +  streams: {
     ssh: {     ssh: {
-      cmd: ['journalctl', '-fn0', '-u', 'ssh.service'],+      cmd: ['journalctl', '-fn0', '-u', 'sshd.service'],
       filters: {       filters: {
 +        badssh: lib.filter_default + {
 +          regex: [
 +            @'User root from <ip> not allowed because not listed in AllowUsers',
 +          ],
 +        // ce filtre est peu pertinent avec sshguard et va le ralentir dans sa lutte contre les méchants.
         failedlogin: {         failedlogin: {
           regex: [           regex: [
Ligne 112: Ligne 400:
             @'Failed password for .* from <ip>',             @'Failed password for .* from <ip>',
           ],           ],
-          retry: 6,+          retry: 3,
           retryperiod: '6h',           retryperiod: '6h',
-          actions: banFor('48h') + sendmail('<ip>','"Banni 48h pour tentative de co à SSH"'),+          actions: lib.banFor('48h', 'SSH: login échoué'),
         },         },
       },       },
     },     },
 +  },
 +}
 </code> </code>
  
-===== Actions par défaut ===== +  * ''local lib import '_lib.jsonnet';'' => il est nécessaire d'appeler le fichier avec les définitions pour la suite, et on fait une variable pour les appels la concernant (''lib''). 
-On peut créer des actions par défaut qu'on appelle ensuite ; cela permet par exemple de définir pour tout le monde la même durée de bannissement et de la changer à un seul endroit si besoin+  * ''cmd: ['journalctl', '-fn0', '-u', 'sshd.service']'' => indique quel fichier de log lire. 
 +  * ''lib.filter_default'' et ''lib.banFor'' : on utilise les actions ''banFor'' et ''filter_default'' mais comme elles sont dans le fichier ''_lib.jsonnet'', on appelle la variable définie avant (''lib''). Sinon, ben... ça marche pas.
  
-Ce n'est pas forcément pertinent pour toutpar exemple je préfère préciser la raison du bannissement pour savoir quels services se font attaquer.+Concernant l'appel des diverses fonctionsil y a plein de petites subtilités. Par exemple, si on veut appliquer les actions par défaut (même durée de ban, même valeurs pour retry, retryperiod) mais déclarer une action complémentaire comme "stream_name", alors on fait ceci : 
  
-On déclare la commande : 
 <code> <code>
-local filter_default +[...] 
-  retry3, +        apache_auth: lib.filter_default 
-  retryperiod: '6h', +          regex[ 
-  actions: banFor('24h'), +            @'^.*client <ip>.* regex', 
-}; +          ]
-</code> +          actionslib.filter_default.actions + lib.stream_name('apache_auth'),
- +
-Et dans les streams, on l'appelle de cette façon :  +
-<code> +
-+
-  streams: { +
-    ssh: { +
-      filters: { +
-        failedlogin: filter_default + { +
-          regex['...'],+
         },         },
-      }, +[...]
-    }, +
-  }, +
-}+
 </code> </code>
-===== Découper la configuration ===== 
-On peut tout mettre dans un seul fichier, moi j'aime bien séparer pour m'y retrouver. Faut avouer que je prévois max de regex à un moment, et max de services couverts aussi. 
  
 +Ici ''lib.filter_default.actions'' précise qu'il faut appliquer les actions par défaut de "filter_default". ''+ lib.stream_name('apache_auth')'' permet d'ajouter l'action stream_name (qui permet de loguer pour "quoi" on a banni), en précisant une raison personnalisée donc. Il faut quand même déclarer ''lib.filter_default'' au niveau du stream (après "apache_auth" dans l'exemple), afin que les paramètres "retry" et "retryperiod" s'appliquent sur le stream. 
 +==== Réaliser des actions plus compliqués ====
 +<WRAP center round tip 60%>
 +Ce qui suit est plus pour retrouver comment faire au besoin, mais la configuration précédente (log, sans mail en plus, intégré à banFor) me semble suffisante en général.
 +</WRAP>
  
 +Lorsqu'il y a bannissement via Reaction, j'aimerais pouvoir réaliser certaines actions, comme être avertie par mail. 
  
-Dans le fichier principalon peut appeler un autre fichier avec cette syntaxe : +Bonen vrai, ça dépendSur certains services, la surveillance via des mails, au début, permet d'affiner les règles et éviter les faux positifs. Ensuiteça fait surtout du bruit. Il devient alors plus utile de loguer les ip bannieset de vérifier s'il y a des schémas : même plages d'ip par exemple. Ou ip qui continuent d'être bannies mois après mois. 
-<code>+
-  streams: { +
-    ssh: import 'ssh.jsonnet', +
-  }, +
-}</code>+
  
-et ''ssh.jsonnet'' va contenir notre morceau propre au stream+Bref, tout ça va se faire via notre fichier _lib.jsonnet, avec des actions diverses.
  
-<WRAP center round todo 60%> +=== Lancer un script === 
-Mais j'ai du manquer un boutça me fait des erreurs ça... Il ne reconnait pas le "banforpourtant déclaré dans le fichier principal+Si on veut bannir ET réaliser une action autre dans la foulée (par exemple envoyer un mail), le plus simple sera finalement de faire un script de ce type :  
-</WRAP>+ 
 +<code bash _ban.sh> 
 +#!/bin/bash 
 +# Bannissement 
 +nft46 add element inet reaction ipvXbans { "$1
 + 
 +# Envoi d'un mail 
 +# cf le script plus bas..
 +</code>
  
-===== Mes bouts de stream et de config ===== +On modifie alors _lib.jsonnet sur la partie cmd : 
-Fichier principal +<code json _lib.jsonnet>
-<code jsonnet server.jsonnet> +
-// This file is using JSONNET, a complete configuration language based on JSON // See https://jsonnet.org +
-// action pour bannir/débannir+
 local banFor(time) = { local banFor(time) = {
   ban: {   ban: {
-    cmd: ['ip46tables', '-w', '-A', 'reaction', '-s', '<ip>', '-j', 'DROP'], +    cmd: ['nft46', 'add element inet reaction ipvXbans { <ip> }'], 
-  }, +  } 
-  unban: { +</code> 
-    after: time, +devient  
-    cmd: ['ip46tables', '-w', '-D', 'reaction', '-s', '<ip>', '-j', 'DROP'], +<code json _lib.jsonnet> 
-  }, +local banFor(time) = { 
-};+  ban: { 
 +    cmd: ['sh', '-c', '/etc/reaction/_ban.sh <ip>'], 
 +  } 
 +</code>
  
-// Envoyer un mail lors des actions et précisant ip et raison +Ce qui autorise toutes les fantaisies. 
-local sendmail(ip,rule) = {+ 
 +=== Action : envoi de mail === 
 +On peut aussi déclarer une action spéciale pour l'envoi de mail, ce qui permet de l'appeler dans les streams et de personnaliser le message en rapport avec le filtre déclenché.  
 + 
 +Le script suivant prend deux variables : ''ip'' (l'ip bannie) et ''rule'' (la raison du bannissement). 
 + 
 +<code json _lib.jsonnet>local sendmail(ip,rule) = {
   mail: {   mail: {
-    cmd: ['sh', '/root/scripts/sendmailreaction/mailreaction.sh', ip, rule],+    cmd: ['sh', '/etc/reaction/_mail.sh', ip, rule],
   },   },
 }; };
 +</code>
  
-// pourquoi ça ouvre ici ? Mais, ça marche. +Le script : 
-{ +<code>#!/bin/bash 
-// patterns are substitued in regexes. when a filter performs an action, it replaces the found pattern. +# Envoyer un mail 
-// reaction regex syntax is defined here: https://github.com/google/re2/wiki/Syntax +dossiermail="/root/scripts/sendmailreaction" 
-// jsonnet's @'string' is for verbatim strings. +titremail="$1 banni" 
-  patterns: { +# Création du fichier du corps du mail dans un fichier temporaire qui évite toute collision 
-    // IPs can be IPv4 or IPv6 +corpmail="$(mktemp -p $dossiermail)" 
-    // ip46tables (C program also in this repo) handles running the good commands +listeip="/var/lib/reaction/reaction-matches.db"
-    ip: { +
-      regex: @'(([0-9]{1,3}\.){3}[0-9]{1,3})|([0-9a-fA-F:]{2,90})', +
-      ignore: ['127.0.0.1', '::1'], +
-    }, +
-  },+
  
-// Commandes exécutées au lancement +# Vérifier si l'adresse a déjà été banni pour éviter de flooder lorsqu'on relance 
-  start: [ +if grep -q $1 $listeip; then 
-    ['ip46tables', '-w', '-N', 'reaction'], +    exit 
-    ['ip46tables', '-w', '-I', 'INPUT', '-p', 'all', '-j', 'reaction'], +else 
-    ['echo', 'Le service a démarré', '|', 'mail', '-s', '"Démarrage de Reaction"', 'root'], +    # Message pour le corps du mail 
-  ], +    echo "Méchant $1 ! $2 \n \n>> $corpmail 
-  // Commandes exécutées à l'arrêt, après tout le reste +    # Ajouter les logs pour les détails 
-  stop: [ +    journalctl --since yesterday | grep $1 >> $corpmail 
-    ['ip46tables', '-w', '-D', 'INPUT', '-p', 'all', '-j', 'reaction'], +    #Envoyer le mail 
-    ['ip46tables', '-w', '-F', 'reaction'], +    cat $corpmail | mail -s "$titremail" root 
-    ['ip46tables', '-w', '-X', 'reaction'], +    # Effacer le corps du crime pour éviter d'encombrer sans intérêt 
-    ['echo', 'Le service est éteint', '|', 'mail', '-s', '"Extinction de Reaction"', 'root'], +    rm $corpmail 
-  ],+fi</code>
  
-// Streams : c'est là qu'on va définir les services et règles menant au bannissement +Penser évidement à créer le dossier ''/root/scripts/sendmailreaction/'' avant de lancer le reste.
-  streams: { +
-    ssh: import 'ssh.jsonnet', +
-    kernel: import 'kernel.jsonnet', +
-    badguypostfix: import 'badguypostfix.jsonnet', +
- }, +
-+
-</code>+
  
-Pour ssh et kernelil s'agit des configurations par défaut auxquelles j'ai ajouté mon envoi de mail : +Dans les streams (en ayant des fichiers modulaires)comment dire qu'il faut envoyer un mail (exemple sur ssh) 
-<code jsonnet ssh.jsonnet+<code>  streams: { 
-    {+    ssh: {
       cmd: ['journalctl', '-fn0', '-u', 'ssh.service'],       cmd: ['journalctl', '-fn0', '-u', 'ssh.service'],
       filters: {       filters: {
Ligne 238: Ligne 518:
           retry: 6,           retry: 6,
           retryperiod: '6h',           retryperiod: '6h',
-          actions: banFor('48h') + sendmail('<ip>','"Banni 48h pour tentative de co à SSH"'),+          actions: lib.banFor('48h') + lib.sendmail('<ip>','"Banni 48h pour tentative de co à SSH"'),
         },         },
       },       },
     },     },
- 
 </code> </code>
  
-<code jsonnet kernel.jsonnet> +===== Transformer les règles de fail2ban ===== 
-    // Ban hosts which knock on closed ports. +Ça c'est du chantier...
-    // It needs this iptables chain to be used to drop packets: +
-    // ip46tables -N log-refuse +
-    // ip46tables -A log-refuse -p tcp --syn -j LOG --log-level info --log-prefix 'refused connection: ' +
-    // ip46tables -A log-refuse -m pkttype ! --pkt-type unicast -j nixos-fw-refuse +
-    // ip46tables -A log-refuse -j DROP +
-    { +
-      cmd: ['journalctl', '-fn0', '-k'], +
-      filters: { +
-        portscan: { +
-          regex: ['refused connection: .*SRC=<ip>'], +
-          retry: 4, +
-          retryperiod: '6h', +
-          actions: banFor('720h') + sendmail('<ip>','"est banni un mois pour avoir fait un truc louche sur un port fermé"'), +
-        }, +
-      }, +
-    },+
  
 +Quand on a fail2ban installé, retrouver les regex est un sacré bazar. Il n'y a pas de solution "immédiatement fonctionnelle". Mais deux commandes utiles.
  
-</code>+  fail2ban-client -d 
 +Celle-ci va lister toutes les règles actives, dans un format permettant de voir quel fichier de log est surveillé et divers détails. C'est assez verbeux, mais utile.
  
-Pour postfix, pour le moment je cible certains bot cons. +  fail2ban-client get JAIL failregex 
-<code jsonnet badguypostfix.jsonnet> +Remplacer "JAIL" par le nom de la jail ; ne liste que les regex dans un format relativement lisible (enfinça reste des regex)On remarque cependant que la partie "<HOST>" n'est pas renseignéc'est ballot hein ? Suivant les jailcette variable qui capture l'ip de l'attaquant est soit dans un préfixe (à trouver...) soit sous un format décompressé
-    { +
-      cmd: ['journalctl', '-fn0', '-u', 'postfix@-.service'], +
-      filters:+
-        badguy: { +
-          regex: [ +
-            @'^.* improper command pipelining after CONNECT from unknown\[<ip>\].*', +
-            @'^.*\[<ip>\].*tiscali.it.*', +
-            @'^.* NOQUEUE: reject: RCPT from unknown\[<ip>\]: 504 5.5.2 .* Helo command rejected: need fully-qualified hostname; .*', +
-            @'^.*connect from .*censys.*\[<ip>\]', +
-            @'^.*connect from .*stretchoid.*\[<ip>\]', +
-            @'^.*RCPT from unknown\[<ip>\].*5yuehaoyunqi\.xyz.*', +
-            @'^.*RCPT from unknown\[<ip>\].*funteensex\.com.*',+
  
-          ]+Jails que je tente d'analyser et recopier :  
-          retry1+  * Apacheerror.log 
-          retryperiod: '6h', +    * ''apache-auth'' la plupart des règles semblent utiles. Pas forcément un ban trop violent au premier abord. 
-          actionsbanFor('720h') + sendmail('<ip>','"Banni un mois sans seconde chance pour avoir mal causé à Postfix"'), +    * ''apache-botsearch'' : c'est surtout le scan de pages à attaquer ; déjà mis dans mes filtres Reaction et aucune pitié avec çamais on note que là c'est la version pour "error" et non "access" donc pas les mêmes ? 
-        }+    * ''apache-modsecurity'' faut que je creuse plus, ça semble relié à un mod apache que je ne gère pas. 
-      }, +    * ''apache-nohome'' : alorslà, lui, je ne sais pas ce qu'il vise... 
-    }+    * ''apache-noscript'' peu de règles, lisibles et très importantes car en effet les badbots les cherchent ! 
-</code>+    * ''apache-overflows'' : à analyser plus finementcar sans doute qu'il y a des histoires de "retrypour que ce soit pertinent. 
 +    * ''apache-shellshock'' : j'sais pô. 
 +  * Apache access.log 
 +    * ''apache-badbots'' : me semble assez douteuse. Ça ban d'après les users agentmais ce qui est cherché est plein de vieux trucs. Après, ça peut éviter que les bots indexent trop, trop vite, ou de se faire aspirer le site. Mais j'aurais tendance à plutôt mettre des limiteurs de vitesse ? 
 +      * Notons cependant qu'on a des badbots repérables aux user-agents et que les ban peut réduire le trafic simplement. 
 +    * ''apache-fakegooglebot'' : celle-ci semble sacrément foireuse ? 
 + 
 +Autres servicesoù en théorie on va tout reprendre (plus les notres) : 
 + 
 +  * postfix 
 +  * dovecot 
 +  * sieve 
 +  * sshd
  
-{{tag>fail2ban sysadmin iptable}}+{{tag>fail2ban sysadmin iptable nftables}}
  
 [[https://creativecommons.org/publicdomain/zero/1.0/deed.fr|{{ https://liev.re/imagesweb/licences/cc-zero.png?100 | Ce texte est placé sous licence CC0}}]] [[https://creativecommons.org/publicdomain/zero/1.0/deed.fr|{{ https://liev.re/imagesweb/licences/cc-zero.png?100 | Ce texte est placé sous licence CC0}}]]
  
  
pratique/informatique/parefeu/reaction.1742591297.txt.gz · Dernière modification : 21/03/2025 22:08 de Zatalyz