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 [04/10/2024 19:05] – [Installation et lancement automatique] Zatalyz | pratique:informatique:parefeu:reaction [14/07/2025 16:57] (Version actuelle) – [Démarrage automatique] Zatalyz | ||
|---|---|---|---|
| Ligne 12: | Ligne 12: | ||
| J' | J' | ||
| </ | </ | ||
| + | |||
| + | ===== Commandes de base ===== | ||
| + | Commandes: | ||
| + | * **'' | ||
| + | * '' | ||
| + | * Options possibles : '' | ||
| + | * Par exemple, pour un fichier '' | ||
| + | * Et un dossier : '' | ||
| + | * Dans le cas des dossiers, les seuls fichiers du dossier qui seront lus automatiquement | ||
| + | * Doivent se terminer par les extensions '' | ||
| + | * Ne doivent pas démarrer par '' | ||
| + | * **'' | ||
| + | * '' | ||
| + | * '' | ||
| + | * Options possibles : '' | ||
| + | * **'' | ||
| + | * '' | ||
| + | * Options possibles : '' | ||
| + | * Débannir quelqu' | ||
| + | * **'' | ||
| + | * reaction test-regex --config < | ||
| + | * Arguments: | ||
| + | * < | ||
| + | * [LINE] | ||
| + | * Options possibles : '' | ||
| + | * **'' | ||
| + | * '' | ||
| + | * Options possibles : '' | ||
| + | * **'' | ||
| + | * **'' | ||
| + | |||
| + | Pour les options possibles (suivant les commandes) : | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | ==== 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 ''/ | ||
| ===== Installation et lancement automatique ===== | ===== Installation et lancement automatique ===== | ||
| - | Pour l' | + | Pour l' |
| < | < | ||
| Ligne 23: | Ligne 81: | ||
| sudo apt install ./ | sudo apt install ./ | ||
| </ | </ | ||
| - | | + | |
| - | Ensuite on crée un fichier | + | Ensuite on crée le ou les fichiers |
| sudo mkdir / | sudo mkdir / | ||
| sudo nano / | sudo nano / | ||
| - | (voir plus bas la/les confs). | ||
| - | | + | ==== Démarrage automatique ==== |
| + | |||
| + | <WRAP center round tip 100%> | ||
| + | Avant de démarrer | ||
| + | | ||
| + | |||
| + | On peut aussi vérifier la syntaxe avec : | ||
| + | sudo reaction test-config -c / | ||
| + | </ | ||
| + | |||
| + | <WRAP center round info 100%> | ||
| + | Avec la version 2 il y a un à présent un service systemd déjà fourni. Il se trouve sur /lib/ | ||
| + | |||
| + | 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 si ça lui arrive. | + | <code bash / |
| + | [Unit] | ||
| + | Description=Envoie une alerte si Reaction plante | ||
| - | Et puis on y lance : | + | [Service] |
| - | < | + | Type=oneshot |
| - | sudo service reaction start</ | + | ExecStart=/ |
| + | </ | ||
| - | <WRAP center round tip 60%> | + | Et le script pour envoyer un mail (ça aurait pu être autre chose, mais j'aime bien les mails). |
| - | Mais avant de démarrer | + | < |
| - | sudo reaction start -c /etc/reaction/server.jsonnet | + | #!/bin/bash |
| - | </ | + | LOCKFILE="/ |
| + | # Délai en minutes, donc 3 h = 180 | ||
| + | DELAY=180 | ||
| - | ===== Commandes | + | # Si une alerte a déjà été envoyée il y a moins de $DELAY, on quitte |
| - | Voir qui est banni : | + | if [ -e " |
| - | reaction | + | |
| + | exit 0 | ||
| + | fi | ||
| - | Débannir quelqu' | + | touch " |
| - | reaction flush IP | + | |
| - | Consulter l'aide : | + | SUBJECT=" |
| - | reaction --help | + | TO=" |
| - | ===== Envoyer des mails quand il y a une action | + | BODY="Le service Reaction sur $(hostname) semble avoir un souci. |
| - | J'ai une commande qui va envoyer un mail via un script, en ayant en paramètre deux variables : '' | + | Arrêt à $(date). |
| - | < | + | " |
| - | mail: { | + | |
| - | cmd: [' | + | echo " |
| - | }, | + | logger -t reaction-alert " |
| - | }; | + | |
| </ | </ | ||
| - | Le script | + | On démarre le service |
| - | < | + | < |
| - | # Envoyer un mail | + | sudo service reaction start</code> |
| - | dossiermail=" | + | </WRAP> |
| - | titremail=" | + | |
| - | # Création du fichier du corps du mail dans un fichier temporaire qui évite toute collision | + | |
| - | corpmail=" | + | |
| - | listeip=" | + | |
| - | # Vérifier si l' | ||
| - | if grep -q $1 $listeip; then | ||
| - | exit | ||
| - | else | ||
| - | # Message pour le corps du mail | ||
| - | echo " | ||
| - | # Ajouter les logs pour les détails | ||
| - | journalctl --since yesterday | grep $1 >> $corpmail | ||
| - | #Envoyer le mail | ||
| - | cat $corpmail | mail -s " | ||
| - | # Effacer le corps du crime pour éviter d' | ||
| - | rm $corpmail | ||
| - | fi</ | ||
| - | Penser évidement à créer les dossiers ''/ | + | ==== Envoyer un mail en cas de plantage ==== |
| - | Plus loin dans le fichier de configuration de Reaction, dans les streams, comment dire qu'il faut envoyer un mail (exemple sur ssh) : | ||
| - | < | ||
| - | ssh: { | ||
| - | cmd: [' | ||
| - | filters: { | ||
| - | failedlogin: | ||
| - | regex: [ | ||
| - | @' | ||
| - | @' | ||
| - | @' | ||
| - | ], | ||
| - | retry: 6, | ||
| - | retryperiod: | ||
| - | actions: banFor(' | ||
| - | }, | ||
| - | }, | ||
| - | }, | ||
| - | </ | ||
| - | ===== Actions par défaut | + | ===== Configuration |
| - | 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 | + | Pour la doc complète, se référer à [[https:// |
| + | * Utilisation | ||
| + | * Découpage de la configuration par services (pour faciliter certaines automatisations) | ||
| + | * Analyse des ip bannies pour en déduire des plages | ||
| + | * Rapport (journalier ?) sur les ip bannies (histoire | ||
| - | Ce n'est pas forcément pertinent | + | 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 " |
| + | ==== Découpage des fichiers ==== | ||
| + | Il s'agit de me simplifier un peu la lecture et la maintenance. Sur l' | ||
| + | * Définitions : ce qu'est l' | ||
| + | * '' | ||
| + | * Les fichiers de stream ('' | ||
| - | On déclare la commande | + | On paramètre évidement le lancement de Reaction (avec service ou direct en ligne de commande) pour lire tout le contenu du dossier (ici / |
| - | < | + | |
| - | local filter_default | + | === 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: |
| - | retryperiod: | + | |
| - | actions: banFor(' | + | |
| - | }; | + | |
| - | </ | + | |
| - | Et dans les streams, on l' | + | < |
| - | < | + | |
| { | { | ||
| - | | + | |
| - | | + | |
| - | | + | // Both IPv4 and IPv6, do not accept malformed IPs |
| - | failedlogin: filter_default + { | + | |
| - | regex: [' | + | |
| - | | + | // Ne pas oublier la virgule après chaque ip, pour l' |
| - | | + | |
| + | | ||
| + | // Sous-réseau d'une box | ||
| + | ' | ||
| + | // Sous-réseau proxmox ? | ||
| + | //' | ||
| + | // Ip fixes de sysadmins | ||
| + | // ip de bastion | ||
| + | | ||
| }, | }, | ||
| }, | }, | ||
| + | |||
| + | start: [ | ||
| + | [' | ||
| + | table inet reaction { | ||
| + | set ipv4bans { | ||
| + | type ipv4_addr | ||
| + | flags interval | ||
| + | auto-merge | ||
| + | } | ||
| + | set ipv6bans { | ||
| + | type ipv6_addr | ||
| + | flags interval | ||
| + | auto-merge | ||
| + | } | ||
| + | chain input { | ||
| + | type filter hook input priority 0 | ||
| + | policy accept | ||
| + | ip saddr @ipv4bans drop | ||
| + | ip6 saddr @ipv6bans drop | ||
| + | } | ||
| + | } | ||
| + | ||| ], | ||
| + | ], | ||
| + | stop: [ | ||
| + | [' | ||
| + | ], | ||
| } | } | ||
| + | |||
| </ | </ | ||
| - | ===== 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. | ||
| + | === _lib.jsonnet : actions par défaut === | ||
| + | Ce fichier sert à définir les actions telles que " | ||
| + | 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. | ||
| - | Dans le fichier | + | Le fait que le fichier |
| - | < | + | |
| - | | + | |
| - | ssh: import | + | |
| - | }, | + | |
| - | }</ | + | |
| - | + | ||
| - | et '' | + | |
| - | + | ||
| - | <WRAP center round todo 60%> | + | |
| - | Mais j'ai du manquer un bout, ça me fait des erreurs ça... Il ne reconnait pas le " | + | |
| - | </ | + | |
| - | ===== Mes bouts de stream et de config ===== | + | < |
| - | Fichier principal | + | |
| - | < | + | |
| - | // This file is using JSONNET, a complete configuration language based on JSON // See https:// | + | |
| - | // action pour bannir/ | + | |
| local banFor(time) = { | local banFor(time) = { | ||
| ban: { | ban: { | ||
| - | cmd: ['ip46tables', | + | cmd: ['nft46', 'add element inet reaction |
| }, | }, | ||
| unban: { | unban: { | ||
| + | cmd: [' | ||
| after: time, | after: time, | ||
| - | cmd: [' | ||
| }, | }, | ||
| }; | }; | ||
| - | // Envoyer un mail lors des actions et précisant ip et raison | + | // retry et retryperiod sont quand il y a plusieurs tentatives autorisées |
| - | local sendmail(ip,rule) = { | + | // juste mettre le banFor sinon... |
| - | | + | |
| - | cmd: ['sh', '/ | + | // Filtre |
| - | | + | local filter_default |
| + | | ||
| + | | ||
| + | | ||
| }; | }; | ||
| - | // pourquoi ça ouvre ici ? Mais, ça marche. | + | // Filtre doux : c'est peut-être légitime. Et peut-être pas. |
| - | { | + | local filter_soft = { |
| - | // patterns are substitued in regexes. when a filter performs an action, it replaces the found pattern. | + | retry: 6, |
| - | // reaction regex syntax is defined here: https:// | + | |
| - | // jsonnet's @' | + | |
| - | | + | }; |
| - | // IPs can be IPv4 or IPv6 | + | |
| - | // ip46tables (C program also in this repo) handles running the good commands | + | |
| - | ip: { | + | |
| - | regex: @'(([0-9]{1, | + | |
| - | | + | |
| - | }, | + | |
| - | }, | + | |
| - | // Commandes exécutées au lancement | + | // Filtre violent |
| - | start: [ | + | local filter_hard = { |
| - | [' | + | |
| - | | + | }; |
| - | [' | + | |
| - | | + | |
| - | // Commandes exécutées à l' | + | |
| - | stop: [ | + | |
| - | ['ip46tables', ' | + | |
| - | | + | |
| - | [' | + | |
| - | [' | + | |
| - | ], | + | |
| - | // Streams : c'est là qu'on va définir les services et règles menant au bannissement | + | local stream_name(name) ={ |
| - | | + | |
| - | | + | |
| - | kernel: import | + | }, |
| - | | + | }; |
| - | }, | + | |
| + | // Exposer les définitions précédentes pour qu'elles soient accessibles depuis un autre fichier Jsonnet | ||
| + | { | ||
| + | banFor: banFor, | ||
| + | | ||
| + | | ||
| + | filter_hard: | ||
| + | stream_name: | ||
| } | } | ||
| + | |||
| </ | </ | ||
| - | Pour ssh et kernel, il s'agit des configurations par défaut auxquelles j'ai ajouté mon envoi de mail : | + | |
| - | < | + | |
| - | { | + | === Streams === |
| - | cmd: [' | + | Je met ici une config très basique pour ssh (<wrap todo> |
| + | |||
| + | < | ||
| + | local lib = import ' | ||
| + | { | ||
| + | streams: { | ||
| + | | ||
| + | cmd: [' | ||
| filters: { | filters: { | ||
| - | failedlogin: | + | failedlogin: |
| regex: [ | regex: [ | ||
| @' | @' | ||
| Ligne 233: | Ligne 323: | ||
| @' | @' | ||
| ], | ], | ||
| - | retry: | + | retry: |
| retryperiod: | retryperiod: | ||
| - | actions: banFor(' | + | actions: |
| }, | }, | ||
| }, | }, | ||
| }, | }, | ||
| + | }, | ||
| + | } | ||
| </ | </ | ||
| - | <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 | + | Concernant l' |
| - | // ip46tables -A log-refuse -m pkttype ! --pkt-type unicast -j nixos-fw-refuse | + | |
| - | // ip46tables -A log-refuse -j DROP | + | < |
| - | { | + | [...] |
| - | | + | |
| - | | + | regex: [ |
| - | | + | @'^.*client |
| - | regex: ['refused connection: | + | |
| - | | + | actions: |
| - | retryperiod: | + | |
| - | actions: | + | |
| }, | }, | ||
| - | }, | + | [...] |
| - | }, | + | </ |
| + | Ici '' | ||
| + | ==== Réaliser des actions plus compliqués ==== | ||
| + | Lorsqu' | ||
| + | Bon, en vrai, ça dépend. Sur certains services, la surveillance via des mails, au début, permet d' | ||
| + | Bref, tout ça va se faire via notre fichier _lib.jsonnet, | ||
| + | |||
| + | === 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 { " | ||
| + | |||
| + | # Envoi d'un mail | ||
| + | # cf le script plus bas... | ||
| </ | </ | ||
| - | Pour postfix, pour le moment je cible certains bot cons. | + | On modifie alors _lib.jsonnet sur la partie cmd : |
| - | < | + | <code json _lib.jsonnet> |
| - | { | + | local banFor(time) = { |
| - | cmd: [' | + | 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 | ||
| + | |||
| + | Le script suivant prend deux variables : '' | ||
| + | |||
| + | < | ||
| + | mail: { | ||
| + | | ||
| + | }, | ||
| + | }; | ||
| + | </ | ||
| + | |||
| + | Le script : | ||
| + | < | ||
| + | # Envoyer un mail | ||
| + | dossiermail="/ | ||
| + | titremail=" | ||
| + | # Création du fichier du corps du mail dans un fichier temporaire qui évite toute collision | ||
| + | corpmail=" | ||
| + | listeip="/ | ||
| + | |||
| + | # Vérifier si l' | ||
| + | if grep -q $1 $listeip; then | ||
| + | exit | ||
| + | else | ||
| + | # Message pour le corps du mail | ||
| + | echo " | ||
| + | # Ajouter les logs pour les détails | ||
| + | journalctl --since yesterday | grep $1 >> $corpmail | ||
| + | #Envoyer le mail | ||
| + | cat $corpmail | mail -s " | ||
| + | # Effacer le corps du crime pour éviter d' | ||
| + | rm $corpmail | ||
| + | fi</ | ||
| + | |||
| + | Penser évidement à créer le dossier ''/ | ||
| + | |||
| + | Dans les streams (en ayant des fichiers modulaires), | ||
| + | < | ||
| + | ssh: { | ||
| + | cmd: [' | ||
| filters: { | filters: { | ||
| - | | + | |
| regex: [ | regex: [ | ||
| - | @'^.* improper command pipelining after CONNECT from unknown\[<ip>\].*', | + | @'authentication failure;.*rhost=< |
| - | @'^.*\[<ip>\].*tiscali.it.*', | + | @'Connection reset by authenticating user .* < |
| - | @'^.* NOQUEUE: reject: RCPT from unknown\[< | + | @'Failed password for .* from < |
| - | @' | + | |
| - | @' | + | |
| ], | ], | ||
| - | retry: | + | retry: |
| retryperiod: | retryperiod: | ||
| - | actions: banFor(' | + | actions: |
| }, | }, | ||
| }, | }, | ||