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 [11/06/2025 10:37] – on continue les maj, mais c'est pas fini. Zatalyzpratique:informatique:parefeu:reaction [14/07/2025 16:57] (Version actuelle) – [Démarrage automatique] Zatalyz
Ligne 51: Ligne 51:
   * ''-v'', ''--verbose'' : affiche plus d'infos en sortie.   * ''-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. 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.
Ligne 79: Ligne 96:
 </WRAP> </WRAP>
  
-On va créer un service : +<WRAP center round info 100%> 
-  sudo nano /etc/systemd/system/reaction.service+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. 
 + 
 +Je laisse La vieille doc "pour mémoire" mais ce n'est plus forcément utile.  
 +</WRAP> 
 + 
 +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%> 
 +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/ 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((Je précise que ça date des premières versions du logicielqui a bien évolué depuis)). <wrap todo>Ce serait bien d'être averti quand ça plantececi dit</wrap>+<code bash /etc/systemd/system/reaction-alert.service> 
 +[Unit] 
 +Description=Envoie une alerte si Reaction plante 
 + 
 +[Service] 
 +Type=oneshot 
 +ExecStart=/usr/local/bin/reaction-alert.sh 
 +</code> 
 + 
 +Et le script pour envoyer un mail (ça aurait pu être autre chosemais j'aime bien les mails).  
 +<code> 
 +#!/bin/bash 
 +LOCKFILE="/tmp/reaction-alert.lock" 
 +# Délai en minutesdonc 3 h = 180 
 +DELAY=180 
 + 
 +# Si une alerte a déjà été envoyée il y 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 :  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>
 +
 +
 +==== Envoyer un mail en cas de plantage ====
 +
  
 ===== Configuration ===== ===== Configuration =====
Ligne 107: Ligne 187:
   * Rapport (journalier ?) sur les ip bannies (histoire de pouvoir suivre ce qui se passe et la charge) ?   * 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 ==== ==== 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 Il s'agit de me simplifier un peu la lecture et la maintenance. Sur l'exemple ici, j'ai 3 types de fichiers
Ligne 185: Ligne 266:
 }; };
  
 +// retry et retryperiod sont quand il y a plusieurs tentatives autorisées
 +// juste mettre le banFor sinon...
 +
 +// Filtre (et options) par défaut : ni trop doux, ni trop cruel.
 local filter_default = { local filter_default = {
   retry: 3,   retry: 3,
   retryperiod: '3h',   retryperiod: '3h',
-  actions: banFor('24h'),+  actions: banFor('48h')
 +}; 
 + 
 +// Filtre doux : c'est peut-être légitime. Et peut-être pas. 
 +local filter_soft = { 
 +  retry: 6, 
 +  retryperiod: '3h', 
 +  actions: banFor('10s'), 
 +}; 
 + 
 +// Filtre violent : un seul essai, banni un mois. 
 +local filter_hard = { 
 +  actions: banFor('720h'), 
 +}; 
 + 
 +local stream_name(name) ={ 
 +  reason: { 
 +    cmd: ['logger', 'REACTION BLOCK <ip> because: ', name], 
 +  },
 }; };
  
 +// Exposer les définitions précédentes pour qu'elles soient accessibles depuis un autre fichier Jsonnet
 { {
   banFor: banFor,   banFor: banFor,
   filter_default: filter_default,   filter_default: filter_default,
 +  filter_soft: filter_soft,
 +  filter_hard: filter_hard,
 +  stream_name: stream_name,
 } }
  
Ligne 230: Ligne 337:
   * ''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.   * ''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.
  
 +Concernant l'appel des diverses fonctions, il 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 : 
  
-<WRAP center round todo 100%+<code
-Ce qui est ici date des premières versions de reactionon va voir ce qu'on garde+[...] 
 +        apache_auth: lib.filter_default + { 
 +          regex: [ 
 +            @'^.*client <ip>.* regex', 
 +          ], 
 +          actions: lib.filter_default.actions + lib.stream_name('apache_auth'), 
 +        }, 
 +[...] 
 +</code> 
 +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 ==== 
 +Lorsqu'il y a bannissement via Reaction, j'aimerais pouvoir réaliser certaines actions, comme être avertie par mail
  
-==== Envoyer des mails quand il y a une action ==== +Bon, en vrai, ça dépend. Sur 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 bannies, et 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.  
-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). + 
-<code>local sendmail(ip,rule) = {+Bref, tout ça va se faire via notre fichier _lib.jsonnet, avec des actions diverses. 
 + 
 +=== Lancer un script === 
 +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 :  
 + 
 +<code bash _ban.sh> 
 +#!/bin/bash 
 +# Bannissement 
 +nft46 add element inet reaction ipvXbans { "$1"
 + 
 +# Envoi d'un mail 
 +# cf le script plus bas... 
 +</code> 
 + 
 +On modifie alors _lib.jsonnet sur la partie cmd : 
 +<code json _lib.jsonnet> 
 +local banFor(time) = { 
 +  ban: { 
 +    cmd: ['nft46''add element inet reaction ipvXbans { <ip> }'], 
 +  } 
 +</code> 
 +devient  
 +<code json _lib.jsonnet> 
 +local banFor(time) = { 
 +  ban: { 
 +    cmd: ['sh', '-c', '/etc/reaction/_ban.sh <ip>'], 
 +  } 
 +</code> 
 + 
 +Ce qui autorise toutes les fantaisies. 
 + 
 +=== 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> </code>
  
Ligne 247: Ligne 401:
 <code>#!/bin/bash <code>#!/bin/bash
 # Envoyer un mail # Envoyer un mail
-dossiermail="/root/scripts/sendmailreaction/mail"+dossiermail="/root/scripts/sendmailreaction"
 titremail="$1 banni" titremail="$1 banni"
 # Création du fichier du corps du mail dans un fichier temporaire qui évite toute collision # Création du fichier du corps du mail dans un fichier temporaire qui évite toute collision
Ligne 267: Ligne 421:
 fi</code> fi</code>
  
-Penser évidement à créer les dossiers ''/root/scripts/sendmailreaction/'' et ''/root/scripts/sendmailreaction/mail'' avant de lancer le reste.+Penser évidement à créer le dossier ''/root/scripts/sendmailreaction/'' avant de lancer le reste.
  
-Plus loin dans le fichier de configuration de Reaction, dans les streams, comment dire qu'il faut envoyer un mail (exemple sur ssh) :+Dans les streams (en ayant des fichiers modulaires), comment dire qu'il faut envoyer un mail (exemple sur ssh) :
 <code>  streams: { <code>  streams: {
     ssh: {     ssh: {
Ligne 282: Ligne 436:
           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>
- 
- 
-==== Mes bouts de stream et de config ==== 
-Fichier principal 
-<code jsonnet server.jsonnet> 
- 
-// Envoyer un mail lors des actions et précisant ip et raison 
-local sendmail(ip,rule) = { 
-  mail: { 
-    cmd: ['sh', '/root/scripts/sendmailreaction/mailreaction.sh', ip, rule], 
-  }, 
-}; 
- 
-// pourquoi ça ouvre ici ? Mais, ça marche. 
-{ 
-// patterns are substitued in regexes. when a filter performs an action, it replaces the found pattern. 
-// reaction regex syntax is defined here: https://github.com/google/re2/wiki/Syntax 
-// jsonnet's @'string' is for verbatim strings. 
-  patterns: { 
-    // IPs can be IPv4 or IPv6 
-    // ip46tables (C program also in this repo) handles running the good commands 
-    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 
-  start: [ 
-    ['ip46tables', '-w', '-N', 'reaction'], 
-    ['ip46tables', '-w', '-I', 'INPUT', '-p', 'all', '-j', 'reaction'], 
-    ['echo', 'Le service a démarré', '|', 'mail', '-s', '"Démarrage de Reaction"', 'root'], 
-  ], 
-  // Commandes exécutées à l'arrêt, après tout le reste 
-  stop: [ 
-    ['ip46tables', '-w', '-D', 'INPUT', '-p', 'all', '-j', 'reaction'], 
-    ['ip46tables', '-w', '-F', 'reaction'], 
-    ['ip46tables', '-w', '-X', 'reaction'], 
-    ['echo', 'Le service est éteint', '|', 'mail', '-s', '"Extinction de Reaction"', 'root'], 
-  ], 
- 
-// Streams : c'est là qu'on va définir les services et règles menant au bannissement 
-  streams: { 
-    ssh: import 'ssh.jsonnet', 
-    kernel: import 'kernel.jsonnet', 
-    badguypostfix: import 'badguypostfix.jsonnet', 
- }, 
-} 
-</code> 
- 
-Pour ssh et kernel, il s'agit des configurations par défaut auxquelles j'ai ajouté mon envoi de mail : 
-<code jsonnet ssh.jsonnet> 
-    { 
-      cmd: ['journalctl', '-fn0', '-u', 'ssh.service'], 
-      filters: { 
-        failedlogin: { 
-          regex: [ 
-            @'authentication failure;.*rhost=<ip>', 
-            @'Connection reset by authenticating user .* <ip>', 
-            @'Failed password for .* from <ip>', 
-          ], 
-          retry: 6, 
-          retryperiod: '6h', 
-          actions: banFor('48h') + sendmail('<ip>','"Banni 48h pour tentative de co à SSH"'), 
-        }, 
-      }, 
-    }, 
- 
-</code> 
- 
-<code jsonnet kernel.jsonnet> 
-    // Ban hosts which knock on closed ports. 
-    // 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é"'), 
-        }, 
-      }, 
-    }, 
- 
- 
-</code> 
- 
-Pour postfix, pour le moment je cible certains bot cons. 
-<code jsonnet badguypostfix.jsonnet> 
-    { 
-      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.*', 
- 
-          ], 
-          retry: 1, 
-          retryperiod: '6h', 
-          actions: banFor('720h') + sendmail('<ip>','"Banni un mois sans seconde chance pour avoir mal causé à Postfix"'), 
-        }, 
-      }, 
-    }, 
-</code> 
-</WRAP> 
- 
  
 {{tag>fail2ban sysadmin iptable}} {{tag>fail2ban sysadmin iptable}}
pratique/informatique/parefeu/reaction.1749631064.txt.gz · Dernière modification : 11/06/2025 10:37 de Zatalyz