Rotación de logs

El daemon maneja los dos esquemas estándar de rotación:

  • Rotate-and-rename (el log se renombra, un nuevo fichero toma su lugar). Detectado por cambio de inodo/device en la ruta vigilada.
  • Copytruncate (el contenido se copia a otro sitio y luego se trunca el original a cero). Detectado por tell() > stat().

En ambos casos el daemon reabre el fichero desde el principio y escribe info reopen after rotate dev=… ino=… (rotate-and-rename) o info truncate file shrank (copytruncate) en stderr.

Si observas entradas perdidas alrededor de la rotación, revisa la config de la herramienta de rotación:

  • En OpenBSD newsyslog, la rotación por defecto es rotate-and-rename — no requiere configuración especial.
  • En Linux logrotate, preferir el default (rotar y SIGHUP al servidor web) sobre el modo copytruncate. Ambos funcionan, pero rotate-and-rename es más barato y no pierde líneas.

Script rc.d (OpenBSD)

Variante mínima /etc/rc.d/abusive_http_watch:

#!/bin/ksh
daemon="/usr/local/sbin/abusive_http_watch"
daemon_flags="/var/www/logs/access.log"
daemon_user="_www"
. /etc/rc.d/rc.subr
rc_bg=YES
rc_reload=NO
rc_cmd $1

Luego:

doas rcctl enable abusive_http_watch
doas rcctl start abusive_http_watch
doas rcctl check abusive_http_watch

En Linux con systemd, ver la doc de la distribución; una unidad simple Type=simple apuntando a /usr/local/sbin/abusive_http_watch con los mismos argumentos basta.

Eventos syslog

El daemon emite una clase de evento — added — cuando una IP cruza el umbral de hits. Formato:

<syslog-tag>[<pid>]: added ip=<ip> path=<ruta-blocklist>

Ejemplo:

abusive_http_watch[12345]: added ip=198.51.100.42 path=/var/www/run/abusive_http_hosts

Rutar vía syslog.conf a un fichero dedicado para revisión:

local0.notice                                          /var/log/abusive_http.log

Combinado con ABUSIVE_SYSLOG_FACILITY=local0, da un log de auditoría limpio de cada IP bloqueada.

Señales de monitorización

Para monitorización automatizada, dos indicadores útiles:

  • Tasa de crecimiento de la blocklistwc -l /var/www/run/abusive_http_hosts en el tiempo. En un servidor web público debería crecer de forma sostenida (varias IPs por día a muchas por hora, según exposición). Una línea plana de 24h en un sitio público sugiere que el daemon no corre o lee el log equivocado.
  • Salud del procesopgrep -f abusive_http_watch debería retornar siempre un PID. Alertar si cero.

Para sistemas con Prometheus, un exporter pequeño que solo wc -l el fichero blocklist y exponga el conteo como gauge es suficiente. No hay endpoint de métricas dentro del daemon — por diseño, para mantenerlo sin dependencias.

Troubleshooting

El fichero blocklist se queda vacío

Causas posibles, por orden de probabilidad:

  1. El daemon lee el fichero equivocado. Lanzar en primer plano con ABUSIVE_DEBUG=1. Confirmar que logea start following dev=… ino=… para la ruta esperada. Si no, corregir el argumento.
  2. No hay tráfico de ataque real. Improbable en un servidor web público, posible en uno interno. Lanzar con ABUSIVE_DEBUG=1 y buscar líneas [dbg] ip=… sin [dbg] token match asociado.
  3. La whitelist se traga todo. Revisar /etc/abusive_http_whitelist — una entrada CIDR /0 errante whitelistea internet entero. Quitar y reiniciar.
  4. Umbral de hits demasiado alto. Bajar ABUSIVE_MIN_HITS a 1 temporalmente y observar.

La blocklist crece pero el firewall no bloquea

El daemon escribe el fichero. Hay que decirle al firewall que lo lea. Verificar:

  • Cron recarga la tabla pf / el set nftables cada pocos minutos (el daemon no llama a pfctl por sí mismo).
  • La regla firewall que referencia la tabla está en la cadena correcta y suficientemente quick para tomar efecto antes de cualquier pass posterior.

Usuarios legítimos están siendo bloqueados

Añadir su IP pública o rango CIDR a /etc/abusive_http_whitelist. El daemon relee la whitelist en cada línea — los cambios surten efecto en segundos.

Para retirar una IP individual de la blocklist existente, editar el fichero directamente y recargar la tabla firewall. El daemon no necesita reinicio — no la añadirá de nuevo a menos que el usuario vuelva a tocar tokens.

El daemon crashea tras una rotación de logs

No debería ocurrir dado el manejo de rotación descrito arriba. Si ocurre, lanzar en primer plano con ABUSIVE_DEBUG=1, provocar una rotación, capturar stderr. Enviar el log capturado por la página de Contacto — eso es un bug real que merece arreglarse.

Apagado limpio

El daemon captura SIGTERM y sale limpiamente tras la iteración actual:

doas rcctl stop abusive_http_watch

Logea [info] TERM received, exiting, cierra el handle del log, flushea cualquier escritura pendiente y sale con status 0. Sin corrupción de estado, ni siquiera en un log muy activo.