Protección DDoS mediante iptables

Cuando me inicié en el mundo de Linux hace ya más de 15 años (joder que viejo soy) una de las las características que más me enganchó desde el principio fueron sus capacidades para usarlo como cortafuegos de red. Primero con ipfwadm, luego ipchains y después y sobretodo iptables/Netfilter, fueron muchos los firewalls Linux que desplegué durante años. Y después de tanto tiempo y aún a la espera de la inminente (y tardía) adopción de nftables, me siguen maravillando sus posibilidades y capacidades de defensa antes las últimas amenazas de nuestra era...

Y precisamente una de las amenazas más tangibles de esta década son los famosos ataques de denegación de servicio distribuidos (DDoS) que vemos tanto en los medios y que provocan a las empresas pérdidas millonarias con sólo afectar a la disponibilidad de sus sistemas durante un breve (o no) periodo de tiempo.

Para contrarrestar estos ataques, iptables y en general el kernel de Linux nos ofrece ciertas protecciones que debemos tener en cuenta. El genial artículo de Constantin Oesterling en Javapipe da buena cuenta de ello, así que repasaremos algunas de sus medidas para mitigar DDoS.

Tuneando el kernel

Lo primero que tenemos que hacer antes de abordar las reglas como tal es optimizar el kernel para mitigar los efectos de los ataques DDoS. El siguiente ejemplo se basa en CentOS 7 ya que esta distro incluye una versión reciente de iptables y soporta synproxy. Sólo hay que ponerla en /etc/sysctl.conf y aplicar la configuración con sysctl -p:

kernel.printk = 4 4 1 7
kernel.panic = 10
kernel.sysrq = 0
kernel.shmmax = 4294967296
kernel.shmall = 4194304
kernel.core_uses_pid = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
vm.swappiness = 20
vm.dirty_ratio = 80
vm.dirty_background_ratio = 5
fs.file-max = 2097152
net.core.netdev_max_backlog = 262144
net.core.rmem_default = 31457280
net.core.rmem_max = 67108864
net.core.wmem_default = 31457280
net.core.wmem_max = 67108864
net.core.somaxconn = 65535
net.core.optmem_max = 25165824
net.ipv4.neigh.default.gc_thresh1 = 4096
net.ipv4.neigh.default.gc_thresh2 = 8192
net.ipv4.neigh.default.gc_thresh3 = 16384
net.ipv4.neigh.default.gc_interval = 5
net.ipv4.neigh.default.gc_stale_time = 120
net.netfilter.nf_conntrack_max = 10000000
net.netfilter.nf_conntrack_tcp_loose = 0
net.netfilter.nf_conntrack_tcp_timeout_established = 1800
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 10
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 20
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 20
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 20
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 20
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 10
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.ip_no_pmtu_disc = 1
net.ipv4.route.flush = 1
net.ipv4.route.max_size = 8048576
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.tcp_congestion_control = htcp
net.ipv4.tcp_mem = 65536 131072 262144
net.ipv4.udp_mem = 65536 131072 262144
net.ipv4.tcp_rmem = 4096 87380 33554432
net.ipv4.udp_rmem_min = 16384
net.ipv4.tcp_wmem = 4096 87380 33554432
net.ipv4.udp_wmem_min = 16384
net.ipv4.tcp_max_tw_buckets = 1440000
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_orphans = 400000
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_rfc1337 = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_sack = 1
net.ipv4.tcp_fack = 1
net.ipv4.tcp_ecn = 2
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 60
net.ipv4.tcp_keepalive_probes = 10
net.ipv4.tcp_no_metrics_save = 1
net.ipv4.ip_forward = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.rp_filter = 1

Estos ajustes en sysctl.conf ayudan a maximizar el rendimiento de un servidor bajo un ataque DDoS, así como la eficacia de las reglas de iptables que veremos a continuación.


Filtrar lo máximo posible en la tabla mangle y la cadena PREROUTING


 Lo segundo que hay que tener en cuenta que el rendimiento de las reglas de iptables es extremadamente importante. La mayoría de los ataques DDoS basados ​​en TCP se realizan con una alta tasa de transferencia de paquetes, es decir, con un gran número de paquetes por segundo lo que hace que el servidor se caiga. Es por eso que hay que asegurarse de que se pueda procesar y bloquear la mayor cantidad de paquetes por segundo como sea posible.

Muchas guías sobre cómo bloquear los ataques DDoS usando iptables utilizan la tabla de filtros y la cadena INPUT para las reglas anti-DDoS. El problema con este enfoque es que la cadena de INPUT sólo se procesa después de las cadenas PREROUTING y FORWARD y, por lo tanto, sólo se aplica si el paquete no coincide con ninguna de estas dos cadenas. Esto provoca un retraso en el filtrado de los paquetes que consume recursos.

Por ello, para hacer las reglas más eficaces, tenemos que hacer que nuestro anti-DDoS utilice el menor número de cadenas posible. La primera cadena que puede aplicarse a un paquete es la cadena PREROUTING, por lo que lo ideal es filtrar el máximo de paquetes en esa cadena. Sin embargo, la tabla de filtros o filter no es compatible con la cadena PREROUTING. Para solucionar este problema, podemos simplemente utilizar la tabla mangle en lugar de la tabla de filtros para nuestras reglas de iptables anti-DDoS ya que es compatible con la mayoría, si no todas, las reglas.

En resumen, la mejor solución para aumentar drásticamente el rendimiento de las reglas de iptables y por lo tanto la cantidad de ataques DDoS (TCP) que pueden filtrar es utilizar la tabla mangle y la cadena PREROUTING.

Siguiendo las conexiones

Ahora bien, los ataques DDoS son complejos. Hay muchos tipos diferentes de DDoS y es casi imposible mantener reglas basadas en firmas contra todos ellos. Pero por suerte hay algo llamado seguimiento de conexiones (el módulo del kernel nf_conntrack) que nos puede ayudar a mitigar casi cualquier ataque DDoS basado en TCP que no utilizan paquetes SYN que parecen legítimos. Esto incluye todos los tipos de ataques DDoS ACK y SYN-ACK, así como ataques DDoS que utilizan flags TCP falsos.

Vamos a empezar sólo con cinco reglas de iptables sencillas que detendrán muchos ataques DDoS basados en TCP.

1.- Bloquear paquetes no válidos: Esta regla bloquea todos los paquetes que no son un paquete SYN y no pertenecen a una conexión TCP establecida.

iptables -t mangle -A PREROUTING -m conntrack --ctstate INVALID -j DROP

2.- Bloquear nuevos paquetes que no son SYN: Esto bloquea todos los paquetes que son nuevos (no pertenecen a una conexión establecida) y no utilizan el indicador SYN, complementa la regla anterior.

iptables -t mangle -A PREROUTING -p tcp ! --syn -m conntrack --ctstate NEW -j DROP

3.- Bloquear valores MSS poco frecuentes: bloquea nuevos paquetes (después de aplicar las dos reglas anteriores sólo podrán ser SYN) que utilizan un valor TCP MSS que no es común. Esto ayuda a bloquear inundaciones SYN sencillas.

iptables -t mangle -A PREROUTING -p tcp -m conntrack --ctstate NEW -m tcpmss ! --mss 536:65535 -j DROP

4.- Bloquear paquetes con flags TCP falsos: el siguiente conjunto de reglas bloquea paquetes que utilizan indicadores TCP falsos, es decir, flags TCP que los paquetes legítimos no usarían.

iptables -t mangle -A PREROUTING -p tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags FIN,SYN FIN,SYN -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags FIN,RST FIN,RST -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags FIN,ACK FIN -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags ACK,URG URG -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags ACK,FIN FIN -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags ACK,PSH PSH -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL ALL -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL NONE -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL FIN,PSH,URG -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL SYN,FIN,PSH,URG -j DROP
iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP

5.- Bloquear paquetes desde subredes privadas (spoofing): estas reglas bloquean los paquetes falsificados procedentes de subredes privadas (locales). En un interfaz de red público por lo general no debemos recibir paquetes IP de origen privado. Estas reglas asumen que el interfaz de loopback utiliza el espacio IP 127.0.0.0/8.

iptables -t mangle -A PREROUTING -s 224.0.0.0/3 -j DROP
iptables -t mangle -A PREROUTING -s 169.254.0.0/16 -j DROP
iptables -t mangle -A PREROUTING -s 172.16.0.0/12 -j DROP
iptables -t mangle -A PREROUTING -s 192.0.2.0/24 -j DROP
iptables -t mangle -A PREROUTING -s 192.168.0.0/16 -j DROP
iptables -t mangle -A PREROUTING -s 10.0.0.0/8 -j DROP
iptables -t mangle -A PREROUTING -s 0.0.0.0/8 -j DROP
iptables -t mangle -A PREROUTING -s 240.0.0.0/5 -j DROP
iptables -t mangle -A PREROUTING -s 127.0.0.0/8 ! -i lo -j DROP

Reglas adicionales

1.- Bloquear de ICMP: Esto descarta todos los paquetes ICMP. ICMP se utiliza sobretodo para hacer ping a un host para averiguar si todavía está vivo. Se suele utilizar para troubleshooting pero representa una vulnerabilidad que los atacantes pueden explotar, así que es recomendable bloquear todos los paquetes ICMP para mitigar Ping de la Muerte (ping flood), ICMP flood y ICMP fragmentation flood.

iptables -t mangle -A PREROUTING -p icmp -j DROP

2.- Bloquear ataques de conexiones masivas: esta regla rechaza las conexiones desde equipos que tienen más de 80 conexiones establecidas. Se puede elevar el límite ya que podría causar problemas con los clientes legítimos que establecen un gran número de conexiones TCP.

iptables -A INPUT -p tcp -m connlimit --connlimit-above 80 -j REJECT --reject-with tcp-reset

3.- Limitar las nuevas conexiones TCP que un cliente puede establecer por segundo: esto puede ser útil contra los ataques de conexión, pero no tanto contra inundaciones SYN porque suelen usar un sinfín de direcciones IP diferentes.

iptables -A INPUT -p tcp -m conntrack --ctstate NEW -m limit --limit 60/s --limit-burst 20 -j ACCEPT
iptables -A INPUT -p tcp -m conntrack --ctstate NEW -j DROP

4.- Bloquear paquetes fragmentados: esto podría mitigar la inundación UDP pero la mayoría de las veces utilizan una gran cantidad de ancho de banda que agota la capacidad de la tarjeta de red, por lo que hace que esta regla sea opcional y probablemente no la más útil.

iptables -t mangle -A PREROUTING -f -j DROP

5.- Limitar los paquetes TCP RST entrantes: podría mitigar este tipo de inundaciones pero su efectividad es cuestionable.

iptables -A INPUT -p tcp --tcp-flags RST RST -m limit --limit 2/s --limit-burst 2 -j ACCEPT
iptables -A INPUT -p tcp --tcp-flags RST RST -j DROP

Mitigando SYN floods con SYNPROXY

Synproxy es un nuevo target de iptables que se ha añadido en Linux en la versión 3.12 de kernel y en la 1.4.21 de iptables. CentOS 7 lo incluye incluso antes, por defecto desde la versión 3.10 del kernel.

El propósito de synproxy es comprobar si el host que envió el paquete SYN en realidad establece una conexión TCP completa o, simplemente, no hace nada después de que enviara el paquete SYN. Si no hace nada, se descarta el paquete con un impacto mínimo en el rendimiento.

Mientras que las reglas de iptables que se proporcionan más arriba bloquean la mayoría de los ataques basados ​​en TCP, el tipo de ataque que todavía puede evadirlos, si es lo suficientemente sofisticado, es un SYN flood. Es importante tener en cuenta que el rendimiento de las reglas siempre será mejor si nos encontramos con un cierto patrón o la firma de bloqueo, tales como la longitud del paquete (-m length), TOS (-m tos), TTL (-m ttl) o cadenas y valores hexadecimales (-m string y  -m u32 para los usuarios más avanzados). Sin embargo, en algunos casos raros ésto no es posible o al menos no es fácil de conseguir. Por lo tanto, en estos casos, se puede hacer uso de synproxy.

Aquí están las reglas de iptables synproxy que ayudan a mitigar las inundaciones SYN que omiten las otras reglas:

iptables -t raw -D PREROUTING -p tcp -m tcp --syn -j CT --notrack
iptables -D INPUT -p tcp -m tcp -m conntrack --ctstate INVALID,UNTRACKED -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460
iptables -D INPUT -m conntrack --ctstate INVALID -j DROP
 
Estas reglas se aplican a todos los puertos. Si deseas utilizar synproxy sólo en determinados puertos TCP (es recomendable también bloquear todos los puertos TCP que no estén en uso utilizando la tabla mangle y la cadena PREROUTING), puedes por ejemplo simplemente añadir --dport 80 a cada una de las reglas si quieres utilizar synproxy en el puerto 80 solamente.

Para verificar que synproxy está funcionando puedes hacer 'watch -n1 cat /proc/net/stat/synproxy'. Si los valores cambian cuando se establece una nueva conexión TCP con el puerto que utiliza el synproxy, funciona.

El conjunto de reglas IPtables completo

Si no quieres copiar y pegar cada regla expuesta en esta entrada, puedes utilizar el conjunto de reglas de a continuación para una protección básica DDoS de tu servidor Linux.

### 1: Drop invalid packets ###
/sbin/iptables -t mangle -A PREROUTING -m conntrack --ctstate INVALID -j DROP

### 2: Drop TCP packets that are new and are not SYN ###
/sbin/iptables -t mangle -A PREROUTING -p tcp ! --syn -m conntrack --ctstate NEW -j DROP

### 3: Drop SYN packets with suspicious MSS value ###
/sbin/iptables -t mangle -A PREROUTING -p tcp -m conntrack --ctstate NEW -m tcpmss ! --mss 536:65535 -j DROP

### 4: Block packets with bogus TCP flags ###
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags FIN,SYN FIN,SYN -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags FIN,RST FIN,RST -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags FIN,ACK FIN -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ACK,URG URG -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ACK,FIN FIN -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ACK,PSH PSH -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL ALL -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL NONE -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL FIN,PSH,URG -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL SYN,FIN,PSH,URG -j DROP
/sbin/iptables -t mangle -A PREROUTING -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP

### 5: Block spoofed packets ###
/sbin/iptables -t mangle -A PREROUTING -s 224.0.0.0/3 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 169.254.0.0/16 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 172.16.0.0/12 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 192.0.2.0/24 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 192.168.0.0/16 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 10.0.0.0/8 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 0.0.0.0/8 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 240.0.0.0/5 -j DROP
/sbin/iptables -t mangle -A PREROUTING -s 127.0.0.0/8 ! -i lo -j DROP

### 6: Drop ICMP (useless protocol) ###
/sbin/iptables -t mangle -A PREROUTING -p icmp -j DROP

### 7: Drop fragments in all chains ###
/sbin/iptables -t mangle -A PREROUTING -f -j DROP

### 8: Limit connections per source IP ###
/sbin/iptables -A INPUT -p tcp -m connlimit --connlimit-above 111 -j REJECT --reject-with tcp-reset

### 9: Limit RST packets ###
/sbin/iptables -A INPUT -p tcp --tcp-flags RST RST -m limit --limit 2/s --limit-burst 2 -j ACCEPT
/sbin/iptables -A INPUT -p tcp --tcp-flags RST RST -j DROP

### 10: Limit new TCP connections per second per source IP ###
/sbin/iptables -A INPUT -p tcp -m conntrack --ctstate NEW -m limit --limit 60/s --limit-burst 20 -j ACCEPT
/sbin/iptables -A INPUT -p tcp -m conntrack --ctstate NEW -j DROP

### 11: Use SYNPROXY on all ports (disables connection limiting rule) ###
#/sbin/iptables -t raw -D PREROUTING -p tcp -m tcp --syn -j CT --notrack
#/sbin/iptables -D INPUT -p tcp -m tcp -m conntrack --ctstate INVALID,UNTRACKED -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460
#/sbin/iptables -D INPUT -m conntrack --ctstate INVALID -j DROP

Bonus: reglas adicionales

Las siguientes reglas de iptables son útiles para aumentar la seguridad global de un servidor Linux.

### SSH brute-force protection ###
/sbin/iptables -A INPUT -p tcp --dport ssh -m conntrack --ctstate NEW -m recent --set
/sbin/iptables -A INPUT -p tcp --dport ssh -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 10 -j DROP

### Protection against port scanning ###
/sbin/iptables -N port-scanning
/sbin/iptables -A port-scanning -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit --limit 1/s --limit-burst 2 -j RETURN
/sbin/iptables -A port-scanning -j DROP

Conclusión

Las guía de Constantin Oesterling muestra algunos de los métodos más eficaces para detener los ataques DDoS usando iptables. De hecho, dicen haber mitigado con éxito ataques de DDoS que han alcanzado incluso varios millones de paquetes por segundo utilizando estas reglas de iptables.

Como bien dicen, si se utiliza correctamente, iptables es una herramienta extremadamente poderosa que es capaz de bloquear diferentes tipos de ataques DDoS hacia una tarjeta de 1 GbE, e incluso casi de 10 GbE. 


¡No hay que subestimar el poder de iptables!

6 comentarios :

  1. muy bueno. unas observaciones
    rts genera muchos falsos positivos
    la regla de portscan se puede usar como alternativa psad o -m recent --name portscan --rcheck --seconds XXX (donde xxx son los segundos a banear y luego de ese tiempo remove)
    Podrias haber incluido proteccion anti-ddos a well-knows ports

    PD: SYNPROXY para deb en este enlace https://r00t-services.net/knowledgebase/14/Homemade-DDoS-Protection-Using-IPTables-SYNPROXY.html
    Y para sysctl.conf hay un proyecto muy bueno aqui
    https://klaver.it/linux/sysctl.conf

    ResponderEliminar
  2. hola vicente. Muy instructivo el post. Solo veo unos valores un poco altos. Supongo que es por lo del ataque...
    Por ejemplo:
    net.ipv4.neigh.default.gc_thresh3 = 16384 (Este valor haría que la tabla de arp se llene al punto que se puede volver inmanejable)
    net.ipv4.neigh.default.gc_thresh2 = 8192 (Depende. Para las redes /24 el valor es 1024. Si la red es superior, entonces se puede subir a 8192)
    net.ipv4.neigh.default.gc_thresh1 = 4096 (un poco alto)
    net.core.somaxconn = 65535 (Si el servidor aguanta todas esas conexiones entrantes....)
    net.ipv4.tcp_max_orphans = 400000 (Muy extremo)
    kernel.shmall = 4194304 y kernel.shmmax = 429496729 (Si hay suficiente memoria en el servidor...)
    PD; net.ipv4.ip_forward = 0... si tienes un proxy controlando las conexiones de la red local, hay que cambiarlo a 1
    saludos

    ResponderEliminar
    Respuestas
    1. hola Alej, es cierto que los valores del post son valores bastante altos pero quizás es mejor empezar así y luego ir afinando según las características de tu entorno (hardware servidor, direccionamiento IP, servicios, etc.)
      Aún así muy acertados tus comentarios, muchas gracias :)

      Eliminar
  3. Información muy útil la mostrada en este post, sencillamente genial. Como añadido, nosotros en la empresa utilizamos un script denominado viewDOS a la hora de identificar IPs. Es muy útil cuando se utilizan clusters en entornos web.

    https://github.com/Busindre/viewDOS

    ResponderEliminar
  4. Excelentes aportaciones amigos.. �� también existe un pequeño script para proteger de ataques DDoS.. Fork of DDoS Deflate with fixes, improvements and new features https://github.com/jgmdev/ddos-deflate

    ResponderEliminar