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 [30/05/2024 08:22] – supprimée - modification externe (Unknown date) 127.0.0.1pratique:informatique:parefeu:reaction [04/02/2025 10:13] (Version actuelle) – [Commandes de base] Zatalyz
Ligne 1: Ligne 1:
 +====== Reaction, alternative à Fail2ban ======
 +[[https://framagit.org/ppom/reaction/-/tree/main|Reaction]] (comme dans "action -> réaction") est un logiciel permettant de faire comme Fail2ban mais sans les défauts de ce dernier :
 +  * Consomme moins
 +  * Plus facile à customiser
 +  * Carrément plus simple de faire des regex
 +
 +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. 
 +
 +<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''
 +</WRAP>
 +
 +===== 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...
 +
 +<code>sudo apt install minisign
 +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
 +minisign -VP RWSpLTPfbvllNqRrXUgZzM7mFjLUA7PQioAItz80ag8uU4A2wtoT2DzX -m reaction_1.4.1-1_amd64.deb &&
 +  rm reaction_1.4.1-1_amd64.deb.minisig &&
 +  sudo apt install ./reaction_1.4.1-1_amd64.deb
 +</code>
 +  
 +Ensuite on crée un fichier de config, on le modifie, et aussi un truc pour systemd.
 +  sudo mkdir /etc/reaction/
 +  sudo nano /etc/reaction/reaction.jsonnet
 +(voir plus bas la/les confs).
 +
 +  sudo nano /etc/systemd/system/reaction.service
 +
 +<code>
 +[Install]
 +WantedBy=multi-user.target
 +[Service]
 +ExecStart=/usr/bin/reaction start -c /etc/reaction/server.jsonnet
 +StateDirectory=reaction
 +RuntimeDirectory=reaction
 +WorkingDirectory=/var/lib/reaction
 +Restart=on-failure
 +RestartSec=3
 +</code>
 +
 +Vu que j'ai eu des plantages muets, je lui dis de se relancer si ça lui arrive.
 +
 +Et puis on y lance : 
 +<code>sudo systemctl enable reaction.service
 +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>
 +
 +===== 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.
 +===== Envoyer des mails quand il y a une action =====
 +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) = {
 +  mail: {
 +    cmd: ['sh', '/root/scripts/sendmailreaction/mailreaction.sh', ip, rule],
 +  },
 +};
 +
 +</code>
 +
 +Le script :
 +<code>#!/bin/bash
 +# Envoyer un mail
 +dossiermail="/root/scripts/sendmailreaction/mail"
 +titremail="$1 banni"
 +# 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
 +if grep -q $1 $listeip; then
 +    exit
 +else
 +    # Message pour le corps du mail
 +    echo "Méchant $1 ! $2 \n \n" >> $corpmail
 +    # 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.
 +
 +Plus loin dans le fichier de configuration de Reaction, dans les streams, comment dire qu'il faut envoyer un mail (exemple sur ssh) :
 +<code>  streams: {
 +    ssh: {
 +      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>
 +
 +===== Actions par défaut =====
 +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. 
 +
 +Ce n'est pas forcément pertinent pour tout, par exemple je préfère préciser la raison du bannissement pour savoir quels services se font attaquer.
 +
 +On déclare la commande :
 +<code>
 +local filter_default = {
 +  retry: 3,
 +  retryperiod: '6h',
 +  actions: banFor('24h'),
 +};
 +</code>
 +
 +Et dans les streams, on l'appelle de cette façon : 
 +<code>
 +{
 +  streams: {
 +    ssh: {
 +      filters: {
 +        failedlogin: filter_default + {
 +          regex: ['...'],
 +        },
 +      },
 +    },
 +  },
 +}
 +</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.
 +
 +
 +
 +Dans le fichier principal, on peut appeler un autre fichier avec cette syntaxe :
 +<code>{
 +  streams: {
 +    ssh: import 'ssh.jsonnet',
 +  },
 +}</code>
 +
 +et ''ssh.jsonnet'' va contenir notre morceau propre au stream. 
 +
 +<WRAP center round todo 60%>
 +Mais j'ai du manquer un bout, ça me fait des erreurs ça... Il ne reconnait pas le "banfor" pourtant déclaré dans le fichier principal.
 +</WRAP>
 +
 +===== Mes bouts de stream et de config =====
 +Fichier principal
 +<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) = {
 +  ban: {
 +    cmd: ['ip46tables', '-w', '-A', 'reaction', '-s', '<ip>', '-j', 'DROP'],
 +  },
 +  unban: {
 +    after: time,
 +    cmd: ['ip46tables', '-w', '-D', 'reaction', '-s', '<ip>', '-j', 'DROP'],
 +  },
 +};
 +
 +// 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>\]',
 +          ],
 +          retry: 1,
 +          retryperiod: '6h',
 +          actions: banFor('720h') + sendmail('<ip>','"Banni un mois sans seconde chance pour avoir mal causé à Postfix"'),
 +        },
 +      },
 +    },
 +</code>
 +
 +{{tag>fail2ban sysadmin iptable}}
 +
 +[[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}}]]
 +
  
CC Attribution-Noncommercial-Share Alike 4.0 International Driven by DokuWiki