Proteggere un server Rust dagli attacchi DDoS

Pubblicato il 16 min di lettura

I server Rust vengono attaccati quasi sempre al wipe oppure nel bel mezzo di un raid. Che cosa puoi mettere in sicurezza da solo, dove l'autodifesa incontra limiti fisici e che cosa deve succedere prima, nella rete.

Un server Rust va offline raramente per caso. Il momento tradisce quasi sempre il motivo: l'attacco parte nel minuto esatto del wipe oppure nel bel mezzo di un raid. Chi è sotto tiro non ha bisogno di una discussione di principio, ma di un ordine di lavoro. Questo articolo mostra prima che cosa puoi cambiare tu stesso sul server, poi dove queste possibilità si fermano e infine che cosa deve succedere prima, nella rete.

Perché proprio i server Rust vengono attaccati così spesso

Rust è un gioco in cui il progresso è legato al tempo. Un raid dura minuti, un ciclo di wipe settimane. Per questo qui un disservizio vale più che in quasi ogni altro gioco: chi difende guadagna tempo se il server cade. Chi attacca impedisce all'avversario di tornare in partita. E chi gestisce una community concorrente sa che la prima serata di wipe decide il numero di giocatori del mese.

A questo si aggiunge un punto che nessuna configurazione può togliere di mezzo: un server Rust è pubblicamente reperibile con indirizzo IP e porta, altrimenti nessuno potrebbe entrare. A differenza di un sito web dietro un reverse proxy, un game server deve pubblicare il suo indirizzo reale. La domanda quindi non è mai se chi attacca troverà il tuo IP, ma soltanto che cosa succede quando ci spara contro.

Le porte coinvolte

Nella configurazione abituale un server Rust occupa quattro porte:

  • 28015/UDP, la porta di gioco (server.port). Qui passa tutto il traffico di gioco. UDP non prevede nessuna apertura di connessione, ogni pacchetto vale per sé e l'indirizzo del mittente si può falsificare. Per chi attacca questo significa nessuna tracciabilità e comunque lavoro per il tuo server a ogni pacchetto.
  • La porta di query (server.queryport), anch'essa UDP. Su questa porta il server risponde alle richieste Steam A2S_INFO, A2S_PLAYERS e A2S_RULES, e senza di essa non compare in nessuna lista dei server. Se non le assegni un valore esplicito, si posiziona subito accanto alla porta di gioco, mentre molte righe di avvio la impostano su 28017/UDP. Controlla la tua riga di avvio invece di fidarti di un valore predefinito.
  • 28016/TCP, RCON (rcon.port), con rcon.web 1 nella variante WebSocket.
  • 28082/TCP, l'app companion Rust+ (app.port).

La porta di query è la più scomoda delle quattro, perché una risposta A2S è molto più grande della richiesta. Chi attacca può interrogare game server altrui con un indirizzo mittente falsificato e dirottare le risposte sul suo vero bersaglio. A quel punto il tuo server non è più soltanto vittima, ma diventa amplificatore contro terzi. Proprio per questo Valve ha aggiunto ad A2S_INFO una richiesta di challenge, cosa che ha attenuato il problema senza però chiuderlo. Da che cosa si riconosce un attacco lo spiega l'articolo Riconoscere un attacco DDoS sul server.

Che cosa puoi fare da solo prima di spendere soldi

La parte che segue non costa nulla e vale la pena a prescindere da dove si trovi il tuo server. Non ti toglie di mezzo un attacco volumetrico, ma fa in modo che gli attacchi piccoli restino senza effetto e che nel momento critico tu non debba tirare a indovinare.

1. Ricognizione: che cosa è davvero in ascolto

Prima di scrivere una regola, chiarisci quali servizi sono raggiungibili. Su un game server cresciuto nel tempo sono quasi sempre più di quanti te ne aspetti:

ss -lntup

Tutto quello che è associato a 127.0.0.1 o a ::1 non ha bisogno di nessuna apertura. Tutto quello che è in ascolto su 0.0.0.0 o su [::] è raggiungibile da internet, compreso il servizio di database che si è portato dietro un plugin. Confronta il risultato con la tua riga di avvio:

./RustDedicated -batchmode -nographics \
  +server.port 28015 \
  +server.queryport 28017 \
  +server.identity "wipe" \
  +server.maxplayers 150 \
  +rcon.port 28016 \
  +rcon.web 1 \
  +rcon.password "LA-TUA-LUNGA-PASSWORD-CASUALE"

Se la tua installazione di Rust è arrivata tramite SteamCMD, per la base ti aiuta l'articolo Installare un game server con SteamCMD.

2. Lascia aperte solo le porte che a Rust servono davvero

Quattro porte, non una di più. RCON non ha nulla da fare su internet aperto, va limitato al tuo indirizzo, e Rust+ va aperta solo se usi davvero l'app companion:

ufw allow 28015/udp comment "Porta di gioco Rust"
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"

Sostituisci 203.0.113.10 con il tuo indirizzo. Se il tuo indirizzo cambia spesso, la strada giusta passa da un tunnel SSH.

Un avvertimento che ogni anno costa dei server: l'ordine con cui attivi un firewall decide se ti chiudi fuori da solo. Quell'ordine, con la relativa via di ritorno, sta nell'articolo Configurare il firewall UFW. Se dovesse succedere comunque: sui server root KVM e sui server dedicati di KernelHost raggiungi la macchina tramite la console VNC nell'area clienti. Quella console non dipende dallo stack di rete del sistema ospite e nessuna regola firewall interna al guest può bloccarla.

3. Metti in sicurezza la porta di query senza sparire dalla lista dei server

Il riflesso immediato, cioè bloccare la porta di query, è l'errore più costoso di tutta la materia. Senza quella porta il tuo server sparisce dal browser dei server, mostra numeri di giocatori sbagliati e viene dato per offline dai siti di liste. Avresti portato a termine tu stesso l'attacco.

La soluzione corretta è un limite di frequenza per singolo indirizzo mittente. Un client vero, mentre sfoglia la lista, interroga qualche volta al secondo, uno strumento di riflessione lo fa migliaia di volte. Con nftables, in una tabella dedicata che viene valutata prima della catena di filtraggio:

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
    }
}

Il file lo carichi con nft -f. La priorità -10 fa sì che la regola intervenga prima della catena di filtraggio che UFW crea con priorità 0. Con il classico iptables lo stesso risultato lo ottiene il modulo hashlimit:

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

Parti con un margine ampio e stringi il limite solo quando hai la prova che le richieste legittime passano. Un limite troppo stretto, altrimenti, salta fuori proprio il giorno del wipe.

4. Alleggerisci il tracciamento delle connessioni

Questo punto viene quasi sempre trascurato e spiega disservizi che sembrano un attacco volumetrico senza esserlo. Per il traffico UDP il kernel crea voci nel tracciamento delle connessioni (conntrack) e, con indirizzi mittente falsificati, ogni nuovo indirizzo significa una nuova voce. Quando la tabella è piena il kernel scarta i pacchetti senza distinzione: l'attacco e i tuoi giocatori volano fuori insieme. Nel log di sistema compare allora nf_conntrack: table full, dropping packet. Lo verifichi così:

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

Il passo più efficace è non far tracciare affatto il traffico di gioco. Rust non ha bisogno di nessun tracciamento di stato nel kernel, gestisce le sue sessioni per conto proprio:

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

Con iptables l'equivalente è:

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

Solo dopo questo passaggio vale la pena aumentare nf_conntrack_max. Chi comincia allargando la tabella sposta il problema di qualche minuto e in cambio consuma RAM.

5. Buffer di ricezione e parametri del kernel

Se i pacchetti arrivano più in fretta di quanto il processo di Rust riesca a prelevarli, il buffer di ricezione del socket va in overflow. Per i giocatori sembra perdita di pacchetti, anche se la linea è libera:

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

Salva i valori sotto /etc/sysctl.d/ e attivali con sysctl -p. Se servano davvero te lo dice il kernel: se UdpRcvbufErrors cresce in nstat -az, oppure se in ss -lunp resta stabilmente qualcosa nella coda di ricezione, allora hanno effetto. Se entrambi restano a zero, la modifica non cambia niente. Questa è riserva, non protezione.

6. Metti in sicurezza RCON

Una porta RCON aperta con una password debole non è un problema di DDoS, è una presa di controllo: chi ha RCON può bannare, sbannare e fermare la partita. Non lasciare mai rcon.password vuota e non sceglierla a caso, un valore da openssl rand -base64 32 si genera in cinque secondi. E non aprire la porta al pubblico, limitala al tuo indirizzo.

7. Misure lato anti-cheat e plugin

Una parte consistente dei disservizi che i gestori segnalano come DDoS non lo è affatto. Sono crash che un singolo client provoca con poche centinaia di pacchetti, perché nel binario del server o in un plugin è rimasta aperta una falla. Contro questo serve manutenzione, non banda:

  • Tieni aggiornato il binario del server. L'update mensile che impone il wipe è allo stesso tempo un update di sicurezza. Chi lo rimanda si tiene in casa i bug già noti.
  • Tieni aggiornato il framework dei plugin. Oxide/uMod e Carbon si adeguano dopo ogni update di Rust. Un framework che non corrisponde alla versione del server è la causa più frequente dei crash nella serata di wipe.
  • Meno plugin. Ogni plugin è codice aggiuntivo nello stesso processo. I plugin con servizi web propri (mappe interattive, pagine di statistiche) aprono altre porte e spesso pubblicano proprio l'indirizzo che vuoi proteggere.
  • Cura le liste di ban. I tentativi di connessione ripetuti dallo stesso account si bloccano con gli strumenti di serie. Rust salva proprietari e moderatori in server/<identity>/cfg/users.cfg, i ban in server/<identity>/cfg/bans.cfg. Un ban impostato con banid sopravvive al riavvio.

Una whitelist Rust non la porta di serie, arriva tramite il framework dei plugin. Su un server privato o di community funziona bene. Su un server wipe pubblico non è un'opzione: un server in cui nessuno può entrare è vuoto esattamente come uno offline.

8. La lista dei server e il tuo indirizzo

Sull'IP pubblico del server di gioco non si può cambiare nulla, su tutto quello che gli sta attorno sì. Spesso chi attacca si trova davanti l'intero contorno: il webserver con lo shop, l'host del bot Discord, il server dei backup, l'accesso al pannello. Questi indirizzi non devono finire né nello stesso annuncio né in vecchi record DNS. Verifica una volta a trimestre quale sottodominio punta dove.

9. Registra i dati, così durante l'attacco non tiri a indovinare

Durante un attacco conta una sola domanda: quanto arriva e su quale porta. Bastano tre comandi:

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

Il primo comando mostra pacchetti, errori e scarti per ogni interfaccia. Eseguilo due volte a dieci secondi di distanza e avrai una frequenza invece di un valore assoluto. Da te l'interfaccia e l'unità del servizio possono chiamarsi diversamente, quindi controlla entrambe con ip -br link e systemctl list-units --type=service. Che aspetto hanno i pacchetti lo mostra un campione, che conviene tenere breve perché una cattura sotto carico consuma tempo di calcolo:

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

Dove finisce l'autodifesa

Adesso la parte onesta. Tutto quello descritto finora ha effetto solo quando i pacchetti sono già arrivati sulla tua scheda di rete. Un server è collegato di norma a 1 Gbit/s oppure a 10 Gbit/s. Con i pacchetti più piccoli possibili una linea da 1 Gbit/s trasporta circa 1,49 milioni di pacchetti al secondo, una da 10 Gbit/s circa 14,88 milioni. Questo è il limite fisico, indipendente da CPU, kernel e firewall.

Di fronte a questo ci sono gli attacchi reali. Due esempi presi dall'esercizio quotidiano di KernelHost, entrambi filtrati in tempo reale: un UDP flood contro un game server ARK sulla porta 7777/UDP con oltre 112,2 Gbit/s e oltre 8,7 milioni di pacchetti al secondo, e un attacco multi-vettore contro un server voice sulla porta 9987/UDP con oltre 473,4 Gbit/s e oltre 41,5 milioni di pacchetti al secondo.

Confronta il dato con la tua linea: 473,4 Gbit/s sono circa 470 volte una connettività da 1 Gbit/s e comunque ancora circa 47 volte una connettività da 10 Gbit/s. La tua regola può essere corretta quanto vuoi, non verrà mai eseguita, perché la perdita nasce già sul router davanti. E molto prima che la linea sia satura è la CPU ad arrendersi: ogni pacchetto costa un interrupt e un passaggio completo nello stack di rete, anche quando subito dopo viene scartato.

Per questo i due freni d'emergenza più diffusi sono entrambi insoddisfacenti. Il null-routing (blackholing) toglie dalla rete l'IP attaccato e mette fine all'attacco, ma anche al tuo server. E una deviazione reattiva verso un sistema di filtraggio consuma, nel tempo di commutazione, esattamente i minuti in cui si decide il raid. Funziona solo un filtraggio che gira in modo permanente nella rete, prima del server.

Che cosa mette davanti KernelHost

La protezione permanente che gira su ogni server

La protezione DDoS di KernelHost è costruita su due livelli ed è permanentemente attiva, senza che tu debba accendere nulla. Il primo livello è una rete di scrubbing globale con 17 Tbps di capacità di mitigazione, che intercetta gli attacchi volumetrici vicino alla loro origine, prima che raggiungano il datacenter. Il secondo livello è un filtraggio Arbor in tempo reale da 3,2 Tbps direttamente in loco a Francoforte sul Meno, che si occupa del lavoro di dettaglio sui protocolli e scarta gli schemi complessi dal layer 3 al layer 7.

Due punti sono decisivi. Primo, il filtraggio è sempre in funzione, quindi non esiste un tempo di riconoscimento e di commutazione durante il quale i tuoi giocatori vengono espulsi. Secondo, non viene usato il null-routing: l'IP attaccato resta in rete, cadono solo i pacchetti dannosi. La protezione è inclusa in ogni pacchetto server senza sovrapprezzo, senza un pacchetto di protezione separato e senza configurazione. I server si trovano nel maincubes Premium Datacenter di Francoforte sul Meno (Germania), certificato TÜV TIER3+ e direttamente collegato al DE-CIX. Il fornitore è KernelHost GmbH, con sede a Vienna (Austria). Quali giochi e protocolli siano coperti lo elenca l'articolo Protezione DDoS per game server in tempo reale.

Advanced DDoS Protection per progetti attaccati di continuo

Certi progetti Rust non vengono colpiti ogni tanto, ma per settimane in modo mirato, con schemi sempre diversi e sempre puntuali al wipe. Per questi casi c'è la Advanced DDoS Protection a partire da 50,00 € al mese, PrePaid e senza durata minima. Porta con sé tre cose che la protezione permanente inclusa non offre in questa forma:

  • Un IP di protezione dedicato. Il tuo server viene spostato su quell'indirizzo all'interno della nostra rete, senza che tu debba modificare nulla dalla tua parte.
  • Regole di protezione gestibili da te per porta e protocollo. Nell'area clienti stabilisci quale porta viene filtrata con quale profilo, per esempio la 28015/UDP in modo diverso dalla porta di query. Le modifiche hanno effetto in tempo reale, senza ticket e senza attese.
  • Un profilo di protezione adatto al singolo gioco. Per Rust come per oltre 40 altri giochi, servizi e protocolli, più profili TCP e UDP liberamente assegnabili per i server modificati.

Anche qui vale il modello PrePaid: nessuna durata minima, nessun preavviso di disdetta, nessun contratto e nessun costo di attivazione. Quando l'ondata di attacchi è passata, semplicemente non rinnovi.

I due livelli a confronto

Caratteristica Protezione permanente inclusa Advanced DDoS Protection
Prezzo Inclusa in ogni pacchetto server, senza sovrapprezzo da 50,00 € al mese, PrePaid senza durata minima
Attivazione Attiva dal primo minuto, niente da configurare Ordini, ricevi l'IP di protezione, il server viene spostato
Indirizzo IP IP del server dalla rete di Francoforte IP di protezione dedicato aggiuntivo
Filtraggio 17 Tbps di scrubbing globale, più 3,2 Tbps di filtraggio Arbor in tempo reale a Francoforte sul Meno Lo stesso filtraggio, più regole tue per porta e protocollo
Modifica delle regole Gestite da KernelHost, messa a punto tramite ticket Da te nell'area clienti, efficaci in tempo reale
Profili di gioco Oltre 40 giochi e protocolli Profilo selezionabile per porta, anche per server modificati
Null-routing durante l'attacco No No
Adatta a Ogni server, fin dal primo wipe Progetti attaccati in modo continuo e mirato

Errori frequenti e soluzioni

Il server è sparito dal browser dei server ma continua a girare: quasi sempre la porta di query è bloccata oppure ha un limite di frequenza troppo stretto. Verifica con ss -lunp se è in ascolto e allarga il limite un passo alla volta. Se Rust+ resta muta, di solito è chiusa la app.port.

Tutti i giocatori hanno ping alto e rubberbanding, ma la linea non è satura: il segnale punta sulla frequenza dei pacchetti, non sul volume. Guarda i pacchetti scartati in ip -s link show e i contatori UDP in nstat -az. Un buffer di ricezione pieno o un tracciamento delle connessioni esaurito producono esattamente questo quadro.

La regola firewall è corretta e non ha comunque effetto: allora la linea davanti al server è satura. Una regola che non viene mai eseguita, perché il pacchetto è già caduto sul router a monte, non può ottenere nulla. Da questo punto in poi serve soltanto il filtraggio nella rete.

Dopo l'attivazione del firewall non c'è più accesso SSH: accedi tramite la console VNC nell'area clienti. Da lì puoi disattivare il firewall e aggiungere la regola mancante, anche quando dalla rete non passa più nulla.

Dopo un cambio di IP l'attacco si ferma e torna dopo 1-2 giorni: è la norma. Il tuo server pubblica da solo il nuovo indirizzo nella lista dei server non appena torna online. Un cambio di IP regala qualche ora, non una soluzione.

Sul server girano comandi da amministratore che non hai dato tu: non è un DDoS, è un accesso RCON compromesso. Cambia subito la password, limita la porta al tuo indirizzo e controlla la lista dei ban.

Se sei sotto attacco proprio adesso

Se il tuo server gira già da KernelHost, il filtraggio è permanentemente attivo e non devi accendere nulla. Se noti comunque delle anomalie, apri un ticket di supporto, così il nostro team può ritarare le regole di filtraggio per il tuo IP. Durante un attacco in corso ci raggiungi anche tramite la chat WhatsApp di emergenza al numero +43 650 8209883.

Indica subito quattro dati: indirizzo IP, porta, intervallo di tempo nel tuo fuso orario e in breve che cosa vedi (i giocatori vengono espulsi, il server non è raggiungibile, ping alto). Così eviti un giro di domande di chiarimento, e quel giro pesa quando il wipe è in corso.

Domande frequenti

Il mio server Rust adesso non è raggiungibile: è un attacco?
Guarda prima l'interfaccia di rete. Se in "ip -s link show" crescono molto i pacchetti scartati e in "nstat -az" gli errori UDP, mentre la CPU del processo di Rust resta normale, il quadro parla di un attacco. Se invece entrambi i valori restano tranquilli e il processo non c'è più, era un crash.
Quali porte deve avere aperte un server Rust?
La 28015/UDP per il traffico di gioco, la porta di query (server.queryport, spesso 28017/UDP) per la lista dei server, la 28016/TCP per RCON e la 28082/TCP solo se usi l'app companion Rust+. RCON va limitato al tuo indirizzo IP, tutto il resto resta chiuso.
Serve a qualcosa bloccare semplicemente la porta di query?
No, fa danni. Senza una porta di query raggiungibile il tuo server sparisce dal browser dei server e viene dato per offline dai siti di liste. Quello che funziona è un limite di frequenza per singolo indirizzo mittente, per esempio con un meter di nftables oppure con il modulo hashlimit di iptables.
Un cambio di IP aiuta contro un attacco in corso?
Solo per poco. Il tuo server ripubblica da solo il nuovo indirizzo nella lista dei server non appena torna online. Nella pratica l'attacco ritorna dopo 1-2 giorni. Un cambio di IP regala qualche ora, non risolve nulla.
Posso proteggermi con un firewall sul server stesso?
Contro gli attacchi piccoli sì, contro quelli volumetrici no. Le tue regole entrano in funzione solo quando i pacchetti sono già arrivati sulla scheda di rete. Una linea da 1 Gbit/s trasporta circa 1,49 milioni di pacchetti piccoli al secondo e gli attacchi reali stanno molte volte più in alto. A quel punto la perdita nasce già sul router davanti.
KernelHost mette offline il mio IP durante un attacco?
No. Non viene usato né il null-routing né il blackholing. L'IP attaccato resta in rete, cadono soltanto i pacchetti dannosi. Il filtraggio è sempre in funzione, quindi non esiste nemmeno un tempo di commutazione all'inizio di un attacco.
Che cosa è incluso nella protezione DDoS e quanto costa il livello Advanced?
La protezione permanente a due livelli è inclusa in ogni pacchetto server senza sovrapprezzo: 17 Tbps di capacità di mitigazione nella rete di scrubbing globale più 3,2 Tbps di filtraggio Arbor in tempo reale a Francoforte sul Meno. La Advanced DDoS Protection, con IP di protezione dedicato e regole tue per porta, parte da 50,00 € al mese, PrePaid, senza durata minima e senza costi di attivazione.
Che cosa scrivo nel ticket mentre l'attacco è in corso?
Per cominciare bastano quattro dati: l'indirizzo IP colpito, la porta, l'intervallo di tempo nel tuo fuso orario e in breve che cosa vedi. Con questi le regole di filtraggio per il tuo IP si possono ritarare senza un giro di domande di chiarimento. Nei casi urgenti ci raggiungi anche tramite la chat WhatsApp di emergenza al numero +43 650 8209883.

Server Rust Protezione DDoS Rust Protezione game server UDP flood Porta di query nftables Advanced DDoS Protection Wipe