Je mets en place Crowdsec pour sécuriser les échanges de mon reverse-proxy (Traefik) avec mes applications.
Introduction
Le serveur sur lequel est hébergé ce site est un serveur public. Ce dernier subit pas mal de tentative d’intrusion.
Les seuls que j’identifie et traite sont les tentatives de connexions SSH avec
fail2ban
.
On peut voir que sur une heure, il travaille déjà pas mal :
grep Ban /var/log/fail2ban.log | tail
2025-03-08 14:05:26,445 fail2ban.actions [759]: NOTICE [sshd] Ban 45.119.81.249
2025-03-08 14:13:23,740 fail2ban.actions [759]: NOTICE [sshd] Ban 218.92.0.197
2025-03-08 14:18:30,694 fail2ban.actions [759]: NOTICE [sshd] Ban 45.162.145.14
2025-03-08 14:23:39,855 fail2ban.actions [759]: NOTICE [sshd] Ban 45.119.81.249
2025-03-08 14:25:42,066 fail2ban.actions [759]: NOTICE [sshd] Ban 218.92.0.197
2025-03-08 14:38:15,677 fail2ban.actions [759]: NOTICE [sshd] Ban 218.92.0.197
2025-03-08 14:46:55,067 fail2ban.actions [759]: NOTICE [sshd] Ban 58.34.135.138
2025-03-08 14:50:47,738 fail2ban.actions [759]: NOTICE [sshd] Ban 125.124.176.254
2025-03-08 14:51:55,085 fail2ban.actions [759]: NOTICE [sshd] Ban 218.92.0.197
2025-03-08 15:03:29,375 fail2ban.actions [759]: NOTICE [sshd] Ban 218.92.0.197
L’autre porte ouverte de mon serveur est la porte du Web, l’HTTP(s).
Je n’avais pas de connaissances sur les protections possibles sur le sujet.
J’ai découvert Crowdsec mais je n’étais pas trop
emballé par l’aspect communautaire du truc.
Après un peu de recherche, j’ai compris l’intérêt de ce mode de fonctionnement.
En effet, Crowdsec se base sur les remontés de la communauté afin de traiter plus efficacement les menaces. Pour prendre un exemple simple, si une menace est identifiée par plusieurs utilisateurs, on bénéficie de cette information pour traiter immédiatement le problème s’il se produit dans notre environnement.
Dans mes recherches, j’ai vu que c’était bien fait pour être intégrable avec
mon reverse-proxy Traefik. Comme tout mon trafic Web passe dessus, c’est plutôt
pratique.
Nous allons donc voir ici comment mettre cela en place.
Mise en place
Fichier crowdsec/compose.yml
:
|
|
Pour fonctionner avec Traefik, il faut qu’il soit dans le même réseau que ce
dernier. Si comme moi, votre composition n’est pas dans celui de Treafik,
vous devez spécifier ce réseau externe.
La variable d’environnement COLLECTIONS
va préciser des ensembles de règles
d’analyses et de scénarios rassemblés en collections. La liste des collections
est disponible sur le Hub de Crowdsec.
On ajoute aussi le dossier de logs de Traefik afin que Crowdsec puissent les
lire une fois la configuration faite.
Ensuite, on ajoute le fichier de configuration pour Traefik
(acquisition par fichier) dans un fichier /etc/crowdsec/acquis.d/traefik.yaml
:
filenames:
- "/var/log/traefik/*.log"
labels:
type: "traefik"
On démarre donc notre conteneur et on vérifie les logs.
docker compose up -d
docker compose logs
Configuration
Crowdsec
Pour que Crowdsec fonctionne pour Traefik, il faut décider quoi faire avec les données relevées. C’est le rôle d’un décisionnaire que Crowdsec baptise bouncer (“videur” dans le sens “videur de boîte de nuit”).
Pour cela, on va ajouter le bouncer pour Traefik qui est un conteneur.
Je souhaite le mettre dans mon dossier Crowdsec mais ne pas l’ajouter au
fichier compose.yml
mais dans un fichier différent.
J’ai pensé à faire un fichier compose.override.yml
mais cela n’est pas très
scalable si je continue j’ajouter des bouncers.
La solution est de passer par un fichier d’environnement. En créant un fichier
.env
dans le dossier, on peut y configurer une liste de fichier à utiliser
par Docker Compose.
Mon fichier crowdsec/.env
:
COMPOSE_FILE='compose.yml,traefik.yml'
COMPOSE_PATH_SEPARATOR=','
Et donc mon fichier traefik.yml
:
|
|
Pour obtenir la clé d’API, il va falloir faire un cscli bouncers add <my-bouncer>
dans le conteneur.
Je l’ai fait ainsi depuis l’hôte :
docker exec crowdsec cscli bouncers add bouncer-traefik
Avertissement
Comme l’indique le message qui suit cette commande, la clé d’API n’est donnée qu’une seule fois. Pensez donc bien à le noter quelque part.
Traefik
Maintenant que la partie Crowdsec est prêt à traiter les requêtes typées Traefik, il faut que Traefik les transmettent au bouncer.
Pour cela, il faut déléguer les authentifications au bouncer via la définition d’un middleware de type ForwardAuth.
Pour ma part, je le fais en configuration dynamique :
Fichier traefik/config/dynamique/crowdsec.yml
:
http:
middlewares:
crowdsec-bouncer:
forwardauth:
address: http://bouncer-traefik:8080/api/v1/forwardAuth
trustForwardHeader: true
Utilisation
Une fois tout cela fait, il suffit de démarrer le bouncer et de redémarrer Traefik.
Une fois cela fait, il faut bien vérifier que nos applications sont toujours accessibles.
Ci-dessous, une liste de commandes Crowdsec.
# Lister les métriques
docker exec crowdsec cscli metrics
# Voir les banissements
docker exec crowdsec cscli decisions list
# Mettre à jour du hub
docker exec crowdsec cscli hub update
docker exec crowdsec cscli hub upgrade
# Banir/débanir une adresse IP
docker exec crowdsec cscli decisions add --ip 192.168.17.92
docker exec crowdsec cscli decisions delete --ip 192.168.17.92
Comme vous pouvez le déduire, il faut de temps en temps faire un
update
/upgrade
. J’ai ajouté une tâche CRON pour cela.
Fichier /etc/cron.d/crowdsec_update_hub
:
@daily root /usr/bin/docker exec crowdsec cscli hub update && /usr/bin/docker exec crowdsec cscli hub upgrade
Pour comprendre ce qui se passe, au lieu de spammer cscli decisions list
,
j’ai ajouté une notification via Gotify.
En suivant la documentation,
mon fichier /etc/crowdsec/notifications/http.yaml
:
|
|
Et je me rends compte que cela fonctionne :
Remarque
Il existe aussi un dashboard mais je n’ai pas voulu le mettre en place.
Post-installation
Whitelisting
En publiant cet article, mon CI c’est donc lancé et a eu pour conséquence le bannissement de mon adresse IP !
Pour y remédier, j’ai suivis la doc
en créant le fichier /etc/crowdsec/parsers/s02-enrich/my-whitelist.yaml
:
name: perso/whitelist
description: "My Whitelist"
whitelist:
reason: "Moi moi et moi ipv4/ipv6 ip/ranges"
ip:
- "92.17.4.23"
cidr:
- "cafe:cafe:cafe:cafe::/64"
Ici, je whitelist mon IPv4 publique ainsi que mon préfixe IPv6 publique.
Conclusion
J’ai donc réussi à apporter un premier niveau de filtrage à mes applications exposées en Web.
En vue de ce que je vois sur fail2ban
, finalement, je ne suis malheureusement
pas très choqué de voir le nombre de bannissements.
Ainsi, je me rends compte que cela n’est que le début. Je pourrais aller plus
loin en visant les applications comme Nginx ou autres.
Il y aura donc probablement une suite à cet article.