Uno de los retos más interesantes al desarrollar un panel de hosting propio es la seguridad real en producción. No la teórica, sino la que funciona cuando hay tráfico, bots y ataques constantes.

En este artículo explico cómo hemos pasado de no poder proteger WordPress a nivel servidor a tener un sistema automático, centralizado y sin dependencias del CMS, utilizando Fail2Ban junto con Caddy y PHP-FPM.


El problema inicial

El objetivo era claro: bloquear ataques de fuerza bruta contra wp-login.php y xmlrpc.php sin instalar plugins en WordPress.

Fail2Ban necesita algo básico para funcionar:

  • Logs con peticiones HTTP
  • IP del cliente
  • Rutas accedidas

Pero en nuestro sistema había un problema crítico:

Caddy no estaba generando access logs para los hostings.

Esto implicaba que:

  • No había registro de accesos
  • No había forma de detectar ataques
  • Fail2Ban no podía actuar

No era un problema de configuración de seguridad, sino de falta de datos.


Por qué ocurría

Nuestro stack funciona así:

  • Caddy gestiona las peticiones HTTP
  • PHP se ejecuta mediante PHP-FPM
  • Los vhosts se generan automáticamente desde el panel

El error estaba en el sistema de provisioning:

  • Solo algunos dominios tenían logging activado
  • La mayoría de hostings no generaban ningún log

Esto hacía imposible proteger rutas como /wp-login.php, porque simplemente no existía información sobre los accesos.


La solución: logging centralizado

La solución no estaba en Fail2Ban, sino en el servidor web.

Se implementó un sistema para que todos los hostings escriban logs de acceso de forma automática en un único archivo:

 
/var/log/caddy/hosting-access.log
 

Resultado en producción:

  • 21 hostings activos registrados
  • 42 dominios (incluyendo www)
  • Todas las peticiones HTTP registradas

Ahora cada acceso a cualquier sitio pasa por ese log, incluyendo:

  • /wp-login.php
  • /xmlrpc.php

Automatización desde el panel

Este cambio no es manual.

Se ha integrado en el sistema de creación de hostings:

  • Cada vez que se crea un nuevo sitio
  • Se genera su configuración en Caddy
  • Se activa automáticamente el logging

Esto se ejecuta internamente en el flujo:

 
addCaddyRoute() → ensureHostingAccessLog()
 

El resultado es que todos los hostings nuevos ya incluyen logging sin intervención adicional.


Configuración de Fail2Ban

Una vez disponibles los logs, Fail2Ban puede trabajar correctamente.

Actualmente hay cuatro jails activos:

Seguridad base

  • sshd: protege accesos SSH

Panel interno

  • musedock-panel: protege login de administración (puerto 8444)
  • musedock-portal: protege acceso de clientes (puerto 8446)

WordPress

  • musedock-wordpress: protege /wp-login.php y /xmlrpc.php en todos los hostings

Este último es el más relevante porque aplica a todos los sitios simultáneamente.


Cómo funciona el sistema

El flujo completo es el siguiente:

  1. Un atacante intenta acceder a WordPress
  2. Realiza múltiples peticiones POST a /wp-login.php
  3. Caddy registra cada petición en el log
  4. Fail2Ban analiza el log en tiempo real
  5. Si detecta 10 intentos en 5 minutos:
    • Banea la IP
    • Bloquea acceso en puertos 80 y 443
    • Durante 1 hora

Todo esto ocurre sin instalar ningún plugin en WordPress.


Verificación en tiempo real

Para comprobar que el sistema funciona:

 
tail -f /var/log/caddy/hosting-access.log | grep wp-login
 

Al hacer un intento de login en cualquier WordPress:

  • La petición aparece en el log inmediatamente
  • Fail2Ban puede procesarla

Gestión desde el panel

Además de la protección automática, se ha construido una interfaz para gestionar Fail2Ban sin usar consola.

Funcionalidades añadidas

  • Estado del servicio (activo, uptime, jails)
  • Número total de IPs baneadas
  • Listado de IPs bloqueadas por jail

Acciones disponibles

  • Banear IP manualmente
  • Desbanear IP
  • Añadir IP a whitelist

Whitelist (ignoreip)

El sistema permite gestionar IPs que nunca deben ser bloqueadas.

Esto se guarda en:

 
/etc/fail2ban/jail.local
 

Características:

  • Soporte para IPs individuales y rangos CIDR
  • Mantiene siempre:
    • 127.0.0.1/8
    • ::1
  • Recarga automática de Fail2Ban tras cambios

Esto evita problemas típicos como bloquearse a uno mismo durante pruebas.


UX y operativa

Se han añadido confirmaciones visuales en acciones críticas:

  • Banear IP
  • Desbanear IP
  • Añadir a whitelist

El objetivo es evitar errores y hacer el sistema usable sin necesidad de conocimientos avanzados.


Qué hemos aprendido

Este desarrollo deja varias conclusiones importantes:

  1. Fail2Ban depende completamente de los logs
  2. Si el servidor web no registra accesos, no hay seguridad posible
  3. El problema no siempre está donde parece (no era Fail2Ban, era Caddy)
  4. La automatización en el provisioning es clave para escalar

Conclusión

Ahora el sistema:

  • Protege todos los WordPress automáticamente
  • No depende de plugins
  • Funciona a nivel servidor
  • Escala con cada nuevo hosting

Y lo más importante:

La seguridad ya no depende del usuario final ni de la configuración de cada instalación.

 

Es parte de la infraestructura.