Proteger un servidor de Rust frente a ataques DDoS

Publicado el 17 min de lectura

Los servidores de Rust reciben ataques casi siempre en el wipe o en mitad de un raid. Lo que puedes asegurar tú mismo, dónde topa la protección propia con límites físicos y qué tiene que ocurrir por delante, en la red.

Un servidor de Rust rara vez se cae por casualidad. La hora del ataque delata casi siempre el motivo: empieza en el minuto exacto del wipe o en mitad de un raid. Quien está recibiendo los paquetes no necesita un debate de fondo, sino un orden de trabajo. Este artículo muestra primero lo que puedes cambiar tú mismo en el servidor, después dónde se acaban esas posibilidades y, al final, lo que tiene que ocurrir por delante, en la red.

Por qué los servidores de Rust reciben ataques con tanta frecuencia

Rust es un juego en el que el progreso está atado al tiempo. Un raid dura minutos, un ciclo de wipe dura semanas. Por eso una caída vale aquí más que en casi cualquier otro juego: quien defiende gana tiempo si el servidor se cae. Quien ataca impide que el bando contrario llegue a conectarse. Y quien lleva una comunidad rival sabe que la primera noche de wipe decide el número de jugadores de todo el mes.

A eso se suma un punto que no se arregla configurando: un servidor de Rust es localizable en público por dirección IP y puerto, porque de otro modo nadie podría entrar. A diferencia de una web detrás de un proxy, un servidor de juego tiene que publicar su dirección real. La pregunta nunca es, por tanto, si el atacante va a encontrar tu IP, sino qué pasa cuando dispare contra ella.

Los puertos que importan

En la configuración habitual, un servidor de Rust ocupa cuatro puertos:

  • 28015/UDP, el puerto de juego (server.port). Por ahí pasa todo el tráfico de juego. UDP no establece ninguna conexión previa, cada paquete va por su cuenta y la dirección de origen se puede falsificar. Para un atacante eso significa dos cosas: ningún rastro que seguir y, aun así, trabajo para tu servidor con cada paquete.
  • El puerto de consulta (server.queryport), también UDP. Por él responde el servidor a las consultas de Steam A2S_INFO, A2S_PLAYERS y A2S_RULES, y sin él no aparece en ninguna lista de servidores. Si no le das un valor explícito, queda justo al lado del puerto de juego, y muchas líneas de arranque lo fijan en 28017/UDP. Mira tu propia línea de arranque en lugar de fiarte de un valor por defecto.
  • 28016/TCP, RCON (rcon.port), en la variante WebSocket con rcon.web 1.
  • 28082/TCP, la aplicación complementaria Rust+ (app.port).

El puerto de consulta es el más incómodo de los cuatro, porque una respuesta A2S es bastante más grande que la petición. Un atacante puede consultar servidores de juego ajenos con la dirección de origen falsificada y dirigir las respuestas hacia su verdadero objetivo. Tu servidor deja entonces de ser solo víctima y pasa a ser amplificador contra terceros. Por eso Valve añadió a A2S_INFO una consulta de desafío, algo que rebaja el problema pero no lo elimina. Cómo se reconoce un ataque lo explica el artículo Detectar un ataque DDoS en el servidor.

Lo que puedes hacer tú mismo antes de gastar dinero

Lo que viene ahora no cuesta nada y merece la pena esté donde esté tu servidor. No te va a quitar de encima un ataque volumétrico, pero hace que los ataques pequeños se queden en nada y que, llegado el momento, no tengas que adivinar.

1. Inventario: qué está escuchando de verdad

Antes de escribir una sola regla, aclara qué servicios son accesibles. En un servidor de juego que lleva tiempo en marcha casi siempre son más de los que esperabas:

ss -lntup

Todo lo que esté enlazado a 127.0.0.1 o a ::1 no necesita ninguna apertura. Todo lo que esté en 0.0.0.0 o en [::] es accesible desde internet, incluido el servicio de base de datos que trajo consigo algún plugin. Compara la salida con tu línea de arranque:

./RustDedicated -batchmode -nographics \
  +server.port 28015 \
  +server.queryport 28017 \
  +server.identity "wipe" \
  +server.maxplayers 150 \
  +rcon.port 28016 \
  +rcon.web 1 \
  +rcon.password "TU-CONTRASEÑA-LARGA-Y-ALEATORIA"

Si tu instalación de Rust llegó a través de SteamCMD, para la capa de debajo te sirve el artículo Instalar un servidor de juego con SteamCMD.

2. Deja abiertos solo los puertos que Rust necesita de verdad

Cuatro puertos, ni uno más. RCON no pinta nada en internet abierto y va limitado a tu dirección, y Rust+ solo se abre si usas la aplicación complementaria:

ufw allow 28015/udp comment "Rust puerto de juego"
ufw allow 28017/udp comment "Rust Query"
ufw allow from 203.0.113.10 to any port 28016 proto tcp comment "Rust RCON"
ufw allow 28082/tcp comment "Rust Companion"

Sustituye 203.0.113.10 por tu propia dirección. Si tu dirección cambia cada poco, el camino pasa por un túnel SSH.

Un aviso que cada año se lleva por delante unos cuantos servidores: el orden en el que activas un firewall decide si te quedas fuera de tu propio servidor. Ese orden, con la vía de vuelta incluida, está en el artículo Configurar el firewall UFW. Y si aun así ocurre: en los servidores root KVM y en los servidores dedicados de KernelHost llegas al servidor por la consola VNC del área de cliente. No depende del stack de red del sistema invitado y ninguna regla de firewall de dentro del invitado puede bloquearla.

3. Asegura el puerto de consulta sin salir de la lista de servidores

El reflejo más inmediato, cerrar el puerto de consulta, es el error más caro de todo este asunto. Sin él tu servidor desaparece del navegador de servidores, muestra números de jugadores falsos y las páginas de listados lo dan por offline. Habrías rematado tú mismo el ataque.

Lo correcto es un límite de tasa por dirección de origen. Un cliente real consulta unas pocas veces por segundo mientras hojea la lista, mientras que una herramienta de reflexión lo hace miles de veces. Con nftables, en una tabla propia que se evalúa antes de la cadena de filtrado:

table inet rust {
    chain input {
        type filter hook input priority -10; policy accept;
        udp dport 28017 meter rustquery { ip saddr limit rate over 15/second } drop
    }
}

El archivo se carga con nft -f. La prioridad -10 hace que la regla actúe antes de la cadena de filtrado que UFW crea con prioridad 0. Con el iptables clásico, el módulo hashlimit consigue lo mismo:

iptables -A INPUT -p udp --dport 28017 -m hashlimit \
  --hashlimit-name rustquery --hashlimit-mode srcip \
  --hashlimit-above 15/sec --hashlimit-burst 30 -j DROP

Empieza con un margen amplio y aprieta el límite solo cuando tengas comprobado que las consultas legítimas pasan. Un límite demasiado estrecho, si no, se nota justo el día del wipe.

4. Alivia el seguimiento de conexiones

Este punto se pasa por alto casi siempre y explica caídas que parecen un ataque de volumen sin serlo. El kernel crea entradas en el seguimiento de conexiones (conntrack) para el tráfico UDP y, con direcciones de origen falsificadas, cada dirección nueva significa una entrada nueva. Cuando la tabla se llena, el kernel descarta paquetes sin distinguir: el ataque y tus jugadores se van fuera juntos. En el log del sistema aparece entonces nf_conntrack: table full, dropping packet. Se comprueba así:

sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
dmesg | grep -i conntrack

El paso más eficaz es no dejar que el tráfico de juego llegue siquiera a seguirse. Rust no necesita para eso ningún seguimiento de estado en el kernel, porque gestiona sus sesiones por su cuenta:

table inet raw {
    chain prerouting {
        type filter hook prerouting priority raw; policy accept;
        udp dport 28015 notrack
    }
}

Con iptables, el equivalente es:

iptables -t raw -A PREROUTING -p udp --dport 28015 -j NOTRACK

Solo después de eso merece la pena subir nf_conntrack_max. Quien empieza agrandando la tabla no hace más que aplazar el problema unos minutos, y encima gasta RAM en ello.

5. Búferes de recepción y parámetros del kernel

Si los paquetes llegan más rápido de lo que el proceso de Rust los recoge, el búfer de recepción del socket se desborda. Para los jugadores eso se ve como pérdida de paquetes, aunque la línea esté libre:

net.core.rmem_max = 16777216
net.core.rmem_default = 1048576
net.core.netdev_max_backlog = 16384

Guarda los valores en /etc/sysctl.d/ y actívalos con sysctl -p. Si hacen falta o no te lo dice el propio kernel: si UdpRcvbufErrors sube en nstat -az, o si en ss -lunp hay algo parado de forma permanente en la cola de recepción, entonces sirven de algo. Si los dos se quedan a cero, el ajuste no cambia nada. Esto es margen, no protección.

6. Asegura RCON

Un puerto RCON abierto con una contraseña débil no es un problema de DDoS, sino una toma de control: quien tiene RCON puede banear, desbanear y parar el juego. No dejes nunca rcon.password vacío ni elijas algo que se pueda adivinar, porque un valor de openssl rand -base64 32 se genera en cinco segundos. Y no abras el puerto al público, limítalo a tu dirección.

7. Medidas por el lado del anti-cheat y de los plugins

Buena parte de las caídas que los operadores comunican como DDoS no lo son. Son cierres inesperados que un único cliente provoca con unos pocos cientos de paquetes, porque en el binario del servidor o en algún plugin hay un agujero abierto. Contra eso ayuda el mantenimiento, no el ancho de banda:

  • Mantén al día el binario del servidor. La actualización mensual que obliga al wipe es al mismo tiempo una actualización de seguridad. Quien la retrasa se queda con los fallos ya conocidos.
  • Mantén al día el framework de plugins. Oxide/uMod y Carbon se ponen al día después de cada actualización de Rust. Un framework que no encaja con la versión del servidor es el motivo más habitual de las caídas de la noche del wipe.
  • Menos plugins. Cada plugin es código adicional dentro del mismo proceso. Los plugins con servicios web propios (visores de mapa, páginas de estadísticas) abren más puertos y, de paso, publican muchas veces justo la dirección que quieres proteger.
  • Mantén las listas de baneos. Los intentos de conexión repetidos desde la misma cuenta se cortan con los medios propios de Rust. Rust guarda propietarios y moderadores en server/<identity>/cfg/users.cfg, y los baneos en server/<identity>/cfg/bans.cfg. Un baneo puesto con banid sobrevive al reinicio.

Rust no trae whitelist de serie, llega a través del framework de plugins. Para un servidor privado o de comunidad es eficaz. Para un servidor público de wipe no es una opción: un servidor en el que nadie puede entrar está igual de vacío que uno que está offline.

8. La lista de servidores y tu propia dirección

La IP pública del servidor de juego no se puede cambiar, pero todo lo que hay a su alrededor sí. Muchas veces el atacante encuentra de golpe todo el entorno: el servidor web con la tienda, el host del bot de Discord, el servidor de backups, el acceso al panel. Esas direcciones no deberían aparecer ni en el mismo anuncio ni en registros DNS antiguos. Revisa una vez al trimestre qué subdominio apunta a dónde.

9. Registra datos para no tener que adivinar durante el ataque

Durante un ataque solo cuenta una pregunta: cuánto está llegando y a qué puerto. Con tres comandos basta:

ip -s link show eth0
nstat -az | grep -i udp
journalctl -u rust-server -f

El primer comando muestra paquetes, errores y descartes por interfaz. Ejecútalo dos veces con diez segundos de diferencia y tendrás una tasa en lugar de un valor absoluto. Puede que en tu servidor la interfaz y la unidad de servicio se llamen de otra manera, así que comprueba las dos con ip -br link y systemctl list-units --type=service. Qué aspecto tienen los paquetes lo enseña una muestra, que conviene que sea corta, porque una captura bajo carga cuesta tiempo de CPU:

tcpdump -ni eth0 -c 200 "udp port 28015"

Dónde se acaba la protección propia

Ahora la parte honesta. Todo lo descrito hasta aquí solo actúa cuando los paquetes ya han llegado a tu tarjeta de red. Un servidor cuelga normalmente de una conectividad de 1 Gbit/s o de 10 Gbit/s. Con el tamaño de paquete más pequeño posible, una línea de 1 Gbit/s transporta unos 1,49 millones de paquetes por segundo y una de 10 Gbit/s unos 14,88 millones. Ese es el techo físico, con independencia de la CPU, del kernel y del firewall.

Enfrente están los ataques reales. Dos ejemplos del día a día en KernelHost, los dos filtrados en tiempo real: un flood UDP contra un servidor de juego de ARK en el puerto 7777/UDP con más de 112,2 Gbit/s y más de 8,7 millones de paquetes por segundo, y un ataque multivector contra un servidor de voz en el puerto 9987/UDP con más de 473,4 Gbit/s y más de 41,5 millones de paquetes por segundo.

Compara eso con tu línea: 473,4 Gbit/s son unas 470 veces una conectividad de 1 Gbit/s y todavía unas 47 veces una de 10 Gbit/s. Por muy correcta que sea tu regla, no llega a ejecutarse nunca, porque la pérdida se produce en el router que hay delante. Y mucho antes de que la línea se llene, la CPU ya está al límite: cada paquete cuesta una interrupción y un recorrido por el stack de red, aunque después se descarte.

Por eso los dos frenos de emergencia más extendidos resultan igual de insatisfactorios. El null-routing (blackholing) saca de la red la IP atacada y termina con el ataque, sí, pero también con tu servidor. Y un desvío reactivo hacia un sistema de filtrado se lleva, en el tiempo de conmutación, exactamente los minutos en los que se decide el raid. Lo único eficaz es un filtrado que corra de forma permanente en la red, por delante del servidor.

Lo que KernelHost coloca por delante

La protección permanente que corre en cada servidor

La protección DDoS de KernelHost está montada en dos niveles y está activa de forma permanente, sin que tengas que encender nada. El primer nivel es una red global de scrubbing con 17 Tbps de capacidad de mitigación, que intercepta los ataques volumétricos cerca de su origen, antes de que lleguen al centro de datos. El segundo nivel es un filtrado Arbor en tiempo real con 3,2 Tbps directamente sobre el terreno, en Frankfurt am Main, que se encarga del trabajo fino a nivel de protocolo y descarta los patrones complejos en las capas 3 a 7.

Hay dos puntos decisivos. Primero, el filtrado corre de forma permanente, así que no hay un tiempo de detección y de conmutación en el que tus jugadores se queden fuera. Segundo, no se recurre al null-routing: la dirección IP atacada sigue en la red y solo caen los paquetes maliciosos. La protección viene incluida en todos los paquetes de servidor sin recargo, sin un paquete de protección aparte y sin nada que configurar. Los servidores están en el maincubes Premium Datacenter de Frankfurt am Main (Alemania), certificado TÜV TIER3+ y conectado directamente al DE-CIX. El proveedor es KernelHost GmbH, con sede en Viena (Austria). Qué juegos y protocolos están cubiertos lo enumera el artículo Protección DDoS para servidores de juego en tiempo real.

Advanced DDoS Protection para proyectos bajo ataque permanente

A algunos proyectos de Rust no los atacan de vez en cuando, sino durante semanas y de forma dirigida, con patrones cambiantes y siempre justo en el wipe. Para esos casos está la Advanced DDoS Protection desde 50,00 € al mes, PrePaid y sin permanencia mínima. Aporta tres cosas que la protección permanente incluida no ofrece:

  • Una IP de protección dedicada. Tu servidor se pasa a ella dentro de nuestra propia red, así que por tu parte no hay nada que reconstruir.
  • Reglas de protección que gestionas tú mismo, por puerto y protocolo. En el área de cliente decides qué puerto se filtra con qué perfil, por ejemplo el 28015/UDP de una forma y el puerto de consulta de otra. Los cambios surten efecto en tiempo real, sin ticket y sin esperas.
  • Un perfil de protección ajustado a cada juego. Para Rust y también para más de 40 juegos, servicios y protocolos adicionales, además de perfiles TCP y UDP de libre asignación para servidores modificados.

Aquí también se aplica el modelo PrePaid: sin permanencia mínima, sin plazo de preaviso, sin contrato y sin cuota de alta. Cuando pase la oleada de ataques, simplemente no renuevas.

Los dos niveles en comparación

Característica Protección permanente incluida Advanced DDoS Protection
Precio Incluida en todos los paquetes de servidor, sin recargo desde 50,00 € al mes, PrePaid y sin permanencia mínima
Activación Activa desde el primer minuto, no hay nada que configurar La pides, recibes la IP de protección y tu servidor se pasa a ella
Dirección IP La IP del servidor, de la red de Fráncfort IP de protección dedicada adicional
Filtrado 17 Tbps de scrubbing global, más 3,2 Tbps de filtrado Arbor en tiempo real en Frankfurt am Main El mismo filtrado y, además, reglas propias por puerto y protocolo
Cambiar las reglas Las mantiene KernelHost, el ajuste fino se pide por ticket Tú mismo en el área de cliente, con efecto en tiempo real
Perfiles de juego Más de 40 juegos y protocolos Perfil elegible por puerto, también para servidores modificados
Null-routing durante el ataque No No
Recomendable para Cualquier servidor, desde el primer wipe Proyectos atacados de forma continua y dirigida

Errores habituales y sus soluciones

El servidor ha desaparecido del navegador de servidores pero sigue funcionando: casi siempre el puerto de consulta está cerrado o el límite de tasa es demasiado estrecho. Comprueba con ss -lunp si está escuchando y afloja el límite paso a paso. Si Rust+ se queda mudo, lo normal es que app.port esté cerrado.

Todos los jugadores tienen mucho ping y rubberbanding, pero la línea no está llena: eso apunta a la tasa de paquetes y no al volumen. Mira los paquetes descartados en ip -s link show y los contadores UDP en nstat -az. Un búfer de recepción lleno o un seguimiento de conexiones agotado producen exactamente esa imagen.

La regla del firewall es correcta y aun así no sirve de nada: entonces la línea que hay delante del servidor está saturada. Una regla que no llega a ejecutarse nunca, porque el paquete ya cayó en el router anterior, no puede conseguir nada. A partir de ese punto solo ayuda el filtrado en la red.

Después de activar el firewall ya no hay acceso por SSH: inicia sesión por la consola VNC del área de cliente. Desde ahí puedes desactivar el firewall y añadir la regla que falta, aunque por la red ya no funcione nada.

El ataque se detiene tras un cambio de IP y vuelve al cabo de uno o dos días: eso es lo normal. Tu propio servidor publica la nueva dirección en la lista de servidores en cuanto vuelve a estar online. Un cambio de IP te da horas, no una solución.

En el servidor se están ejecutando comandos de administrador ajenos: no es un DDoS, sino un acceso RCON comprometido. Cambia la contraseña de inmediato, limita el puerto a tu dirección y revisa la lista de baneos.

Si te están atacando ahora mismo

Si tu servidor ya está en KernelHost, el filtrado está activo de forma permanente y no tienes que encender nada. Si aun así notas algo raro, abre un ticket de soporte para que nuestro equipo reajuste las reglas de filtrado de tu IP. Durante un ataque en curso puedes contactarnos además por el chat de emergencia de WhatsApp en el +43 650 8209883.

Indica ya de entrada cuatro datos: dirección IP, puerto, franja horaria en tu zona horaria y, en pocas palabras, qué estás viendo (jugadores que se caen, servidor inaccesible, ping alto). Eso ahorra una ronda de preguntas, y esa ronda cuenta cuando el wipe está en marcha.

Preguntas frecuentes

Mi servidor de Rust no está accesible ahora mismo: ¿es un ataque?
Mira primero la interfaz de red. Si en "ip -s link show" suben con fuerza los paquetes descartados y en "nstat -az" los errores UDP, mientras la CPU del proceso de Rust se mantiene normal, eso apunta a un ataque. Si los dos valores se quedan tranquilos y el proceso ha desaparecido, fue una caída del propio proceso.
¿Qué puertos tiene que tener abiertos un servidor de Rust?
El 28015/UDP para el tráfico de juego, el puerto de consulta (server.queryport, a menudo 28017/UDP) para la lista de servidores, el 28016/TCP para RCON y el 28082/TCP solo si usas la aplicación complementaria Rust+. RCON va limitado a tu propia dirección IP y todo lo demás se queda cerrado.
¿Sirve de algo bloquear sin más el puerto de consulta?
No, además hace daño. Sin un puerto de consulta accesible, tu servidor desaparece del navegador de servidores y las páginas de listados lo dan por offline. Lo que sí funciona es un límite de tasa por dirección de origen, por ejemplo con un meter de nftables o con el módulo hashlimit de iptables.
¿Ayuda un cambio de IP contra el ataque que está en curso?
Solo un rato. Tu propio servidor vuelve a publicar la nueva dirección en la lista de servidores en cuanto está online. En la práctica, el ataque vuelve al cabo de uno o dos días. Un cambio de IP te da horas, pero no resuelve nada.
¿Puedo protegerme con un firewall en el propio servidor?
Contra los ataques pequeños sí, contra los volumétricos no. Tus reglas no se ejecutan hasta que los paquetes han llegado a la tarjeta de red. Una línea de 1 Gbit/s transporta unos 1,49 millones de paquetes pequeños por segundo, y los ataques reales están muy por encima de esa cifra. La pérdida se produce entonces ya en el router que hay delante.
¿KernelHost saca mi IP de la red durante un ataque?
No. No se recurre ni al null-routing ni al blackholing. La dirección IP atacada sigue en la red y solo caen los paquetes maliciosos. El filtrado corre de forma permanente, así que tampoco hay un tiempo de conmutación al principio de un ataque.
¿Qué incluye la protección DDoS y cuánto cuesta el nivel Advanced?
La protección permanente de dos niveles viene incluida en todos los paquetes de servidor sin recargo: 17 Tbps de capacidad de mitigación en la red global de scrubbing más 3,2 Tbps de filtrado Arbor en tiempo real en Frankfurt am Main. La Advanced DDoS Protection, con IP de protección dedicada y reglas propias por puerto, cuesta desde 50,00 € al mes, PrePaid, sin permanencia mínima y sin cuota de alta.
¿Qué pongo en el ticket si el ataque está en curso?
Para empezar bastan cuatro datos: la dirección IP afectada, el puerto, la franja horaria en tu zona horaria y, en pocas palabras, qué estás viendo. Con eso se pueden reajustar las reglas de filtrado de tu IP sin una ronda de preguntas. En casos urgentes puedes contactarnos además por el chat de emergencia de WhatsApp en el +43 650 8209883.

Servidor Rust Protección DDoS Rust Protección de servidores de juego UDP Flood Puerto de consulta nftables Advanced DDoS Protection Wipe