Diferencia entre revisiones de «Entendiendo iptables hashlimit»

De Jose Castillo Aliaga
Ir a la navegación Ir a la búsqueda
Línea 42: Línea 42:


== Ejemplos con paquetes ==
== Ejemplos con paquetes ==
 
=== Límite en el firewall ===
Aquí vamos a establecer un límite de 2 paquetes por minuto y un ''burst'' de 5.  
Aquí vamos a establecer un límite de 2 paquetes por minuto y un ''burst'' de 5.  
<pre class="code">
<pre class="code">
Línea 78: Línea 78:
* El quinto es la cantidad de crédito que se pierde cada vez que detecta un nuevo paquete. Hay un cálculo interesante que hacer aquí: 4800000/960000 = 5 (El burst)
* El quinto es la cantidad de crédito que se pierde cada vez que detecta un nuevo paquete. Hay un cálculo interesante que hacer aquí: 4800000/960000 = 5 (El burst)


 
=== Límite en el servidor ===
El ejemplo anterior está hecho en el contenedor que hace de cortafuegos, ya que el ejemplo era el de un aula que se quiere controlar desde ahí. Pero puede estar en el servidor de destino:
El ejemplo anterior está hecho en el contenedor que hace de cortafuegos, ya que el ejemplo era el de un aula que se quiere controlar desde ahí. Pero puede estar en el servidor de destino:
<pre class="code">
<pre class="code">
Línea 96: Línea 96:
Esta regla, cuando se envía un ping cada segundo, elimina 2 de cada 3 pings, excepto los 25 primeros. Es decir, 20 por minuto.
Esta regla, cuando se envía un ping cada segundo, elimina 2 de cada 3 pings, excepto los 25 primeros. Es decir, 20 por minuto.


 
=== Límite de intentos de login ===
En el siguiente ejemplo, lo que hacemos es cortar los intentos de SSH:
En el siguiente ejemplo, lo que hacemos es cortar los intentos de SSH:
<pre class="code">
<pre class="code">

Revisión del 19:25 7 nov 2017

Cuando nos proponemos limitar el ancho de banda o las conexiones a o desde Internet, hay algunas opciones a tener en cuenta. De forma general, todo el mundo te recomienda leer el lartc y tienen razón. Es bastante complicado hacerlo bien y algunos han hecho herramientas como Wondershaper. Esta ha quedado obsoleta ([1]) y no funciona correctamente. Otra alternativa es echar mano de IPtables y su extensión hashlimit.

Antes de empezar, voy a suponer un caso real sobre el que voy a explicar las alternativas: Tenemos un aula con unos 25 pcs y algunos móviles conectados por Wifi. Todos pasan por un Ubuntu server que hace de router. La conexión al exterior es relativamente buena, pero si algunos alumnos usan mucho el youtube o otros servicios de streaming, comienza a ir lento. Nos proponemos limitar el ancho de banda de la forma más respetuosa posible y sin dañar a los que quieren trabajar.

Iptables tiene una extensión llamada hashlimit. Esta es parecida a otra llamada limit. La diferencia es que hashlimit directamente puede trabajar con múltiples IPs de origen y de destino y limit es para toda la tarjeta de red.

El funcionamiento es el siguiente:

  1. Hashlimit registra cada conexión entre ips de origen y de destino.
  2. Si esta supera el límite de conexiones por segundo o de velocidad, ejecuta la regla. (Posiblemente DROP)
  3. Cuando pasa un tiempo, permite de nuevo conectar, pero si se supera la velocidad, vuelve a cortar.

Aquí hay un problema: Muchas conexiones pueden ir muy rápido pero no ser muy pesadas. Podríamos estar cortando conexiones sin parar sólo por la velocidad de la red o del servidor remoto.

Para solucionar este problema, hashlimit cuenta con la opció --hashlimit-burst que permite una conexión rápida durante una cantidad máxima de datos.

Entorno de pruebas

Para probar el funcionamiento voy a usar containers LXD. Estos serian los comandos para crear un container que hace de cortafuegos y uno que se conecta a través de él a Internet:

# lxc launch ubuntu:16.04 firewall
# lxc launch ubuntu:16.04 cliente
# lxc profile create clientes
# lxc profile apply cliente clientes
# echo -e "\n\nauto switch1\niface switch1 inet static\naddress 192.168.99.1\nnetmask 255.255.255.0\nbridge-ports none" >> /etc/network/interfaces
# brctl addbr switch1
# lxc config device add cliente eth1 nic nictype=bridged parent=switch1 name=eth1
# lxc config device add firewall eth1 nic nictype=bridged parent=switch1 name=eth1
# lxc exec cliente -- bash -c 'echo -e "\n\nauto eth1\niface eth1 inet static\naddress 10.20.30.1\nnetmask 255.255.255.0\ngateway 10.20.30.254" >> /etc/network/interfaces'
# lxc exec firewall -- bash -c 'echo -e "\n\nauto eth1\niface eth1 inet static\naddress 10.20.30.254\nnetmask 255.255.255.0" >> /etc/network/interfaces'
# lxc exec cliente -- ifdown eth1
# lxc exec cliente -- ifup eth1
# lxc exec firewall -- ifdown eth1
# lxc exec firewall -- ifup eth1
# lxc exec firewall -- sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
# lxc exec firewall -- apt update
# lxc exec firewall -- iptables -A FORWARD -j ACCEPT
# lxc exec firewall -- iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# lxc exec firewall -- apt install iptables-persistent
# lxc exec firewall -- bash -c 'iptables-save > /etc/iptables/rules.v4'
# lxc exec firewall -- reboot

Ejemplos con paquetes

Límite en el firewall

Aquí vamos a establecer un límite de 2 paquetes por minuto y un burst de 5.

root@firewall:~# iptables -I FORWARD -m hashlimit --hashlimit-above 2/minute --hashlimit-burst 5 --hashlimit-mode srcip,dstip --hashlimit-name bwlimit -j DROP 

A continuación, desde el cliente hacemos ping a otra màquina (en el ejemplo lo hago cada 5 segundos para tener menos líneas).

root@cliente:~# ping -O -i 5 10.182.234.51
PING 10.182.234.51 (10.182.234.51) 56(84) bytes of data.
64 bytes from 10.182.234.51: icmp_seq=1 ttl=63 time=0.124 ms
64 bytes from 10.182.234.51: icmp_seq=2 ttl=63 time=0.116 ms
64 bytes from 10.182.234.51: icmp_seq=3 ttl=63 time=0.116 ms
64 bytes from 10.182.234.51: icmp_seq=4 ttl=63 time=0.120 ms
64 bytes from 10.182.234.51: icmp_seq=5 ttl=63 time=0.104 ms
no answer yet for icmp_seq=6
64 bytes from 10.182.234.51: icmp_seq=7 ttl=63 time=0.102 ms
no answer yet for icmp_seq=8
no answer yet for icmp_seq=9
no answer yet for icmp_seq=10
no answer yet for icmp_seq=11
no answer yet for icmp_seq=12
64 bytes from 10.182.234.51: icmp_seq=13 ttl=63 time=0.116 ms

Como se puede ver, los primeros 5 pings funcionan, pero luego van fallando todos hasta que, después de un tiempo va dejando pasar otros pero no más de 2 por minuto.

Este es el fichero /proc/net/ipt_hashlimit/bwlimit

59 10.20.30.1:0->10.182.234.51:0 283648 4800000 960000
51 10.182.234.51:0->10.20.30.1:0 283648 4800000 960000
  • El primer número son los segundos que quedan hasta que la regla desaparezca. Sólo bajan si no se producen nuevas peticiones.
  • Después van las IP de origen y destino.
  • El tercer número és el crédito que queda. Si está por debajo del quinto número eliminará los paquetes que entren. Este número aumenta mientras no llegan paquetes nuevos o está eliminando los que van llegando. De esta manera, cuando llega a ser mayor que el quinto número deja pasar un paquete y se decrementa en 96000.
  • El cuarto número es el crédito máximo que se puede tener con el burst.
  • El quinto es la cantidad de crédito que se pierde cada vez que detecta un nuevo paquete. Hay un cálculo interesante que hacer aquí: 4800000/960000 = 5 (El burst)

Límite en el servidor

El ejemplo anterior está hecho en el contenedor que hace de cortafuegos, ya que el ejemplo era el de un aula que se quiere controlar desde ahí. Pero puede estar en el servidor de destino:

# iptables -A INPUT -p icmp -m hashlimit --hashlimit-mode srcip --hashlimit-srcmask 32 --hashlimit-above 20/minute --hashlimit-burst 25 --hashlimit-htable-expire 30000 --hashlimit-name LIMITARICMP -j DROP
##            |          |                                   |                       |                      |                         |                             |                       |
##  (Corta la entrada) (protocolo icmp)  (un hash para cada ip) (Con 32 bits de máscara corta sólo esa IP) (20 paquetes/m)  (25 paquetes inicialmente) (La tabla expira a los 30 segundos) (/proc/net/ipt_hashlimit/LIMITARICMP)   

Miremos el fichero /proc/net/ipt_hashlimit/LIMITARICMP:

 29 10.182.234.6:0->0.0.0.0:0 108672 2400000 96000
  • 29 segundos para que expire (se refresca a 29 cada vez que el cliente intenta un ping)
  • IP del cliente hacia cualquier IP, en este caso la del servidor.
  • 108672: El crédito que tenemos actualmente. Es menor que el crédito de cada token, por tanto, el siguiente paquete será eliminado.
  • 2400000: El crédito total contando el burst.
  • 96000: El crédito que vale cada token y que se descuenta al primer valor cada vez que se acepta un paquete.

Esta regla, cuando se envía un ping cada segundo, elimina 2 de cada 3 pings, excepto los 25 primeros. Es decir, 20 por minuto.

Límite de intentos de login

En el siguiente ejemplo, lo que hacemos es cortar los intentos de SSH:

iptables -A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -m hashlimit --hashlimit-above 2/hour --hashlimit-burst 3 --hashlimit-mode srcip --hashlimit-name SSH --hashlimit-htable-expire 60000 -j DROP
##            |          |                                   |                       |                      |                                                      |                       |
##  (Corta la entrada) (protocolo tcp)      (Conexiones nuevas)          (2 conexiones nuevas por hora) (3 la primera vez)                   (/proc/net/ipt_hashlimit/SSH) (La tabla expira a los 60 segundos)

Esto evita ataques constantes, pero si el atacante espera 60 segundos después de cada 3 intentos, puede intentarlo 3 veces por minuto. Para que sólo se permitan 3 intentos cada hora, habría que poner 3600000 en el tiempo de expiración. Pero el problema es que el administrador si se equivoca 3 veces o hay cortes de comunicación, ha de esperar una hora entera para entrar.

Ejemplos con velocidad

Vamos a analizar esta regla:

root@firewall:~# iptables -I FORWARD -m hashlimit --hashlimit-above 3000kb/s --hashlimit-burst 5mb --hashlimit-mode srcip,dstip --hashlimit-name bwlimit -j DROP

Aquí estamos permitiendo conexiones a velocidades menores de 3000kb/s. Si se detecta una descarga a más velocidad, enviará sus paquetes al DROP. Pero no corta la conexión, ya que después de un poco de tiempo, vuelve a permitirla. Se reanuda y si vuelve a esa velocidad volverá a eliminar los paquetes.

Pero si las descargas son menores a 5mb no se aplica la regla, ya que tiene un hashlimit-burst de 5mb. Esto permite que funcione correctamente en webs poco pesadas.

Para esta prueba, hago una petición desde el cliente por wget a un servidor que tiene un fichero de 100MB:

root@cliente:~# wget 10.182.234.51/100MB.txt

Al principio, wget informa de mucha velocidad, después va bajando la media hasta situarse un poco por debajo de los 3MB/s. Esa velocidad alta inicial son los primeros 5MB (el burst). La conexión, a partir de 5MB comienza a perder muchos paquetes hasta que se alcanza una velocidad media de 3MB/s. Estos paquetes perdidos son reclamados por el protocolo TCP y no se pierden los datos.

El funcionamiento en detalle se puede analizar observando el fichero /proc/net/ipt_hashlimit/bwlimit en él se detallan todas las conexiones detectadas. Vamos a fijarnos en esta línea. Ha aparecido mientras se descargaba un archivo de la 10.20.2.2:

45 192.168.9.100:0->10.20.2.2:0 4194304000 2 65534

El primer número es un contador de 60 segundos hacia atrás. Cuando se produce una conexión a esa velocidad se registra. Si esa conexión se acaba, el contador va hacia atrás hasta que es 0 y desaparece. Mientras existe el contador, los paquetes a más velocidad que 3000kb/s que superen el se eliminarán. Mientras continue la conexión y se supere el límite, el contador se refrescará a 60.


Utilidad

Cortar conexiones que superen una cantidad de paquetes o una cierta velocidad es útil sobretodo para evitar ataques DOS o por fuerza bruta. En los ejemplos hechos en un entorno local y controlado, las velocidades se corresponden con lo que se pide en la regla y la cantidad de paquetes también se ve limitada.

En el caso de FORWARD y limitando la velocidad, en un entorno real con conexiones al exterior, se puede ver algún cliente más perjudicado de lo deseable. Estas son algunas ideas para ajustarlo mejor:

  • Poner un límite muy bajo pero un burst alto. De esta manera, las webs con no muchas cosas siguen funcionando correctamente mientras que el streaming, que es una descarga continuada se va viendo perjudicado al poco tiempo.
  • Poner un límite a dstip sólo para limitar la velocidad de toda una IP de cliente, ya que si ponemos srcip también, permitimos que un cliente tenga muchas conexiones simultáneas.
  • Lo contrario de lo anterior también puede funcionar al limitar una IP de origen, puede que servicios de streaming se perjudiquen más, ya que varios clientes pueden intentar acceder a ellos simultáneamente.

En resumen, para cortar por paquetes para evitar fuerza bruta o DOS es ideal, para limitar la velocidada puede que tc sea más correcto.

Enlaces

http://tlfabian.blogspot.com.es/2014/06/how-does-iptables-hashlimit-module-work.html

https://unix.stackexchange.com/questions/215903/what-do-the-fields-in-proc-net-ipt-hashlimit-file-mean

https://making.pusher.com/per-ip-rate-limiting-with-iptables/