Skip to main contentSkip to footer

Bloqueo con el agente para Linux

El feed comunitario en el núcleo y las direcciones que este host bloquea por su cuenta: las dos clases de conjunto y sus duraciones, cómo empieza y termina un bloqueo local, la escalada con números reales, la cadena en el núcleo, los dos interruptores mode y ban.enabled, y cómo se levanta un bloqueo o se desmonta todo.

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.

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
grouprip-group, rip-group-v6todos los puertos, véase más abajo

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 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. La respuesta se guarda en caché hasta 24 horas, y solo una condición api_key, license o reputation abierta hace que vuelva a preguntar en cada pasada, así que un cambio de plan llega al host en un día como mucho. 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 de salida 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.

La lista de grupo

Una clave en un grupo recibe una sexta lista, descargada en json junto con el feed comunitario. Cada dirección se descarta en todos los puertos en los conjuntos rip-group y rip-group-v6, sea cual sea el servicio que ofrezca este host. Una clave sin grupo no cambia nada: el conjunto se queda vacío, y status muestra group=none.

La lista blanca del grupo, si lleva una, se lee en la misma petición y se escribe en /var/lib/reportedip-agent/group-whitelist, en el formato de whitelist.conf con cada nota como comentario. No edites ese archivo a mano, la siguiente sincronización lo vuelve a escribir. Se aplica en la misma pasada en los tres sitios donde actúa una lista blanca: el conjunto de lista blanca del núcleo, el filtro de cada lista y la puerta de notificación del demonio watch, una capa más junto a la propia whitelist.conf de este host y su lista blanca automática. Una clave que abandona su grupo pierde el archivo con la siguiente sincronización.

whitelist list y status muestran las entradas del grupo en líneas marcadas con group:, con sus notas. status también nombra el grupo y su tamaño junto a la línea de la cuenta y muestra el número de entradas de la lista de grupo bajo las listas como en cualquier otra. Véase Grupos para lo que es un grupo, los límites por plan, la lista blanca y el webhook en el lado del portal.

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

Lista negra comunitariaHallazgos locales
Conjuntosrip-ssh, rip-mail, rip-web, rip-ftp, rip-edge, rip-group, 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

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 almacén 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. Con ban.enabled: true, 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

La cadena en el núcleo

Con el backend ipset, el agente posee exactamente una cadena, rip-blacklist, y un salto hacia ella. Una sincronización reconstruye la cadena en cuanto su huella (la configuración y la cadena tal como la muestra el núcleo) difiere, así que su contenido es siempre lo que dice la configuración; si no, deja la cadena como está para que los contadores de paquetes sigan contando. 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 -A rip-blacklist -m set --match-set rip-group src -j DROP

# Only with a scan source in the config: a SYN to a port this host
# does not listen on is logged, at most ten a minute. Listening ports
# and the passive range of a running FTP server return first, then any
# port a socket listens on at this very moment.
iptables -N rip-blacklist-scan
iptables -A rip-blacklist-scan -p tcp -m multiport --dports 21,22,25,80,443,40110:40210 -j RETURN
iptables -A rip-blacklist-scan -p tcp -m socket --nowildcard -j RETURN
iptables -A rip-blacklist-scan -m limit --limit 10/min --limit-burst 20 \
         -j LOG --log-prefix "rip-scan: "
iptables -A rip-blacklist -p tcp --syn -j rip-blacklist-scan

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 reglas de ssh, mail, web y ftp 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 y group se aplican a todos los puertos: en edge eso es lo que significa la categoría que hay detrás, en group es el sentido de compartir un bloqueo.

Con el backend 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-<list>:) 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. De serie los dos están activos (mode: drop, ban.enabled: true). Con ban.enabled: false, mode: drop aplica la lista comunitaria y no obtienes nada de tus 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; con ipset la cadena se queda con sus reglas, con nftables la cadena se borraAnotados en el almacén de bloqueos, 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 falla de forma visible si un salto sobrevive, porque decir «off» y seguir descartando es peor que un error. Con nftables se borra la propia cadena. Los conjuntos conservan sus entradas con los dos backends, 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 backend 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

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 en la columna 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 almacén de bloqueos. 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 <address> 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, the sets keep their entries.
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 por sí solo y no necesita nada guardado. status advierte cuando encuentra rip- en rules.v4, rules.v6, /etc/sysconfig/iptables, ip6tables o /etc/nftables.conf. En /etc/nftables.d no mira.

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

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