CSF + ModSecurity en nginx: MODSEC_LOG y regex custom
El problema
En servidores DirectAdmin con nginx + ModSecurity 3 (no Apache), CSF/LFD no detecta los bloqueos de ModSecurity. Un usuario puede ser bloqueado repetidamente por ModSecurity (HTTP 406) sin que LFD lo registre ni tome acción (ban temporal, notificación, etc.).
Consecuencia: ModSecurity bloquea requests pero LFD es ciego a esos eventos. Si una IP llega a csf.deny, es por otro mecanismo (DA BFM, manual), nunca por LF_MODSEC.
Causa raíz (triple)
1. MODSEC_LOG apunta al fichero equivocado
El default de CSF es:
MODSEC_LOG = "/var/log/httpd/error_log"
Pero en DirectAdmin con nginx + ModSecurity 3, ModSecurity corre en nginx, no en Apache. Los eventos de "Access denied" se escriben en los error.log per-domain de nginx, no en httpd:
/var/log/nginx/domains/dominio.com.error.log
2. CSF no tiene regex para nginx-modsec3
El fichero built-in RegexMain.pm solo tiene regex para el formato ModSecurity v2 (Apache). El formato nginx-modsecurity3 tiene campos extra (PID#TID: *CONN) que la regex no matchea.
Formato Apache-modsec2:
[date] [error] [client IP] ModSecurity: Access denied ...
Formato nginx-modsec3:
YYYY/MM/DD HH:MM:SS [error] PID#TID: *CONN [client IP] ModSecurity: Access denied ...
3. Logs distribuidos por dominio
Los "Access denied" de nginx-modsec3 se escriben en los error.log per-domain de nginx, no en un fichero centralizado. CSF necesita leer TODOS los error.log de dominio.
Solución
Paso 1: Corregir MODSEC_LOG en csf.conf
# En /etc/csf/csf.conf, cambiar:
MODSEC_LOG = "/var/log/nginx/domains/*.error.log"
CSF soporta globs — se evalúan al iniciar LFD. Cada *.error.log de cada dominio será monitoreado.
Consecuencia: tras crear un nuevo dominio, hay que reiniciar LFD (csf -ra) para que monitorice su error.log nuevo.
Paso 2: Crear regex custom para nginx-modsec3
En /usr/local/csf/bin/regex.custom.pm, añadir dentro de la sección de reglas (antes del return () final):
# nginx-modsecurity3 format
# YYYY/MM/DD HH:MM:SS [error] PID#TID: *CONN [client IP] ModSecurity: Access denied ...
if (($globlogs{MODSEC_LOG}{$lgfile}) and ($line =~ /^\S+ \S+ \[\S+\] \S+ \*\d+ \[client (\S+)\] ModSecurity:(( \[[^\]]+\])*)? Access denied/)) {
my $ip = $1;
my $domain = "";
if ($line =~ /\[hostname "([^"]+)"\]/) {$domain = $1}
my $ruleid = "unknown";
if ($line =~ /\[id "(\d+)"\]/) {$ruleid = $1}
$ip =~ s/^::ffff://;
return ("mod_security v3 (id:$ruleid domain:$domain) triggered by", $ip, "modsecnginx", "5", "80,443", "7200", "0");
}
Qué hace:
- Matchea el formato nginx-modsec3
- Extrae IP, dominio (hostname), y rule ID
- Retorna al LFD con trigger type "modsecnginx", 5 hits para ban, puertos 80/443, ban temporal de 2 horas
Paso 3: Verificar sintaxis y reiniciar
# Verificar que el Perl compila
perl -c /usr/local/csf/bin/regex.custom.pm
# Reiniciar CSF + LFD
csf -ra
Paso 4: Verificar que LFD monitoriza los ficheros
# Debe mostrar líneas de "watching" para *.error.log
grep "watching" /var/log/lfd.log | grep nginx | tail -10
Persistencia
regex.custom.pmsobrevive upgrades de CSF — es el lugar oficial para customizacionescsf.conftambién sobrevive upgrades, pero verificar tras actualizaciones mayores de CSF- Los globs en
MODSEC_LOGsolo se evalúan al iniciar LFD — tras crear un nuevo dominio, reiniciar LFD concsf -ra
Diagnóstico
Si sospechas que LFD no está detectando bloqueos ModSecurity:
# 1. Verificar que hay eventos ModSecurity en los logs nginx
grep "ModSecurity: Access denied" /var/log/nginx/domains/*.error.log | tail -5
# 2. Verificar que MODSEC_LOG apunta a nginx
grep "MODSEC_LOG" /etc/csf/csf.conf
# 3. Verificar que LFD registra triggers modsec
grep "modsec" /var/log/lfd.log | tail -10
# 4. Si no hay nada en lfd.log pero sí hay eventos en nginx:
# → MODSEC_LOG incorrecto o regex no matchea
Servidores aplicados
| Servidor | Fecha | Estado |
|---|---|---|
| srv120 | 2026-03-05 | Activo |
| srv121 | 2026-03-05 | Activo |
| kvm456 | 2026-03-05 | Activo |
| amazzal | 2026-03-05 | Activo |
| dar | 2026-03-05 | Activo |
| titrit | N/A | No tiene modsec en nginx |
Checklist para nuevos servidores DA
-
MODSEC_LOGen csf.conf apunta a/var/log/nginx/domains/*.error.log -
regex.custom.pmtiene la regex para nginx-modsec3 -
perl -c regex.custom.pmcompila sin errores -
csf -raejecutado - Verificar con
grep "watching" /var/log/lfd.log | grep nginx