CrowdSec es un motor de seguridad de código abierto que lee los logs de tus servicios, detecta comportamientos maliciosos (escaneos, fuerza bruta, explotación de CVE conocidos) y genera decisiones de bloqueo. Esas decisiones las aplican los bouncers, componentes que se integran en el punto de entrada del tráfico. En este tutorial instalarás CrowdSec en Ubuntu 24.04 y lo conectarás a Nginx con el bouncer Lua oficial y a Traefik v3 con el plugin de CrowdSec, comprobando en cada caso que una IP bloqueada recibe un 403.
Las partes 1 y 2 son independientes: sigue la que corresponda al proxy que usas. Si usas los dos, hazlo en servidores distintos, porque ambos necesitan los puertos 80 y 443.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, y un usuario no root con privilegios
sudo. - Para la parte 1: Nginx instalado desde los repositorios de Ubuntu y sirviendo al menos un sitio.
- Para la parte 2: Docker Engine y el plugin Docker Compose instalados, y un dominio (
your_domain) con un registro DNS A que apunte al servidor, para que Traefik obtenga el certificado TLS. - Un segundo equipo con una IP pública distinta desde la que hacer las pruebas de bloqueo. No bloquees la IP desde la que administras el servidor.
Parte 1: CrowdSec con Nginx
En esta parte CrowdSec se ejecuta como servicio del sistema, lee /var/log/nginx/access.log y el bouncer de Nginx consulta la API local de CrowdSec antes de atender cada petición.
Paso 1: Instalar CrowdSec
CrowdSec publica sus paquetes en un repositorio propio. El script oficial solo añade ese repositorio y su clave; descárgalo y revísalo antes de ejecutarlo:
curl -fsSL -o crowdsec-repo.sh https://install.crowdsec.net
less crowdsec-repo.sh
sudo sh crowdsec-repo.sh
Instala el motor:
sudo apt update
sudo apt install crowdsec
Durante la instalación, CrowdSec detecta los servicios presentes (Nginx, SSH) e instala sus colecciones y la configuración de lectura de logs. También registra la instancia en la API central de CrowdSec para recibir la lista comunitaria de IPs maliciosas.
Comprueba que el servicio está activo y que la API local (LAPI) responde:
sudo systemctl status crowdsec --no-pager
sudo cscli lapi status
● crowdsec.service - Crowdsec agent
Loaded: loaded (/usr/lib/systemd/system/crowdsec.service; enabled; preset: enabled)
Active: active (running) since ...
...
Loaded credentials from /etc/crowdsec/local_api_credentials.yaml
Trying to authenticate with username ... on http://127.0.0.1:8080/
You can successfully interact with Local API (LAPI)
Paso 2: Revisar colecciones y fuentes de logs
Una colección agrupa los parsers (cómo leer un log) y los escenarios (qué comportamiento es un ataque) de un servicio. Instala las de Nginx, los CVE HTTP más explotados y SSH. Si ya estaban instaladas, cscli lo indica y no hace nada:
sudo cscli collections install crowdsecurity/nginx crowdsecurity/http-cve crowdsecurity/sshd
Comprueba que CrowdSec tiene configurada la lectura de los logs de Nginx:
sudo grep -rn nginx /etc/crowdsec/acquis.yaml /etc/crowdsec/acquis.d/ 2>/dev/null
Si el comando no devuelve nada, crea un archivo de adquisición para Nginx:
sudo nano /etc/crowdsec/acquis.d/nginx.yaml
filenames:
- /var/log/nginx/access.log
- /var/log/nginx/error.log
labels:
type: nginx
Recarga CrowdSec para aplicar los cambios:
sudo systemctl reload crowdsec
Haz unas peticiones al sitio y revisa las métricas para ver si CrowdSec procesa el log:
curl -s -o /dev/null http://localhost/
sudo cscli metrics
En la salida, busca la tabla Acquisition Metrics: la fila file:/var/log/nginx/access.log debe tener valores en las columnas Lines read y Lines parsed, y la columna Lines unparsed debe estar vacía o ser muy baja.
Puedes ver cómo interpreta CrowdSec una línea concreta con cscli explain:
sudo cscli explain --log '203.0.113.5 - - [25/Sep/2026:10:20:30 +0000] "GET /wp-login.php HTTP/1.1" 404 162 "-" "curl/8.5.0"' --type nginx
La salida muestra cada parser por el que pasa la línea y los escenarios en los que acaba.
Paso 3: Instalar el bouncer de Nginx
Hasta ahora CrowdSec detecta, pero no bloquea nada. El bouncer de Nginx usa el módulo Lua de Nginx para consultar a la LAPI si la IP de cada petición tiene una decisión activa. Instálalo desde el mismo repositorio:
sudo apt install crowdsec-nginx-bouncer
El paquete instala el módulo Lua de Nginx si falta, registra el bouncer en CrowdSec, guarda su clave de API en /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf y añade /etc/nginx/conf.d/crowdsec_nginx.conf. Ese archivo se carga en el bloque http y aplica la comprobación a todos los sitios, así que no hace falta tocar cada server.
Valida la configuración de Nginx y recárgala:
sudo nginx -t
sudo systemctl reload nginx
Comprueba que el bouncer está registrado:
sudo cscli bouncers list
Debe aparecer un bouncer con un nombre que empieza por crowdsec-nginx-bouncer, la IP 127.0.0.1 y marcado como válido.
Paso 4: Probar el bloqueo
Desde tu equipo de pruebas, comprueba que el sitio responde con normalidad:
curl -I http://your_server_ip/
HTTP/1.1 200 OK
...
En el servidor, crea una decisión de bloqueo de cinco minutos para la IP pública del equipo de pruebas (sustituye your_test_ip):
sudo cscli decisions add --ip your_test_ip --duration 5m --reason "prueba del bouncer"
Repite la petición desde el equipo de pruebas. Ahora Nginx devuelve la página de bloqueo de CrowdSec:
HTTP/1.1 403 Forbidden
...
Elimina la decisión al terminar:
sudo cscli decisions delete --ip your_test_ip
Consejoel bouncer de Nginx solo protege el tráfico HTTP. Para bloquear también SSH y el resto de puertos, instala el bouncer de firewall con
sudo apt install crowdsec-firewall-bouncer-nftables, que aplica las mismas decisiones en nftables.
Parte 2: CrowdSec con Traefik en Docker
En esta parte todo se ejecuta con Docker Compose: Traefik v3 escribe su log de acceso en un volumen compartido, un contenedor de CrowdSec lo analiza y el plugin de CrowdSec para Traefik actúa como middleware que consulta la LAPI antes de pasar la petición al servicio. Como servicio de ejemplo se usa traefik/whoami.
Paso 1: Preparar el proyecto
Crea el directorio del proyecto:
sudo mkdir -p /opt/traefik-crowdsec/crowdsec
sudo chown -R "$USER": /opt/traefik-crowdsec
cd /opt/traefik-crowdsec
El contenedor de CrowdSec puede registrar un bouncer al arrancar con una clave que tú le pasas, lo que evita tener que copiarla a mano después. Genera una clave aleatoria y guárdala en un archivo .env, que Docker Compose lee automáticamente:
echo "CROWDSEC_BOUNCER_KEY=$(openssl rand -hex 32)" > .env
chmod 600 .env
Crea el archivo de adquisición que indica a CrowdSec qué logs leer:
nano crowdsec/acquis.yaml
filenames:
- /var/log/traefik/access.log
labels:
type: traefik
Paso 2: Escribir el archivo de Docker Compose
Crea docker-compose.yml:
nano docker-compose.yml
Sustituye your_domain y admin@your_domain. La versión del plugin (v1.4.2 en el ejemplo) debe ser una publicada en el repositorio maxlerebourg/crowdsec-bouncer-traefik-plugin; consulta la última en su página de releases:
services:
traefik:
image: traefik:v3
restart: unless-stopped
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.websecure.address=:443
- --certificatesresolvers.le.acme.email=admin@your_domain
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
- --accesslog=true
- --accesslog.filepath=/var/log/traefik/access.log
- --experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
- --experimental.plugins.bouncer.version=v1.4.2
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
- traefik-logs:/var/log/traefik
labels:
- traefik.enable=true
- traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true
- traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecLapiHost=crowdsec:8080
- traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecLapiKey=${CROWDSEC_BOUNCER_KEY}
depends_on:
- crowdsec
crowdsec:
image: crowdsecurity/crowdsec:latest
restart: unless-stopped
environment:
COLLECTIONS: "crowdsecurity/traefik crowdsecurity/http-cve"
BOUNCER_KEY_traefik: ${CROWDSEC_BOUNCER_KEY}
volumes:
- ./crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml:ro
- crowdsec-config:/etc/crowdsec
- crowdsec-data:/var/lib/crowdsec/data
- traefik-logs:/var/log/traefik:ro
whoami:
image: traefik/whoami
restart: unless-stopped
labels:
- traefik.enable=true
- traefik.http.routers.whoami.rule=Host(`your_domain`)
- traefik.http.routers.whoami.entrypoints=websecure
- traefik.http.routers.whoami.tls.certresolver=le
- traefik.http.routers.whoami.middlewares=crowdsec@docker
volumes:
traefik-logs:
crowdsec-config:
crowdsec-data:
Puntos clave del archivo:
- Las opciones
experimental.plugins.bouncer.*hacen que Traefik descargue el plugin al arrancar. - Las etiquetas del servicio
traefikdefinen el middlewarecrowdseccon la dirección y la clave de la LAPI. BOUNCER_KEY_traefikregistra en CrowdSec un bouncer llamadotraefikcon la misma clave.- Cada router que quieras proteger debe incluir
crowdsec@dockeren sus middlewares, como hacewhoami.
Paso 3: Arrancar la pila y verificarla
Levanta los servicios:
docker compose up -d
Comprueba que Traefik ha cargado el plugin sin errores y que CrowdSec ha registrado el bouncer:
docker compose logs traefik | grep -i plugin
docker compose exec crowdsec cscli bouncers list
El bouncer traefik debe aparecer como válido. Desde el equipo de pruebas, abre el sitio:
curl -I https://your_domain
HTTP/2 200
...
Tras unas cuantas peticiones, comprueba que CrowdSec lee el log de Traefik:
docker compose exec crowdsec cscli metrics
La fuente file:/var/log/traefik/access.log debe mostrar líneas leídas y procesadas.
Paso 4: Probar el bloqueo
Crea una decisión para la IP del equipo de pruebas:
docker compose exec crowdsec cscli decisions add --ip your_test_ip --duration 5m --reason "prueba del plugin"
Repite la petición desde ese equipo. En modo live, el plugin guarda en caché la respuesta de la LAPI durante un tiempo corto, así que el bloqueo puede tardar hasta un minuto en aplicarse:
HTTP/2 403
...
Elimina la decisión:
docker compose exec crowdsec cscli decisions delete --ip your_test_ip
Operación diaria
Estos comandos sirven igual en la parte 1 (con sudo cscli) y en la parte 2 (con docker compose exec crowdsec cscli):
sudo cscli decisions list
sudo cscli alerts list --since 24h
sudo cscli alerts inspect 12
decisions list muestra las IPs bloqueadas ahora mismo por tus escenarios, y alerts inspect detalla qué escenario disparó una alerta concreta (sustituye 12 por el ID de la alerta). Para mantener parsers y escenarios al día, actualiza el índice del hub y los elementos instalados:
sudo cscli hub update
sudo cscli hub upgrade
sudo systemctl reload crowdsec
Solución de problemas
Las líneas del log aparecen como Lines unparsed. El tipo de la adquisición no coincide con el formato del log. Comprueba que type es nginx o traefik según el caso y que el log usa el formato por defecto; los formatos de log personalizados en Nginx no los reconocen los parsers estándar.
Nginx no arranca tras instalar el bouncer. Revisa el error con sudo nginx -t. Si menciona Lua, comprueba que el módulo está instalado con dpkg -l libnginx-mod-http-lua y que no has compilado Nginx por tu cuenta ni usas un paquete de otro repositorio.
El bouncer está registrado pero no bloquea. Verifica que la decisión existe con cscli decisions list y que la IP es la que llega realmente al servidor. Si Nginx o Traefik están detrás de otro proxy o de una CDN, verán la IP del proxy en lugar de la del cliente; en ese caso debes configurar la IP real del cliente (módulo realip en Nginx, forwardedHeaders.trustedIPs en Traefik) antes de que CrowdSec pueda funcionar.
Una IP legítima termina bloqueada. Crea una lista blanca como parser de enriquecimiento:
sudo nano /etc/crowdsec/parsers/s02-enrich/mis-ips.yaml
name: local/mis-ips
description: "IPs propias que nunca deben bloquearse"
whitelist:
reason: "Redes de la oficina"
ip:
- "198.51.100.20"
cidr:
- "10.0.0.0/8"
Recarga CrowdSec con sudo systemctl reload crowdsec y elimina la decisión existente con sudo cscli decisions delete --ip seguido de la IP.
Conclusión
Tienes CrowdSec analizando los logs de Nginx o de Traefik, recibiendo la lista comunitaria de IPs maliciosas y un bouncer que bloquea esas IPs antes de que las peticiones lleguen a tus aplicaciones. Como siguientes pasos, puedes añadir el bouncer de firewall para cubrir el resto de puertos, inscribir la instancia en la consola web de CrowdSec con cscli console enroll para ver alertas centralizadas y escribir escenarios propios para los endpoints de inicio de sesión de tus aplicaciones.
