En una puesta a punto reciente de un servidor propio quería proteger tanto las aplicaciones web publicadas por Traefik como los servicios de infraestructura, entre ellos SSH y SMTP. CrowdSec permite cubrir esas dos superficies, pero no lo hace con un único componente: el motor analiza eventos y los bouncers aplican las decisiones.
La solución que adopté mantiene CrowdSec en un contenedor, usa el plugin de Traefik para inspeccionar tráfico HTTP y ejecuta el firewall bouncer como servicio del host. Añadí además un panel local para consultar decisiones y alertas sin exponer el LAPI a Internet.
Idea clave: el motor CrowdSec detecta y registra; los bouncers son quienes bloquean. Un bouncer web y uno de firewall operan en capas distintas y se complementan.[1]
Arquitectura: detección y remediación en dos capas
Peticiones HTTP ──> Traefik + plugin CrowdSec ──> servicios web
│
├── LAPI de CrowdSec
└── AppSec (reglas WAF)
SSH, SMTP y otros puertos ──> firewall del host
▲
│ iptables/ipset
firewall bouncer
│
LAPI de CrowdSec
Panel local ──> LAPI de CrowdSec
CrowdSec recibe eventos de sus fuentes de datos: registros de servicios y, en el caso de AppSec, peticiones HTTP que el proxy envía para su evaluación. El motor correlaciona esos eventos y publica decisiones en la API local (LAPI). Los bouncers consultan esas decisiones y las aplican en el punto que protegen.[1][2]
| Componente | Dónde actúa | Para qué sirve |
|---|---|---|
| Plugin de Traefik y AppSec | Capa HTTP, antes de llegar a la aplicación | Inspeccionar peticiones web y aplicar reglas WAF o decisiones de CrowdSec |
| Firewall bouncer | Firewall del sistema operativo | Bloquear una IP a nivel de red en los puertos y flujos cubiertos por sus cadenas |
| Panel local | Interfaz de administración | Consultar decisiones y la evidencia asociada a alertas |
Protección web con Traefik y AppSec
El plugin de CrowdSec en Traefik puede consultar decisiones y comunicarse con el componente AppSec. Este último evalúa solicitudes HTTP frente a reglas de seguridad, como intentos de explotar vulnerabilidades conocidas. Si una petición incumple una regla, Traefik puede rechazarla antes de que llegue al backend.[3]
Esto es distinto de una decisión de bloqueo por IP. Una respuesta HTTP 403 puede deberse a una regla AppSec aplicada a una petición concreta; por sí sola no demuestra que el firewall haya bloqueado la IP. Para comprobar si existe una decisión de baneo, consulta la LAPI:
docker exec crowdsec cscli decisions list
En producción conviene pensar qué debe ocurrir si AppSec no está disponible. Una política que rechace las peticiones cuando el servicio AppSec no responde mejora el cierre de seguridad, pero puede afectar a la disponibilidad de las aplicaciones durante una caída del componente. Esa decisión debe ser explícita y probarse antes de aplicarla a todos los servicios.[3]
Bloqueo a nivel de host y contenedores
Para bloquear también conexiones a servicios que no pasan por un proxy HTTP —por ejemplo, SSH o SMTP— instalé el firewall bouncer en el propio host Linux. CrowdSec puede seguir ejecutándose en Docker; el bouncer es un servicio del host porque necesita modificar las reglas de red del sistema.[2]
En modo iptables, el bouncer utiliza iptables e ipset para mantener conjuntos de IPs y reglas que los consultan. Para cubrir conexiones dirigidas al host y puertos publicados por Docker, la configuración incluye las cadenas INPUT y DOCKER-USER:[2]
mode: iptables
iptables_chains:
- INPUT
- DOCKER-USER
INPUT procesa tráfico entrante al propio servidor. DOCKER-USER permite aplicar reglas antes de las reglas de filtrado que Docker crea para el tráfico reenviado a contenedores.[4] Por eso, abrir posteriormente otro puerto publicado —por ejemplo, el 8080— no debería permitir el acceso desde una IP que ya figura en la lista de bloqueo, siempre que el tráfico pase por esas cadenas y llegue con la IP de origen real.
En mi servidor, iptables -V devuelve una versión con nf_tables: es la interfaz iptables usando el backend nftables del sistema. Eso no significa que UFW administre las decisiones de CrowdSec. UFW mantiene sus propias reglas; las cadenas y conjuntos del bouncer se consultan en el firewall directamente.
# Decisiones que la LAPI conoce
docker exec crowdsec cscli decisions list
# Reglas y contadores de la cadena del bouncer
sudo iptables -L CROWDSEC_CHAIN -n -v
# Conjunto de IPs administrado por el bouncer IPv4
sudo ipset list crowdsec-blacklists
Por tanto, sudo ufw status sigue siendo útil para revisar las reglas de UFW, pero no es una vista completa de las decisiones aplicadas por otros componentes de netfilter. CrowdSec documenta tanto el uso de DOCKER-USER para aplicaciones Docker publicadas como la consulta de métricas del bouncer.[2]
Cuidado con los proxies y NAT: el firewall bloquea la dirección de origen que ve el host. Si un proxy o un NAT oculta la IP del cliente, una decisión sobre esa IP original no coincidirá en el firewall; podría bloquearse la dirección compartida del intermediario y afectar a más usuarios. En las aplicaciones web, la capa de Traefik puede disponer de la IP original si se configuran correctamente los encabezados reenviados de fuentes de confianza.
Panel web local para decisiones y alertas
Para administrar decisiones desde una interfaz web instalé CrowdSec Local Dashboard, un proyecto independiente de CrowdSec. El panel sincroniza decisiones y evidencia de alertas con la LAPI y guarda su histórico en una base de datos SQLite.[5]
El proyecto está en fase beta, así que puede cambiar entre versiones. Lo mantuve accesible únicamente desde la red privada, enlazando el puerto publicado a la dirección de Tailscale del servidor. El contenedor se conecta a la red Docker donde está la LAPI; no hace falta publicar el puerto del LAPI en todas las interfaces ni exponer el panel a Internet.
El panel necesita dos tipos de credenciales diferentes:[6]
- Credenciales de máquina (watcher): el
loginypassworddelocal_api_credentials.yaml. Permiten consultar la evidencia de alertas; si son incorrectos, pueden aparecer las decisiones pero fallar el detalle de alertas con401 Unauthorized. - Token de bouncer: una clave dedicada creada para el panel con
cscli bouncers add. Se usa para leer decisiones. No reutilices la clave del bouncer de Traefik ni guardes estos secretos en Git.
La lista de orígenes se puede limitar, por ejemplo, a crowdsec,cscli. Incluir todas las decisiones de CAPI y listas comunitarias puede llenar el panel con muchos bloqueos que no tienen evidencia de alertas local.[7]
Pruebas y comprobaciones
Una vez desplegados los componentes, conviene comprobar cada capa de forma independiente:
- AppSec: consulta sus métricas y confirma que las reglas de prueba se procesan. Un
403aislado no confirma por sí mismo un bloqueo de firewall. - Firewall bouncer: comprueba que aparece como válido en
cscli bouncers listy que la cadena y el conjunto del sistema contienen las reglas esperadas. - Decisiones: revisa
cscli decisions listy compara una decisión activa con las entradas del conjunto de bloqueo. - Panel: inicia sesión y confirma que se sincronizan decisiones y evidencia de alertas.
- Prueba externa: usa un cliente de prueba fuera de la red y una decisión de duración corta. No uses la IP desde la que administras el servidor: podrías perder el acceso SSH.
También revisé qué contenedores de métricas eran realmente necesarios. El exporter de Postfix solo alimentaba a Prometheus en ese stack, así que retiré ambos; las instancias de Grafana que tenían otras funciones se mantuvieron. El panel de CrowdSec no sustituye a Prometheus para métricas generales: está pensado para administrar decisiones y revisar alertas de CrowdSec.
Conclusiones
- CrowdSec separa la detección de la remediación; el motor necesita uno o más bouncers para aplicar bloqueos.
- Traefik con AppSec protege las peticiones HTTP; el firewall bouncer cubre servicios del host y tráfico Docker que atraviese las cadenas configuradas.
- UFW y el bouncer pueden coexistir, pero sus vistas y reglas son distintas. Para diagnosticar bloqueos hay que consultar también
cscli,iptableseipset. - Un panel local facilita la gestión, pero hay que protegerlo, mantener sus secretos fuera de Git y tener presente que el proyecto consultado está en beta.
- Si un proxy o NAT oculta la IP del cliente, el firewall solo puede decidir sobre la dirección que recibe realmente.
📚 Fuentes consultadas
- Introducción a CrowdSec y sus componentes - Documentación de CrowdSec
- Firewall bouncer para Linux: iptables, ipset y cadenas Docker - Documentación de CrowdSec
- Configurar CrowdSec AppSec con Traefik - Documentación de CrowdSec
- Filtrado de paquetes con iptables y la cadena DOCKER-USER - Docker Docs
- CrowdSec Local Dashboard - repositorio del proyecto
- Credenciales de LAPI para CrowdSec Local Dashboard
- Variables de configuración y orígenes de decisiones del panel