--- title: "Protección contra DDoS en Linux con nftables, sysctl y Nginx" description: "Protege un servidor Ubuntu 24.04 frente a DDoS con SYN cookies, límites por IP en nftables y rate limiting en Nginx, y aprende a detectar un ataque en curso." category: "Seguridad de Red Avanzada" language: es source: https://cubepath.com/es/docs/seguridad-de-red-avanzada/estrategias-de-proteccion-contra-ddos-para-servidores-linux updated: 2026-09-25 --- # Protección contra DDoS en Linux con nftables, sysctl y Nginx Un ataque de denegación de servicio distribuido (DDoS) intenta agotar un recurso de tu servidor: el ancho de banda del enlace, la tabla de conexiones del kernel o los procesos de la aplicación. Ningún ajuste en el servidor puede frenar un ataque que satura el enlace, pero sí puedes conseguir que los ataques más pequeños, las inundaciones de SYN y los abusos a nivel HTTP no tumben tus servicios. En este tutorial aplicarás esas defensas en capas en Ubuntu 24.04: ajustes del kernel, límites por IP con nftables y limitación de peticiones en Nginx, y aprenderás a reconocer un ataque en curso. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Nginx instalado si quieres aplicar la parte de la capa de aplicación. - Acceso a la consola de emergencia (VNC o similar) de tu proveedor, por si una regla de cortafuegos te deja sin acceso SSH. ## Qué puede mitigar el servidor y qué no Antes de tocar nada, conviene saber en qué capa se frena cada tipo de ataque: | Tipo de ataque | Qué agota | Dónde se mitiga | |----------------|-----------|-----------------| | Volumétrico UDP, amplificación DNS/NTP/memcached | Ancho de banda del enlace | En la red del proveedor o un servicio de mitigación, antes de llegar al servidor | | SYN flood | Cola de conexiones pendientes del kernel | Kernel (SYN cookies) y cortafuegos | | Muchas conexiones desde pocas IP | Tabla de conexiones, workers | Cortafuegos (límites por IP) | | HTTP flood, fuerza bruta en URLs costosas | CPU de la aplicación y la base de datos | Servidor web (rate limiting), caché, CDN o WAF | | Slowloris y conexiones lentas | Conexiones abiertas del servidor web | Tiempos de espera del servidor web | Si el tráfico de ataque supera la capacidad de tu enlace, los paquetes se pierden antes de llegar al kernel y nada de lo que configures en el servidor servirá. Para esos casos depende de la protección DDoS de la red de tu proveedor o de un CDN delante de tus servicios web. Lo que sigue en esta guía cubre las otras filas de la tabla. ## Paso 1: Medir el estado normal Para reconocer un ataque necesitas saber qué es lo normal en tu servidor. Consulta el resumen de sockets: ```bash ss -s ``` ```output Total: 312 TCP: 48 (estab 21, closed 9, orphaned 0, timewait 9) ``` Cuenta las conexiones en estado `SYN-RECV`, es decir, handshakes a medio completar: ```bash ss -Htn state syn-recv | wc -l ``` ```output 0 ``` Revisa los contadores del kernel relacionados con la cola de conexiones y las SYN cookies: ```bash nstat -az TcpExtSyncookiesSent TcpExtListenOverflows TcpExtListenDrops ``` ```output #kernel TcpExtSyncookiesSent 0 0.0 TcpExtListenOverflows 0 0.0 TcpExtListenDrops 0 0.0 ``` En un servidor sano, `SYN-RECV` se mantiene cerca de cero y estos contadores apenas crecen. Anota los valores habituales. ## Paso 2: Ajustar el kernel frente a SYN floods Las SYN cookies permiten al kernel responder a los SYN sin reservar memoria cuando la cola de conexiones pendientes se llena, lo que neutraliza la mayoría de las inundaciones de SYN. Ubuntu ya las activa por defecto, pero conviene fijarlas junto al resto de ajustes en un fichero propio: ```bash sudo nano /etc/sysctl.d/60-ddos.conf ``` ```ini # SYN cookies cuando se llena la cola de conexiones pendientes net.ipv4.tcp_syncookies = 1 # Cola de conexiones pendientes (SYN-RECV) más grande net.ipv4.tcp_max_syn_backlog = 8192 # Menos reintentos de SYN-ACK: las conexiones falsas se liberan antes net.ipv4.tcp_synack_retries = 2 # Cola de conexiones aceptadas por socket en escucha net.core.somaxconn = 8192 # Paquetes en cola por CPU antes de que el kernel los procese net.core.netdev_max_backlog = 16384 # Descartar paquetes con origen falsificado (filtro de ruta inversa) net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 # No aceptar redirecciones ICMP net.ipv4.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 ``` Aplica todos los ficheros de `/etc/sysctl.d`: ```bash sudo sysctl --system ``` Comprueba que los valores están activos: ```bash sysctl net.ipv4.tcp_syncookies net.ipv4.tcp_max_syn_backlog net.core.somaxconn ``` ```output net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_syn_backlog = 8192 net.core.somaxconn = 8192 ``` > Nota: `rp_filter = 1` (modo estricto) descarta paquetes que llegan por una interfaz distinta a la que se usaría para responder. Si tu servidor tiene varias interfaces con rutas asimétricas, usa el valor `2` (modo flexible). `somaxconn` es solo el límite máximo: cada aplicación pide su propia cola al abrir el puerto. En Nginx, por ejemplo, se ajusta con el parámetro `backlog` de la directiva `listen`. ## Paso 3: Limitar conexiones por IP con nftables Las reglas siguientes van en una tabla nftables propia, `inet ddos`, que solo descarta tráfico abusivo y deja pasar el resto. Así conviven con UFW o con cualquier otro cortafuegos que ya tengas: un paquete aceptado aquí sigue pasando por las reglas de UFW. Instala nftables si no está presente: ```bash sudo apt install -y nftables ``` Crea el fichero de reglas: ```bash sudo nano /etc/nftables-ddos.nft ``` ```nft #!/usr/sbin/nft -f # Crear y borrar la tabla permite recargar el fichero sin duplicar reglas table inet ddos delete table inet ddos table inet ddos { # IP que abren conexiones nuevas demasiado rápido set syn_rate_v4 { type ipv4_addr flags dynamic, timeout timeout 1m } set syn_rate_v6 { type ipv6_addr flags dynamic, timeout timeout 1m } # Recuento de conexiones simultáneas por IP hacia la web set web_conn_v4 { type ipv4_addr flags dynamic } set web_conn_v6 { type ipv6_addr flags dynamic } chain input { type filter hook input priority filter - 10; policy accept; # Paquetes que no pertenecen a ninguna conexión válida ct state invalid counter drop # Conexiones TCP nuevas que no empiezan con un SYN ct state new tcp flags & (fin|syn|rst|ack) != syn counter drop # Más de 30 conexiones nuevas por segundo desde una misma IP ct state new tcp flags syn update @syn_rate_v4 { ip saddr limit rate over 30/second burst 60 packets } counter drop ct state new tcp flags syn update @syn_rate_v6 { ip6 saddr limit rate over 30/second burst 60 packets } counter drop # Más de 100 conexiones simultáneas a HTTP/HTTPS desde una misma IP tcp dport { 80, 443 } ct state new add @web_conn_v4 { ip saddr ct count over 100 } counter drop tcp dport { 80, 443 } ct state new add @web_conn_v6 { ip6 saddr ct count over 100 } counter drop # Limitar el ping entrante icmp type echo-request limit rate over 10/second burst 20 packets counter drop icmpv6 type echo-request limit rate over 10/second burst 20 packets counter drop } } ``` Los umbrales son un punto de partida razonable para un servidor web. Si detrás de una misma IP hay muchos usuarios legítimos (una oficina con NAT, un proxy corporativo), súbelos. Comprueba la sintaxis sin cargar nada: ```bash sudo nft -c -f /etc/nftables-ddos.nft ``` Si no hay salida, la sintaxis es correcta. Carga las reglas: ```bash sudo nft -f /etc/nftables-ddos.nft ``` Verifica que la tabla existe y que puedes seguir abriendo sesiones SSH nuevas desde otra terminal antes de cerrar la actual: ```bash sudo nft list chain inet ddos input ``` ```output table inet ddos { chain input { type filter hook input priority filter - 10; policy accept; ct state invalid counter packets 12 bytes 480 drop ct state new tcp flags & (fin | syn | rst | ack) != syn counter packets 0 bytes 0 drop ... } } ``` ### Cargar las reglas al arrancar No uses `/etc/nftables.conf` para esto: su contenido por defecto empieza con `flush ruleset`, que borraría también las reglas de UFW. Crea una unidad de systemd que cargue solo este fichero: ```bash sudo nano /etc/systemd/system/nftables-ddos.service ``` ```ini [Unit] Description=Reglas nftables contra DDoS Wants=network-pre.target Before=network-pre.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/sbin/nft -f /etc/nftables-ddos.nft ExecReload=/usr/sbin/nft -f /etc/nftables-ddos.nft ExecStop=/usr/sbin/nft delete table inet ddos [Install] WantedBy=multi-user.target ``` Habilítala y comprueba su estado: ```bash sudo systemctl daemon-reload sudo systemctl enable --now nftables-ddos.service systemctl status nftables-ddos.service --no-pager ``` ```output ● nftables-ddos.service - Reglas nftables contra DDoS Loaded: loaded (/etc/systemd/system/nftables-ddos.service; enabled; preset: enabled) Active: active (exited) since Thu 2026-09-25 13:02:11 UTC; 3s ago ``` Cuando cambies los umbrales, recarga con `sudo systemctl reload nftables-ddos`. ## Paso 4: Limitar peticiones en Nginx Los ataques HTTP usan conexiones TCP completas y peticiones válidas, así que el cortafuegos no los distingue del tráfico normal. Nginx puede limitar el número de peticiones por segundo y de conexiones simultáneas por IP, y cerrar las conexiones que envían datos demasiado despacio. Crea un fichero en `conf.d`, que Ubuntu incluye dentro del bloque `http`: ```bash sudo nano /etc/nginx/conf.d/ddos.conf ``` ```nginx # Zonas compartidas: 10 MB guardan unas 160.000 IP limit_req_zone $binary_remote_addr zone=req_per_ip:10m rate=10r/s; limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m; limit_req_status 429; limit_conn_status 429; # Cortar clientes lentos (Slowloris) client_header_timeout 10s; client_body_timeout 10s; send_timeout 10s; keepalive_timeout 15s; ``` Después aplica los límites en el bloque `server` de tu sitio: ```bash sudo nano /etc/nginx/sites-available/your_domain ``` ```nginx server { listen 80; server_name your_domain; # 10 peticiones/s por IP con ráfagas de hasta 20 sin retrasarlas limit_req zone=req_per_ip burst=20 nodelay; # Máximo 20 conexiones simultáneas por IP limit_conn conn_per_ip 20; # ... resto de la configuración del sitio } ``` Puedes aplicar un límite más estricto solo en las rutas costosas, como el login o la búsqueda, poniendo `limit_req` dentro de su bloque `location`. Comprueba la configuración y recarga: ```bash sudo nginx -t sudo systemctl reload nginx ``` ```output nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` Para verificar el límite, lanza desde otra máquina una ráfaga de peticiones con `ab` (paquete `apache2-utils`): ```bash ab -n 300 -c 50 http://your_domain/ ``` En el resultado, la línea `Non-2xx responses` indica cuántas peticiones han recibido un 429. En el servidor puedes contarlas en el log: ```bash sudo awk '$9 == 429' /var/log/nginx/access.log | wc -l ``` ```output 262 ``` > Advertencia: si Nginx está detrás de un balanceador o un CDN, `$binary_remote_addr` es la IP del proxy y el límite se aplicaría a todos los visitantes a la vez. En ese caso configura antes el módulo `realip` para que Nginx use la IP real del cliente. ## Paso 5: Identificar un ataque en curso Cuando el servicio se degrada, estos comandos te dicen en pocos segundos qué está pasando. Compara los resultados con los que anotaste en el paso 1. Conexiones a medio abrir y contadores de SYN cookies. Si `SYN-RECV` es de miles y `TcpExtSyncookiesSent` crece rápido, es un SYN flood: ```bash ss -Htn state syn-recv | wc -l nstat -z TcpExtSyncookiesSent TcpExtListenDrops ``` IP con más conexiones establecidas al puerto 443: ```bash ss -Htn state established '( sport = :443 )' | awk '{sub(/:[0-9]+$/, "", $4); print $4}' | sort | uniq -c | sort -rn | head ``` ```output 412 198.51.100.23 38 192.0.2.77 3 203.0.113.8 ``` IP que envían más paquetes SYN, capturando 2.000 paquetes. Sustituye `eth0` por tu interfaz: ```bash sudo tcpdump -nn -i eth0 -c 2000 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0' 2>/dev/null | awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn | head ``` Paquetes descartados por cada regla de la tabla `ddos` y direcciones que están superando el límite ahora mismo: ```bash sudo nft list chain inet ddos input | grep counter sudo nft list set inet ddos syn_rate_v4 ``` Las peticiones más frecuentes en Nginx durante los últimos minutos, útiles para detectar un HTTP flood contra una URL concreta: ```bash sudo tail -n 20000 /var/log/nginx/access.log | awk '{print $1, $7}' | sort | uniq -c | sort -rn | head ``` Si identificas unas pocas IP responsables, bloquéalas de forma temporal con UFW (`sudo ufw deny from 198.51.100.23`) o con Fail2Ban. Si el tráfico procede de miles de direcciones o el enlace está saturado, el bloqueo tiene que hacerse aguas arriba: contacta con el soporte de tu proveedor con las IP de destino afectadas, la hora de inicio y el tipo de tráfico que ves. ## Solución de problemas - **Usuarios legítimos reciben errores 429**: los umbrales de `limit_req` son demasiado bajos para tu tráfico. Sube `rate` o `burst`, o aplica el límite solo en las rutas costosas. - **Las conexiones desde una oficina o un proxy fallan**: muchos usuarios comparten IP y superan el límite `ct count` de nftables o el `limit_conn` de Nginx. Aumenta los umbrales o añade una regla `ip saddr 192.0.2.0/24 accept` al principio de la cadena `input` de la tabla `ddos` para esa red. - **`nft -c` da `Error: Could not process rule`**: suele indicar que falta un módulo del kernel o una errata en la regla. Comprueba el número de línea del error y que el kernel es el de Ubuntu 24.04 (`uname -r`). - **Te has quedado sin acceso SSH**: entra por la consola de emergencia del proveedor y ejecuta `sudo systemctl stop nftables-ddos`, que borra la tabla. ## Conclusión Tu servidor responde ahora mejor ante SYN floods gracias a las SYN cookies y a una cola de conexiones mayor, descarta en nftables a las IP que abren demasiadas conexiones y limita en Nginx las peticiones por cliente. También sabes cómo medir el estado normal y reconocer un ataque en curso. Como siguientes pasos, puedes usar Fail2Ban para bloquear de forma automática las IP que acumulan respuestas 429, poner un CDN delante de tus sitios web para absorber ataques de capa 7 y monitorizar los contadores del paso 5 con tu sistema de métricas para recibir alertas.