Après 6 mois de développement à temps très partiel, je suis fièr·e de vous présenter mon petit bébé : reaction.
NdM: écrit en Go et C, placé sous AGPLv3.
Action… Réaction !
L'article sur mon blog présente le logiciel en détail, alors je vous fais un résumé que j'espère convaincant :
- À charge de travail égale, reaction consomme 30x moins de CPU et 10x moins de RAM.
- Il est codé en Go, ce qui est pour ce cas d'usage un bon équilibre entre facilité de développement et efficacité.
- La configuration est possible en JSON, YAML ou JSONnet.
- JSONnet est un langage étendant JSON, permettant d'écrire des commentaires, de définir des variables, des fonctions… Je l'aime beaucoup. C'est un peu comme le langage Nix de NixOS, mais avec une syntaxe agréable.
- Protéger un service, c'est seulement 8 lignes de configuration environ !
- Contrairement à fail2ban, reaction ne vient pas avec ses 160 fichiers de configuration par défaut (soit 8900 lignes de TOML bizarre).
- Au lieu de ça, il y a un wiki centralisant des bonnes pratiques, des expressions rationnelles (regex), des exemples, une FAQ.
Aperçu de la configuration
Je vous présente le coeur du fichier de configuration, en utilisant cette fois le YAML.
En remplacement des jails de fail2ban, on a des streams, qui définissent une source de données (par exemple tail -f /var/log/nginx/access.log
pour nginx).
streams:
ssh:
cmd: ['journalctl', '-fu', 'sshd.service']
À ces streams, on attache un ou plusieurs filters. Ce sont des groupes d'expressions régulières. C'est aussi sur un filter qu'on décide du nombre de mauvais essais (retry
) qu'on accorde à une IP avant de réagir.
streams:
ssh:
cmd: ['journalctl', '-fu', 'sshd.service']
filters:
fail:
regex:
- 'authentication failure;.*rhost='
retry: 3
retryperiod: '3h'
À un filter, on rajoute une ou plusieurs actions, qui seront exécutées quand il se déclenche.
streams:
ssh:
cmd: ['journalctl', '-fu', 'sshd.service']
filters:
fail:
regex:
- 'authentication failure;.*rhost='
retry: 3
retryperiod: '3h'
actions:
ban:
cmd: ['iptables', '-w', '-A', 'reaction', '-s', '', '-j', 'DROP']
Elles peuvent être effectuées tout de suite, ou être délayées avec after
, ce qui permet de bannir une IP maintenant, et de la débannir quelque temps plus tard.
streams:
ssh:
cmd: ['journalctl', '-fu', 'sshd.service']
filters:
fail:
regex:
- 'authentication failure;.*rhost='
retry: 3
retryperiod: '3h'
actions:
ban:
cmd: ['iptables', '-w', '-A', 'reaction', '-s', '', '-j', 'DROP']
unban:
cmd: ['iptables', '-w', '-D', 'reaction', '-s', '', '-j', 'DROP']
after: '24h'
Je recommande toutefois d'utiliser JSONnet, qui permettra d'éviter les répétitions, ici en définissant un ensemble d'actions banFor
:
local banFor(time) = {
ban: {
cmd: ['iptables', '-w', '-A', 'reaction', '-s', '', '-j', 'DROP'],
},
unban: {
after: time,
cmd: ['iptables', '-w', '-D', 'reaction', '-s', '', '-j', 'DROP'],
},
};
{
streams: {
ssh: {
cmd: ['journalctl', '-f', '-u', 'sshd.service'],
filters: {
login: {
regex: [ @'authentication failure;.*rhost=' ],
retry: 3,
retryperiod: '3h',
actions: banFor('24h'),
},
},
},
},
}
Version 2
Et pour vous vendre quelque chose qui n'est pas encore fait :
La v2 sera capable de fonctionner en grappe, en échangeant en pair-à-pair des adresses IP à bannir (ou des recettes de cuisine, configurez-le comme vous voulez !)
Une bibliothèque Go couvre exactement mes cas d'usage : elle a été écrite pour Nomad, une alternative à Kubernetes.
J'ai donc confiance en l'arrivée de cette v2.
Mais avant de m'y atteler, j'essaie de faire connaitre un maximum reaction, pour avoir des retours le plus tôt possible.