Skip to main contentSkip to footer

Agente ReportedIP para Linux

El agente es un único binario estático que hace dos trabajos en un servidor Linux: mantiene la Community Blacklist en el cortafuegos del núcleo y notifica a los atacantes que encuentra en los registros de ese servidor. De forma opcional también bloquea él mismo esos hallazgos locales. Sustituye al script de sincronización escrito a mano que documenta este sitio, y asume el trabajo que hacía fail2ban.

Versión actual: 0.3.8 para linux/amd64 y linux/arm64. Cada servidor necesita una licencia, una viene incluida en Professional y tres en Business, véase Licencias de servidor. Las licencias se añaden y se retiran en Agent Servers, dentro de tu cuenta.

Qué hace

1

La Community Blacklist en el núcleo

Cinco listas, una por servicio expuesto (ssh, mail, web, ftp, edge), descargadas con la frecuencia que permite tu plan y cambiadas de forma atómica a un ipset o a un conjunto de nftables. Una lista que llega vacía o con una longitud inverosímil no se aplica nunca: una descarga fallida deja en su lugar el conjunto anterior en vez de abrir la puerta.

2

Ataques locales reconocidos y notificados

El agente sigue los registros que el servidor ya escribe, cuenta las coincidencias por dirección frente a un umbral propio de esa fuente y notifica la dirección en cuanto se alcanza el umbral. Ninguna línea de registro, ningún nombre de usuario y ninguna URL sale de la máquina.

3

Hallazgos locales bloqueados, si así lo quieres

Desactivado tras la instalación. Una vez activado, una dirección que el agente ha cazado él mismo queda bloqueada en un conjunto del núcleo con un plazo de caducidad, y un reincidente se bloquea más tiempo en cada ocasión.

Requisitos

Cuatro cosas, y solo la última tiene que venir de nosotros.

  • Un núcleo Linux con un filtro de paquetes en el que se pueda escribir. O bien ipset junto con iptables, o bien nftables. En un host Debian o Ubuntu eso es apt install ipset iptables o apt install nftables; en la familia RHEL, dnf install ipset iptables o dnf install nftables. El agente también usa ip6tables cuando está presente; sin esa herramienta los bloqueos IPv6 se registran pero no se aplican.
  • systemd para el temporizador y el servicio de vigilancia. En un host sin systemd el agente funciona igualmente, con la sincronización desde cron y el demonio desde tu propio sistema de arranque, y la sección de más abajo explica cómo.
  • Root. Escribe reglas de cortafuegos y lee registros que no son legibles por todos, y ambas cosas lo exigen. No hay un modo reducido para un usuario normal.
  • Una clave API de tu cuenta. La clave se usa dos veces durante la instalación, una para descargar la versión con licencia y otra para registrar el host. Una clave por host es la recomendación; varios hosts con una misma clave están permitidos y no cuestan nada más, porque un host se identifica por su identificador de instalación y no por la clave.

Nada más. El binario está enlazado estáticamente y compilado sin cgo, así que no trae intérprete, ni biblioteca, ni repositorio de paquetes, ni utilidad de cron propia. Para la descarga y la suma de comprobación, install.sh necesita curl o wget además de sha256sum, y sin la herramienta de verificación no instala nada. El agente se desarrolló y se midió en Debian 12 (bookworm) sobre arm64 con ISPConfig, nginx, Postfix, pure-ftpd y bind.

ipset o nftables, y qué decide auto

Los dos motores hacen el mismo trabajo, y en un host recién instalado ninguno es mejor. Lo que importa es cuál usa ya el host, porque dos filtros de paquetes escribiendo la misma cadena INPUT es la forma en que desaparece una tarde.

backendQué ocurre
auto (predeterminado)Se prefiere ipset cuando ipset e iptables están ambos presentes; si no, se usa nft. Esa preferencia existe porque CSF y la guía de cortafuegos publicada emplean esa cadena de herramientas: un host con reglas ya existentes las conserva así.
ipsetConjuntos en ipset, reglas en la cadena rip-blacklist de iptables e ip6tables. Si falta una de las herramientas, el agente se detiene con un mensaje claro en vez de recurrir a otra.
nftablesConjuntos y reglas en la tabla propia inet reportedip. Se detiene si falta nft.

Ninguna comprobación de versión decide esto. Solo cuenta un binario que exista realmente, porque iptables --version en un host con el motor nft responde algo que parece correcto y no significa nada. Tras la primera sincronización, el motor elegido queda anotado, y un cambio posterior exige reportedip-agent sync --migrate-backend en lugar de construir en silencio un segundo juego de reglas.

Comprobar primero el host

doctor es el comando que se ejecuta antes de instalar. No cambia absolutamente nada: ningún archivo, ningún directorio, ninguna regla, ningún conjunto. Todo lo que informa lo ha leído.

bash
reportedip-agent doctor

Responde a seis preguntas, en este orden:

  • system: la distribución tal y como la nombra su propio os-release, el núcleo, la plataforma, si systemd está realmente en marcha (leído en /run/systemd/system, no deducido de que exista systemctl), la zona horaria en la que se leen los registros sin desplazamiento, y el espacio libre allí donde estará el directorio de estado.
  • ssh: qué unidad ejecuta sshd, y el puerto. El caso interesante es ssh.socket: con activación por socket, sshd -T sigue respondiendo 22 mientras el puerto real está en la unidad de socket. El puerto se toma entonces del proceso que escucha. doctor dice cuál de los dos caminos se aplicó, porque quien ve un puerto equivocado en la configuración mira aquí primero.
  • firewall: dónde están ipset, iptables, ip6tables y nft, qué motor resulta de ello, y si el conjunto de bloqueos locales lleva un plazo de caducidad por entrada. Ese último punto decide si el núcleo hace caducar un bloqueo por sí solo, y eso es precisamente lo que evita que un agente caído deje un bloqueo permanente.
  • panel: ISPConfig, Plesk, cPanel o DirectAdmin. Solo la disposición de registros por sitio de ISPConfig es conocida por esta versión. Los demás se nombran en vez de adivinarse, porque un patrón equivocado haría que el agente leyera contadores de bytes en lugar de registros de acceso.
  • sources: cada fuente configurada, resuelta a archivos reales, con cuántos hay y cuántos son legibles, el mayor de ellos con una estimación de líneas, y una nota sobre los archivos que existen y están vacíos. En el host de panel más grande medido, el patrón web se resuelve en 196 archivos, y quien instale el agente allí debería saberlo antes y no después.
  • mail: si un aviso podría salir realmente de este host. De dieciocho hosts medidos, catorce podían entregar correo, dos tenían sendmail con el MTA detenido, y dos no tenían ningún MTA. Sin esta sección, alguien espera un correo que nunca llega.

Leer el veredicto

El último bloque se llama verdict y se corresponde con el código de salida, así que una comprobación de monitorización no tiene ninguna salida que analizar.

SalidaVeredictoSignificado
0está todo lo que el agente necesitaInstalar.
1funciona en este host, con las limitaciones indicadasCada limitación aparece nombrada en la salida. Un motor de cortafuegos ausente significa que este host notifica y no bloquea, y eso es una instalación válida. Una fuente que no se resuelve en nada conviene arreglarla antes.
2no puede funcionar en este hostNo es un host Linux, o es un host que no puede ni bloquear ni leer un solo registro. Corrige la causa antes de instalar.

Vuelve a ejecutar doctor después de la instalación. Con una configuración presente ya no adivina, informa de las fuentes configuradas en vez de las detectadas, y añade la desviación de reloj por fuente que ha medido el demonio. Una fuente cuyo registro marca cada línea a más de un minuto del reloj del sistema tiene una ventana de recuento que nunca se llena, y ese es el único fallo que se parece exactamente a «el detector no funciona».

Instalación

Un comando, como root. Lee la versión actual de los metadatos públicos, descarga con tu clave la versión compilada para tu arquitectura, la verifica contra SHA256SUMS, la instala en /usr/local/bin/reportedip-agent y después configura el host.

bash
curl -fsSL https://reportedip.com/agent/install.sh | REPORTEDIP_KEY=<YOUR-KEY> sh

La clave va en el entorno porque se necesita dos veces, una para la descarga con licencia y otra para registrar el host, y pegarla dos veces es la forma en que una clave acaba con un formato equivocado en el historial del shell. El script es sh POSIX a propósito: tiene que funcionar en hosts donde bash no es el shell predeterminado. Se detiene con un motivo nombrado y no instala nada si no se ejecuta como root, si falta la clave, si la plataforma no es Linux, si la arquitectura no es amd64 ni arm64, si no existe ni curl ni wget, y si falta sha256sum.

Las dos vías de descarga están separadas a propósito. Los metadatos de versión se obtienen sin la clave y los archivos con licencia con ella, de modo que la clave nunca va a un destino de redirección que solo sirve metadatos públicos. Del archivo de sumas se comprueba únicamente la línea de tu propia arquitectura, porque el archivo también incluye la otra.

Qué hace realmente el instalador

Todo lo que sigue es idempotente. El mismo comando en un host que ya tiene el agente actualiza el binario y deja la configuración exactamente como estaba.

  1. Lee https://reportedip.com/agent/latest sin la clave y extrae de ahí la versión. Ese archivo es el mismo JSON que el propio agente consulta para las actualizaciones.
  2. Descarga reportedip-agent_linux_<arch> y SHA256SUMS con la cabecera X-Key en un directorio temporal que se borra en cualquier vía de salida.
  3. Verifica la suma de comprobación y no instala nada si hay discrepancia.
  4. Instala el binario con modo 0755 en /usr/local/bin/reportedip-agent y muestra la versión que acaba de dejar.
  5. Reinicia reportedip-agent.service si esa unidad está activa. Esto importa en una actualización: el demonio de vigilancia seguiría ejecutando el código antiguo hasta el siguiente reinicio de la máquina. La unidad de sincronización es de tipo oneshot y toma el binario nuevo por sí misma en su siguiente arranque.
  6. Ejecuta la configuración del host, salvo que REPORTEDIP_NO_SETUP=1 esté definido o que /etc/reportedip-agent/config.yaml ya exista. En el segundo caso lo dice y deja la configuración en paz.

La configuración del host es el paso que escribe archivos, y es lo mismo que reportedip-agent install --key. Por orden:

  • Crea /etc/reportedip-agent y /var/lib/reportedip-agent, el segundo con modo 0700, y corrige el modo de un directorio que ya estuviera.
  • Detecta el puerto SSH a partir de sshd -T, de SSH_CONNECTION y de los procesos sshd que escuchan, y toma la unión de los tres. Si no encuentra nada, se detiene y pide --ssh-port en vez de suponer 22.
  • Decide cuáles de las cinco listas del feed se configuran. ssh y edge están siempre activas. mail, web y ftp se añaden cuando una unidad correspondiente está activa o un puerto correspondiente escucha: un host sin servidor web no recibe por tanto una lista web.
  • Detecta las fuentes de registro y las escribe de forma explícita en la configuración. La detección ocurre una vez, en la instalación, y el resultado permanece en el archivo: un servicio detenido por mantenimiento nunca debe desactivar una fuente en silencio.
  • Escribe /etc/reportedip-agent/config.yaml con modo 0600 y con mode: log y ban.enabled: false. Una configuración existente nunca se sobrescribe, y entonces --key se ignora con una nota que indica cambiar api_key en el archivo.
  • Escribe la lista blanca automática en el directorio de estado: la dirección desde la que venía tu sesión SSH, más cada dirección de interfaz del host. La dirección de sesión de una ejecución anterior se conserva, para que una segunda ejecución desde otra sesión no retire la dirección en la que confiaba la primera. Las direcciones de interfaz se vuelven a determinar en cada ejecución, para que una dirección eliminada no se quede colgando.
  • Genera el identificador de instalación, un UUID v4 aleatorio en /var/lib/reportedip-agent/install-id. Esa es desde ese momento la identidad de este servidor frente a la API. El nombre del host no se usa nunca ni se transmite.
  • Advierte de dos cosas que no puede arreglar por ti: una tabla de nftables inet reportedip que dejó el script de shell documentado mientras el motor resuelto es ipset, y un nginx que lee cabeceras de Cloudflare sin set_real_ip_from. La fuente web contaría entonces direcciones de Cloudflare en lugar de las del visitante.
  • Escribe las tres unidades de systemd en /etc/systemd/system, ejecuta daemon-reload, activa reportedip-agent-sync.service, y activa e inicia reportedip-agent-sync.timer y reportedip-agent.service.
  • Llama a verify-key con la configuración recién escrita y muestra tu rol y los informes consumidos hoy frente al límite diario. Un 401 o un 403 aquí significa que la clave está mal, y ese es el único fallo que conviene corregir de inmediato.
La instalación no toca ninguna regla de cortafuegos. Ninguna cadena, ningún conjunto, ningún salto hacia INPUT. Todo eso lo crea el primer reportedip-agent sync, y por eso el instalador termina diciéndote que lo ejecutes. Hasta entonces el host está exactamente como estaba, y una instalación que decidas descartar cuesta un rm de dos directorios.

Las variables de entorno de install.sh

VariablePredeterminadoPara qué sirve
REPORTEDIP_KEYninguna, obligatoriaTu clave API. Sin ella el script se detiene antes de descargar nada.
REPORTEDIP_VERSIONla versión actualFija una versión, para un despliegue por fases o para reinstalar una versión concreta. Los directorios de versiones antiguas se conservan en el punto de distribución exactamente para eso.
REPORTEDIP_PREFIX/usr/local/binAdónde va el binario. Si lo cambias, las unidades de systemd tienen que seguirlo, porque nombran la ruta absoluta.
REPORTEDIP_BASEhttps://reportedip.com/agentLa base de descarga. Para un espejo interno que sirva latest, SHA256SUMS, SHA256SUMS.sig y los dos binarios. La comprobación de firma se aplica allí igual.
REPORTEDIP_NO_SETUPsin definir1 deja el binario y se detiene. No se detecta nada, no se escribe ninguna configuración y no se instala ninguna unidad. Es la variable para una imagen base, una capa de contenedor o una ejecución de gestión de configuración que posee ella misma el archivo de configuración.
bash
# Binary only, no host setup: for an image or for Ansible
curl -fsSL https://reportedip.com/agent/install.sh \
  | REPORTEDIP_KEY=<YOUR-KEY> REPORTEDIP_NO_SETUP=1 sh

# Later, on the running host, or from your playbook:
reportedip-agent install --key "<YOUR-KEY>"

Instalación a mano

El paso de configuración es un comando propio y acepta tres opciones. Ejecútalo cuando REPORTEDIP_NO_SETUP=1 solo haya dejado el binario, o cuando la detección haya adivinado algo que quieras corregir.

bash
reportedip-agent install --key "<YOUR-KEY>"

# The detection could not find the SSH port (socket activation,
# a non-standard unit, a port from an include file):
reportedip-agent install --key "<YOUR-KEY>" --ssh-port 2222

# The session address is not where you administer this host from
# (a jump host, a console, a serial connection):
reportedip-agent install --key "<YOUR-KEY>" --admin-ip 203.0.113.0/24

--admin-ip acepta una sola dirección o un rango CIDR y sustituye por completo la suposición. Merece la pena siempre que la sesión SSH no sea representativa, porque la dirección adivinada es lo que se interpone entre tú y tu propia regla de bloqueo el primer día. Toda la ejecución de instalación tiene un plazo de cinco minutos, así que un nft o un systemctl colgado termina en un error y no en un proceso que tengas que interrumpir.

Un host sin systemd

Nada del agente depende de systemd. Las unidades son una comodidad, y doctor lo dice en vez de fallar cuando no se pueden usar. En un host así hay que preparar dos cosas a mano.

bash
# 1. The feed sync, from cron. Every 15 minutes is safe on any
#    plan: the agent skips a fetch its own plan interval does
#    not allow yet, so nothing is requested too often.
*/15 * * * * root /usr/local/bin/reportedip-agent sync >/dev/null 2>&1
@reboot      root sleep 120 && /usr/local/bin/reportedip-agent sync

# 2. The watch daemon, from your init system. It runs in the
#    foreground, logs to stderr and stops on SIGTERM.
/usr/local/bin/reportedip-agent watch

La entrada para el arranque no es opcional. Los plazos de caducidad de ipset no sobreviven a un reinicio, y los conjuntos tampoco: sin una sincronización tras el arranque, el host vuelve a levantarse sin reglas y sin bloqueos restaurados. En un host con systemd ese camino lo cubre reportedip-agent-sync.service, activado para multi-user.target, que se ejecuta después de cada servicio de cortafuegos y sin desplazamiento aleatorio. En un host sin journald deja log_file definido, porque allí stderr puede no llevar a ninguna parte.

La primera hora, en orden

El orden de abajo no es una sugerencia. Cada paso responde a una pregunta de la que depende el siguiente, y otro orden es la forma en que una instalación que funciona parece estropeada.

  1. reportedip-agent doctor. Antes que todo lo demás, porque es el único comando que no cambia nada. Lo que nombre como limitación es una limitación que, si no, volverás a descubrir como síntoma dos días más tarde.
  2. reportedip-agent sync. La primera ejecución construye la cadena, crea los conjuntos y descarga las cinco listas. Tarda un momento: las listas se obtienen una tras otra con una pausa breve entre ellas.
  3. reportedip-agent status. Ahora hay algo que mirar. Comprueba que cada lista configurada tenga un número de direcciones IPv4 verosímil, que la cadena indique ok, que la lista blanca contenga las direcciones que esperas, y que account muestre tu rol y el estado de licencia de este host.
  4. Corregir la lista blanca. El instalador incluyó la dirección desde la que venía tu sesión SSH. Eso es una suposición. Sustitúyela por el rango desde el que administras realmente, y añade de paso tu monitorización, tu host de copias de seguridad y el rango de tu oficina.
  5. Esperar y volver a leer status. Un día basta, dos es mejor. En mode: log las reglas están construidas por completo y se aplican, solo que registran en vez de descartar. El registro del núcleo te muestra por tanto exactamente qué habría cortado drop.
  6. Pasar a mode: drop y ejecutar sync, que es lo que reconstruye las reglas.
  7. Solo después considerar ban.enabled: true y reiniciar a continuación el servicio de vigilancia. Ese es el segundo interruptor, y se refiere a tus propios registros en lugar de la lista comunitaria.
bash
reportedip-agent doctor
reportedip-agent sync
reportedip-agent status
reportedip-agent whitelist add 203.0.113.0/24 "management"
reportedip-agent whitelist add 198.51.100.7   "monitoring"
reportedip-agent whitelist list
La instalación arranca en mode: log, y eso es intencionado. No se descarta nada hasta que tú lo decidas, lo que te da dos días para advertir el rango que todavía falta en la lista blanca. Primero la lista blanca, después drop: el orden inverso también funciona, justo hasta la única vez en que no.

Configuración

Un archivo, /etc/reportedip-agent/config.yaml, propiedad de root y con modo 0600. El agente se niega a arrancar cuando es legible por el grupo o por otros, y lo dice junto con el comando chmod que lo corrige, porque ese archivo contiene tu clave API. El estado vive en /var/lib/reportedip-agent: posiciones de lectura, la cola de informes, la memoria de deduplicación, el registro de bloqueos, el estado de salud y el estado de actualización.

El archivo que escribe el instalador ya encaja con el host. La mayoría de las instalaciones cambian dos cosas en su primera semana, el valor de mode y si el bloqueo local está activo, y nada más después.

Una clave desconocida detiene el agente con el código 2. El analizador comprueba los nombres de campo de forma estricta, así que una errata es un error con número de línea y no un valor que nunca se aplica en silencio. Es intencionado: una errata en window_minutes que se descartara sin ruido te dejaría creyendo en un umbral que no tienes. Todas las claves válidas están más abajo, y una clave que no figure en esa lista no existe. Comprueba el archivo después de cada cambio antes de confiar en él: reportedip-agent status termina con el código 2 y nombra la clave cuando el archivo no se puede leer.

Cada clave de config.yaml

Aquí nada es obligatorio salvo api_key. Cada otra clave tiene el valor predeterminado que se indica, y una clave que omitas mantiene ese valor.

ClavePredeterminadoEfecto
api_keyningunoTu clave API. La única clave sin valor predeterminado. REPLACE_ME cuenta como ausente.
api_urlhttps://reportedip.com/wp-json/reportedip/v2La base REST. Tiene que ser una URL https absoluta; el http simple solo se admite en el bucle local, para un proxy local.
update_urlderivado de api_urlDe dónde vienen los binarios del agente. Sin indicación son el esquema y el host de api_url con la ruta /agent. Se define para un espejo, que también tiene que ser https.
auto_updatetrueBuscar una versión más reciente durante la sincronización e instalarla. Ponlo en false cuando la versión de este host se decida en otro sitio, por ejemplo con tu propio paquete o con un despliegue canario. reportedip-agent update sigue funcionando a mano, y status sigue informando de cuánto retraso lleva el host.
log_levelinfoinfo o debug. Nada más.
log_file/var/log/reportedip-agent.logEl registro propio del agente, además de stderr, para que journald conserve todo lo que tiene hoy. Vacío desactiva el archivo. Solo la sincronización y el demonio de vigilancia escriben ahí.
log_max_mb10Tamaño al que el agente rota su propio archivo, comprobado en cada escritura. Rango de 1 a 1024.
log_keep3Generaciones rotadas que se conservan, rango de 0 a 20. Con los valores predeterminados el peor caso en disco son 40 MB. No hace falta ningún fragmento de logrotate, y tampoco estorba ninguno.
backendautoauto, ipset o nftables. Véase más arriba.
modeloglog, drop u off. Se aplica a las reglas, o sea a las listas del feed y a los bloqueos locales.
confidence90El umbral del feed, de 1 a 100. Más bajo significa una lista más larga y más casos límite. El servidor indica un mínimo para tu plan, 75 desde Professional y 90 en Contributor, y de los dos valores gana el más alto.
limit50000Número máximo de direcciones por petición de lista, de 1 a 50000.
listsssh, edgeQué listas del feed están activas. ssh y edge están siempre activas y no se pueden desactivar. lists.ssh.ports es la única opción por lista; las demás listas tienen juegos de puertos fijos.
portsdel proceso real que escuchaDentro de lists.ssh: los puertos a los que se aplica la regla ssh. El instalador los escribe a partir del proceso detectado. Una lista vacía hace que la regla se aplique a todos los puertos.
min_entriesssh 1000, mail 500, web 500, ftp 500, edge 1000El mínimo de verosimilitud por lista: por debajo de ese número de direcciones la lista no se cambia. Al menos 1, porque un mínimo de cero dejaría pasar a producción una lista vacía.
whitelist_file/etc/reportedip-agent/whitelist.confTu lista de «nunca bloquear, nunca notificar». Una dirección o un CIDR por línea, # inicia un comentario. Una línea rota se omite con una advertencia y nunca desactiva la protección.
sourcesuna entrada sshdLas fuentes de registro. Un archivo con una clave sources decide solo, así que una entrada tuya tiene que ir junto a las del instalador y no en su lugar.
typeningunoDentro de una fuente: uno de los trece tipos de fuente. Un tipo desconocido es un error que enumera los válidos.
pathningunoDentro de una fuente: un archivo de registro. Define path o glob, nunca ambos. Sin ninguno de los dos, el agente elige el canal él mismo, journald o el archivo predeterminado de la distribución.
globningunoDentro de una fuente: un patrón sobre varios archivos, reevaluado cada diez minutos. Como máximo cinco segmentos con comodín.
excludeningunoDentro de una fuente: patrones que retiran archivos que un patrón ha captado. Patrones de tipo shell, no expresiones regulares. Un patrón sin barra coincide solo con el nombre del archivo; uno con barra tiene que cubrir la ruta completa. Todo patrón que no coincida con nada se advierte al arrancar.
poll_minutes5Dentro de una fuente: solo para imunify360, que consulta un comando en vez de leer un archivo. Rango de 1 a 60. En cualquier otro tipo es un error.
thresholdsvacíoSustituye la tabla interna por fuente de evento. Véase la sección siguiente.
hits, window_minutesvéase la tabla de abajoDentro de una entrada de thresholds: el par que significa «tantas coincidencias en tantos minutos».
min_hits, window_minutes5, 10El par global para toda fuente sin entrada propia en la tabla interna.
web_min_hits, web_window_minutes50, 120El mismo par para los registros de acceso web, que necesitan volumen en lugar de un código de estado.
dedup_hours6La misma dirección se notifica como máximo una vez por ventana de esta duración.
queue_max5000Archivos de informe en espera. Por encima se descartan los más antiguos.
disk_min_mb200Mebibytes libres exigidos allí donde está el directorio de estado. Por debajo no se pone en cola ningún informe nuevo mientras la detección y el bloqueo continúan. 0 desactiva el límite.
jail_categoriestabla internaCorrespondencia adicional entre un nombre de jail de fail2ban e identificadores de Threat Categories, de 1 a 58. Solo relevante mientras fail2ban siga siendo una fuente.
bandesactivadoEl bloque de bloqueos locales, con enabled, time_minutes, max_time_minutes, escalate y memory_hours. Véase Bloqueo.
notifysin correoEl bloque de correo, con email, cooldown_hours y el subbloque smtp (host, port, from, user, password, starttls).

Un archivo completo, con cada bloque que usa un host normal:

yaml
# /etc/reportedip-agent/config.yaml   root:root 0600
api_key: "YOUR_API_KEY"
api_url: https://reportedip.com/wp-json/reportedip/v2

log_level: info              # info | debug
log_file: /var/log/reportedip-agent.log
log_max_mb: 10
log_keep: 3
auto_update: true

backend: auto                # auto | ipset | nftables
mode: log                    # log | drop | off
confidence: 90               # the feed threshold
limit: 50000

lists:                       # ssh and edge are always on
  ssh:
    ports: [22]              # written by install from the real listener
  edge: {}
  mail: {}
  web: {}

min_entries:                 # below this many addresses a list is not applied
  ssh: 1000
  mail: 500
  web: 500
  ftp: 500
  edge: 1000

whitelist_file: /etc/reportedip-agent/whitelist.conf

min_hits: 5                  # the global detection pair
window_minutes: 10
web_min_hits: 50             # web access logs use their own
web_window_minutes: 120
dedup_hours: 6
queue_max: 5000
disk_min_mb: 200

sources:                     # detected at install time
  - type: sshd
  - type: web
    glob: /var/www/*/log/access.log
  - type: web-error
    path: /var/log/nginx/error.log
  - type: postfix
    path: /var/log/mail.log
  - type: dovecot
    path: /var/log/mail.log

thresholds: {}               # per source overrides, see below

ban:                         # block local finds, not only the feed
  enabled: false
  time_minutes: 30
  max_time_minutes: 10080
  escalate: 4
  memory_hours: 24

notify:
  email: ""                  # empty means no mail
  cooldown_hours: 24

Límites de detección, por fuente

Un umbral es un par: cuántas coincidencias, dentro de cuántos minutos. Es la misma idea que maxretry y findtime de un jail de fail2ban, y los valores de abajo proceden de la configuración de jails del host en el que se midió todo esto. Desactivar fail2ban no cambia por tanto la sensibilidad de un servicio.

Tres niveles deciden qué par se aplica a una fuente de evento, y gana el primero que tenga respuesta: una entrada explícita en thresholds, luego la tabla interna, luego el par global.

Fuente de eventoUmbralProcedencia
sshd5 en 10 minmin_hits / window_minutes
web-error5 en 10 minmin_hits / window_minutes
modsec5 en 10 minmin_hits / window_minutes
exim5 en 10 minmin_hits / window_minutes
web50 en 120 minweb_min_hits / web_window_minutes
web-app20 en 60 mininterno
postfix-sasl3 en 60 mininterno, medido
postfix-reject5 en 60 mininterno, solo rechazos 5xx
postfix-amavis3 en 60 mininterno
dovecot10 en 60 mininterno, medido
ftp20 en 60 mininterno
named20 en 30 mininterno
panel5 en 60 mininterno
fail2ban, csf, imunify360sin umbralEsa fuente ya ha contado, así que una línea es un informe.

Para cambiar una fuente y nada más, nómbrala en thresholds. La clave es la fuente de evento de la tabla de arriba, no el tipo de fuente de sources, y un nombre que no figure en esa lista es un error en lugar de un ajuste que nunca se aplica. Los dos números tienen que ser al menos 1.

yaml
# A recursive resolver that sees a lot of refused queries:
# fewer minutes, more hits, and nothing else changes.
thresholds:
  named:
    hits: 40
    window_minutes: 5

# A mail server whose customers keep mistyping passwords:
# SASL a little more forgiving, Dovecot untouched.
thresholds:
  postfix-sasl:
    hits: 6
    window_minutes: 60

Un umbral más alto hace al agente más discreto y deja pasar a los atacantes lentos. Uno más bajo es la dirección que cuesta algo: por debajo de unas tres coincidencias por hora en una fuente de correo o web acabarás notificando a un cliente con una contraseña mal puesta en su programa de correo, y un informe es público dentro de la comunidad. El par web es alto justo por eso, porque un WordPress normal responde a una contraseña equivocada con un HTTP 200 y el detector no puede apoyarse en el código de estado.

Hay otra magnitud que no es configurable. Una dirección con historial de bloqueos necesita menos coincidencias que una desconocida, y esa es la misma ponderación que tenía el jail recidive: un bloqueo anterior cuenta una línea el doble, dos la cuentan el triple, luego cinco y nueve veces. El peso se recorta al umbral de su fuente de evento, así que un décimo bloqueo no convierte una sola línea en un informe.

Límites de bloqueo

Cuatro valores, todos en el bloque ban, todos en la unidad que nombra la clave. Solo se aplican a direcciones que el agente ha encontrado en tus propios registros. La lista comunitaria no tiene ningún tiempo de bloqueo, se reemplaza por completo en cada pasada.

ClavePredeterminadoSignificado, y qué pasa en los extremos
enabledfalseSi los hallazgos locales se bloquean en absoluto. false es un host que notifica y no bloquea. Un ban add manual funciona en ambos casos.
time_minutes30El primer bloqueo de una dirección. Al menos 1. Valores muy cortos hacen de la escalada lo único relevante; valores muy largos significan que un falso positivo se queda mucho tiempo en el conjunto.
max_time_minutes10080 (7 días)El techo de la escalada. No debe estar por debajo de time_minutes, o el techo acortaría el primer bloqueo, y el agente rechaza un archivo así. Bajarlo más tarde acorta también los bloqueos en curso: la sincronización siguiente los restaura recortados al techo de hoy.
escalate4El multiplicador por reincidencia dentro de la ventana de memoria. Al menos 1, y 1 significa que cada bloqueo dura time_minutes y no hay escalada alguna.
memory_hours24Cuánto tiempo cuenta un bloqueo para la escalada siguiente. Al menos 1. Un registro se conserva además el triple de la duración de su último bloqueo cuando eso es más largo, y eso es precisamente lo que hace alcanzable el techo sin que los bloqueos cortos hagan crecer el archivo.
yaml
# Careful: a short first ban, a low cap, a long memory.
ban:
  enabled: true
  time_minutes: 10
  max_time_minutes: 1440     # one day
  escalate: 3
  memory_hours: 72

# Strict: a long first ban and a hard escalation.
ban:
  enabled: true
  time_minutes: 60
  max_time_minutes: 43200    # thirty days
  escalate: 6
  memory_hours: 168          # a week

Límites de funcionamiento

Estos valores acotan lo que consume el propio agente. No son ajustes de detección, y los dos que de verdad querrás cambiar algún día son el tamaño de la cola y el límite de disco.

ClavePredeterminadoUnidadDemasiado bajoDemasiado alto
queue_max5000archivos en esperaSe pierden informes durante una caída de la API, los más antiguos primero.Una caída larga deja miles de archivos pequeños por enviar después.
dedup_hours6horasEl mismo atacante se notifica una y otra vez y se come tu cuota diaria.Una dirección que vuelva a atacar la semana que viene se notifica tarde.
disk_min_mb200MB libresEl agente puede contribuir a llenar /var, y eso se lleva al host entero.Los informes se detienen en un host perfectamente sano. 0 retira el límite.
log_max_mb10MBRotación en cada segunda escritura y un historial demasiado corto para investigar nada.Junto con log_keep es el peor caso en disco.
log_keep3generaciones0 no conserva ningún archivo rotado.Como máximo 20, y el coste en disco es el producto de los dos valores.
cooldown_hours24horasAl menos 1, y 1 significa un correo por sincronización para un estado que sigue abierto.Un problema de esta mañana se notifica mañana.
poll_minutes5minutosUn comando lanzado demasiado a menudo en un host cargado.Como máximo 60, y los hallazgos llegan más tarde en la misma medida.
limit50000direcciones por listaUna lista truncada: las peores direcciones están, el resto no.50000 es el máximo que sirve la API.
min_entries1000 / 500direccionesUna lista de brevedad inverosímil pasa a producción y el host está peor protegido de lo que cree.Una lista realmente pequeña se rechaza de forma permanente, y el conjunto se queda en su generación anterior.

Por debajo del límite de disco, el agente no pone en cola ningún informe nuevo, anota el estado y lo envía por correo, y sigue detectando y bloqueando. Ese orden tiene un motivo: un /var lleno es peor que un informe perdido, y proteger un servidor rompiéndolo no es protección.

Un correo cuando algo va mal

Un notify.email vacío es el valor predeterminado y significa que este host no envía nada. Lo que se notifica es un estado y no un evento, así que un feed que falla cada hora es un mensaje y no veinticuatro. El agente lleva ocho estados (feed, api_key, chain, disk, source, update, queue, license), recuerda cuándo se notificó cada uno por última vez para que un reinicio no empiece de nuevo, y envía un aviso de fin cuando uno se ha terminado.

yaml
# A host with a local MTA: postfix, exim or msmtp. The agent
# uses "sendmail -t -i" and no password lives in a file.
notify:
  email: "ops@example.org"
  cooldown_hours: 24

# A host without an MTA: SMTP, and starttls stays on.
notify:
  email: "ops@example.org"
  cooldown_hours: 12
  smtp:
    host: mail.example.org
    port: 587
    from: agent@example.org
    user: agent@example.org
    password: "..."
    starttls: true

Con starttls: true el correo se rechaza en lugar de enviarse en claro si el servidor no ofrece STARTTLS. Un correo lleva el nombre del estado, la versión, una frase genérica con números y el comando con el que mirar. Nunca una línea de registro, nunca una dirección, nunca una carga útil. Ejecuta reportedip-agent doctor después de esta configuración: la sección mail dice si un correo podría salir realmente del host, y esa es una pregunta distinta de si la configuración se lee bien.

Cuándo tiene efecto un cambio

El agente lee su configuración al arrancar y no vigila el archivo, y no existe ninguna señal de recarga. El comando necesario depende de la parte que hayas tocado.

CambiadoQué lo hace efectivo
mode, lists, min_entries, confidence, limit, backendreportedip-agent sync. La sincronización construye la cadena y las reglas, así que un modo nuevo está activo al final de ella.
thresholds, min_hits, window_minutes, web_min_hits, web_window_minutes, dedup_hours, ban, notify, log_*systemctl restart reportedip-agent.service. Eso es lo que lee el demonio de vigilancia.
Una fuente añadida a sourcessystemctl restart reportedip-agent.service.
Una fuente retirada de sourcesUna parada y un arranque, no un reinicio. En un host en producción un systemctl restart no bastó y el demonio siguió leyendo el archivo retirado: systemctl stop reportedip-agent.service && systemctl start reportedip-agent.service.
El contenido de whitelist_fileNada, si usas reportedip-agent whitelist add: eso escribe el archivo y el conjunto del núcleo en un paso y tiene efecto de inmediato. Editar el archivo a mano requiere un sync.
api_key, api_url, auto_update, update_urlNada para el siguiente sync, que es de tipo oneshot y vuelve a leer el archivo. Reinicia el servicio de vigilancia para que la vía de notificación también tome la clave.
bash
# The safe sequence after any edit
reportedip-agent status >/dev/null && echo "config parses"
reportedip-agent sync
systemctl restart reportedip-agent.service
systemctl status reportedip-agent.service --no-pager

El feed comunitario

Esta es la parte que la página Bloqueo a nivel de red describe como un script de shell. El agente hace lo mismo, con las partes que es fácil equivocar cuando uno lo escribe por su cuenta ya resueltas: una petición condicional por lista para que una lista sin cambios no cueste nada, una comprobación de tamaño antes de que algo pase a producción, un cambio atómico para que no haya nunca una ventana con un conjunto vacío, y reglas ligadas a puertos para que un atacante web no deje a nadie fuera de SSH.

La correspondencia de categorías es la misma que en esa página. Cuáles de las cinco listas están activas depende de lo que escuche realmente, y no hay una lista unificada, porque un conjunto por servicio es lo que hace posible la regla ligada a puertos.

ListaConjunto del núcleoPuertos a los que se aplica la regla
sshrip-ssh, rip-ssh-v6de lists.ssh.ports
mailrip-mail, rip-mail-v625, 465, 587, 110, 995, 143, 993
webrip-web, rip-web-v680, 443
ftprip-ftp, rip-ftp-v621
edgerip-edge, rip-edge-v6todos los puertos, a propósito

Con qué frecuencia se descarga el feed depende de tu plan, y lo decide el servidor, no el agente. En un servidor con licencia el intervalo es de 15 minutos con una confianza mínima de 75 desde Professional, y de una hora con una confianza mínima de 90 en Contributor. Business y Enterprise se comportan como Professional. El agente pregunta en cada pasada qué valores se aplican a este host y sigue la respuesta, así que una mejora de plan tiene efecto sin que nadie edite un archivo. No existe ninguna clave de configuración para el intervalo, y una clave que te inventes para eso detiene el agente con el código 2.

Encima de eso hay dos reglas más. Una lista descargada hace menos de 15 minutos no se vuelve a descargar, porque ese es el tiempo que el servidor la mantiene en caché. Ejecutar sync a mano está por tanto siempre permitido y nunca es perjudicial. Y cada descarga es condicional: una lista sin cambios responde 304 y cuesta una petición y ninguna transferencia, y por eso un intervalo corto es económico para ambas partes.

Qué encuentra el agente en tus registros

Cada fuente tiene su propio detector y su propio umbral, porque cinco inicios de sesión SSH fallidos en diez minutos y cincuenta peticiones web sospechosas en dos horas son la misma afirmación sobre un atacante y no el mismo número. La rotación, copytruncate y un registro que desaparece un rato están cubiertos, y un archivo ilegible produce una advertencia y no una por lectura.

Los trece tipos de fuente

TipoRuta típicaQué cuenta como coincidencia
sshdjournald, si no /var/log/auth.log o /var/log/secureContraseñas equivocadas, usuarios inválidos, claves rechazadas para un usuario que no existe, límites de intentos superados, y los fallos previos a la autenticación que caracterizan a los escáneres: ninguna cadena de identificación, una versión de protocolo errónea, datos inservibles en el intercambio de banner. Una clave rechazada para un usuario válido no cuenta a propósito, porque un administrador con cinco claves escribe cuatro de esas por cada inicio de sesión correcto. Un inicio de sesión correcto borra los fallos de esa dirección.
web/var/log/nginx/access.log o un patrón por sitioLos POST de inicio de sesión, contados sea cual sea el código de estado, y las rutas que ningún cliente legítimo pide. Todo lo demás de un registro de acceso se ignora, porque esta es la fuente que tiene clientes de verdad detrás.
web-error/var/log/nginx/error.log, /var/log/apache2/error.logLas peticiones que el propio servidor web ha rechazado, los fallos de autenticación HTTP básica, y las líneas de ModSecurity de gravedad crítica cuando caen aquí en lugar de en un registro de auditoría. Las líneas de limitación de tasa no cuentan a propósito: un límite se dispara también con un navegador real con veinte pestañas. Los mensajes de negociación TLS y de PHP dicen algo del servidor y nada del cliente.
web-applos mismos registros de accesoSe ejecuta después de web sobre las mismas líneas y cubre los ataques a nivel de aplicación: enumeración de usuarios de WordPress, sondeo de plugins y del núcleo, rutas de inicio de sesión y de explotación de Drupal. Un POST de inicio de sesión se cuenta una vez, por web.
postfix/var/log/mail.log, /var/log/maillogTres fuentes de evento distintas a partir de un solo archivo: los fallos de autenticación SASL, los rechazos NOQUEUE que son realmente 5xx (un 450 es greylisting y no cuenta), y los bloqueos de amavis. Un correo entregado nunca es una coincidencia.
dovecotel mismo registro de correoFallos de inicio de sesión IMAP y POP3, uno por línea, diga lo que diga «N attempts». La dirección remota viene del campo rip=, nunca de lip=, que es la dirección propia del servidor. Un inicio de sesión abortado sin intento de autenticación no es una coincidencia: eso es un escaneo TLS, y no se probó ninguna contraseña.
exim/var/log/exim4/mainlog, /var/log/exim/main.logFallos de autenticación y remitentes rechazados. No verificado contra un host real: los patrones vienen del formato de registro documentado.
ftp/var/log/syslog, /var/log/messagesFallos de autenticación de pure-ftpd, proftpd y vsftpd. Los tres registran a través de syslog, y cada uno escribe el par de forma distinta, así que se usan tres patrones. pure-ftpd está medido: 494 de 494 líneas de un host de producción tenían una única forma.
namedel mismo syslogSolo una consulta entrante que bind ha rechazado. La gran trampa es el sentido inverso: connection refused resolving es el resolvedor de este host que no alcanza el servidor de nombres de otra persona, y el 90 por ciento de las líneas medidas eran eso. Un detector sin esa excepción notifica los servidores de nombres ajenos como atacantes.
panel/var/log/ispconfig/auth.logInicios de sesión fallidos en el panel de ISPConfig. El archivo tiene 0 bytes en todos los hosts medidos, y eso no es un defecto: el panel simplemente no escribe nada ahí. doctor lo señala al cabo de una semana.
modsec/var/log/apache2/modsec_audit.logUn evento por transacción cuyo veredicto lleva gravedad crítica. Es el único detector con estado, porque una transacción abarca varias líneas. Nada de la petición viaja en el evento: ni el identificador de regla, ni la parte coincidente, ni la URI.
csf/var/log/lfd.logLo que CSF ha bloqueado realmente, nunca lo que solo ha detectado. Sin un segundo umbral por encima: lfd ya había contado antes de que el agente viera la línea, así que un bloqueo es un informe.
fail2ban/var/log/fail2ban.log, si no el diario de la unidadSolo acciones de bloqueo. Un Restore Ban nunca se notifica, porque eso es lo que un reinicio de fail2ban reproduce desde su propia base de datos y no un ataque nuevo. Una línea Found de un filtro tampoco se notifica: el jail aún no ha decidido.
imunify360consulta imunify360-agentIncidentes del CLI, asociados a categorías por el prefijo del nombre de regla. Experimental y no verificado contra una instalación con licencia; el decodificador omite lo que no puede leer en vez de convertir una respuesta inesperada en una fuente muerta.

Añadir una fuente que el instalador no encontró

Una fuente que el instalador no encontró simplemente falta en la configuración, y añadirla es una entrada con una ruta o un patrón. Como un archivo con un bloque sources sustituye por completo la lista predeterminada, tu entrada va junto a las existentes y no en un segundo bloque.

yaml
sources:
  - type: sshd
  - type: web
    path: /var/log/nginx/access.log

  # A second web server on a different path
  - type: web
    path: /var/log/caddy/access.log

  # Every site of a panel host, rescanned every ten minutes.
  # At most five wildcard segments.
  - type: web
    glob: /var/www/clients/*/web*/log/access.log
    exclude:
      # This site reports to the API on its own, through Hive or a
      # honeypot. Without the exclude the host sends every address
      # twice and pays twice out of the daily quota.
      - /var/log/ispconfig/httpd/honeypot.example.com/*

  # Exim on a host the installer saw as a Postfix machine
  - type: exim
    path: /var/log/exim4/mainlog

Dos rutas que apuntan al mismo archivo no son un problema y no necesitan exclude: ISPConfig publica cada registro de acceso dos veces, en /var/www y en /var/log/ispconfig, y el agente lee un archivo así una sola vez. Antes de confiar en una entrada nueva, apunta reportedip-agent test al archivo, reinicia después el servicio de vigilancia y mira en status: la sección sources muestra las coincidencias por fuente, y una fuente con files= a cero no se resuelve en nada.

Qué sale de la máquina

Un informe contiene la dirección, los identificadores de las Threat Categories y una frase generada. Eso es todo. Ninguna línea de registro, ningún nombre de usuario, ningún cuerpo de petición, ningún User-Agent y ninguna URL se transmite nunca, así que un informe no puede filtrar tus clientes, tus rutas ni tus credenciales, ni siquiera por accidente.

En la versión actual el agente no envía ningún nombre de host. Un servidor se identifica ante la API con un UUID aleatorio generado durante la instalación, y esa es la identidad que ves en tu cuenta. Le pones una etiqueta allí si quieres reconocerlo; la etiqueta se queda en la cuenta y nunca se deriva de la máquina.

Cuatro cosas no se notifican nunca y no se bloquean nunca, y eso es una frontera en el código y no un ajuste: los rangos privados y reservados, el bucle local, cada dirección de las interfaces de este servidor junto con su puerta de enlace predeterminada, y todo lo que esté en tu lista blanca. La dirección del cliente SSH tomada en la instalación está en la lista blanca automática y pertenece por tanto al último grupo.

Bloqueo

El agente escribe dos cosas muy distintas en el núcleo, y quien viene de fail2ban suele confundirlas la primera semana. Tienen orígenes distintos, duraciones distintas e interruptores distintos, así que merece la pena separarlas una vez y bien.

Dos clases de conjunto, dos duraciones

Community BlacklistHallazgos locales
Conjuntosrip-ssh, rip-mail, rip-web, rip-ftp, rip-edge, y un conjunto -v6 para cada unorip-local y rip-local-v6
De dónde vienen las direccionesDe cada informante de la comunidad, filtradas por tu confidenceSolo de los registros de este host
Cómo entra una entradaEl conjunto entero se reemplaza en cada pasada del feedUna dirección a la vez, cuando se alcanza un umbral
Cómo desaparece una entradaEn el cambio siguiente, cuando el feed ya no la incluyeEl núcleo la hace caducar cuando se agota su propio plazo
Plazo por entradaningunosí, es todo el diseño
Interruptormodeban.enabled, y mode por encima
Necesita licenciasí, es la parte que se pagano

El cambio del feed no toca nunca rip-local. Los conjuntos del feed se reemplazan completos y no llevan plazo; los bloqueos locales llegan de uno en uno y caducan por sí solos. Esa separación explica por qué una caída del feed no puede levantar tus bloqueos locales, y por qué un bloqueo local no sobrevive como huérfano después de reconstruir el conjunto del feed.

Cómo empieza un bloqueo local y cómo termina

Un bloqueo empieza cuando el contador de un detector para una dirección supera el umbral de su fuente de evento. Lo que pasa entonces, por orden:

  1. La lista blanca se comprueba primero, antes incluso de preguntar cualquier otra cosa. Una dirección en la lista blanca no deja ningún rastro: nunca encontrarás tu propio rango de administración en reportedip-agent ban list preguntándote si estuvo bloqueado. La comprobación indica qué capa respondió: un rango reservado interno, una de las direcciones propias de este host, o tu archivo.
  2. Se consulta al registro de bloqueos, /var/lib/reportedip-agent/bans.json, el tiempo de bloqueo. Guarda la dirección, cuándo caduca el bloqueo, cuántos bloqueos cayeron en la ventana de memoria, cuándo empezó el último, la fuente y las categorías. El archivo se escribe antes de la entrada en el núcleo, así que un fallo justo después de escribir en el núcleo no puede perder el registro que hace caducar el bloqueo.
  3. La dirección entra en rip-local con ese tiempo como plazo por entrada. El núcleo la hace caducar. No hay temporizador de limpieza, ni tarea de cron, ni cola de desbloqueo, y por eso un agente caído o eliminado no deja ningún bloqueo permanente.
  4. Se escribe una línea en el registro con la dirección, el tiempo de bloqueo, la fuente, cuántos bloqueos tiene esa dirección en la ventana, el desfase entre la línea de registro y la entrada en el núcleo, y los dos comandos que lo deshacen.

Un bloqueo en curso no se acorta nunca. Cuando llega una coincidencia nueva para una dirección que ya está bloqueada, no pasa nada: tampoco es una escalada, porque la escalada cuenta bloqueos y no coincidencias, exactamente como el jail recidive al que sustituye. Una dirección que ya está bloqueada no puede bloquearse otra vez. Esa regla también mantiene honesto el periodo de transición, cuando el agente ve un ataque dos veces, una como línea cruda de auth.log y otra como línea de bloqueo de fail2ban.log.

Una línea de registro reproducida tampoco produce un bloqueo. Una coincidencia cuya marca de tiempo no sea más reciente que el último bloqueo de esa dirección es una relectura tras una parada brusca y no una nueva infracción, y la vía de notificación tiene la misma protección en su archivo de deduplicación.

El estado sobrevive a más que un reinicio. Cada sincronización compara bans.json con lo que el núcleo tiene realmente y vuelve a poner lo que falta, con el tiempo restante:

  • Tras un reinicio de la máquina son todos, porque ni un plazo de ipset ni el propio conjunto sobreviven a un reinicio. Por eso el servicio de sincronización está activado para multi-user.target y se ejecuta después de cada servicio de cortafuegos.
  • En funcionamiento normal es lo que se llevó el conjunto: un firewall-cmd --reload, un csf -r, un ipset flush. Las entradas que el núcleo todavía tiene se dejan intactas, así que el hueco es una parte de un intervalo de sincronización y no el resto del bloqueo. El registro dice de forma explícita cuándo faltaban entradas bajo bloqueos activos, porque eso significa que algo ha modificado el conjunto por debajo del agente.
  • Un registro caducado no vuelve nunca. El atacante de ayer no es motivo para cortar a alguien hoy.
  • La lista blanca se vuelve a comprobar en cada restauración. Puede haber crecido mientras el host estaba apagado, y una dirección que pusiste ayer en la lista blanca no debe volver a bloquearse por un registro del día anterior.

La escalada, con números reales

El primer bloqueo dura time_minutes. Cada reincidencia dentro de la ventana de memoria lo multiplica por escalate, y max_time_minutes lo limita. Con los valores predeterminados (30 minutos, multiplicador 4, techo de una semana), una dirección que vuelve una y otra vez sigue este camino:

BloqueoCálculoDuración
1.ºtime_minutes30 minutos
2.º30 × 42 horas
3.º120 × 48 horas
4.º480 × 432 horas
5.º1920 × 4128 horas, cinco días y ocho horas
6.º y siguientesserían 512 horas, limitado7 días, el valor de max_time_minutes

El cálculo concreto. Un atacante llama a tu puerto SSH el lunes a las 09:00 y alcanza cinco fallos en diez minutos: a las 09:07 recibe por tanto un bloqueo de 30 minutos que caduca a las 09:37. Vuelve a las 11:00 y se le bloquea de nuevo, esta vez dos horas, porque el primer bloqueo sigue dentro de la memoria de 24 horas. El tercer intento a las 14:00 cuesta ocho horas y lo lleva hasta las 22:00. El cuarto, a primera hora del martes, cuesta 32 horas. A partir de ahí el registro se conserva el triple de la duración del último bloqueo en lugar de las 24 horas redondas, y eso es precisamente lo que hace alcanzable el quinto escalón: un bloqueo largo vale una memoria larga, un bloqueo de 30 minutos solo el día configurado, y el archivo de bloqueos no crece por tanto a causa de bloqueos cortos.

Dos ajustes cambian la forma de esa curva. escalate: 1 desactiva la escalada por completo y da la misma duración a cada bloqueo. Un memory_hours corto en relación con time_minutes significa que una dirección vuelve a ser primeriza tras un día de pausa, y eso es a veces justo lo adecuado para un servidor de correo con clientes de verdad.

Bajar max_time_minutes se aplica también a los bloqueos que ya están en curso. La sincronización siguiente los restaura recortados al techo de hoy, porque quien baja el techo tras un incidente espera que los bloqueos en curso lo sigan en vez de quedarse una semana en el núcleo.

La cadena en el núcleo

Con el motor ipset, el agente posee exactamente una cadena, rip-blacklist, y un salto hacia ella. La cadena se vacía y se reconstruye en cada sincronización, así que su contenido es siempre lo que dice la configuración, y el salto solo se inserta en la posición 1 de INPUT si no está ya.

bash
# What a sync builds, in this order:
iptables -N rip-blacklist
iptables -F rip-blacklist
iptables -A rip-blacklist -m set --match-set rip-whitelist src -j RETURN
iptables -A rip-blacklist -m set --match-set rip-local src -j DROP
iptables -A rip-blacklist -p tcp --dport 22 \
         -m set --match-set rip-ssh src -j DROP
iptables -A rip-blacklist -p tcp -m multiport --dports 25,465,587,110,995,143,993 \
         -m set --match-set rip-mail src -j DROP
iptables -A rip-blacklist -p tcp -m multiport --dports 80,443 \
         -m set --match-set rip-web src -j DROP
iptables -A rip-blacklist -m set --match-set rip-edge src -j DROP
iptables -I INPUT 1 -j rip-blacklist

La lista blanca es la primera regla y retorna, no es la última regla. Un RETURN por delante de todo lo demás significa que una dirección de la lista blanca sale de la cadena antes de que se consulte ningún conjunto, así que está por encima de cada bloqueo, incluido uno que ya esté en rip-local. Eso es lo que convierte a whitelist add en una salida real de un cierre accidental, sin sincronización y sin reinicio. Una lista blanca colocada al final sería una lista de excepciones que llegan demasiado tarde, y en un host en el que te has quedado fuera, «demasiado tarde» es todo el problema.

La regla de los bloqueos locales no está ligada a ningún puerto, porque un host que ha atacado tu servidor de correo tampoco tiene nada que hacer en el puerto 22. Las cinco reglas del feed están ligadas a puertos: una dirección de la lista web se descarta por tanto en 80 y 443 y sigue alcanzando SSH. Es intencionado: una dirección compartida, un NAT de operador o un proxy comprometido en la lista web no deben dejar a un administrador fuera de la máquina. Solo edge se aplica a todos los puertos, y eso es lo que significa la categoría que hay detrás.

Con el motor nftables la forma es la misma en versión nativa: una tabla inet reportedip, una cadena colgada de input con prioridad -10 y política accept, primero las dos reglas return de la lista blanca, luego rip-local, luego las listas del feed con sus puertos. Los conjuntos de la lista blanca son conjuntos de intervalos para poder contener rangos CIDR; los conjuntos locales llevan la opción de plazo, que las dos herramientas exigen en el conjunto antes de que un elemento pueda llevar un plazo.

En mode: log se construyen las mismas reglas, y el veredicto es una línea de registro con tasa limitada (seis por minuto, con el prefijo rip-<lista>:) en lugar de DROP. Todo lo demás es idéntico, y por eso el modo de registro es un ensayo de verdad y no una simulación.

mode y ban.enabled son dos interruptores

Este es el malentendido más frecuente, así que tiene su propio párrafo. mode decide qué hacen las reglas. ban.enabled decide si tus propios hallazgos llegan a ser alguna vez una entrada. Quien solo pone mode: drop aplica la lista comunitaria y no obtiene nada de sus propios hallazgos.

modeban.enabledLista comunitariaTus propios hallazgos
logfalseSe aplica y registra, no se descarta nadaNotificados a la comunidad, no bloqueados localmente
logtrueSe aplica y registraAnotados en rip-local y visibles en ban list, registrados en vez de descartados
dropfalseSe descartaSolo notificados. Es un estado permanente perfectamente razonable.
droptrueSe descartaTambién se descartan, con escalada. La configuración completa.
offcualquieraNada en la vía de paquetes: el salto se retira y la cadena se queda con sus reglasAnotados en el registro, sin efecto

off es el interruptor de emergencia y es honesto al respecto. Retira lo que cuelga la cadena de la vía de paquetes, hasta cinco veces para atrapar saltos duplicados, y se detiene con estruendo si un salto sobrevive, porque decir «off» y seguir descartando es peor que un error. Los conjuntos conservan sus entradas, así que volver atrás no requiere ninguna descarga completa.

Un bloqueo manual ignora los dos interruptores a propósito: reportedip-agent ban add funciona incluso con ban.enabled: false, porque es un acto deliberado de un operador, igual que lo era fail2ban-client set banip. La lista blanca sigue aplicándose, y en un host sin motor de cortafuegos el comando se niega.

Pasar de log a drop

bash
# 1. What would have been dropped? The rules already match in
#    log mode, so ask the kernel log.
journalctl -k --since "24 hours ago" | grep -c "rip-"
journalctl -k --since "24 hours ago" | grep -o "rip-[a-z]*:" | sort | uniq -c

# 2. Is anything of yours in there? Check every source address
#    against your whitelist before you switch.
reportedip-agent whitelist list

# 3. Switch, and rebuild the rules.
sed -i "s/^mode: log/mode: drop/" /etc/reportedip-agent/config.yaml
reportedip-agent sync
reportedip-agent status

# 4. Only now the second switch, if you want it.
#    ban.enabled: true, then restart the daemon.
systemctl restart reportedip-agent.service
reportedip-agent ban list

Si el paso 1 devuelve un número de cuatro cifras, es normal en un host expuesto y es la lista comunitaria haciendo su trabajo. Lo que importa no es la cantidad, sino si alguna dirección de ese registro te pertenece. Dos días en modo de registro bastan para averiguarlo, y la respuesta es aquí mucho más barata que después del cambio.

Mirar qué hay realmente en el núcleo

reportedip-agent status es el resumen, y lee tanto los archivos de estado como el núcleo en marcha para que se puedan comparar los dos. Donde se contradicen, el núcleo es la verdad, y status lo dice: un registro sin entrada en el núcleo es un bloqueo que no tiene efecto.

bash
# The agent's own view
reportedip-agent status
reportedip-agent ban list

# ipset backend, straight from the kernel
ipset list -t rip-ssh            # header and entry count only
ipset list rip-local             # entries, each with its timeout
iptables -S rip-blacklist
iptables -L INPUT -n --line-numbers | head

# nftables backend
nft list table inet reportedip
nft list set inet reportedip rip-local

En ban list, la columna kernel es lo que el núcleo todavía tiene en el contador y record lo que espera bans.json. Un guion bajo kernel junto a un registro activo significa que el bloqueo no tiene efecto y que la sincronización siguiente lo restaurará. Una fila cuyo registro dice none existe solo en el núcleo: alguien usó ipset o nft a mano, o se perdió el registro. Caducará igualmente, pero ningún reinicio la traerá de vuelta.

Levantar un bloqueo, y desmontarlo todo

La salida de un cierre accidental es un solo comando. La regla de la lista blanca va delante de la regla de bloqueo, así que reportedip-agent whitelist add <dirección> actúa de inmediato y está por encima de cada bloqueo, incluido uno que ya esté en el conjunto. Escribe el archivo y el conjunto del núcleo en un paso y no necesita ni sincronización ni reinicio.
bash
# Lift one ban and forget its history. The next detection starts
# from a first offence rather than escalating on top of a verdict
# you disagreed with.
reportedip-agent unban 203.0.113.45

# Never ban and never report this address or range, from now on.
reportedip-agent whitelist add 203.0.113.0/24 "customer office"

# Ban by hand. Works even with ban.enabled: false. A time given
# here is the time, not the first step of an escalation.
reportedip-agent ban add 203.0.113.45
reportedip-agent ban add 203.0.113.45 1440

# Stop blocking without uninstalling: no jump, chain and sets kept.
sed -i "s/^mode: .*/mode: off/" /etc/reportedip-agent/config.yaml
reportedip-agent sync

# Remove the agent from the packet path completely (ipset backend).
iptables  -D INPUT -j rip-blacklist; iptables  -F rip-blacklist; iptables  -X rip-blacklist
ip6tables -D INPUT -j rip-blacklist; ip6tables -F rip-blacklist; ip6tables -X rip-blacklist
for s in $(ipset list -n | grep "^rip-"); do ipset destroy "$s"; done

# nftables backend
nft delete table inet reportedip

unban y whitelist add toman ambos el bloqueo de sincronización, así que el paso de restauración de una sincronización en curso no puede volver a poner lo que estás levantando. Retirar una entrada del archivo de la lista blanca es la única operación que no es inmediata: tiene efecto con la reconstrucción de la sincronización siguiente, y retirar una excepción nunca necesita ser instantáneo.

No dejes nunca que un juego de reglas guardado haga referencia a un conjunto rip-. iptables-restore descarta todo el archivo en cuanto topa con un conjunto desconocido, así que una línea olvidada en /etc/iptables/rules.v4 puede quitarte todo el cortafuegos en el siguiente arranque. El agente reconstruye su cadena en cada arranque y no necesita nada guardado. status advierte cuando encuentra rip- en rules.v4, rules.v6, /etc/sysconfig/iptables, ip6tables, /etc/nftables.conf o /etc/nftables.d.

Comandos

ComandoEfectoSalida
install [--key K] [--admin-ip IP] [--ssh-port N]Detecta los servicios, escribe la configuración, la lista blanca automática y el identificador de instalación, instala y activa las unidades y verifica la clave. Nunca sobrescribe una configuración existente. Solo como root.0, 1 si la clave se rechaza, 2 si hay un problema de configuración
syncUna pasada del feed: reconstruir el conjunto de la lista blanca, descargar cada lista de forma condicional, cambiar lo que ha pasado la comprobación de tamaño, reconstruir la cadena, restaurar los bloqueos locales que falten, hacer la limpieza, buscar una actualización cada seis horas. Es lo que ejecuta el temporizador, y lo que se ejecuta al arrancar.0, 1 degradado, también 0 si otra sincronización tiene el bloqueo
watchEl demonio. Sigue cada fuente, cuenta, notifica y bloquea si eso está activado. Termina con SIGTERM.1 si el bloqueo de la cola sigue retenido tras un minuto, 2 si hay un problema de configuración
report-queueEnvía una vez los informes en espera. Útil tras una caída de la API, o en un host donde el demonio no se ejecuta.0, 1 degradado
statusVersión, motor, modo, herramientas, cantidad y último éxito por lista, lista blanca, comprobación de la cadena, posiciones de lectura y coincidencias por fuente, cola y emisor, cuenta y licencia, disco, bloqueos locales, estados abiertos.0 sano, 1 degradado, 2 configuración
doctorLo que ofrece el host y lo que falta. No cambia nada, funciona sin configuración.0, 1 con limitaciones, 2 imposible
test <archivo>... [--type T] [--lines]Hace pasar los detectores por archivos de registro reales. No notifica nada, no bloquea nada, no escribe nada.0, 2 si hay un problema de archivo o de tipo
ban list | add <ip> [minutos] | rm <ip>Muestra los bloqueos locales frente a lo que el núcleo tiene realmente, pone uno a mano, levanta uno.0, 1 si falla el núcleo, 2 si un argumento es erróneo
unban <ip>Lo mismo que ban rm, bajo la palabra que teclea un operador cuando hay un incendio.como arriba
whitelist add <ip|cidr> [comentario] | list | rm <ip|cidr> [--auto]Gestiona la lista de «nunca bloquear, nunca notificar». add escribe el archivo y el conjunto del núcleo de una vez. list muestra tu archivo y la lista blanca automática. --auto retira una entrada escrita por el instalador.0, 1 si el archivo cambió y el conjunto no, 2 si la dirección es errónea
update [--check]Comprueba el punto de distribución, sustituye este binario y después reinicia el servicio de vigilancia. --check solo informa. Es el único comando que lee la configuración de forma laxa, así que funciona también en un host cuya configuración es más reciente que su binario.0, 1 si la comprobación o la instalación falló
housekeepingRetira los restos propios del agente y muestra las cifras. La sincronización hace lo mismo en silencio.0, 2 si hay un problema de configuración o de estado
versionMuestra la versión y nada más.0
helpLa lista de comandos, con las rutas de configuración y de estado.0

test, en lugar de fail2ban-regex

test hace pasar la cadena de detectores por un archivo de registro que ya tienes, con los umbrales y la lista blanca de este host, y muestra qué direcciones habrían superado un umbral y en qué punto del archivo. No notifica nada, no bloquea nada y no escribe nada, así que es seguro en un host de producción y es la respuesta honesta a la pregunta de si esto habría cazado el ataque de la semana pasada.

bash
# A configured source: the type is taken from the config
reportedip-agent test /var/log/auth.log

# Any other file: name the type yourself
reportedip-agent test --type postfix /var/log/mail.log.1

# A rotated log and its successor as one stream, in this order,
# so a window that spans the rotation counts like the daemon saw it.
# A .gz is read inflated.
reportedip-agent test /var/log/mail.log.2.gz /var/log/mail.log.1 /var/log/mail.log

# Every single hit, with line number, address and timestamp
reportedip-agent test --lines /var/log/auth.log

La línea de resumen indica las líneas leídas, cuántas coincidieron, cuántas no llevaban una marca de tiempo utilizable y cuántas se omitieron por ser demasiado largas. Después, una fila por dirección con sus coincidencias, sus reinicios, si habría sido bloqueada y cuántas veces, la hora de la primera coincidencia y el umbral aplicado. Una dirección que esté en tu lista blanca se muestra con la capa que respondió y marcada como nunca bloqueada y nunca notificada, lo que hace de este comando la vía más rápida para demostrar que una entrada de la lista blanca funciona de verdad.

Dos cosas que test no hace. No adivina ningún formato: sin --type el archivo tiene que ser una de las rutas de fuente configuradas, y si no lo dice y enumera los tipos válidos. Y no aplica la ponderación del historial de bloqueos, así que el demonio necesita menos coincidencias para una dirección con bloqueos anteriores de las que sugiere esta relectura.

Funcionamiento

  • Un temporizador al ritmo de tu plan, con dispersión. reportedip-agent-sync.timer se dispara unos minutos después de su activación y después de forma repetida en el intervalo que trae tu plan, con un desplazamiento aleatorio pero fijo, para que una flota no alcance la API en el mismo segundo. Qué intervalo es eso lo dice el servidor, véase El feed comunitario. La vía de arranque está separada: reportedip-agent-sync.service está activado para multi-user.target y se ejecuta después de network-online.target y después de cada servicio de cortafuegos, sin desplazamiento aleatorio, porque un host no debe quedarse un cuarto de hora sin protección tras un reinicio.
  • Un servicio de vigilancia que se queda parado cuando debe. reportedip-agent.service se reinicia tras un fallo al cabo de diez segundos, como máximo cinco veces en cinco minutos, y nunca con el código de salida 2. Un host mal configurado se queda parado con un motivo legible en vez de reiniciarse sin fin. Las dos unidades se ejecutan con NoNewPrivileges, ProtectHome, PrivateTmp, una personalidad bloqueada y un conjunto restringido de familias de direcciones.
  • Su propio registro, su propia rotación. El agente escribe en stderr, que journald recoge, y en su propio archivo cuando log_file está definido. Rota ese archivo él mismo en log_max_mb y conserva log_keep generaciones. No depende de logrotate y no llena el diario.
  • Un correo cuando algo va mal, otro cuando está arreglado. Por estado y no por evento, con un tiempo de espera, así que un feed roto produce un mensaje y no uno por hora.
  • Un mínimo de disco. Por debajo de disk_min_mb el agente no pone en cola informes nuevos y lo dice, mientras la detección y el bloqueo continúan.
  • housekeeping. Archivos de cola antiguos, entradas de deduplicación caducadas, posiciones de lectura antiguas de archivos que ya no existen, archivos temporales y de bloqueo sobrantes, registros de bloqueo caducados que ya no alimentan ninguna escalada, y el binario anterior en cuanto cumple un mes. Se ejecuta en cada sincronización, y a mano cuando quieres ver las cifras.
  • Actualización automática cada seis horas. Una versión nueva se verifica antes de instalarse contra una firma Ed25519 con la clave pública compilada en el binario, así que una descarga manipulada falla en tu máquina en vez de darse por fiable porque llegó por HTTPS. El binario anterior se conserva para una vuelta atrás y lo retira la limpieza después de treinta días.
bash
systemctl list-timers reportedip-agent-sync.timer
systemctl status reportedip-agent.service --no-pager
journalctl -u reportedip-agent.service -n 50 --no-pager
journalctl -u reportedip-agent-sync.service -n 50 --no-pager
tail -n 100 /var/log/reportedip-agent.log

Códigos de salida

CódigoSignificadoQué hacer
0SanoNada.
1Degradado, pero volverá a intentarloLeer la salida. Un nuevo intento puede arreglarlo perfectamente. Un estado de error abierto en el registro de salud tiñe el código de salida incluso si la ejecución en sí salió bien, porque un código 0 junto a un error anotado no es una señal para una monitorización.
2No repetible sin una personaLa configuración, los permisos de archivo, una herramienta ausente, un bloqueo retenido por otro proceso. Ejecutar doctor. systemd no reinicia el servicio de vigilancia con un 2.

Con eso, reportedip-agent status se puede usar directamente como comprobación de monitorización, sin script envoltorio y sin analizar la salida.

Actualizaciones y notas de versión

El agente busca una versión nueva cada seis horas, durante la sincronización. Verifica la descarga contra una firma Ed25519 con la clave pública integrada en el binario, conserva la anterior para una vuelta atrás y reinicia él mismo el servicio de vigilancia. reportedip-agent update hace lo mismo a petición, y update --check solo informa de lo que está publicado.

Volver a ejecutar el comando de instalación también actualiza un host, y es la vía a seguir cuando una versión eleva la versión mínima: el agente rechaza entonces la actualización automática y lo dice, porque necesita a una persona. Las vueltas a una versión anterior se rechazan en general, así que un host que ha descargado una versión más reciente no retrocede por sí solo.

Un caso de actualización conviene conocerlo de antemano. El analizador de configuración es estricto con las claves desconocidas, así que un binario más antiguo no puede leer una configuración que ya lleva un bloque más reciente. Para un cliente el orden es inocuo, porque el agente se actualiza primero y una clave nueva solo aparece cuando alguien la añade. Si aun así acabas con un binario más antiguo que su configuración, update es la salida: es el único comando que lee el archivo de forma laxa, y nombra las claves que no conoce.

Lo que ha cambiado la versión actual es público y no necesita ninguna clave:

bash
curl -s https://reportedip.com/agent/whats-new
reportedip-agent update --check

Los cambios en la propia REST API, o sea los campos de respuesta y los códigos de estado que afectan a una integración, están en cambio en el Changelog de la API.

Licencias de servidor

El agente se licencia por servidor. Los servidores forman su propia reserva y se cuentan por separado de los dominios del plugin Hive, así que instalar el agente nunca te cuesta un dominio.

PlanServidores incluidosMás servidores
FreeningunoNo contratable
ContributorningunoNo contratable
Professional1Tantos como quieras
Business3Tantos como quieras
Enterprisesegún contratosegún contrato

Una licencia adicional cuesta 4,90 € al mes o 49 € al año por servidor, IVA incluido, y se abarata por servidor a medida que sube la cantidad: 4,90 € para los cuatro primeros, 3,90 € desde el quinto, 2,90 € desde el décimo, 1,90 € desde el vigesimoquinto y 1,40 € desde el quincuagésimo. El multiplicador de volumen de Business aumenta tu número de dominios y no tus servidores incluidos, porque los servidores son la parte que se paga aparte.

La cantidad la regulas tú mismo en Agent Servers, dentro de tu cuenta. Un host aparece ahí en cuanto su agente llama a la API por primera vez, con su identificador de instalación, su versión y cuándo se le oyó por última vez. Si la cuenta tiene una licencia libre, el host la toma de inmediato. Si no tiene ninguna, la fila lo dice y un botón añade una. No hay nada que registrar por adelantado ni nada que copiar en un archivo: el host ya tiene una clave API de tu cuenta, y eso es una prueba más fuerte que cualquier archivo de verificación.

La identidad es el identificador de instalación, nunca el nombre del host y nunca la clave. Cambiar la clave API en un host no crea una segunda licencia, y renombrar la máquina no cambia nada. Si tienes más agentes que licencias, los hosts más antiguos conservan la suya. Una licencia de servidor cancelada sigue funcionando siete días.

Qué pasa sin licencia

Solo una cosa se detiene: el feed ya no se descarga. Todo lo demás sigue, y eso es intencionado. Un servidor no debe quedarse sin protección porque se haya quedado atrás una factura, y un host sin licencia no es un host indefenso. Simplemente ya no recibe la lista comunitaria.

FunciónSin licencia
Descarga del feedSe detiene. Es la parte que se paga.
La lista que ya está en el núcleoSe queda. Nunca se vacía y sigue bloqueando.
Detección localSigue. Las trece fuentes, todos los umbrales.
Bloqueo localSigue, escalada incluida, restauración tras un reinicio incluida.
InformesSiguen, dentro del límite diario de tu plan.
Lista blanca, cadena, limpieza, rotación de registrosSiguen.
Actualización automáticaSigue. Un agente obsoleto en un host sin pagar es nuestro riesgo, no nuestra palanca.
statusMuestra el estado de licencia, el motivo, la edad de la lista y qué hacer al respecto.

El servicio no se detiene, los conjuntos no se vacían, los bloqueos locales no se levantan, y no hay publicidad en el registro. Una cuenta Free puede por tanto usar el agente como simple informante con protección local completa, y eso es un uso legítimo.

Sustituir fail2ban

El agente no se coloca al lado de fail2ban, lo releva. fail2ban sigue disponible como una fuente entre muchas, así que un host que todavía lo ejecuta sigue viendo sus bloqueos notificados. Pero un host sin fail2ban no pierde absolutamente nada: para cada tipo de ataque que antes llegaba por un jail, ahora hay un detector que lee el registro original.

La comparación se midió y no se afirmó. A lo largo de once días en un host de producción, el agente encontró 187 de las 189 direcciones que fail2ban había bloqueado en la misma máquina. Las dos que faltaban eran direcciones de prueba introducidas a mano. Además de eso notificó 78 direcciones más que atacaban realmente y se habían quedado por debajo de los umbrales de fail2ban, y en FTP acertó exactamente, tres de tres.

Dos diferencias prácticas. La escalada para los reincidentes viene del historial de bloqueos propio del agente y no de un jail que lee el registro de otro jail, y reportedip-agent test <archivo> ocupa el lugar de fail2ban-regex.

Comprobar primero si algo ya posee estos nombres

Esto costó una tarde en una flota de producción, así que va antes que todo lo demás. Un script de cortafuegos escrito a mano, del tipo que este sitio documentaba antes, puede usar exactamente los nombres que usa el agente. Encontrados en el campo: los conjuntos rip-whitelist, rip-ssh, rip-mail, rip-web, rip-ftp y rip-edge, más una cadena rip-blacklist colgada con -A INPUT -j rip-blacklist, reconstruida cada hora por una tarea de cron, y casi carácter por carácter la cadena que el agente construye él mismo.

En un host así el orden habitual es erróneo en las dos direcciones. La primera sincronización del agente reconstruye la cadena y sustituye las reglas DROP existentes por reglas LOG, lo que deja al host sin protección hasta que la tarea de cron antigua vuelva a ejecutarse, es decir hasta una hora más tarde. Y cuando esa tarea se ejecuta, reconstruye la cadena a su manera y retira de paso la regla rip-local del agente. Dos programas, una cadena, y cada uno deshaciendo el trabajo del otro cada hora.

Comprueba por tanto antes de la primera sincronización, y si los nombres chocan, no te quedes en mode: log en este host. Instala el agente, que no toca ninguna regla, asume la lista blanca antigua, desactiva la tarea de cron antigua, y solo después lanza la primera sincronización con mode: drop ya puesto.

bash
# 1. Does something already own these names?
ipset list -n | grep "^rip-"
iptables -S INPUT | grep rip-
iptables -S rip-blacklist 2>/dev/null | head
crontab -l | grep -iE "ipset|blacklist|reportedip"
ls -l /etc/cron.d/ /etc/cron.hourly/ 2>/dev/null

# 2. If yes, take the old whitelist over FIRST. The feed can be
#    downloaded again; that list cannot.
ipset list rip-whitelist    | sed -n "/^Members/,\$p" | tail -n +2 >  /tmp/rip-wl
ipset list rip-whitelist-v6 | sed -n "/^Members/,\$p" | tail -n +2 >> /tmp/rip-wl
wc -l /tmp/rip-wl
while read -r a; do
  [ -n "$a" ] && reportedip-agent whitelist add "$a" "imported from the old script"
done < /tmp/rip-wl
reportedip-agent whitelist list | wc -l

# 3. Disable the old job, then hand the chain over in one step.
crontab -l | grep -v blacklist | crontab -      # or: rm /etc/cron.d/<job>
sed -i "s/^mode: log/mode: drop/" /etc/reportedip-agent/config.yaml
reportedip-agent sync
reportedip-agent status
El paso 2 no es opcional. En los hosts examinados, la lista blanca del script antiguo contenía 208 prefijos IPv4 y 61 prefijos IPv6: rastreadores de buscadores, Cloudflare y las propias máquinas del operador. Sin esa importación, el agente habría notificado a Googlebot, a Cloudflare y a la propia flota el primer día. Una lista blanca es el único estado que una migración no puede reconstruir a partir de ninguna otra parte, así que asúmela antes de cambiar nada y vuelve a leerla antes de la primera sincronización con reportedip-agent whitelist list.

Ejecutar los dos un tiempo

Ejecutar los dos es seguro y es la forma sensata de migrar. El agente nunca escribe en /etc/fail2ban, y fail2ban no sabe nada de los conjuntos rip-, así que los dos usan cadenas separadas y estados separados. Dos reglas que descartan la misma dirección cuestan una comparación de paquete.

Lo único que no conviene hacer es notificar el mismo evento dos veces. Si conservas la acción de fail2ban que envía los bloqueos por HTTP y configuras fail2ban como fuente del agente, cada bloqueo sale del host dos veces y se paga dos veces de tu cuota diaria. Elige el agente o la acción.

bash
# What did fail2ban ban, and what does the agent make of the
# same logs? Same file, same window, two verdicts.
fail2ban-client status sshd
reportedip-agent test /var/log/auth.log

# Per jail, then the agent over the file behind it
for j in $(fail2ban-client status | sed -n "s/.*Jail list:\t*//p" | tr -d " " | tr "," " "); do
  echo "== $j"; fail2ban-client status "$j" | grep -E "Total banned|Currently banned"
done
reportedip-agent status | sed -n "/^sources:/,/^queue:/p"

Desactivar fail2ban de forma definitiva

Hazlo cuando hayas comparado los dos durante unos días y la lista de fuentes del agente cubra cada jail que tenías.

bash
# 1. Stop it and keep it stopped.
systemctl stop fail2ban
systemctl disable fail2ban

# 2. Remove the fail2ban source from the agent config, then stop
#    and start the daemon. A restart is not enough for a removed
#    source: the daemon keeps tailing the old file.
$EDITOR /etc/reportedip-agent/config.yaml
systemctl stop reportedip-agent.service
systemctl start reportedip-agent.service

# 3. Check what fail2ban left in the kernel. Stopping it does not
#    always clean up: a jail that once used an iptables action
#    leaves old-style f2b- chains behind.
nft list tables | grep f2b || echo "no f2b table"
iptables -S | grep "f2b-"    || echo "no f2b chain"
ipset list -n | grep f2b     || echo "no f2b set"

# 4. Remove the leftovers from the RUNNING ruleset.
iptables -D INPUT -j f2b-sshd
iptables -F f2b-sshd && iptables -X f2b-sshd

# 5. And from the SAVED one, or they come back at the next boot.
grep -n "f2b" /etc/iptables/rules.v4 /etc/iptables/rules.v6
netfilter-persistent save

# 6. Confirm the agent is happy without it.
reportedip-agent status | grep -i fail2ban
reportedip-agent status; echo "exit $?"
El paso 5 es el que se salta la gente. Si borras las cadenas f2b- solo del juego de reglas en marcha, netfilter-persistent las restaura en cada arranque desde /etc/iptables/rules.v4 y desde /etc/iptables/rules.v6. Vuelven vacías, así que no descartan nada y parecen inofensivas, y dentro de un año seguirán ahí desconcertando a quien lea el juego de reglas después. Revisa los dos archivos: en un host los restos estaban solo en el archivo v6, y quien busca únicamente en rules.v4 pasa de largo. Primero limpiar el juego de reglas en marcha, luego guardarlo, y comprobarlo después de un reinicio.

Una vuelta atrás es un comando y tarda segundos, porque el agente nunca tocó /etc/fail2ban: systemctl enable --now fail2ban devuelve cada jail y su propia tabla, y el agente no se ve afectado. Medido en un host en producción, los once jails habían vuelto en ocho segundos.

Consumo de recursos

Medido en un host Debian 12 arm64 con siete archivos de registro seguidos por el agente: 0,085 % de un núcleo y 16,6 MB de memoria residente. No hay intérprete que arrancar, ni base de datos, ni directorio de caché que calentar, y eso es la mayor parte de la explicación de esas cifras tan pequeñas.

En disco, los archivos propios del agente están acotados por la configuración y no por la esperanza: log_max_mb por log_keep más el archivo actual para el registro, queue_max archivos pequeños para la cola, y un registro de bloqueos que solo guarda lo que una escalada todavía necesita. El directorio de estado de un host con carga se queda bastante por debajo de cien megabytes, y por eso el límite de disco predeterminado de 200 MB no se dispara nunca en una máquina sana.

Qué no es el agente

No es un antivirus, ni un cortafuegos de aplicaciones web, ni un sustituto de Imunify360. No lee tus archivos, no examina cuerpos de peticiones y no pone nada en cuarentena. Bloquea y notifica direcciones, y lo hace sobre una superficie pequeña y auditable. Para protección a nivel de una sola petición en un sitio WordPress, usa en su lugar el plugin Hive. Los dos se ejecutan en la misma máquina sin estorbarse.

Resolución de problemas

Un síntoma, una causa probable, un comando. reportedip-agent doctor y reportedip-agent status responden entre los dos a la mayoría de estos casos, y son las dos cosas que el soporte pide primero.

SíntomaCausaComando
No arranca y nombra su archivo de configuraciónEl archivo es legible por el grupo o por otros, y contiene tu clave API.chmod 0600 /etc/reportedip-agent/config.yaml && chown root:root /etc/reportedip-agent/config.yaml
Código 2 con «field ... not found in type»Una clave desconocida en la configuración. El analizador es estricto a propósito.Retirar o corregir la clave que se nombra. La lista de claves válidas está más arriba.
Cada comando termina con código 2 tras una vuelta atrásEl binario es más antiguo que la configuración y no puede leer un bloque más reciente.reportedip-agent update, el único comando que lee el archivo de forma laxa y nombra las claves desconocidas.
Los conjuntos existen pero están vacíosO el feed se rechazó, o la lista falló en la comprobación de tamaño.reportedip-agent status dice cuál de las dos, y la línea de la lista lleva el último error.
status indica unlicensedLa cuenta no tiene ninguna licencia libre para este host.Añadir una en Agent Servers. La lista del núcleo sigue trabajando mientras tanto.
Nunca se notifica nadaLa fuente que habría reaccionado no se detectó nunca, o no se alcanza su umbral.reportedip-agent test /ruta/al/registro, luego reportedip-agent status y leer la sección sources.
Nunca se bloquea nada, aunque salen informesban.enabled sigue en false, o mode sigue en log. Dos interruptores.reportedip-agent ban list dice cuál de los dos en sus últimas líneas.
Ningún estado de fuente en absolutoEl demonio de vigilancia no se ha ejecutado nunca.systemctl enable --now reportedip-agent.service
Una fuente muestra files=0La ruta o el patrón no se resuelve en nada en este host.reportedip-agent doctor nombra la fuente y el patrón que intentó.
Una fuente de registro aparece como ilegibleEl archivo no es legible ni para root. Suele ser un panel de control que pone el modo 000 al rotar.ls -l sobre la ruta, y corregir la rotación que lo ha producido.
Una fuente tiene coincidencias pero nunca notificaSus marcas de tiempo están a más de un minuto del reloj del sistema, así que la ventana de recuento nunca se llena.reportedip-agent doctor, las líneas de desviación en sources. Corregir la zona horaria de ese registro.
Tu propia dirección ha sido bloqueadaNo estaba en la lista blanca.reportedip-agent whitelist add <dirección>. Efecto inmediato, porque la regla de la lista blanca va delante de la de bloqueo.
Un bloqueo está en bans.json pero no en el núcleoUn reinicio sin sincronización, o una recarga del cortafuegos que se llevó el conjunto.reportedip-agent sync lo restaura con el tiempo restante.
Una entrada del núcleo con el registro noneAlguien bloqueó a mano con ipset o nft, o se perdió el registro. Caduca, pero ningún reinicio la trae de vuelta.reportedip-agent ban list
Un conjunto existe con el tipo equivocado o sin plazoUn resto del script de shell documentado o de otra herramienta.ipset destroy <conjunto> y después reportedip-agent sync, que lo vuelve a crear correctamente.
Las dos cadenas de herramientas tienen objetos rip-El motor se cambió sin migración.reportedip-agent sync --migrate-backend, y status muestra los comandos exactos de eliminación para el otro lado.
El cortafuegos ha desaparecido tras un reinicioUn juego de reglas guardado hace referencia a un conjunto rip-, e iptables-restore descarta todo el archivo ante un conjunto desconocido.grep -n rip- /etc/iptables/rules.v4 /etc/iptables/rules.v6 y retirar esas líneas. El agente no necesita nada guardado.
Una fuente retirada se sigue leyendoUn systemctl restart no basta para una fuente retirada.systemctl stop reportedip-agent.service && systemctl start reportedip-agent.service
Los informes se detienen, la detección sigueSe alcanzó el mínimo de disco.Liberar espacio, luego reportedip-agent housekeeping.
HTTP 429 en los informesEl límite diario de informes de tu plan.Los límites están en Autenticación.
La cola no deja de crecerEl emisor está en pausa tras un fallo, con una espera progresiva.reportedip-agent status muestra la pausa y el motivo; reportedip-agent report-queue envía una pasada a mano.
Cada coincidencia web es el mismo puñado de direccionesnginx ve un proxy o Cloudflare y no al visitante.Configurar set_real_ip_from. reportedip-agent install advierte precisamente de este caso.
«more addresses than the counter tracks at once»Un barrido más amplio que el contador por fuente. No es un defecto.Nada. Un barrido tan amplio es un caso para la lista comunitaria y no para bloqueos locales.
status dice que el host va atrasado de versiónLa actualización automática no llega, o auto_update está desactivada.reportedip-agent update --check, luego reportedip-agent update.

Última actualización: · Mantenido por el equipo de ReportedIP

Security Focused
Conforme al RGPD
Made in Germany
Volver a la documentación