Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentesRévision précédenteProchaine 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. Zatalyz | pratique:informatique:parefeu:reaction [14/07/2025 16:57] (Version actuelle) – [Démarrage automatique] Zatalyz | ||
|---|---|---|---|
| Ligne 51: | Ligne 51: | ||
| * '' | * '' | ||
| + | ==== Les plus fréquement utilisées ==== | ||
| + | |||
| + | Voir qui est banni : | ||
| + | |||
| + | reaction show | ||
| + | |||
| + | Débannir quelqu' | ||
| + | |||
| + | reaction flush IP | ||
| + | |||
| + | Tester le dossier de configuration : | ||
| + | reaction test-config -c / | ||
| + | |||
| + | Démarrer (sans systemd) : | ||
| + | reaction start -c / | ||
| + | |||
| + | ==== Base de donnée ==== | ||
| Par défaut les bases de données sont dans ''/ | Par défaut les bases de données sont dans ''/ | ||
| Ligne 79: | Ligne 96: | ||
| </ | </ | ||
| - | On va créer | + | <WRAP center round info 100%> |
| - | sudo nano / | + | Avec la version 2 il y a un à présent |
| + | |||
| + | Je laisse La vieille doc "pour mémoire" | ||
| + | </ | ||
| + | |||
| + | Après installation et vérification, | ||
| + | |||
| + | < | ||
| + | sudo systemctl enable --now reaction@reaction | ||
| + | </ | ||
| + | |||
| + | 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 " | ||
| + | |||
| + | <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 / | ||
| + | [Unit] | ||
| + | Description=Reaction to ban bad ip | ||
| + | After=network.target | ||
| + | # Alerte si ça " | ||
| + | OnFailure=reaction-alert.service | ||
| - | < | ||
| [Install] | [Install] | ||
| WantedBy=multi-user.target | WantedBy=multi-user.target | ||
| + | |||
| [Service] | [Service] | ||
| ExecStart=/ | ExecStart=/ | ||
| StateDirectory=reaction | StateDirectory=reaction | ||
| RuntimeDirectory=reaction | RuntimeDirectory=reaction | ||
| - | WorkingDirectory=/ | + | WorkingDirectory=/ |
| Restart=on-failure | Restart=on-failure | ||
| RestartSec=3 | RestartSec=3 | ||
| + | # Anti-flood de redémarrage en boucle | ||
| + | StartLimitIntervalSec=400 | ||
| + | StartLimitBurst=3 | ||
| </ | </ | ||
| - | Vu que j'ai eu des plantages muets, je lui dis de se relancer | + | <code bash / |
| + | [Unit] | ||
| + | Description=Envoie une alerte | ||
| + | |||
| + | [Service] | ||
| + | Type=oneshot | ||
| + | ExecStart=/ | ||
| + | </ | ||
| + | |||
| + | Et le script pour envoyer un mail (ça aurait pu être autre chose, mais j' | ||
| + | <code> | ||
| + | # | ||
| + | LOCKFILE="/ | ||
| + | # Délai en minutes, donc 3 h = 180 | ||
| + | DELAY=180 | ||
| + | |||
| + | # Si une alerte a déjà été envoyée il y a moins de $DELAY, on quitte | ||
| + | if [ -e " | ||
| + | logger -t reaction-alert " | ||
| + | exit 0 | ||
| + | fi | ||
| + | |||
| + | touch " | ||
| + | |||
| + | SUBJECT=" | ||
| + | TO=" | ||
| + | BODY=" | ||
| + | Arrêt à $(date). | ||
| + | " | ||
| + | |||
| + | echo " | ||
| + | logger -t reaction-alert " | ||
| + | |||
| + | </code> | ||
| On démarre le service : | On démarre le service : | ||
| < | < | ||
| sudo service reaction start</ | sudo service reaction start</ | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== 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' | ||
| ==== Découpage des fichiers ==== | ==== Découpage des fichiers ==== | ||
| Il s'agit de me simplifier un peu la lecture et la maintenance. Sur l' | Il s'agit de me simplifier un peu la lecture et la maintenance. Sur l' | ||
| 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: | retryperiod: | ||
| - | actions: banFor(' | + | actions: banFor(' |
| + | }; | ||
| + | |||
| + | // Filtre doux : c'est peut-être légitime. Et peut-être pas. | ||
| + | local filter_soft = { | ||
| + | retry: 6, | ||
| + | retryperiod: | ||
| + | actions: banFor(' | ||
| + | }; | ||
| + | |||
| + | // Filtre violent : un seul essai, banni un mois. | ||
| + | local filter_hard = { | ||
| + | actions: banFor(' | ||
| + | }; | ||
| + | |||
| + | local stream_name(name) ={ | ||
| + | reason: { | ||
| + | cmd: [' | ||
| + | }, | ||
| }; | }; | ||
| + | // Exposer les définitions précédentes pour qu' | ||
| { | { | ||
| banFor: banFor, | banFor: banFor, | ||
| filter_default: | filter_default: | ||
| + | filter_soft: | ||
| + | filter_hard: | ||
| + | stream_name: | ||
| } | } | ||
| Ligne 230: | Ligne 337: | ||
| * '' | * '' | ||
| + | Concernant l' | ||
| - | <WRAP center round todo 100%> | + | <code> |
| - | Ce qui est ici date des premières versions de reaction, on va voir ce qu' | + | [...] |
| + | apache_auth: | ||
| + | regex: [ | ||
| + | @' | ||
| + | ], | ||
| + | actions: lib.filter_default.actions + lib.stream_name(' | ||
| + | }, | ||
| + | [...] | ||
| + | </ | ||
| + | Ici '' | ||
| + | ==== Réaliser des actions plus compliqués ==== | ||
| + | Lorsqu' | ||
| - | ==== Envoyer | + | Bon, en vrai, ça dépend. Sur certains services, la surveillance via des mails, au début, permet d' |
| - | J' | + | |
| - | < | + | Bref, tout ça va se faire via notre fichier _lib.jsonnet, |
| + | |||
| + | === Lancer un script | ||
| + | Si on veut bannir ET réaliser | ||
| + | |||
| + | <code bash _ban.sh> | ||
| + | # | ||
| + | # Bannissement | ||
| + | nft46 add element inet reaction ipvXbans { " | ||
| + | |||
| + | # Envoi d'un mail | ||
| + | # cf le script plus bas... | ||
| + | </ | ||
| + | |||
| + | On modifie alors _lib.jsonnet sur la partie cmd : | ||
| + | <code json _lib.jsonnet> | ||
| + | local banFor(time) = { | ||
| + | ban: { | ||
| + | cmd: [' | ||
| + | } | ||
| + | </ | ||
| + | devient | ||
| + | <code json _lib.jsonnet> | ||
| + | local banFor(time) = { | ||
| + | ban: { | ||
| + | cmd: [' | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Ce qui autorise toutes les fantaisies. | ||
| + | |||
| + | === Action : envoi de mail === | ||
| + | On peut aussi déclarer une action spéciale pour l' | ||
| + | |||
| + | Le script suivant prend deux variables : '' | ||
| + | |||
| + | < | ||
| mail: { | mail: { | ||
| - | cmd: [' | + | cmd: [' |
| }, | }, | ||
| }; | }; | ||
| - | |||
| </ | </ | ||
| Ligne 247: | Ligne 401: | ||
| < | < | ||
| # Envoyer un mail | # Envoyer un mail | ||
| - | dossiermail="/ | + | dossiermail="/ |
| titremail=" | titremail=" | ||
| # 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</ | fi</ | ||
| - | Penser évidement à créer | + | Penser évidement à créer |
| - | 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 |
| < | < | ||
| ssh: { | ssh: { | ||
| Ligne 282: | Ligne 436: | ||
| retry: 6, | retry: 6, | ||
| retryperiod: | retryperiod: | ||
| - | actions: banFor(' | + | actions: |
| }, | }, | ||
| }, | }, | ||
| }, | }, | ||
| </ | </ | ||
| - | |||
| - | |||
| - | ==== 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, | ||
| - | mail: { | ||
| - | cmd: [' | ||
| - | }, | ||
| - | }; | ||
| - | |||
| - | // 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:// | ||
| - | // jsonnet' | ||
| - | patterns: { | ||
| - | // IPs can be IPv4 or IPv6 | ||
| - | // ip46tables (C program also in this repo) handles running the good commands | ||
| - | ip: { | ||
| - | regex: @' | ||
| - | ignore: [' | ||
| - | }, | ||
| - | }, | ||
| - | |||
| - | // Commandes exécutées au lancement | ||
| - | start: [ | ||
| - | [' | ||
| - | [' | ||
| - | [' | ||
| - | ], | ||
| - | // Commandes exécutées à l' | ||
| - | stop: [ | ||
| - | [' | ||
| - | [' | ||
| - | [' | ||
| - | [' | ||
| - | ], | ||
| - | |||
| - | // Streams : c'est là qu'on va définir les services et règles menant au bannissement | ||
| - | streams: { | ||
| - | ssh: import ' | ||
| - | kernel: import ' | ||
| - | badguypostfix: | ||
| - | }, | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | 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: [' | ||
| - | filters: { | ||
| - | failedlogin: | ||
| - | regex: [ | ||
| - | @' | ||
| - | @' | ||
| - | @' | ||
| - | ], | ||
| - | retry: 6, | ||
| - | retryperiod: | ||
| - | actions: banFor(' | ||
| - | }, | ||
| - | }, | ||
| - | }, | ||
| - | |||
| - | </ | ||
| - | |||
| - | <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 ' | ||
| - | // ip46tables -A log-refuse -m pkttype ! --pkt-type unicast -j nixos-fw-refuse | ||
| - | // ip46tables -A log-refuse -j DROP | ||
| - | { | ||
| - | cmd: [' | ||
| - | filters: { | ||
| - | portscan: { | ||
| - | regex: [' | ||
| - | retry: 4, | ||
| - | retryperiod: | ||
| - | actions: banFor(' | ||
| - | }, | ||
| - | }, | ||
| - | }, | ||
| - | |||
| - | |||
| - | </ | ||
| - | |||
| - | Pour postfix, pour le moment je cible certains bot cons. | ||
| - | <code jsonnet badguypostfix.jsonnet> | ||
| - | { | ||
| - | cmd: [' | ||
| - | filters: { | ||
| - | badguy: { | ||
| - | regex: [ | ||
| - | @'^.* improper command pipelining after CONNECT from unknown\[< | ||
| - | @' | ||
| - | @'^.* NOQUEUE: reject: RCPT from unknown\[< | ||
| - | @' | ||
| - | @' | ||
| - | @' | ||
| - | @' | ||
| - | |||
| - | ], | ||
| - | retry: 1, | ||
| - | retryperiod: | ||
| - | actions: banFor(' | ||
| - | }, | ||
| - | }, | ||
| - | }, | ||
| - | </ | ||
| - | </ | ||
| - | |||
| {{tag> | {{tag> | ||