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

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 traefik definen el middleware crowdsec con la dirección y la clave de la LAPI.
  • BOUNCER_KEY_traefik registra en CrowdSec un bouncer llamado traefik con la misma clave.
  • Cada router que quieras proteger debe incluir crowdsec@docker en sus middlewares, como hace whoami.

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.