Crowdsec : Ajout et configuration d'un bouncer

Crowdsec Cybersécurité Open source

Les bouncers (les "videurs" comme en boite de nuit) appliquent les décisions récupérés depuis l'API de Crowdsec. Dans notre cas, notre bouncer installé sur le serveur Nginx va récupérer les décisions (communautaires et locales) sur le serveur Crowdsec Local API et bloquer les IP des attaquants.

Les bouncers ne nécessitent pas obligatoirement l'installation de Crowdsec sur la machine à protéger. Crowdsec est charge de détecter et de tracer quant aux bouncers ils sont en charge de bloquer.

Serveur Crowdsec Local API :

  • OS : Debian 10
  • RAM : 2048
  • IP : 192.168.1.202/255.255.255.0
  • Nom d'hôte : srv-cs-lapi
  • Domaine de portée locale : lab.lan
  • FQDN : srv-cs-lapi.lab.lan

Serveur Nginx :

  • OS : Debian 10
  • RAM : 2048
  • IP : 192.168.1.203/255.255.255.0
  • Nom d'hôte : srv-nginx-01
  • Domaine de portée locale : lab.lan
  • FQDN : srv-nginx-01.lab.lan
  • Service installé : nginx

Vm malveillante (en charge de simuler des attaques) : Serveur Nginx :

  • OS : Debian 10
  • Bureau : Gnome
  • RAM : 2048
  • IP : dhcp
  • Nom d'hôte : bad-vm

maquette03

Nous allons commencer avec un bouncer utilisant un mécanisme connu de ceux qui utilisent fail2ban : bloquer l'IP malveillante sur le firewall.

Pour installer ce bouncer, il faut vous rendre sur le hub Crowdsec https://hub.crowdsec.net/author/crowdsecurity/bouncers/cs-firewall-bouncer et suivre les instructions d'installation. Pour ma part, j'ai choisi le paquet s'appuyant sur Iptables.

Il faut maintenant déclarer le bouncer sur notre serveur CS LAPI :

# srv-nginx-01-fw est le nom du bouncer (choisi arbitrairement)
cscli bouncers add srv-nginx-01-fw
# Sortie
Api key for 'srv-nginx-01-fw':
   ### Copier la clé API ci-dessous ###
   3b75d863c5fcc06d289990699eb1c1e9

Please keep this key since you will not be able to retrieve it!

# Vérification
cscli bouncers list
---------------------------------------------------------------------------------------------------------------------------------
 NAME                   IP ADDRESS  VALID   LAST API PULL                       TYPE        VERSION 
---------------------------------------------------------------------------------------------------------------------------------
srv-nginx-01-fw                               ✔️        2021-07-09T18:43:40+02:00                
---------------------------------------------------------------------------------------------------------------------------------

Copiez la clé API, puis rendez-vous sur le serveur Nginx afin de modifier le fichier de configuration du bouncer au niveau de l'URL de connexion à l'API et de la clé API :

vim /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml :
(...)
# Modifiez l'URL
api_url: https://srv-cs-lapi.lab.lan:8080/
# Collez la clé
api_key: 3b75d863c5fcc06d289990699eb1c1e9
(...)

Relancez le bouncer et activez le au démarrage : service crowdsec-firewall-bouncer restart && systemctl enable crowdsec-firewall-bouncer.service

De nouveau sur le serveur CS LAPI, vérifiez la connexion du bouncer :

cscli bouncers list
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 NAME             IP ADDRESS     VALID  LAST API PULL              TYPE                       VERSION                                                           
----------------------------------------------------------------------------------------------------------------------------------------------------------------
 srv-nginx-01-fw  192.168.1.203  ✔️      2021-07-09T18:45:23+02:00  crowdsec-firewall-bouncer  v0.0.13-debian-pra.... 
----------------------------------------------------------------------------------------------------------------------------------------------------------------

Notre bouncer est donc maintenant connecté de manière sécurisée à notre serveur CS LAPI et fin prêt à bloquer des IP malveillantes.

Par défaut, les plages d'adresses privées sont "whitelistées". Afin de constater le blocage, nous allons commenter la plage 192.168.0.0/16 et déclarer les IP de nos serveurs.

Sur le serveur Nginx :

vim /etc/crowdsec/parsers/s02-enrich/whitelists.yaml :

name: crowdsecurity/whitelists
description: "Whitelist events from private ipv4 addresses"
whitelist:
  reason: "private ipv4/ipv6 ip/ranges"
  ip:
    - "127.0.0.1"
    - "::1"
    - "192.168.1.202"
    - "192.168.1.203"
  cidr:
  # - "192.168.0.0/16"
    - "10.0.0.0/8"
    - "172.16.0.0/12"
  # expression:
  #   - "'foo.com' in evt.Meta.source_ip.reverse"

Sur la VM "bad-vm", on vérifie l'accès à notre serveur web : acces-site-web-ok

Sur le serveur Nginx, on vérifie les décisions :

root@srv-nginx-01:~# cscli decisions list
No active decisions <-- Pas de décisions locales

root@srv-nginx-01:~# cscli decisions list --all <-- pour afficher les décisions récupérées depuis le serveur Crowdsec Central API. 
+------+--------+-----------------------+-------------------------------------------+--------+---------+----+--------+------------------+----------+
|  ID  | SOURCE |      SCOPE:VALUE      |                  REASON                   | ACTION | COUNTRY | AS | EVENTS |    EXPIRATION    | ALERT ID |
+------+--------+-----------------------+-------------------------------------------+--------+---------+----+--------+------------------+----------+
| 1450 | CAPI   | Ip:139.162.152.16/32  | crowdsecurity/http-backdoors-attempts     | ban    |         |    |      0 | 39m38.784615692s |       20 |
| 1451 | CAPI   | Ip:178.20.157.98/32   | crowdsecurity/http-backdoors-attempts     | ban    |         |    |      0 | 39m38.793069325s |       20 |
| 1452 | CAPI   | Ip:148.72.210.68/32   | crowdsecurity/http-backdoors-attempts     | ban    |         |    |      0 | 39m38.801407933s |       20 |
| 1453 | CAPI   | Ip:185.51.228.241/32  | crowdsecurity/http-backdoors-attempts     | ban    |         |    |      0 | 39m38.811503324s |       20 |
| 1454 | CAPI   | Ip:198.71.227.6/32    | crowdsecurity/http-backdoors-attempts     | ban    |         |    |      0 | 39m38.821115173s |       20 |
(...)

La dernière commande nous permet d'observer tout le bénéfice qu'apporte la fonctionnalité collaborative de Crowdsec en permettant de bloquer de manière préventive les IP malveillantes.

Concrètement, cela donne ceci avec Iptables :

root@srv-nginx-01:~# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
DROP       all  --  anywhere             anywhere             match-set crowdsec-blacklists src

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination 

root@srv-nginx-01:~# ipset list crowdsec-blacklists
Name: crowdsec-blacklists
Type: hash:net
Revision: 6
Header: family inet hashsize 1024 maxelem 65536 timeout 300
Size in memory: 9368
References: 1
Number of entries: 100
Members:
222.186.180.142 timeout 85142
40.73.102.25 timeout 85143
46.0.203.166 timeout 85143
45.136.108.85 timeout 85143
(...)

De nouveau sur la VM "bad-vm", on installe le paquet "nikto" qui va nous permettre de simuler l'attaque (il faut avoir activer le dépôt non-free sur Debian). Une fois le paquet installé, on lance la commande suivante : nikto -host 192.168.1.203

Toujours depuis la "bad-vm", essayez d'accéder au service Web :

olivier@bad-vm:~$ curl --connect-timeout 1 http://192.168.1.203/
curl: (28) Connection timed out after 1016 milliseconds

Depuis le navigateur web de la VM attaquante : Capture%20d%E2%80%99%C3%A9cran%20du%202021-07-10%2018-47-52

On constate qu'on ne peut plus se connecter au service Web depuis la VM attaquante. Du côté du serveur CS LAPI cela donne : Capture%20d%E2%80%99%C3%A9cran%20du%202021-07-10%2018-39-53

Au niveau d'iptables sur le serveur Nginx :

root@srv-nginx-01:~# ipset list crowdsec-blacklists | grep 192.168.1.174
192.168.1.174 timeout 13535

Si l'on veut faire reproduire l'attaque, on peut supprimer manuellement le blocage. Sur le serveur Crowdsec LAPI :

root@srv-cs-lapi:~# cscli decisions list
+------+----------+------------------+-----------------------------------+--------+---------+----+--------+--------------------+----------+
|  ID  |  SOURCE  |   SCOPE:VALUE    |              REASON               | ACTION | COUNTRY | AS | EVENTS |     EXPIRATION     | ALERT ID |
+------+----------+------------------+-----------------------------------+--------+---------+----+--------+--------------------+----------+
| 1869 | crowdsec | Ip:192.168.1.174 | crowdsecurity/http-bad-user-agent | ban    |         |    |      2 | 3h59m50.388448338s |       35 |
+------+----------+------------------+-----------------------------------+--------+---------+----+--------+--------------------+----------+

root@srv-cs-lapi:~# cscli decisions delete -i 192.168.1.174 
INFO[11-07-2021 02:07:25 PM] 6 decision(s) deleted         

root@srv-cs-lapi:~# cscli decisions list
No active decisions

Même si Crowdsec n'est pas trop compliqué dans sa mise en place, on peut se perdre dans les différents mécanismes mis en jeu. Afin de bien comprendre, je vais reprendre les évènements et les détailler dans l'ordre chronologique, de l'attaque simulée jusqu'à son blocage :

  • Évènement de départ : Nous lançons une attaque depuis "bad-vm".
  • Évènement intermédiaire : Détection de l'attaque sur le serveur Nginx.
    1. L'agent crowdsec lit les journaux de Nginx (définis via la configuration d'acquisition /etc/crowdsec/aquis.yaml)
    2. Ces journaux sont analysés via des parsers et éventuellement enrichis ( /etc/crowdsec/parsers/).
    3. Ces journaux normalisés sont ensuite comparés aux scénarios déployés par l'utilisateur.
    4. Les scénarios "http-bad-user-agent" et "http-probing" sont déclenchés, Crowdsec-agent génère deux alertes et deux décisions associées.
    5. Ces informations (le signal, les décisions associées) sont ensuite envoyées à travers l'API au serveur CS Local API et stockées dans la base de données.
  • Évènement final : Blocage de l'attaque sur le serveur Nginx.
    1. Le bouncer cs-firewall-bouncer récupère la décision à travers l'API du serveur CS Local API
    2. Le bouncer applique la décision à travers Iptables sur le serveur Nginx.
    3. Le serveur CS Local API pousse les 2 signaux vers le serveur Central API. Afin de garantir la traçabilité, le serveur CS Local API conserve l'alerte même si la décision est expirée.

Avec un serveur, Crowdsec seul avec un bouncer fera l'affaire et permettra de bénéficier des blocages préventifs communautaires tout en assurant la détection locale.

Dans une infrastructure avec plusieurs serveurs à protéger, un serveur centralisant les alertes et les décisions permettra de partager les alertes et les décisions locales en plus de celles récupérées sur le serveur Central API. Il permettra aussi d'avoir un seul point d'entrée pour consulter les logs, ce qui en fait un autre atout par rapport à fail2ban. Suivant le trafic sur la Local API, il faudra choisir la base de données en conséquence.

À travers cette première série d'articles, nous avons mis en place une base pour démarrer avec Crowdsec et comprendre les principaux mécanismes. D'autres articles suivront :)

Article précédent Article suivant