Comment écrire un bouncer CrowdSec ?

Image illustrant l'article : Comment écrire un bouncer CrowdSec ?

Comment écrire un bouncer CrowdSec ?

par Korben -

Cher lecteur, vous êtes certainement arrivé ici dans le but d’écrire votre propre Bouncer dans le langage de programmation de votre choix. Ce guide est fait pour vous.

Si la tâche n’est pas si complexe, il est important de garder en tête un concept clef : la flexibilité.

En effet, un bouncer efficace doit s’intégrer au mieux au sein de l’environnement du site ou de l’application qui l’héberge. Il doit tout autant s’adapter à l’aspect graphique du site qui l’intègre qu’au contexte des utilisateurs finaux.

Et si mes utilisateurs ont des IPv6 ? Et si la stack qui héberge le bouncer utilise un load balancer ou un CDN ? Comment gérer les situations inconnues ? Par exemple, que faire si CrowdSec indique de procéder à un type de remédiation que mon bouncer ne sait pas traiter ? Autant de questions auxquelles nous allons répondre dans ce guide. Attachez vos ceintures, en route pour une virée au cœur du Bouncer Express (Node.js) :-)

Pour toute question, n’hésitez pas à entrer en contact avec la team CrowdSec sur leur chat Gitter.

Les objectifs que nous atteindrons à la fin de ce guide

Voici la liste des fonctionnalités du bouncer que vous allez créer dans ce guide.

  • Bloquer l’accès ou proposer de remplir un captcha Notre bouncer sera capable de bloquer l’accès ou de présenter un captcha à l’utilisateur suspecté, nous appellerons ces deux pages ban wall et captcha wall.
  • Supporter IPv4 et IPv6 Notre bouncer doit être capable de prendre en charge les IPv4 et les IPv6.
  • Remédier des IP ou des ranges d’IP Les décisions provenant de CrowdSec peuvent concerner des IP seules ou des plages d’IP.
  • Mode rupture Les API de CrowdSec proposent 2 modes de consommation de sa liste de décisions : le mode rupture et le mode stream. Dans cet article, nous implémenterons le mode rupture. Nous aborderons le mode stream dans un prochain article.
  • Prise en charge des remédiations inconnues Conçu pour être extensible, CrowdSec permet de créer autant de type de remédiations que nécessaire (ban, captcha, etc). Notre bouncer doit donc être capable de réagir à toutes les remédiations, même celles qu’il ne connait pas encore.
  • Wall personnalisables L’aspect visuel de la page de ban et du formulaire de captcha (on appelle ces deux pages des “wall”) pourra être personnalisé. L’utilisateur pourra modifier les couleurs de ces pages, les textes et même ajouter sa propre feuille de styles CSS si nécessaire.
  • Flex mode : l’utilisateur n’est jamais bloqué Pour certains usages spécifiques comme le commerce en ligne, il est préférable de ne jamais bloquer l’utilisateur et lui proposer de remplir un captcha dans le pire des cas. Nous appellerons cette fonctionnalité le “flex mode”.
  • CDN ready Lorsqu’on utilise un CDN, un proxy ou un load balancer, l’IP de l’utilisateur est cachée derrière. Heureusement ces intermédiaires transmettent tout de même l’IP de l’utilisateur à travers un header spécifique. Pour éviter l’IP spoofing par un utilisateur qui modifierait ce header à sa guide, le bouncer peut croire ce header seulement si l’IP de l’intermédiaire est inclus dans une liste choisie par l’utilisateur du bouncer.
  • Bypass mode Pour répondre à certains besoins ponctuels, li faut permettre à l’utilisateur de désactiver provisoirement le bouncing.
  • Logger d’événements Pour être Production ready, nous mettrons en œuvre un système de logs des événements en production. Cela permettra également par la suite de consulter les métriques d’usage du bouncer. Nous utiliserons la library populaire Winston.

Au sommaire :

  • Les objectifs que nous atteindrons à la fin de ce guide
  • Les étapes de A à Z
  • Le brief technique (5 min)
  • Les outils indispensables pour ce tutoriel
  • CURL pour vous familiariser avec LAPI (5 min)
  • Étape 1 - Préparer votre base de code (10 min)
  • Étape 2 - Commençons par développer le client REST de LAPI (15 min)
  • Étape 3 : Construire les remédiations (15 min)
  • Étape 4 : Afficher des walls (15 min)
  • Étape 5 : Passons au middleware Express (15 minutes)
  • Étape 7 : Reconnaitre l’IP d’un utilisateur derrière un proxy (10 min)
  • Étape 8 : Publication du module sur NPM (5 min)
  • Conclusion

Les étapes de A à Z

Découvrez les étapes essentielles pour concevoir votre bouncer CrowdSec :

![enter image description here](stack.webp)
1. Préparer votre base de code 2. Commençons par développer le client REST de LAPI 3. Construire une remédiation 4. Afficher des walls 5. Le middleware Express 6. Présenter des captchas 7. Reconnaitre l’IP d’un utilisateur derrière un proxy 8. Publication du bouncer

Le brief technique (5 min)

Voici le cadre technique dans lequel nous allons évoluer pour développer ce bouncer. Il s’agit selon nous de bonnes pratiques pour concevoir un bouncer de qualité.

  • Supporter les versions majeures de Node.js : Une bonne matrice de compatibilité serait Node 10, 12, 14, cf. Google cloud functions et AWS lambdas.
  • Bien documenter et inclure des exemples Une documentation claire et efficace (Markdown) ainsi que des exemples “clef en main” directement utilisables permettront à vos utilisateurs de très rapidement prendre en main votre bouncer.
  • Une library générique Node.js réutilisable Nous allons créer ce bouncer en deux temps, la bibliothèque Node.js puis le middleware Express. Cela permettra de créer plus tard d’autres bouncers Node.js à partir de cette bibliothèque.
  • Licence MIT Elle fait partie des licences les plus permissives, nous la conseillons vivement.
  • Dans le doute, laisser passer Si une erreur inconnue survient, il est préférable de laisser passer l’utilisateur malveillant que de bloquer des utilisateurs bienveillants. Le bouncer ne doit jamais perturber le bon fonctionnement de l’application à sécuriser.
  • Test Driven Development Dans un souci de maintenabilité, nous adoptons cette méthodologie consistant à implémenter les tests avant le code source. Nous utiliserons la library populaire Jest.

Les outils indispensables pour ce tutoriel

Dans ce projet, nous allons utiliser plusieurs utilitaires bien pratiques. Les voici :

  • jq : parser et filtrer une sortie JSON dans le terminal
  • curl : exécuter des requêtes HTTP depuis le terminal
  • n : utiliser des versions spécifiques de Node.js en même temps

Assurez-vous d’installer CrowdSec

Pour développer le bouncer, il faudra disposer d’une instance de CrowdSec accessible depuis votre poste pour faire vos essais.

Notre conseil aux utilisateurs de macOS

Assurez-vous de disposer de Docker puis entrez les commandes suivantes :

alias crowdsec_start="docker run -d -e DISABLE_AGENT=\"true\" -p 8080:8080 --name crowdsec crowdsecurity/crowdsec:latest"
alias crowdsec_stop="docker rm -f crowdsec"
alias crowdsec_reset="crowdsec_stop; crowdsec_start && cscli bouncers add nodejs-bouncer -o raw > .bouncer-key && cat .bouncer-key"
alias cscli="docker exec crowdsec cscli"
  • crowdsec_start va démarrer une instance de l’image officielle CrowdSec
  • crowdsec_stop vous permettra de la stopper
  • crowdsec_reset vous permettra de relancer une instance toute neuve et de générer une clef neuve aussi
  • cscli sera exécuté directement dans votre container.

Pour éviter de configurer ces alias à chaque nouvelle session de votre terminal, ajoutez les directement à l’ouverture de session en ajoutant ces lignes dans votre ~/.bash_profile (ou ~/.zshrc).

Note: Avant de continuer, n’oubliez pas de re-sourcer votre fichier via source ~/.bash_profile ou source ~/.zshrc) puis de démarrer une instance de CrowdSec en tapant crowdsec_start ;-)

CURL pour vous familiariser avec LAPI (5 min)

Notre bouncer Node.js va communiquer avec CrowdSec à travers son API REST.

Nous allons développer notre librairie autour d’une version locale de CrowdSec. Mais le bouncer fonctionnera tout aussi bien avec CAPI, dans quelque temps, lorsque CAPI sera capable de communiquer directement avec les bouncers.

CrowdSec vient avec sa ligne de commande cscli et met à disposition une API REST locale, jouons un peu avec !

Nous allons essayer quelques commandes, cela nous permettra de bien comprendre les interactions que notre bouncer aura avec l’API locale de CrowdSec avec de commencer à coder.

Bannir une IP

Pour débuter, nous allons bannir l’IP 1.2.3.4 durant 4 h

cscli decisions add --ip 1.2.3.4 --duration 4h --type ban

Si votre instance CrowdSec est correctement configurée, la commande affichera en retour :

INFO[0000] Decision successfully added

Listez maintenant les décisions :

cscli decisions list

Vous verrez, en retour, quelque chose comme :

cscli decisions list
#+----+--------+-------------+----------------------------------------------------+--------+---------+----+--------+------------------+----------+
#| ID | SOURCE | SCOPE:VALUE | REASON | ACTION | COUNTRY | AS | EVENTS | EXPIRATION | ALERT ID |
#+----+--------+-------------+----------------------------------------------------+--------+---------+----+--------+------------------+----------+
#| 1 | cscli | Ip:1.2.3.4 | manual 'ban' from | ban | | | 1 | 3h59m28.4942404s | 1 |
#| | | | '6ab0569e32574447b3628ad23f9cb687nV5PJllGFSoAeUUu' | | | | | | |
#+----+--------+-------------+----------------------------------------------------+--------+---------+----+--------+------------------+----------+

Appliquer un captcha pour une IP

Supprimez maintenant toutes les décisions :

cscli decisions delete --all

L’écran affichera :

# INFO[0000] 1 decision(s) deleted

Voici comment appliquer un captcha sur le range d’IPs 2.3.4.5/30 durant 4h :

cscli decisions add --range 2.3.4.5/30 --duration 4h --type captcha

En retour :

# INFO[0000] Decision successfully added

Générer une clef d’API pour notre bouncer

Et voilà comment générer une clef pour votre bouncer, puis la stocker dans un fichier, le temps de vos développements :

cscli bouncers add nodejs-bouncer -o raw > .bouncer-key && cat .bouncer-key

Vous verrez en retour de cette commande la clef du bouncer que vous venez de créer.

Tester la connexion avec l’API

Maintenant, jetons un œil l’API à locale de CrowdSec avec curl et jq.

Pour commencer, vérifiez que l’API soit bien accessible et que la clef fonctionne bien (statut 200):

curl --silent --head --header "X-Api-Key: `cat .bouncer-key`" "http://localhost:8080/v1/decisions"

Le paramètre --head indique qu’il faut utiliser la méthode HTTP HEAD. Celle-ci permet de demander uniquement les headers de réponses sans demander le corps de la réponse (body). C’est généralement utilisé pour s’assurer que l’API fonctionne bien. Nous authentifions notre requête via la clef du bouncer que nous avons généré un peu plus tôt.

Voilà le retour attendu :

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Tue, 26 Jan 2021 11:29:56 GMT

Lister les décisions

Nous pouvons maintenant lister toutes les décisions :

curl --header "X-Api-Key: `cat .bouncer-key`" http://localhost:8080/v1/decisions | jq

Le retour ci-dessous affiche l’ensemble des décisions actuelles de l’instance locale de CrowdSec :

[
{
"duration": "3h57m35.0742635s",
"id": 2,
"origin": "cscli",
"scenario": "manual 'captcha' from '6ab0569e32574447b3628ad23f9cb687nV5PJllGFSoAeUUu'",
"scope": "Range",
"type": "captcha",
"value": "2.3.4.5/30"
}
]

Nous pouvons remarquer qu’il existe 1 décision de type captcha pour le range 2.3.4.5/30 et qu’elle devrait encore durer environ 4h.

Listons maintenant toutes les décisions correspondant à l’IP 2.3.4.5 :

curl -s -H "X-Api-Key: `cat .bouncer-key`" http://localhost:8080/v1/decisions?ip=2.3.4.5 | jq

Note: nous avons passé le filtre ?ip=... dans la requête.

[
{
"duration": "3h56m23.5293443s",
"id": 2,
"origin": "cscli",
"scenario": "manual 'captcha' from '6ab0569e32574447b3628ad23f9cb687nV5PJllGFSoAeUUu'",
"scope": "Range",
"type": "captcha",
"value": "2.3.4.5/30"
}
]

Étape 1 - Préparer votre base de code (10 min)

Checklist

Dans cette étape, nous allons mettre en place tout le nécessaire pour coder efficacement notre bouncer :

  • Séparer le code middleware du code de la library Node.js.
  • Installer les dépendances utiles pour notre bouncer
  • Configurer le logger Winston
  • Configurer l’environnement des tests unitaires et des tests fonctionnels

Conçu pour être réutilisable par les autres frameworks d’application web

Nous nous apprêtons à développer un bouncer pour Express, le très populaire framework d’application web pour Node.js.

Dans le monde de Node.js, Il existe bon nombre de frameworks d’application web.

Pour permettre le développement futur d’autres bouncers Node.js, nous allons concevoir en deux temps notre Bouncer pour Express :

  1. Premièrement, nous développerons une library générique Node.js. Elle inclura le maximum de fonctionnalités possibles, qui seront réutilisables par d’autre frameworks.
  2. Dans un second temps, nous développerons le middleware Express qui utilisera cette library.

Méthodologie de ce guide

Au cours de cet article, nous coderons les deux parties distinctes dans un même repository git pour des raisons pratiques. Il sera ensuite possible d’extraire la library Node.js en toute fin de projet dans un repository dédié.

Afin que vous puissiez avancer efficacement, nous avons ajouté des git tags dans le dépôt du bouncer Express à chacune des 8 grandes étapes de ce guide. Vous n’aurez donc pas de code à copier-coller, il vous suffira de git checkout le bon tag à chacun des 9 tags d’étape. Vous aurez aussi très peu de chance d’être bloqué.

Par exemple git checkout step1 affichera le code tel qu’il devrait être en toute fin d’étape 1 et git checkout step2 affichera le code tel qu’il devrait être en toute fin d’étape 2.

Cloner le projet GIT et installer les dépendances Node.js

  1. Cloner le dépôt GIT
git clone [email protected]:crowdsecurity/cs-express-bouncer.git --branch step1 --single-branch

Grâce au paramètre --branch, nous clonons le dépôt directement sur le tag step1.

  1. Installer les dépendances NPM
npm install

Vous venez d’installer :

  • ip-address La library populaire qui offre des utilitaires de qualité pour manipuler les adresses IP.
  • isomorphic-fetch Elle nous permettra d’exécuter les requêtes HTTP.
  • winston Nous utiliserons ce très populaire logger pour garder trace des événements critiques en production.
  • lodash qui inclue lodash template, un mini-moteur de rendu très simple d’utilisation, similaire à mustache ou handlebars, largement suffisant dans notre cas puisque nos templates sont très simples.
  • svg-captcha, une library de génération de captcha qui ne dépend d’aucune API externe et ne nécessite aucune création de compte.

La configuration du logger (Winston)

  1. Dans votre repository, jetez un œil à src/nodejs-bouncer/lib/logger.js.

Nous l’avons préconfiguré de la façon suivante :

  • en production : les logs seront écrits dans un fichier production.log. le path est personnalisable si nécessaire.
  • en développement : les logs ne sont pas enregistrés, mais affichés directement sur la sortie du terminal, en couleurs pour un confort optimal de lecture.

Il est aussi possible de changer le niveau de log à tout moment, en passant la variable d’environnement CONSOLE_LOGGER_LEVEL avec le bon niveau de log (debug, warning, error…)

Test Driven Development

Nous concevrons ce projet en TDD pour qu’il puisse être maintenu avec plus de facilité dans le temps.

Les tests unitaires

Ils testeront notre code en utilisant des mocks pour l’API de l’instance CrowdSec (LAPI), une sorte de simulateur de réponse de l’API.

  1. Dans votre repository, ouvrez le fichier src/nodejs-bouncer/lib/restClient.mock.js.

Dans ce fichier, nous mockons des réponses possibles de l’API CrowdSec, elles serviront lors de la rédaction de nos tests unitaires.

Lors du développement de notre middleware, nous utiliserons aussi des mocks pour simuler le comportement d’Express.

Les tests fonctionnels

Ils testeront le code du point de vue de l’utilisateur. Ici nous n’utiliserons pas de mock, le code sera testé en étant réellement connecté à une instance de CrowdSec.

Pour provoquer les changements d’état de l’API, nous créerons un utilitaire qui se chargera de taper les commandes cscli pour nous. On l’appellera le CSCLI Commander.

  1. Ouvrez src/nodejs-bouncer/utils/cscliCommander.js.

Ce “CSCLI Commander” permettra d’exécuter des commandes cscli directement depuis Node.js. Cela permettra d’automatiser certaines actions lors des lancements de tests fonctionnels sur notre module, à savoir :

  • Ajouter une décision (ban, captcha ou autre)
  • Supprimer toutes les décisions existantes
  • Créer ou supprimer une bouncer API key

La préparation du projet est terminée ! 🎊

Dans la prochaine étape, nous allons coder le client REST qui va communiquer avec LAPI.

Étape 2 - Commençons par développer le client REST de LAPI (15 min)

Checklist du client REST

Dans cette étape, nous allons construire le client HTTP qui communiquera avec les API REST de CrowdSec. À la fin de cette étape :

  • Le client REST saura traiter les IP, mais aussi les ranges d’IP
  • Le client REST saura traiter les IPv4, mais aussi les IPv6
  • Il doit être possible de tester la connexion à l’API

📌 Obtenir les changements associés à cette étape

Tapez la commande suivante :

git checkout step2

Dans cette évolution de la base de code, vous trouverez le client REST. Jetons-y un œil.

1. Observez le fichier src/nodejs-bouncer/lib/parseExpiration.js (voir sur github)

Ce helper permettant de parser sous forme de secondes le champ duration que nous renvoie l’API CrowdSec. Il sera utilisé par le fichier ci-dessous.

2. Dans le fichier src/nodejs-bouncer/lib/restClient.js (voir sur github) :

  • On utilise isomorphic-fetchpour exécuter les requêtes d’API.
  • Il y a une méthode permettant de tester la connexion à l’API CrowdSec.

3. Passons à src/nodejs-bouncer/lib/restClient.test.js

En modifiant la valeur de la variable USE_CROWDSEC_MOCKS, on activera ou non le mocking des appels HTTP.

En d’autres termes, on lancera un test unitaire lorsque USE_CROWDSEC_MOCKS sera vrai. À l’inverse, on lancera un test fonctionnel lorsque USE_CROWDSEC_MOCKS sera faux.

🤖 Voici les tests que nous avons ajoutés à cette étape :

4. Retrouver les modifications apportées à package.json (voir sur github)

Vous y découvrirez une liste de commandes NPM pour faciliter le lancement des tests :

Commande de testDescription
npm testLancer les tests unitaires
npm run testVerboseLancer les tests unitaires (très verbeux)
npm run testWithIntegrationLancer les tests fonctionnels
npm run testVerboseWithIntegrationLancer les tests fonctionnels (très verbeux)
npm run test10Lancer les tests unitaires avec Node 10
npm run test12Lancer les tests unitaires avec Node 12
npm run testLtsLancer les tests unitaires avec Node LTS
4a. Vérifier que le client REST réponde bien à nos attentes en tapant dans votre terminal :
npm test

Vous devriez obtenir :

PASS src/nodejs-bouncer/lib/restClient.test.js
Rest Client
✓ should configure (1 ms)
✓ should test connection (3 ms)
✓ should retrieve ban decisions matching IP 3.4.5.6 (1 ms)
✓ should retrieve ban decisions matching IPv6 ::ffff:3.4.5.6 (1 ms)
✓ should retrieve decisions matching range 3.4.5.6/30

Test Suites: 1 passed, 1 total
Tests: 5 passed, 5 total
Snapshots: 0 total
Time: 1.27 s, estimated 2 s
Ran all test suites.
4b. Lançons maintenant les tests fonctionnels et vérifions la bonne communication entre CrowdSec et notre client REST :
npm run test:func

Cette commande lance les tests fonctionels en désactivant la variable d’environnement USE_CROWDSEC_MOCKS. Le client va donc cette fois réellement communiquer avec votre instance de CrowdSec.

En retour votre devriez obtenir :

PASS src/nodejs-bouncer/lib/restClient.test.js
Rest Client
✓ should configure
✓ should test connection (8 ms)
✓ should retrieve ban decisions matching IP 3.4.5.6 (278 ms)
✓ should retrieve ban decisions matching IPv6 ::ffff:3.4.5.6 (270 ms)
✓ should retrieve decisions matching range 3.4.5.6/30 (278 ms)

Test Suites: 1 passed, 1 total
Tests: 5 passed, 5 total
Snapshots: 0 total
Time: 2.189 s
Ran all test suites.

5. Essayer l’exemple

Voici un petit exemple qui utilise le client REST que nous venons d’écrire (voir sur GitHub) :

Lancez :

node examples/1-restClient.js

Vous devriez obtenir :

debug: decisions delete --all
debug: time="2021-03-29T18:26:32Z" level=info msg="3 decision(s) deleted"

debug: {
"deleteAllDecisions": "time=\"2021-03-29T18:26:32Z\" level=info msg=\"3 decision(s) deleted\"\n"
}
debug: decisions add --ip 3.4.5.6 --duration 4h --type ban
debug: time="2021-03-29T18:26:32Z" level=info msg="Decision successfully added"

info: {
"decisionsMatching3456": [
{
"duration": "3h59m59.3184369s",
"id": 194,
"origin": "cscli",
"scenario": "manual 'ban' from '888ace3a09de43cea82cca2390491032BnMfEKXnQocqT73s'",
"scope": "Ip",
"type": "ban",
"value": "3.4.5.6",
"expiration": "2021-03-29T22:26:31.658Z"
}
]
}

Le client REST de l’API CrowdSec est prêt à l’emploi ! 🎊

À l’étape suivante, nous allons développer les remédiations.

Étape 3 : Construire les remédiations (15 min)

Comment allons nous procéder ?

Voilà ce qu’on prévoit de faire :

![enter image description here](ban-flow.webp)
Il s’agit des interactions entre chacune des briques permettant d’afficher un *ban wall* à un utilisateur provenant d’une IP malveillante

Ce que nous allons faire

Dans cette étape, nous allons :

  • Développer le cœur de la library Node.js générique en nous appuyant sur le client REST que nous venons de développer à l’étape précédente.
  • À partir d’une liste de décisions renvoyées par l’API pour une IP donnée, nous allons déduire la bonne remédiation
  • Permettre à l’utilisateur de choisir une remédiation à appliquer lorsque la remédiation remontée par l’API n’est pas prise en charge par le bouncer.
  • Permettre à l’utilisateur de limiter le niveau de remédiation du bouncer.

Exemple concret : si CrowdSec indique qu’il faut appliquer un ban alors que le niveau a été limité par l’utilisateur à captcha, alors l’IP indiquée comme malveillante aura tout de même une chance de visiter le site si elle prouve qu’elle n’est pas un bot malveillant. C’est très pratique pour les sites qui ne souhaitent jamais bloquer leurs utilisateurs, comme c’est parfois le cas pour les sites de e-commerce.

📌 Obtenir les changements associés à cette étape

Tapez la commande suivante :

git checkout step3

Dans cette nouvelle étape, la base de code a reçu la base de la library Node.js capable de construire une remédiation à partir d’une liste de décisions. Jetons-y un œil. 👀

1. Dans votre repository, ouvrez src/nodejs-bouncer/lib/constants.js (voir sur GitHub)

Il contient la listes des types de remédiations prises en charge.

2. Ouvrez ensuite src/nodejs-bouncer/index.js (voir sur GitHub)

  • Lorsqu’il y a plusieurs décisions pour une même adresse IP, getHigherRemediation nous permet d’appliquer la décision la plus élevée.
  • Lorsqu’une décision est inconnue, on utilise celle qui est configurée en tant que fallback.
  • Lorsqu’une valeur est plus importante que maxRemediationValue, elle est ignorée. C’est cette fonctionnalité qui permet de proposer le flex mode.

3. Regardez maintenant le fichier src/nodejs-bouncer/index.test.js (voir sur GitHub)

🤖 Voici les tests que nous avons ajoutés :

3a. Pour vérifier que la library Node.js réponde bien à nos attentes, tapez dans votre terminal :

npm test

Vous devriez obtenir :

PASS src/nodejs-bouncer/index.test.js
PASS src/nodejs-bouncer/lib/restClient.test.js

Test Suites: 2 passed, 2 total
Tests: 11 passed, 11 total
Snapshots: 0 total
Time: 1.403 s, estimated 6 s
Ran all test suites.

3b. Tout fonctionne bien ? Bravo ! Lançons maintenant les tests fonctionnels et vérifions la bonne communication entre CrowdSec et notre library Node.js :

npm run test:func

Ce test va lancer les tests en désactivant la variable d’environnement USE_CROWDSEC_MOCKS. Les tests vont donc réellement communiquer avec votre instance CrowdSec.

En retour votre devriez obtenir :

PASS src/nodejs-bouncer/index.test.js
PASS src/nodejs-bouncer/lib/restClient.test.js

Test Suites: 2 passed, 2 total
Tests: 11 passed, 11 total
Snapshots: 0 total
Time: 7.114 s
Ran all test suites.

5. Essayez l’exemple

Voici un petit exemple qui utilise la library que nous venons d’écrire (voir sur GitHub)

Lancez :

node examples/2-library.js

Vous pourrez :

  • tester la connexion et obtenir une remédiation ban
  • tester le flex mode
  • tester le fallback lors d’une décision inconnue (ex :mfa).

La library Node.js sait maintenant construire une remédiation à partir d’une liste de décisions 🎊

À l’étape suivante, nous allons développer le bouncer Express qui s’appuiera sur votre library.

Étape 4 : Afficher des walls (15 min)

Ce que nous allons faire

Dans cette étape, nous allons :

  • Générer le rendu HTML des deux walls, le ban wall qui sera affiché à l’utilisateur dont l’accès est interdit, et le captcha wall qui sera affiché à l’utilisateur dont l’identité est suspectée.
  • Permettre à l’utilisateur du bouncer de rendre cette page personnalisable (texte, couleurs et règles CSS)

Voici le ban wall :

![Le ban wall](ban-wall.webp)
Et voilà le Captcha wall :
![Le captcha wall](captcha-wall.webp)
### 📌 Obtenir les changements associés à cette étape

Tapez la commande suivante :

git checkout step4

Dans cette nouvelle étape, la base de code a reçu le moteur de rendu et les deux templates de ban wall et de captcha wall ! Jetons-y un œil ! 👀

1. Dans votre repository, ouvrez src/nodejs-bouncer/lib/templates/ban.ejs (voir sur GitHub)

C’est le template du ban wall.

Vous voyez à l’oeuvre le système de template lodash template, un mini-moteur de rendu très simple d’utilisation, similaire à mustache ou handlebars, largement suffisant dans notre cas puisque nos templates sont très simples.

Comme vous pouvez le voir ce template est très petit. Le reste du code HTML/CSS se trouve dans le fichier src/nodejs-bouncer/lib/templates/base.ejs (voir sur GitHub). C’est une base commune pour les templates du ban wall et du captcha wall, que vous retrouverez ici : src/nodejs-bouncer/lib/templates/captcha.ejs (voir sur GitHub).

2. Regardez maintenant src/nodejs-bouncer/lib/renderer.js (voir sur github)

C’est le moteur de rendu. Il prend en entrée des variables telles que les textes, les couleurs et du CSS additionnel. Vous y trouverez aussi les valeurs par défaut des templates. Elles pourront être surchargées lors de la configuration du middleware dans les étapes suivantes.

  • Le template peut de ce fait être traduit en plusieurs langues.
  • L’utilisateur peut personnaliser la page avec son propre set de couleurs.
  • L’utilisateur peut ajouter sa feuille de style CSS

3. Essayez l’exemple

Voici un petit exemple qui affiche les templates que nous venons d’écrire (voir sur GitHub)

Lancez :

node examples/3-library-templates.js

Voilà ! Le système de rendu est prêt. 🎊

À l’étape suivante, nous allons développer le bouncer Express qui s’appuiera sur notre library.

Étape 5 : Passons au middleware Express (15 minutes)

Checklist du middleware Express

Dans cette étape, nous allons :

  • Débuter le développement du middleware Express
  • Prendre en charge les remédiations de type ban
  • Prendre en compte les remédiations inconnues du bouncer
  • Permettre à l’utilisateur de personnaliser ses templates
  • Permettre à l’utilisateur de bypasser le bouncing
  • Tester la connexion avec CrowdSec lors de l’initialisation du middleware
  • Catcher toutes les erreurs afin que le middleware ne puisse en aucun cas faire crasher l’application

Nous traiterons la prise en charge du captcha, un peu plus complexe, dans l’étape suivante, elle lui sera dédiée.

📌 Obtenir les changements associés à cette étape

Tapez la commande suivante :

git checkout step5

Dans cette nouvelle étape, la base de code a reçu le middleware incluant la gestion complète du ban wall ! Jetons-y un œil ! 👀

1. Dans votre repository, ouvrez src/express-crowdsec-middleware/index.js (voir sur GitHub)

Comme vous pouvez le remarquer, notre middleware délègue en grande partie ses tâches à la library que nous avons développé précédemment. Voici les point essentiels à retenir dans ce fichier :

  • On a ajouté un paramètre bypass qui permet de désactiver provisoirement le bouncing.
  • On teste la connexion avec l’API à l’initialisation du middleware, mais on a aussi ajouté un paramètre bypassConnectionTest permettant d’éviter ce test lors du lancement.
  • On catche toutes les erreurs possibles pour ne jamais prendre le risque de rendre l’application indisponible.
  • On prend en charge totalement la remédiation de type ban.

2. Ouvrez ensuite src/express-crowdsec-middleware/index.test.js (voir sur GitHub)

🤖 Voici les tests que nous avons ajoutés :

N’oubliez pas de lancer les tests unitaires avec npm test et les tests fonctionnels avec npm run test:func.

3. Essayez l’exemple

Voici un petit exemple qui créé une application express et utilise le middleware (voir sur GitHub)

Lancez :

node examples/4-express.js

Vous pourrez :

  • tester le ban wall
  • tester le flex mode
  • tester le fallback lors d’une décision inconnue (ex : mfa).
  • tester la personnalisation des templates en changeant les couleurs, les textes et en ajoutant du CSS additionnel

Nous avons désormais un middleware Express capable de gérer les remédiations de type ban ! 🎊

À l’étape suivante, nous allons prendre en charge la remédiation captcha dans le middleware.

Étape 7 : Reconnaitre l’IP d’un utilisateur derrière un proxy (10 min)

Checklist de la prise en charge des IP derrière un proxy, CDN et autres load balancers

C’est la dernière étape de code. Vous allez :

  • permettre de reconnaitre l’IP d’un utilisateur derrière un CDN, proxy ou autre load balancer.
  • sécuriser cette phase de reconnaissance en se basant sur une liste d’IP autorisées à être considérée comme CDN
  • enregistrer dans les logs les tentatives d’hameçonnage d’IP via une utilisation détournée du header X-Forwarded-For

📌 Obtenir les changements associés à cette étape

Tapez la commande suivante :

git checkout step7

Dans cette nouvelle étape, la base de code a reçu la prise en charge des IP derrière un proxy.

1. Dans votre repository, ouvrez src/express-crowdsec-middleware/lib/getRealIp.js (voir sur GitHub)

Nous acceptons désormais que l’IP de l’utilisateur provienne du header X-Forwarded-For mais à la stricte condition que l’adresse IP du proxy qui transmet ce header fasse partie d’une liste d’IP autorisées configurée par l’utilisateur du middleware. Cette liste est un tableau qui peut contenir des IP ou encore des ranges d’IP.

2. Ouvrez ensuite src/express-crowdsec-middleware/index.test.js (voir sur GitHub)

🤖 Voici les tests que nous avons ajoutés :

N’oubliez pas de lancer les tests unitaires avec npm test et les tests fonctionnels avec npm run test:func.

3. Essayez l’exemple

Testez la reconnaissance du header X-Forwarded-For dans l’exemple qui créé une application express et utilise le middleware (voir sur GitHub)

Lancez :

node examples/4-express.js

Et… nous avons terminé de coder le bouncer ! 🎊

À l’étape suivante, nous allons publier notre bouncer sur NPM.

Étape 8 : Publication du module sur NPM (5 min)

Checklist de la prise en charge des IP derrière un proxy, CDN et autres load balancers

Lors cette ultime étape, nous allons peaufiner notre bouncer pour ensuite le publier sur NPM :

  • Ajouter la licence MIT à notre bouncer
  • Ajouter une documentation d’installation et de configuration (Readme.md)
  • Versionner et publier le module sur NPM

📌 Obtenir les changements associés à cette étape

Tapez la commande suivante :

git checkout step8

Dans cette nouvelle étape, la base de code a reçu un peu de documentation et une licence MIT !

Cette documentation à destination de nos utilisateurs leur permet d’installer le middleware dans un projet existant.

Publier le module sur NPM

Voici les étapes essentielles pour publier le module sur NPM.

On utilise gh, le très pratique cli de GitHub.

Pour vérifier la version actuelle de votre projet :

gh release view

Pour créer une nouvelle version :

export NEW_VERSION=X.X.X
gh release create v${NEW_VERSION} --title v${NEW_VERSION} --draft

Pour la publier sur NPM :

npm version from-git
npm publish

Nous sommes arrivés à la fin de ce guide. Vous avez maintenant les clefs pour créer votre propre bouncer ! 🎊

Conclusion

Nous avons passé en revue les étapes essentielles à la création d’un bon bouncer efficace et flexible, de la découverte de LAPI à la mise en ligne du bouncer.

Comme vous l’avez certainement constaté, l’aspect le plus important lorsqu’on créé un bouncer est de le penser flexible pour qu’il s’adapte au maximum de situations possibles.

Pour toute question, n’hésitez pas à entrer en contact avec la team CrowdSec sur leur channel Gitter. Vous pouvez aussi parcourir les issues du projet Express Bouncer ou en ajouter.

Nous espérons que vous ayez pris plaisir à parcourir ce guide et qu’il vous inspirera pour créer votre propre ouncer. Vive l’open source et vive la communauté CrowdSec !