DDoS: Mitigando denegación de servicio con IPtables
En esta oportunidad veremos algunos tips y técnicas para mitigar o reducir el efecto de los ataques de denegación de servicio distribuidos, o DDoS por sus siglas en inglés, utilizando el firewall iptables en un sistema GNU/Linux.
En la guía se analizarán algunos puntos importantes:
- Utilizar las cadenas y tablas de iptables para bloquear ataques DDoS.
- Tunear algunas configuraciones del kernel para mitigar los ataques DDoS.
- Usar iptables para bloquear los ataques DDoS basados en TCP.
- Usar iptables para bloquear los floods o inundación de paquetes SYN mediante SYNPROXY.
Este artículo explica algunos métodos que pueden llegar a ser útiles para protección de ataques DDoS en un servidor/firewall, pero cabe aclarar que los ataques de denegación de servicio, y más los grandes, resultan casi imposible de mitigar, incluso para firewalls por hardware especializados. No obstante, con iptables se pueden hacer muchas cosas útiles!
El artículo está orientado a profesionales con conocimientos medios en GNU/Linux, por lo que no se ahondarán las bases de iptables. Sin embargo, si estás interesado en aprender, puedes apuntarte al curso de iptables que dictamos online desde @juncotic.
Sin entrar demasiado en detalles, iptables representa la utilidad de nivel de usuario para administrar y configurar el filtrado de paquetes del kernel Linux, y representa la herramienta por defecto de prácticamente cualquier distribución de este sistema operativo.
Si bien generalmente se usa para bloquear puertos o direcciones IP, también brinda una gran cantidad de opciones de configuración que permiten, entre otras cosas, bloquear, o al menos limitar los efectos de ataques de denegación de servicio y denegación de servicio distribuido.
Configuración del kernel
Antes que nada es importante traer algunas configuraciones del kernel Linux que permiten mejorar la performance y mitigar mejor los ataques de DDoS.
Casi todas las distros actuales, o mejor, desde el kernel Linux v3.12, iptables incorpora un módulo denominado SYNPROXY. SYNPROXY es un módulo de netfilter, en el núcleo Linux, y está optimizado para gestionar millones de paquetes por segundo utilizando toda la CPU disponible sin tener problemas de bloqueos de concurrencia entre conexiones.
En un futuro artículo hablaremos más detenidamente de SYNPROXY, sus características y funcionamiento.
Volviendo al objetivo del apartado, configuraciones del núcleo, aquí pondré algunas interesantes (fuente)
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
Estas configuraciones tienden a maximizar el rendimiento del sistema bajo un ataque de DDoS, y mejora la efectividad de las reglas de iptables que establezcamos. En otro artículo específico explicaré más en detalle estas líneas.
Bloqueando ataques de DDoS con IPtables
Ahora veremos algunos ejemplos de reglas que nos pueden servir para bloquear o mitigar muchos de los ataques de DDoS.
Cabe aclarar que los tipos de ataques de DDoS son muchos, e intentar bloquearlos a todos ellos resulta casi imposible. Afortunadamente disponemos del módulo del kernel denominado nf_conntrack, que puede ayudar a mitigar muchos ataques basados en TCP que no usan paquetes con flag SYN.
Esto incluye los ataques de ACK o SYN-ACK y ataques basados en los flags falsos o erróneos de TCP.
Bloquear paquetes inválidos
Lo primero es bloquear todos los paquetes inválidos, es decir, aquellos que no sean paquetes SYN (de establecimiento de conexión) y que tampoco pertenezcan a ninguna conexión activa:
iptables -t mangle -A PREROUTING -m conntrack --ctstate INVALID -j DROP
Bloquear paquetes nuevos que no son SYN
Ahora a bloquear todos los paquetes que no pertenecen a ninguna conexión establecida, y además no usan el flag SYN. Similar a la anterior, pero captura algunos paquetes que no filtra la regla anterior:
iptables -t mangle -A PREROUTING -p tcp ! --syn -m conntrack --ctstate NEW -j DROP
Bloquear valores anoramales de MSS
Vamos a bloquear ahora paquetes nuevos, es decir, aquellos que contengan el flag SYN activado, pero que además tengan un valor de MSS (Maximum Segment Size) fuera de lo normal, ya que los paquetes SYN suelen ser pequeños, y paquetes SYN grandes podrían indicar inundación de SYN (SYN flood):
iptables -t mangle -A PREROUTING -p tcp -m conntrack --ctstate NEW -m tcpmss ! --mss 536:65535 -j DROP
Bloquear paquetes con flags erróneos
Ahora algunas reglas para filtrar paquetes con flags erróneos, aquellos flags o combinaciones de los mismos que un paquete normal no utilizaría.
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 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
Bloquear paquetes de subredes privadas
Esto es importante para evitar el spoofing o inundación de tráfico por parte de atacantes. Estas reglas bloquean tráfico que proviene de direcciones IP’s privadas, ya que usualmente en la interfaz WAN no se recibe tráfico privado, sino aquel proveniente de direcciones IP’s públicas.
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
Se asume que el equipo utiliza como dirección de loopback una del rango 127.0.0.0/8.
Bloquear tráfico ICMP
Bloqueamos todo el tráfico ICMP entrante. Si bien algunos tipos de mensajes ICMP pueden llegar a ser útiles en caso de congestión de red o fragmentación, en general no suelen haber inconvenientes con bloquear todo el tráfico ICMP entrante con una regla similar a esta:
iptables -t mangle -A PREROUTING -p icmp -j DROP
Esta regla, además de bloquear el ping, también bloquea los paquetes de inundación icmp, icmp de fragmentación, y por supuesto, el ping flood, o «Ping de la muerte» (Ping of Death).
Bloquear tráfico por cantidad de conexiones
Otra regla interesante es la que permite bloquear a equipos que superan un umbral determinado de cantidad de conexiones establecidas. Por ejemplo, si un host en Internet establece 85 conexiones contra el puerto 80 de nuestro servidor web, seguramente sea un tipo de ataque, y podemos bloquearlo con algo como esto:
iptables -A INPUT -p tcp -m connlimit --connlimit-above 85 -j REJECT --reject-with tcp-reset
Bloquear tráfico por cantidad de conexiones por unidad de tiempo
Otro punto importante es el de limitar la cantidad de conexiones por unidad de tiempo, y por ráfaga, desde una misma dirección IP. Esto es interesante ya que muchas veces un atacante intentará inundar de tráfico nuestros servidores intentando establecer muchas conexiones. Así, por ejemplo, si quisiéramos limitar a que una máquina remota pueda establecer como máximo 50 conexiones por segundo, con una ráfaga de 18 conexiones entre segundos, podríamos crear dos reglas en este orden:
iptables -A INPUT -p tcp -m conntrack --ctstate NEW -m limit --limit 50/s --limit-burst 18 -j ACCEPT
iptables -A INPUT -p tcp -m conntrack --ctstate NEW -j DROP
Bloquear paquetes fragmentados
Adicionalmente, una buena técnica es la de bloquear también los paquetes fragmentados. Normalmente no es necesario, pero a veces la inundación de paquetes UDP fragmentados pueden consumir mucho ancho de banda y producir una denegación de servicio. Para mitigar esto podemos descartar todo el tráfico fragmentado con algo así:
iptables -t mangle -A PREROUTING -f -j DROP
Bloquear tráfico TCP RST
Por otro lado, una buena idea puede ser limitar también los floods de paquetes taggeados con el flag de reset RST en TCP, aunque acá podría necesitarse un análisis más profundo:
iptables -A INPUT -p tcp --tcp-flags RST RST -m limit --limit 4/s --limit-burst 4 -j ACCEPT
iptables -A INPUT -p tcp --tcp-flags RST RST -j DROP
Usando SYNPROXY para mitigar los SYN Floods
Como se mencionó arriba, SYNPROXY es un nuevo target de iptables desde la v3.12 del kernel Linux (iptables v1.4.21) en la mayoría de las distros.
Su objetivo principal es el de verificar si el equipo remoto terminó de establecer la conexión (handshake) de TCP luego de enviar el primer segmento SYN. Si luego de este primer segmento no hubo ningún otro envío, SYNPROXY descarta el mensaje.
Las reglas anteriores ya bloquean la mayoría de los ataques de DDoS basados en mensajes TCP, pero la inundación SYN es un poco más complicada de mitigar, y aquí es que entra en acción SYNPROXY.
Siempre, esto como aclaración, el rendimiento de iptables será mayor si pueden encontrarse reglas que permitan, mediante una selección de patrones, bloquear tráfico específico, sin embargo a veces esto es difícil de lograr sin una herramienta como SYNPROXY.
Veamos un ejemplo simple brevemente explicado… SYNPROXY da para un artículo entero (o más :P):
iptables -t raw -A PREROUTING -p tcp -m tcp --syn -j CT --notrack
iptables -A INPUT -p tcp -m tcp -m conntrack --ctstate INVALID,UNTRACKED -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
La primer regla permite configurar algunos parámetros asociados a conexiones. En este caso, desactiva el tracking de conexiones para los paquetes SYN que llegan al firewall. El target CT únicamente está disponible en la tabla raw.
La segunda regla matchea con todo el tráfico tcp cuyo estado de conexión es INVALID o UNTRACKED (como los paquetes SYN que pasan por la regla 1). Esta regla ejecuta el target SYNPROXY y le manda a SYNPROXY el paquete SYN del cliente. SYNPROXY intenta establecer el handshake respondiendo con el SYN+ACK al cliente. El cliente «legal» responderá con su ACK. Si esto ocurre, es decir, SYNPROXY verifica que el cliente intentó establecer una conexión legal, SYNPROXY establece el handshake contra el servidor real, y una vez resuelto, deja al cliente interactuando contra el server.
Estas reglas sirven para todos los puertos, pero pueden restringirse a puertos específicos con los parámetros de selección que ya conocemos.
En el caso de que SYNPROXY no pueda establecer la conexión, descarta el tráfico del cliente y el servidor real nunca recibe ni siquiera el mensaje SYN (espero se entienda eso… sí, ya estoy pensando en un nuevo artículo de SYNPROXY detallado :)).
Conclusiones
Para terminar, un par de reglas que pueden servir para mitigar los escaneos de puertos, y con ello, mejorar la seguridad en los servidores, aunque no tenga que ver específicamente con DDoS, quizás si un atacante no ve (o le resulta difícil ver) los puertos abiertos, va a tener que sortear un escollo más a la hora de establecer un ataque de DDoS.
iptables -N port-scan
iptables -A port-scan -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit --limit 1/s --limit-burst 3 -j RETURN
iptables -A port-scan -j DROP
Espero que resulte de interés! Y por supuesto, cualquier duda que tengan espero sus comentarios!
Como mencioné en el artículo, en un futuro escribiré extensiones de este post para hablar de algunos temas puntuales, como configuraciones del núcleo, y detalles del SYNPROXY.
Hasta la próxima!!