
Usando Linux Debian Bookworm.
Problema
Quiero bloquear todas las conexiones entrantes a mi servidor provenientes de países específicos.
Editar
Como alguien señaló en los comentarios, realmente no debería hacer esto con iptables, ya que esto detendrá mi servidor una vez que la lista de direcciones IP que bloquea crezca a un tamaño inmanejable (como miles de direcciones IP). En su lugar, debería usar ipset
(para bloquear rangos enteros). El problema con esto es que depende de las listas que necesito descargar regularmente. Quiero algo que configuré una vez (preferiblemente).
Entonces, ¿cómo puedo detener las conexiones desde países específicos?El tráfico (y a veces los ataques de fuerza bruta) procedente de China y Rusia se está yendo de las manos. Ponen de rodillas a mi servidor con regularidad. No respetan el archivo robots.txt y rastrean el sitio de forma muy agresiva (con cientos, a veces miles de conexiones simultáneamente). Realmente necesito detener eso. Mi estimación es que más del 90% de todo el tráfico que maneja mi servidor es malicioso o tiene formato incorrecto.
Editar 2
Encontré algunos tutoriales en línea (como éste) cómo hacer esto, pero parece que estos tutoriales son bastante antiguos. Además, requiere descargar y compilar/instalar algunos módulos. Me preocupa que esto pueda fallar en el futuro (al actualizar otros paquetes o incluso la distribución) o que pueda convertirse en un riesgo de seguridad en el futuro. Mantengo mi servidor actualizado (ejecutando apt-get update / apt-get Upgrade / apt-get dist-upgrade regularmente).
Mensaje original
Encontré un script en línea que (en pocas palabras) obtiene una lista de direcciones IP de conexiones establecidas cada segundo y luego usa geoiplookup para obtener el país de la dirección IP. Si el país está en la lista de países que le gustaría bloquear, agrega la dirección IP a su filtro de firewall usando iptables. Hice algunos cambios leves en el script (para hacerlo más legible y porque no estaba interesado en este script, me enviaba un correo electrónico cada vez que bloqueaba una dirección IP). Luego, este script se ejecuta cada minuto a través de crontab:
#!/bin/bash
# Script found on lautenbacher.io"
end=$((SECONDS+55))
while [ $SECONDS -lt $end ]; do
echo $SECONDS
netstat -ant | egrep ':.*ESTABLISHED' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c > testcc.txt
sed 's/^[ t]*//' -i testcc.txt
sed '/^$/d' -i testcc.txt
while read c d; do
if [[ $c > "0" ]]; then
bGF1dGVuYmFjaGVyLmlv=$(geoiplookup $d | awk -v ip="$d" '{FS=" "} {if($4 == "RU," || $4 == "CN," || $4 == "BLR,") {print 1}}')
if [[ $bGF1dGVuYmFjaGVyLmlv = "1" ]]; then
echo "running geolookup"
echo checking ip $d
geoiplookup $d
isCloudflare=0
# The original script made an exception for CLOUDFLARE. I don't want that exception
# bGF1dGVuYmFjaGVyLmlX=$(whois $d)
# if [[ "$bGF1dGVuYmFjaGVyLmlX" == *"CLOUDFLARE "* ]]; then
# isCloudflare=1
# echo Cloudflare detected
# fi
if [[ "$isCloudflare" != 1 ]]; then
echo try to add ip $d to blocklist
sudo iptables -I INPUT -s $d -j DROP
### I would like to sever the connection here ###
fi
fi
fi
done < testcc.txt
sleep 1
:
done
Por cierto, no tengo idea de por qué el autor del script usa nombres de variables tan extraños ("bGF1dGVuYmFjaGVyLmlv").
De todos modos, el problema es que cada vez que encuentra una conexión con una dirección IP de un país prohibido, ejecuta
sudo iptables -I INPUT -s $d -j DROP
en la dirección IP. Todo esto está bien, pero la mayoría de estas conexiones son algo persistentes, lo que significa que un segundo después se encuentra la misma dirección IP y se agrega nuevamente usando iptables. No estoy seguro de qué sucede (¿crea reglas duplicadas?), pero me gustaría interrumpir la conexión inmediatamente después de agregar la dirección IP con iptables.
En resumen: ¿Cómo puedo eliminar una(s) conexión(es) con una dirección IP?
Respuesta1
Desde hace varios años, el kernel de Linux tiene una API para eliminar por la fuerza los sockets IP existentes cualquiera que sea su estado. Se puede hacer con elss
comando y su sintaxis de filtro:
-K
,--kill
Intenta cerrar los enchufes a la fuerza. Esta opción muestra los sockets que se cerraron exitosamente y omite silenciosamente los sockets que el kernel no admite cerrar. Solo admite sockets IPv4 e IPv6.
Puedes reemplazar:
### I would like to sever the connection here ###
con:
ss --kill -n dst = "$d"
Elfiltrar( dst = ...
) es necesario, porque el comando eliminará cualquier socket que se muestre. Sin filtro eso sería más de lo pedido. El proceso local podría obtener un error poco común en su socket, como ECONNABORTED
. El nodo remoto probablemente será notificado con un TCP RST a menos que también se agregue (temporalmente) una regla de SALIDA para evitarlo.
Vea también esta pregunta/respuesta (donde respondí):Elimine TODAS las conexiones TCP (ESTABLECIDAS, RELACIONADAS) en Ubuntu, donde OP intenta protegerse de ataques con unipset(y también tiene problemas con conexiones obsoletas y firewalls con estado).