Cómo instalar CrowdSec como WAF comunitario

Torre de servidor con luces LED indicando operación en modo de defensa

CrowdSec es la evolución moderna de fail2ban, con un enfoque muy distinto al del clásico: separa detección de bloqueo, expone una API, y aprovecha una blocklist alimentada por miles de instalaciones voluntarias repartidas por el mundo. En 2024, tras varios años en desarrollo activo, es una herramienta madura que merece estar en el toolkit de cualquiera que gestione servicios expuestos a internet. Esta guía recorre la instalación y la integración con Traefik pensando en cualquier administrador que esté valorando reemplazar fail2ban, con el énfasis puesto en entender por qué cada pieza existe más que en copiar comandos.

Por qué vale la pena el cambio

La diferencia conceptual más importante entre fail2ban y CrowdSec es que este último separa quién detecta de quién bloquea. fail2ban lee logs, decide, y ejecuta una regla de iptables en el mismo proceso. CrowdSec lee logs y emite decisiones a una base de datos local (la LAPI); esas decisiones son consultadas por uno o varios bouncers, que son los que efectivamente bloquean.

Parece un detalle arquitectónico menor, pero tiene consecuencias importantes. Puedes tener un bouncer en el firewall (para SSH), otro en Traefik (para HTTP), otro en Nginx, y todos actúan sobre las mismas decisiones. Puedes cambiar la detección sin tocar el bloqueo, y viceversa. Y puedes, si quieres, contribuir tus detecciones anónimamente a la comunidad y recibir a cambio una lista actualizada de IPs que están atacando a otros en este momento.

La segunda diferencia es la expresividad. fail2ban usa regex sobre líneas de log; CrowdSec usa scenarios declarativos en YAML que combinan detección, capacidad, ventana de tiempo y grupo. Escribir un scenario para detectar un patrón nuevo es un ejercicio que se resuelve en quince minutos, contra las horas que a veces lleva depurar un filtro de fail2ban.

Instalación del agente

En Debian o Ubuntu, el paquete oficial se instala con el script del proyecto seguido del apt install habitual:

curl -s https://install.crowdsec.net | sudo sh
sudo apt install crowdsec

Lo que queda en el sistema es el agente y la LAPI (una API REST expuesta en localhost) en el mismo proceso. Para una sola máquina es lo cómodo; para una flota, más adelante puedes separar la LAPI en un host dedicado y hacer que los agentes de otros nodos apunten allí. Para empezar, no hace falta.

Una vez instalado, sudo cscli version y sudo systemctl status crowdsec confirman que todo arrancó. Si te encuentras con errores, casi siempre están en la configuración de acquisition (qué logs leer), que veremos en el siguiente paso.

Las collections: lo que te ahorra trabajo

Una collection es un paquete que agrupa parsers (cómo leer un formato de log concreto) y scenarios (patrones de ataque) para una tecnología. CrowdSec mantiene un hub oficial donde la comunidad publica collections; instalarlas es cuestión de un comando por cada una. Para un stack típico con Traefik delante, WordPress detrás y Gitea en algún subdominio, conviene instalar al menos las collections de Traefik, WordPress, Gitea, Nextcloud si lo usas, y Nginx si tienes alguno directo. El comando de instalación es sudo cscli collections install <nombre> y no da dolores de cabeza.

Qué trae cada una es razonable: la de WordPress detecta fuerza bruta en login, escaneo de usuarios, intentos sobre wp-config.php y varios más. La de Traefik aprovecha el log de acceso para detectar escaneos, patrones de bot, DoS básico. La de Gitea captura ataques contra el registro y el login. No necesitas entender cada scenario desde el primer día; basta con confiar en que los autores saben lo que hacen, y a los pocos días ya habrás revisado cuáles se están disparando en tu tráfico real.

Decir al agente de dónde leer

El paso que más se olvida al principio es decirle a CrowdSec qué logs tiene que leer. Esto va en /etc/crowdsec/acquis.yaml, y el concepto es simple: cada bloque declara un origen (fichero, journald, etc.) y una etiqueta de tipo que debe coincidir con la que esperan los parsers. Para un setup con Traefik en Docker cuyo log de acceso está en /var/log/traefik/access.log, WordPress dentro de un contenedor, y SSH en el systemd journal, la configuración típica incluye entradas para el fichero de Traefik con etiqueta traefik, para el journal filtrado por sshd.service con etiqueta syslog, y para el log de Nginx si lo usas con etiqueta nginx.

El detalle a tener en cuenta es que las etiquetas no son arbitrarias: los parsers filtran por ellas, así que si pones type: traefik-access cuando el parser espera type: traefik, no va a detectar nada y no vas a ver ningún error explícito. Este es probablemente el error más común entre usuarios nuevos. Tras editar el fichero, sudo systemctl restart crowdsec aplica los cambios, y a los pocos minutos sudo cscli metrics te muestra si está leyendo líneas.

Primer vistazo a lo que detecta

Una vez arrancado con la acquisition bien configurada, hay tres comandos de diagnóstico que merecen estar en tu rutina. sudo cscli alerts list te da las alertas que se han disparado (detecciones). sudo cscli decisions list te muestra las decisiones activas, que son las IPs que están actualmente bloqueadas o bajo captcha. sudo cscli metrics te da una visión agregada del ritmo de detecciones, aciertos de cache y estado general. Si llevas veinticuatro horas corriendo y ninguno de esos comandos muestra nada, casi con seguridad tu acquisition no está leyendo lo que crees. Es preferible descubrir esto pronto que cuando llega un ataque real.

Los bouncers: de detección a bloqueo efectivo

Aquí es donde el diseño desacoplado empieza a lucir. CrowdSec ya detecta, pero todavía no bloquea nada. Necesitas al menos un bouncer, y en un stack con Traefik por delante lo lógico es usar el plugin nativo de Traefik, mantenido por maxlerebourg/crowdsec-bouncer-traefik-plugin. Se declara en la configuración estática de Traefik como plugin experimental y, en la configuración dinámica, como un middleware que se aplica a las rutas que quieres proteger. El plugin consulta la LAPI en modo stream (cachea decisiones y se actualiza cada pocos segundos), lo que mantiene la latencia en cero perceptibles.

La configuración del plugin necesita una clave de API específica para ese bouncer. Se genera con sudo cscli bouncers add traefik-bouncer, copia el token que te muestra una única vez, y lo pones en el archivo de configuración del middleware. Si se te pierde, se borra y se regenera sin drama.

Para SSH, lo razonable es un firewall-bouncer adicional, que aplica las decisiones a nivel de iptables o nftables. El paquete es crowdsec-firewall-bouncer-iptables o su variante nftables, según lo que uses. Se configura solo; apenas necesita tocar nada. Lo importante es entender que ahora tienes dos bouncers, uno HTTP y otro de red, compartiendo la misma fuente de decisiones, lo que evita duplicar la lógica de detección.

Remediación con captcha, no todo es bloqueo

El error clásico en WAF es bloquear todo lo sospechoso y descubrir a la semana siguiente que estabas bloqueando a clientes legítimos cuya IP se reutilizó. CrowdSec permite emitir decisiones de tipo captcha en lugar de ban, y el bouncer de Traefik sabe interpretarlas: muestra un desafío (típicamente Cloudflare Turnstile, gratis) en vez de cortar. Si lo resuelve, pasa; si no, se bloquea.

La lógica de qué decisiones se convierten en captcha y cuáles en ban vive en /etc/crowdsec/profiles.yaml. El patrón que funciona bien en producción es enviar a captcha los escenarios de fuerza bruta (alguien probando contraseñas) y a ban los escenarios de exploit serio (intentos de explotar CVEs conocidos, acceso a ficheros sensibles). Un usuario legítimo que se equivocó unas cuantas veces resuelve el captcha y sigue con su vida; un bot de exploit se queda fuera.

La integración con Turnstile solo exige dos claves (site key y secret key) obtenidas desde el panel de Cloudflare y puestas en la configuración del middleware. El widget es gratuito y no tiene límites prácticos para uso normal.

El valor real de la blocklist comunitaria

Registrar tu instalación con la CAPI central (sudo cscli capi register) te pone en la red comunitaria. Empiezas a recibir una lista actualizada constantemente de IPs que están atacando a otros en este momento, y contribuyes (anonimizando) las tuyas. En la práctica, esto te bloquea cientos o miles de IPs conocidas antes de que lleguen a hacer nada contra ti. El impacto es claramente medible: revisa tus métricas antes y después de activar la CAPI y verás cómo cae el ruido de intentos de acceso a endpoints típicos.

Hay un matiz ético aquí. Al compartir detecciones contribuyes a una defensa colectiva, pero también estás enviando información sobre tu tráfico (aunque las IPs se anonimizan parcialmente). Para la mayoría de instalaciones el cambio es aceptable; para entornos con exigencias de privacidad estrictas, conviene leer los términos antes de activar.

Monitorización sin la que no tiene sentido operar

CrowdSec expone métricas Prometheus por un endpoint HTTP local que solo necesitas scrapear. Los nombres útiles para alertar son cs_bucket_overflow_count (número total de detecciones acumuladas), cs_active_decisions (IPs bloqueadas en este momento) y las métricas de rendimiento del cache. Hay un dashboard oficial de Grafana que te deja ver el estado global en unos minutos.

Lo que yo siempre configuro además es una alerta básica en Alertmanager: si el agente deja de emitir métricas durante más de cinco minutos, algo está fallando (servicio caído, acquisition rota, disco lleno). Las alertas sobre picos de detección son útiles pero generan ruido; la alerta de ausencia es la que te salva cuando algo deja de funcionar silenciosamente.

Whitelists y errores comunes

Una lección universal del primer día es: antes de bloquear nada en serio, asegúrate de que tus propias IPs legítimas están en whitelist. La oficina desde donde accedes, el VPN que usas, el CI/CD que hace deploys, los monitores externos de uptime. Esto va en /etc/crowdsec/parsers/s02-enrich/whitelists.yaml y acepta IPs individuales y rangos CIDR. La cantidad de administradores que han acabado bloqueándose a sí mismos durante las primeras horas de uso es literalmente mayor que la cantidad de atacantes reales que han frustrado ese mismo día.

Otro error frecuente es no reiniciar el agente tras cada cambio de configuración. CrowdSec no detecta cambios en caliente para la mayoría de ficheros; un systemctl restart crowdsec tras editar es disciplina básica.

Cuándo no compensa CrowdSec

Hay escenarios donde fail2ban sigue siendo más simple y adecuado. Servidor único, bajo tráfico, sin necesidad de compartir inteligencia entre nodos, sin plugin en Traefik porque no usas Traefik. En esos casos, la infraestructura adicional de CrowdSec (agent, LAPI, bouncers separados, acquisition config) es complejidad sin beneficio claro.

CrowdSec empieza a pagarse cuando tienes más de una capa que proteger (web + SSH + aplicaciones específicas), cuando compartir detecciones entre nodos ahorra trabajo, o cuando la blocklist comunitaria corta ruido significativo para tu tráfico. En un VPS único de hobby, quizá no merece la pena; en un stack productivo con exposición real, casi siempre sí.

Mi recomendación

Si vas a probarlo, mi sugerencia es despliegue progresivo. Primero, instala el agente, configura la acquisition, y déjalo una semana en modo detección sin bouncer. Ve qué se dispara, ajusta scenarios si hay ruido, añade whitelists para tus IPs legítimas. Solo entonces activa el primer bouncer (el de Traefik es el más visible). Espera otra semana, añade captcha remediation para fuerza bruta, y por último conecta con la CAPI. Llegar a este punto de madurez lleva entre dos y tres semanas de convivencia.

Comparado con fail2ban, vas a descubrir rápido que CrowdSec ofrece mucha más visibilidad (las CLIs cscli alerts y decisions son cómodas) y mucha más flexibilidad (añadir un scenario custom para tu aplicación propia es un ejercicio trivial). Lo que pagas es una curva de aprendizaje inicial de un par de días. Para cualquier stack con exposición internet seria, la inversión es absolutamente razonable, y a los seis meses probablemente ni recuerdes cómo se configuraba fail2ban.

Entradas relacionadas