Technos

CrowdSec : protéger vos sites et applications web

Je vous avais dit que Nginx Proxy Manager Plus pouvait avoir un WAF. Le voilà : CrowdSec, qui fait aussi IDS et IPS, et qui bannit tout seul.

7 min de lecture commentaires

Le logo CrowdSec et ses alpagas derrière un bouclier, avec le titre Protéger vos sites et applications web

Quand je vous ai parlé de Nginx Proxy Manager Plus, je vous ai dit qu’il pouvait avoir un WAF. Voilà, c’est de ça qu’on va parler. Et pas que.

WAF, IDS, IPS : trois sigles, trois métiers

Commençons par le début, parce que WAF, ça veut dire quoi au juste ?

WAF, c’est Web Application Firewall. Un pare-feu applicatif. Contrairement au pare-feu classique qui regarde des IP et des ports, lui il lit le contenu HTTP : l’URL, les en-têtes, le corps de la requête, les cookies. Et il bloque ce qui ressemble à une attaque applicative : injection SQL, XSS, chemin d’URL douteux, quelqu’un qui vient renifler si tu n’aurais pas laissé traîner un fichier .env à la racine.

Deux autres sigles vont avec, et ils reviennent tout le temps.

IDS, Intrusion Detection System. Il détecte et il alerte, sans agir. C’est un capteur.

IPS, Intrusion Prevention System. Même chose, mais il agit : il coupe la connexion, il bannit l’IP.

Et CrowdSec, lui, il est tout ça à la fois. Le moteur lit les logs, corrèle et décide : c’est l’IDS. Les bouncers appliquent la sanction là où ça compte, dans le reverse proxy par exemple : ça en fait un IPS. Et son composant AppSec inspecte les requêtes HTTP avant qu’elles n’atteignent ton appli : c’est le WAF. Trois casquettes, un seul conteneur.

Communautaire, ça veut dire quoi exactement

C’est le gros argument de CrowdSec, et c’est celui qu’on résume souvent mal. Alors voilà ce qui se passe vraiment.

Quand ton moteur repère une IP qui fait n’importe quoi, il la signale à CrowdSec. Et il envoie seulement ça : l’adresse, et ce qu’elle a tenté. Tes logs ne bougent pas de chez toi.

En face, CrowdSec compare avec ce que remontent tous les autres. Une IP dénoncée par un seul serveur ne fait rien, ça évite les petits malins qui voudraient faire bannir 8.8.8.8 par la terre entière. Mais quand elle est signalée par plein de serveurs qui ne se connaissent pas, elle part dans la liste noire commune, et tout le monde la reçoit.

Résultat : tu bloques des IP malveillantes avant même qu’elles t’aient visé. C’est ça le vrai gain. Un serveur seul apprend de ce qu’il subit, un serveur branché sur CrowdSec apprend de ce que subissent les autres.

Deux nuances honnêtes quand même. D’abord, l’accès à la liste dépend de ta contribution : si tu envoies des signaux régulièrement, ton moteur est abonné automatiquement à la liste complète, sinon tu reçois une version allégée. Ensuite, le partage se désactive, ce n’est pas imposé. Mais si tu le coupes, tu gardes seulement la détection locale et tu ne reçois plus la liste. Donnant-donnant.

Pour savoir où tu en es, une commande suffit :

docker exec crowdsec cscli capi status
You can successfully interact with Central API (CAPI)
Sharing signals is enabled
Pulling community blocklist is enabled

L’installer

Comme d’habitude je passe par Portainer, je trouve que c’est le mieux. Voilà mon compose, adapte les chemins à ta machine :

services:
  crowdsec:
    image: crowdsecurity/crowdsec:latest
    container_name: crowdsec
    restart: unless-stopped
    network_mode: host
    environment:
      GID: "1000"
      COLLECTIONS: "crowdsecurity/linux crowdsecurity/sshd crowdsecurity/http-cve crowdsecurity/base-http-scenarios crowdsecurity/nginx ZoeyVid/npmplus"
    volumes:
      - /path/de/ton/npmplus/data/nginx/logs:/var/log/npmplus:ro
      - /var/log:/var/log/host:ro
      - /home/ton-user/crowdsec/config:/etc/crowdsec
      - /home/ton-user/crowdsec/data:/var/lib/crowdsec/data
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Les COLLECTIONS, ce sont les paquets de scénarios installés au démarrage. Là je prends le Linux de base, SSH, les CVE web connues, les scénarios HTTP génériques, nginx, et celui dédié à NPMplus. Le network_mode: host est important : le moteur doit être joignable en local par le bouncer et par le WAF, sur les ports 8080 et 7422.

Côté NPMplus, il y a quand même trois choses à faire

Sur mon reverse proxy, je pensais qu’il n’y avait rien à toucher. En fait si, un peu. Rien de méchant, mais si tu sautes ces étapes tu auras un CrowdSec qui tourne dans le vide et qui ne bloquera jamais rien.

Un. Créer la clé du bouncer côté CrowdSec :

docker exec crowdsec cscli bouncers add npmplus

Deux. Coller cette clé dans crowdsec.conf, dans le dossier de données de NPMplus, et passer ENABLED à true. Pendant que tu y es, vérifie ces lignes, ce sont elles qui branchent le moteur et le WAF :

ENABLED=true
API_URL=http://127.0.0.1:8080
APPSEC_URL=http://127.0.0.1:7422
APPSEC_FAILURE_ACTION=deny

Trois. Déclarer l’acquisition côté CrowdSec, dans un fichier acquis.d/npmplus.yaml. Il y a deux sources : les fichiers de logs, et le WAF qui écoute en direct.

filenames:
  - /var/log/npmplus/*.log
labels:
  type: npmplus
---
listen_addr: 0.0.0.0:7422
appsec_config: crowdsecurity/appsec-default
name: appsec
source: appsec
labels:
  type: appsec

Et là, le piège dans lequel je suis tombé sans m’en rendre compte : NPMplus n’écrit pas ses logs d’accès sur le disque par défaut. Il faut passer LOGROTATE à true dans son compose. Sinon le dossier reste désespérément vide, et la moitié « lecture de logs » de CrowdSec ne voit rien passer.

Je l’ai découvert en regardant mes métriques, des mois après : mon dossier de logs est vide, et absolument tout mon trafic analysé est passé par AppSec. Ça marchait très bien, remarque, mais je me privais de la moitié des scénarios. Va vérifier chez toi, ça prend dix secondes :

docker exec crowdsec cscli metrics show acquisition

Une interface web pour voir ce qui se passe

Le moteur se pilote très bien en ligne de commande, mais j’ai rajouté un petit projet qui donne une vraie vue sur ce qui se passe, et honnêtement je ne reviendrai pas en arrière : CrowdSec Web UI.

Tableau de bord de CrowdSec Web UI : 1420 alertes totales, 2 décisions actives, l'historique d'activité sur sept jours en barres rouges et bleues, une carte du monde avec les États-Unis en rouge foncé, et les tops par pays, scénario et AS
Les alertes, les bans en cours, d’où ça vient et ce qui a été tenté. Tout sur un seul écran.

Il se branche sur l’API locale avec un compte machine, à créer d’abord côté CrowdSec, en choisissant son mot de passe :

docker exec crowdsec cscli machines add crowdsec-web-ui --password 'ton-mot-de-passe'

Puis le compose, avec ce même mot de passe :

services:
  crowdsec-web-ui:
    image: ghcr.io/theduffman85/crowdsec-web-ui:latest
    container_name: crowdsec-web-ui
    restart: unless-stopped
    ports:
      - "127.0.0.1:3060:3060"
    volumes:
      - /home/ton-user/crowdsec-web-ui/data:/app/data
    environment:
      PORT: "3060"
      CROWDSEC_URL: http://host.docker.internal:8080
      CROWDSEC_USER: crowdsec-web-ui
      CROWDSEC_PASSWORD: "ton-mot-de-passe-genere"
      DB_DIR: /app/data
    extra_hosts:
      - "host.docker.internal:host-gateway"
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Note le 127.0.0.1:3060 devant le port : le conteneur n’écoute que sur la machine elle-même, rien n’est exposé au monde. C’est le reverse proxy qui décidera qui a le droit d’entrer.

Ensuite j’ai fait ce que je fais toujours : un sous-domaine chez Duck DNS, et un proxy host dans NPMplus pointé sur le port 3060. Et sur celui-là, mets une Access List, ou ton SSO si tu en as un. C’est un tableau de bord de sécurité, ce serait dommage de le laisser en libre-service. Le SSO, j’en parlerai la prochaine fois, promis.

Et là, tous tes sites sont protégés

C’est ça le vrai truc. Une fois le bouncer branché, tout ce qui passe par NPMplus passe par CrowdSec. Tous tes domaines, d’un coup, sans rien faire de plus. C’est limite magique.

Et si un site fait des faux positifs, tu peux couper l’inspection domaine par domaine, dans les options du proxy host :

Fenêtre Edit Proxy Host de NPMplus, onglet Details, avec l'option Disable Crowdsec Appsec encadrée en rouge parmi les autres interrupteurs
Un interrupteur par domaine. Attention, il ne coupe que le WAF, pas le bannissement d’IP.

La nuance vaut le coup d’être dite : cet interrupteur désactive l’inspection AppSec pour ce domaine, donc la partie WAF. Le bannissement des IP déjà connues, lui, continue de s’appliquer. C’est plutôt une bonne chose, tu lèves la contrainte gênante sans lever la protection.

Et si c’est une IP précise qui se fait jeter à tort, la tienne au hasard, tu la sors du lot :

docker exec crowdsec cscli allowlists create mes-ip -d "IP de confiance"
docker exec crowdsec cscli allowlists add mes-ip 203.0.113.42
docker exec crowdsec cscli decisions delete --ip 203.0.113.42

Ou alors tu ne tapes rien du tout et tu passes par l’interface web : la page Décisions liste les bans en cours et tu débannis en un clic. Elle sait aussi faire l’inverse, bannir une IP à la main avec la durée que tu veux, si tu en croises une qui t’agace.

Ce que ça donne chez moi

Des chiffres, tant qu’à faire, parce que c’est ça qui parle. Depuis que ça tourne : 483 850 requêtes inspectées par le WAF, 10 110 bloquées. 1 420 alertes remontées. Et au moment où j’écris, deux bans actifs seulement, parce qu’un ban expire tout seul au bout de quelques heures.

Le scénario qui se déclenche le plus chez moi, et de loin, c’est vpatch-env-access : des robots qui tournent en boucle en cherchant un fichier .env à télécharger, en espérant y trouver des mots de passe de base de données. Ils viennent en grande majorité de machines louées chez un gros hébergeur cloud. Ce ne sont pas des hackers dans une cave, ce sont des scanners industriels qui ratissent l’Internet entier, en permanence, et qui tombent sur toi parce que tu existes.

Aucune de ces requêtes n’a atteint mes applis. C’est exactement le boulot.

Franchement, c’est devenu indispensable

Je le recommande vraiment. C’est une protection en plus, elle se pose en une soirée, et elle évite les mauvaises surprises. Le jour où une CVE sort sur un truc que tu héberges, le virtual patching est déjà en place avant que tu aies eu le temps de mettre à jour.

Et si vraiment ça te gêne quelque part, tu ajoutes une IP en liste blanche, ou tu désactives carrément le WAF sur un domaine. Tu gardes la main. Mais ça vaut le coup de le laisser travailler : après tout, il fait le tri pendant que tu dors.

Image d’en-tête générée par IA.

SmeagolWorms4

Les nouveaux articles par mail

Un mail par article publié. Rien d'autre, et désinscription en un clic.

Commentaires

Chargement des commentaires…

    Laisser un commentaire

    Vérification anti-robot en cours…

    Jamais publié.