🛡️ Sécurisation avec Crowdsec
Introduction
Crowdsec est un IDS/IPS communautaire puissant et relativement simple à déployer. Nous allons voir comment l'intégrer à Traefik afin de bloquer les IP malveillantes que cela soit en mode préventif ou en mode détection.
Dans le cadre de cette documentation, je pars du principe que vous avez déjà déployé un serveur Crowdsec. Si ce n'est pas le cas, vous pouvez vous inspirer de la série d'articles que j'avais rédigé ici. Si vous êtes patient, il est prévu que je reprenne cette série d'articles pour la mettre à jour.
Si ce n'est pas le cas, vous pouvez faire tourner le serveur Crowdsec directement dans Docker Swarm.
Nous en profiterons pour déployer une nouvelle fonctionnalité de Crowdsec : AppSec. AppSec est un composant qui est utilisé pour protéger les applications Web. Cela permet tout simplement de mettre en place un WAF. Et ça c'est génial ! En effet, en plus de détecter par le bais des parsers, de partager avec la communauté Crowdsec et de bloquer les IP malveillantes à travers les bouncers, cette nouvelle fonctionnalité va nous permettre de bénéficier par exemple du mécanisme de virtual pacthing et de la collection bien fournie qui va avec.
Virtual Patching
Lorsque des vulnérabilités sont découvertes sur une application, l’éditeur produit en général un correctif, ou « patch », permettant de corriger les vulnérabilités ou de les rendre inexploitables. Cependant, l’application de ce correctif, nécessite souvent une mise à jour du logiciel en question et celle-ci peut être relativement compliqué dans certains cas (coupure d’activité impossible…). Sans compter le temps de la sortie du patch...
Le virtual patching permet alors de protéger les applications vulnérables beaucoup plus rapidement. Il consiste à développer un correctif protégeant les les applications sans toucher au code des applications.
Configuration du serveur Crowdsec
Appsec
Il faut commencer par vérifier la prise en charge des fichiers contenus dans "/etc/crowdsec/acquis.d". En effet, si vous faites tourner Crowdsec depuis quelques années, il se peut que le fichier de configuration ne soit pas à jour avec les dernières directives.
# Vérification de présence du dossier acquis.d
root@srv-cs:~# ls -d /etc/crowdsec/acquis.d
/etc/crowdsec/acquis.d
# Vérification de la prise en charge de ce dossier
root@srv-cs:~# cat /etc/crowdsec/config.yaml
(...)
crowdsec_service:
acquisition_path: /etc/crowdsec/acquis.yaml
acquisition_dir: /etc/crowdsec/acquis.d
parser_routines: 1
(...)
# Installation des collections pour Appsec
root@srv-cs:~# cscli collections install crowdsecurity/appsec-virtual-patching
root@srv-cs:~# cscli collections install crowdsecurity/appsec-generic-rules
# Configuration de Appsec
root@srv-cs:~# vim /etc/crowdsec/acquis.d/appsec.yaml
listen_addr: IP_LOCALE_DU_SERVEUR_CS:7422
appsec_config: crowdsecurity/appsec-default
name: myAppSecComponent
source: appsec
labels:
type: appsec
# Relance de Crowdsec
root@srv-cs:~# systemctl restart crowdsec
# Vérification
root@srv-cs:~# tail -f /var/log/crowdsec.log
time="2024-06-02T10:47:33+02:00" level=info msg="1 appsec runner to start" name=myAppSecComponent type=appsec
time="2024-06-02T10:47:33+02:00" level=info msg="creating TCP server on 192.168.1.9:7422" name=myAppSecComponent type=appsec
time="2024-06-02T10:47:33+02:00" level=info msg="Appsec Runner ready to process event" name=myAppSecComponent runner_uuid=8a4e00b6-8805-49cf-ae17-41abc191dd90 type=appsec
Les collections installées fournissent :
- La configuration du composant AppSec (crowdsecurity/appsec-default).
- Toutes les règles de patch virtuel.
- Les parsers.
- Le(s) scénario(s).
Les bouncers interrogeront le serveur sur la socket "IP_LOCALE_DU_SERVEUR_CS:7422" à chaque requête vers les applications web. Suivant le traffic de vos applications et vos ressources, il faudra donc veiller à régler le mode pour interroger le serveur CS (Stream, Live...).
Si la requête correspond à une règle contenue dans AppSec alors ce dernier répondra par un code HTTP 403 à Traefik indiquant que la requête doit être bloquée. Traefik présentera alors la page "Crowdsec Access Forbidden" fournie par le bouncer.
Pour faire simple : Crowdsec AppSec transforme ici Traefik en firewall d'applications web.
Ajout de la machine agent
Serveur Crowdsec Local API
Le serveur Crowdsec Local API est en charge de centraliser les alertes et les décisions remontées par les clients dits machines, ces dernier faisant tourner les agents Crowdsec. Par souci de simplicité, j'utiliserai "serveur Crowdsec" dans la documentation au lieu de "serveur Crowdsec Local API".
La machine agent, permettant de parser les logs de Traefik et faire remonter les informations au serveur Crowdsec, tournera sur le cluster Swarm.
Selon la documentation officielle, il faut ajouter l'agent directement sur le serveur Crowdsec. Cette partie n'était d'ailleurs pas très claire. Dans tous les cas, il faut préciser une sortie fichier sinon cela écrasera le fichier "/etc/crowdsec/local_api_credentials.yaml" du serveur ou retournera une erreur.
root@srv-cs:~# cscli machines add agent-traefik --auto -f /root/agent-traefik.yaml
Ajout du bouncer
Il faut maintenant déclarer sur le serveur Crowdsec le bouncer. Nous verrons plus tard comment configrer celui-ci avec Traefik.
Conservez la clé pour la configuration du bouncer plus tard.
Déploiement de Crowdec sur Docker Swarm et Traefik
Configuration et déploiement de l'agent crowdsec
olivier@ds01:/mnt/nfsdatas$ sudo mkdir crowdsec
olivier@ds01:/mnt/nfsdatas$ sudo chown olivier: crowdsec
olivier@ds01:/mnt/nfsdatas$ cd crowdsec/
services:
crowdsec-agent:
image: crowdsecurity/crowdsec
environment:
COLLECTIONS: "crowdsecurity/traefik crowdsecurity/http-cve"
GID: "${GID-1000}"
DISABLE_LOCAL_API: "true"
TZ: "Europe/Paris"
# Informations récupérées depuis le fichier /root/agent-traefik.yaml sur le serveur CS
AGENT_USERNAME: "agent-traefik"
AGENT_PASSWORD: "PASSWORD"
LOCAL_API_URL: "http://IP_DU_SERVEUR_CROWDSEC:8080"
volumes:
- ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro # Fichier permettant de déclarer les logs de Traefik
- crowdsec-db:/var/lib/crowdsec/data/
- crowdsec-config:/etc/crowdsec/
# Dossier contenant les fichiers de logs de Traefik
- /mnt/nfsdatas/traefik/traefik/log/:/var/log/traefik/:ro
deploy:
placement:
constraints:
- node.role == manager
restart_policy:
condition: on-failure
replicas: 1
volumes:
crowdsec-config:
crowdsec-db:
```yaml title="Configuration de /mnt/nfsdatas/crowdsec/acquis.yaml"
olivier@ds01:/mnt/nfsdatas/crowdsec$ vim acquis.yaml
filenames:
- /var/log/traefik/*
labels:
type: traefik
olivier@ds01:/mnt/nfsdatas/crowdsec$ docker stack deploy -c docker-stack.yml -d cs-agent
Configuration et déploiement du bouncer Traefik
# À la fin du fichier de conf statique de Traefik
# /mnt/nfsdatas/traefik/traefik/traefik.yml
(...)
experimental:
plugins:
bouncer:
moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
version: v1.3.1
# Editez le fichier de config dynamique
# /mnt/nfsdatas/traefik/traefik/dyn_traefik/traefik.config.yml
http:
middlewares:
crowdsec:
plugin:
bouncer:
enabled: true
logLevel: DEBUG
CrowdsecMode: stream # Recommandé pour les performances
## Avec le mode stream, les décisions sont contenues dans le cache, màj toutes les 60 sec.
## Redis peut être utilisé pour ce cache.
# Config d'AppSec
CrowdsecAppsecEnabled: true
CrowdsecAppsecHost: IP_SERVEUR_CROWDSEC:7422
CrowdsecAppsecFailureBlock: true
# Config de LAPI
CrowdsecLapiScheme: http
CrowdsecLapiTLSInsecureVerify: false
CrowdsecLapiHost: IP_SERVEUR_CROWDSEC:8080
# Déclaration de la clé crée lors de l'ajout du bouncer sur le serveur CS
CrowdsecLapiKey: LA_CLE_DU_BOUNCER
banHTMLFilePath: ./ban.html # Ce fichier sera mappé dans la stack de Traefik
Mappage du fichier ban.html
Récupérez le fichier ban.html sur github
# /mnt/nfsdatas/traefik/docker-stack.yml
version: "3.3"
services:
traefik:
image: "traefik:v3.0.1"
(...)
volumes:
(...)
# Mapping du fichier ban de crowdsec
- "./traefik/crowdsec/ban.html:/ban.html"
(...)
Déclaration du middleware Crowdsec
Avant de relancer Traefik, il faut déclarer le middleware crowdsec@file sur nos applications web. Ici nous renforcerons la sécurité du dashboard de Traefik ainsi que celle de l'application très sympa Cobalt qui permet de récupérer des vidéos/audios publiées sur les réseaux sociaux.
Accès au dashboard de Traefik
Si vous n'avez pas besoin d'exposer le dasboard de Traefik sur Internet ou/et que vous souhaitez également limiter les accès internes (zero trust), utilisez l'option "ipallowlist.sourcerange" de Traefik permettant de limiter les accès aux IP autorisées.
# Sur le dashboard de Crowdsec
# /mnt/nfsdatas/traefik/docker-stack.yml
version: "3.3"
services:
traefik:
image: "traefik:v3.0.1"
(...)
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik-dashboard.rule=Host(`d.dev.quercylibre.fr`)"
- "traefik.http.routers.traefik-dashboard.service=api@internal"
- "traefik.http.routers.traefik-dashboard.entrypoints=websecure"
- "traefik.http.routers.traefik-dashboard.tls.certresolver=letsencrypt"
# Ajout du middleware crowdsec@file
- "traefik.http.routers.traefik-dashboard.middlewares=traefik-dashboard-auth,crowdsec@file"
(...)
services:
cobalt-api:
image: ghcr.io/wukko/cobalt:7
init: true
networks:
- web
environment:
API_URL: "https://cobalt-api.dev.quercylibre.fr/"
API_NAME: "eu-fr"
deploy:
restart_policy:
condition: on-failure
replicas: 1
placement:
constraints:
- node.hostname == ds03
labels:
- "traefik.enable=true"
- "traefik.docker.network=web"
- "traefik.http.routers.cobalt-api.rule=Host(`cobalt-api.dev.quercylibre.fr`)"
- "traefik.http.routers.cobalt-api.tls=true"
- "traefik.http.routers.cobalt-api.tls.certresolver=letsencrypt"
- "traefik.http.routers.cobalt-api.middlewares=crowdsec@file"
- "traefik.http.services.cobalt-api-service.loadbalancer.server.port=9000"
cobalt-web:
image: ghcr.io/wukko/cobalt:7
init: true
networks:
- web
environment:
WEB_URL: "https://cobalt.dev.quercylibre.fr/"
API_URL: "https://cobalt-api.dev.quercylibre.fr/"
deploy:
restart_policy:
condition: on-failure
replicas: 1
placement:
constraints:
- node.hostname == ds03
labels:
- "traefik.enable=true"
- "traefik.docker.network=web"
- "traefik.http.routers.cobalt-web.rule=Host(`cobalt.dev.quercylibre.fr`)"
- "traefik.http.routers.cobalt-web.tls=true"
- "traefik.http.routers.cobalt-web.tls.certresolver=letsencrypt"
- "traefik.http.routers.cobalt-web.middlewares=crowdsec@file"
- "traefik.http.services.cobalt-web-service.loadbalancer.server.port=9001"
networks:
web:
external: true
Une fois les stacks configurées, il faut les relancer.
Nous retrouvons alors sur le dashboard Traefik le bouncer CS déclaré en tant que middleware :
Test
Pour cela je vais saisir l'url : "https://cobalt.dev.quercylibre.fr/rpc2" Résultat :
Nous retrouvons l'alerte dans les logs du fichier /var/log/crowdsec.log sur le serveur Crowdsec avec la règle AppSec correspondante :
root@srv-cs:~# tail -f /var/log/crowdsec.log
time="2024-06-02T11:42:19+02:00" level=info msg="AppSec block: crowdsecurity/vpatch-CVE-2023-42793 from IP_MALVEILLANTE (IP_CS_BOUNCER)"
# Le warning ci-dessous est normal vu que j'utilise une IP privée
time="2024-06-02T11:42:19+02:00" level=warning msg="No range found for ip 'IP_MALVEILLANTE'" id=nameless-flower method=IpToRange name=crowdsecurity/geoip-enrich stage=s02-enrich
time="2024-06-02T11:42:20+02:00" level=info msg="(45be54d228044cac8ebdc84de749a2617rDlp8mTTjYW2RbS) alert : crowdsecurity/vpatch-CVE-2023-42793 by ip IP_MALVEILLANTE (/0)"
Ici nous voyons la puissance du composant Crowdsec qui permet de bloquer en préventif l'attaque. Seule l'attaque est bloquée dans un premier temps, mais si l'attaque persiste, cela déclenche alors un ban de l'IP.
Vous pouvez également tester d'autres scénarios d'attaque avec la commande "nikto -host URL".