Proteger um servidor Rust contra ataques DDoS

Publicado a 17 min de leitura

Os servidores de Rust são quase sempre atacados no wipe ou a meio de um raid. O que pode proteger sozinho, onde a proteção no próprio servidor esbarra em limites físicos e o que tem de acontecer antes disso, na rede.

Um servidor de Rust raramente fica offline por acaso. O momento denuncia quase sempre o motivo: o ataque começa no minuto do wipe ou a meio de um raid. Quem está a ser atacado não precisa de uma discussão de princípios, precisa de saber por que ordem agir. Este artigo mostra primeiro o que pode alterar no próprio servidor, depois onde essas possibilidades acabam e, por fim, o que tem de acontecer antes disso, na rede.

Porque é que os ataques atingem precisamente os servidores de Rust com tanta frequência

O Rust é um jogo em que o progresso está preso ao tempo. Um raid dura minutos, um ciclo de wipe dura semanas. É por isso que uma falha vale aqui mais do que em quase todos os outros jogos: quem defende ganha tempo quando o servidor cai. Quem ataca impede que o outro lado consiga entrar. E quem gere uma comunidade concorrente sabe que a primeira noite de wipe decide o número de jogadores do mês inteiro.

A isto junta-se um ponto que nenhuma configuração resolve: um servidor de Rust é publicamente localizável através do endereço IP e da porta, caso contrário ninguém conseguiria entrar. Ao contrário de um site atrás de um proxy, um gameserver tem de publicar o seu endereço real. A pergunta nunca é, portanto, se quem ataca encontra o seu IP, mas apenas o que acontece quando dispara contra ele.

As portas que interessam

Na configuração habitual, um servidor de Rust ocupa quatro portas:

  • 28015/UDP, a porta de jogo (server.port). É por aqui que passa todo o tráfego de jogo. O UDP não conhece estabelecimento de ligação, cada pacote vale por si e o endereço de origem pode ser falsificado. Para quem ataca, isso significa: nenhuma rastreabilidade e, ainda assim, trabalho para o seu servidor a cada pacote.
  • A porta de consulta (server.queryport), também em UDP. É por ela que o servidor responde às consultas Steam A2S_INFO, A2S_PLAYERS e A2S_RULES e, sem ela, não aparece em nenhuma lista de servidores. Sem um valor explícito, fica logo ao lado da porta de jogo, e muitas linhas de arranque definem-na como 28017/UDP. Verifique na sua própria linha de arranque em vez de confiar num valor predefinido.
  • 28016/TCP, o RCON (rcon.port), com rcon.web 1 na variante WebSocket.
  • 28082/TCP, a aplicação companion Rust+ (app.port).

A porta de consulta é a mais incómoda das quatro, porque uma resposta A2S é bastante maior do que o pedido. Quem ataca consegue consultar gameservers alheios com o endereço de origem falsificado e desviar as respostas para o seu verdadeiro alvo. O seu servidor deixa então de ser apenas vítima e passa também a amplificador contra terceiros. Foi por isso que a Valve acrescentou ao A2S_INFO uma consulta de desafio, o que atenuou o problema sem lhe pôr fim. Os sinais que denunciam um ataque estão descritos no artigo Detetar um ataque DDoS no servidor.

O que pode fazer sozinho antes de gastar dinheiro

A parte que se segue não custa nada e vale a pena independentemente de onde o seu servidor esteja alojado. Não o livra de um ataque volumétrico, mas garante que os ataques pequenos ficam sem efeito e que, no momento crítico, não tem de adivinhar.

1. Levantamento: o que está mesmo à escuta

Antes de escrever uma regra, esclareça que serviços estão acessíveis. Num gameserver que foi crescendo ao longo do tempo, são quase sempre mais do que se espera:

ss -lntup

Tudo o que esteja ligado a 127.0.0.1 ou a ::1 não precisa de qualquer abertura. Tudo o que esteja em 0.0.0.0 ou em [::] está acessível a partir da internet, incluindo o serviço de base de dados que veio com algum plugin. Compare a saída com a sua linha 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 "A-SUA-PALAVRA-PASSE-LONGA-E-ALEATÓRIA"

Se o Rust estiver instalado através do SteamCMD, o artigo Instalar um servidor de jogos com SteamCMD ajuda com a base do sistema.

2. Deixar abertas apenas as portas de que o Rust precisa mesmo

Quatro portas, mais nenhuma. O RCON não pertence à internet aberta, deve ficar limitado ao seu endereço, e a Rust+ só se abre se utilizar mesmo a aplicação companion:

ufw allow 28015/udp comment "Rust porta de jogo"
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"

Substitua 203.0.113.10 pelo seu próprio endereço. Se o endereço mudar com frequência, o caminho passa por um túnel SSH.

Um aviso que todos os anos custa servidores: a ordem pela qual se ativa uma firewall decide se fica ou não fechado fora do seu próprio servidor. Essa ordem, e também o caminho de volta, está no artigo Configurar a firewall UFW. Se ainda assim acontecer: nos servidores root KVM e nos servidores dedicados da KernelHost, chega ao servidor pela consola VNC na área de cliente. Ela não depende da pilha de rede do sistema convidado e nenhuma regra de firewall dentro do convidado a consegue bloquear.

3. Proteger a porta de consulta sem sair da lista de servidores

O reflexo mais imediato, bloquear a porta de consulta, é o erro mais caro nesta matéria. Sem ela, o seu servidor desaparece do navegador de servidores, mostra números de jogadores errados e passa a constar como offline nos sites de listagem. Teria sido o próprio a levar o ataque até ao fim.

O correto é um limite de taxa por endereço de origem. Um cliente real consulta algumas vezes por segundo enquanto percorre a lista, ao passo que uma ferramenta de reflexão o faz aos milhares. Com nftables, numa tabela própria que é avaliada antes da cadeia de filtragem:

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

O ficheiro carrega-se com nft -f. A prioridade -10 faz com que a regra atue antes da cadeia de filtragem que a UFW cria com prioridade 0. Com o iptables clássico, o módulo hashlimit consegue o mesmo:

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

Comece com folga e só aperte o limite quando as consultas legítimas passarem de forma comprovada. Caso contrário, um limite demasiado apertado só dá nas vistas no dia do wipe.

4. Aliviar o seguimento de ligações

Este ponto passa quase sempre despercebido e explica falhas que parecem um ataque de volume, mas não são. Para o tráfego UDP, o kernel cria entradas no seguimento de ligações (conntrack) e, com endereços de origem falsificados, cada novo endereço significa uma nova entrada. Quando a tabela enche, o kernel descarta pacotes sem distinção: o ataque e os seus jogadores caem juntos. No log do sistema aparece então nf_conntrack: table full, dropping packet. Isso verifica-se assim:

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

O passo mais eficaz é não deixar sequer que o tráfego de jogo seja seguido. O Rust não precisa desse seguimento de estado no kernel, porque gere as suas próprias sessões:

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

Com o iptables, o equivalente é:

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

Só depois disso vale a pena aumentar nf_conntrack_max. Quem começa por aumentar a tabela apenas adia o problema alguns minutos e gasta memória RAM nisso.

5. Buffers de receção e parâmetros do kernel

Se os pacotes chegarem mais depressa do que o processo do Rust os recolhe, o buffer de receção do socket transborda. Para os jogadores, isso parece perda de pacotes, apesar de a linha estar livre:

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

Guarde os valores em /etc/sysctl.d/ e ative-os com sysctl -p. Se são mesmo precisos, é o kernel que o diz: se UdpRcvbufErrors subir em nstat -az, ou se em ss -lunp ficar permanentemente alguma coisa na fila de receção, então fazem efeito. Se ambos ficarem a zero, o ajuste não muda nada. Isto é margem de reserva, não é proteção.

6. Proteger o RCON

Uma porta RCON aberta com uma palavra-passe fraca não é um problema de DDoS, é uma tomada de controlo: quem tem RCON consegue banir, desbanir e parar o jogo. Nunca deixe rcon.password vazia nem escolhida à sorte, porque um valor tirado de openssl rand -base64 32 gera-se em cinco segundos. E não abra a porta ao público, limite-a ao seu endereço.

7. Medidas do lado do anti-cheat e dos plugins

Uma parte considerável das falhas que os operadores comunicam como DDoS não o são. São crashes que um único cliente provoca com poucas centenas de pacotes, porque existe uma falha aberta no binário do servidor ou num plugin. Contra isso ajuda a manutenção, não a largura de banda:

  • Manter o binário do servidor atualizado. A atualização mensal que obriga ao wipe é, ao mesmo tempo, uma atualização de segurança. Quem a adia fica com os erros já conhecidos.
  • Manter o framework de plugins atualizado. O Oxide/uMod e o Carbon acompanham cada atualização do Rust. Um framework que não corresponde à versão do servidor é a causa mais frequente de crashes na noite do wipe.
  • Menos plugins. Cada plugin é código adicional no mesmo processo. Os plugins com serviços web próprios (vistas de mapa, páginas de estatísticas) abrem mais portas e, ao fazê-lo, publicam muitas vezes exatamente o endereço que quer proteger.
  • Manter as listas de banimento. As tentativas de ligação repetidas da mesma conta travam-se com os meios que o próprio jogo traz. O Rust guarda os proprietários e os moderadores em server/<identity>/cfg/users.cfg e os banimentos em server/<identity>/cfg/bans.cfg. Um banimento feito com banid sobrevive ao reinício.

O Rust não traz uma whitelist de origem, ela chega através do framework de plugins. Para um servidor privado ou de comunidade é eficaz. Para um servidor de wipe público não é opção: um servidor em que ninguém pode entrar fica tão vazio como um que está offline.

8. A lista de servidores e o seu próprio endereço

No IP público do servidor de jogo não há nada a mudar, mas em tudo o que está à volta há. Muitas vezes, quem ataca encontra logo o ambiente inteiro: o servidor web com a loja, o host do bot de Discord, o servidor de backup, o acesso ao painel. Estes endereços não pertencem ao mesmo anúncio nem a registos DNS antigos. Verifique uma vez por trimestre que subdomínio aponta para onde.

9. Registar dados para não ter de adivinhar durante o ataque

Durante um ataque conta uma pergunta: quanto está a chegar e em que porta. Bastam três comandos:

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

O primeiro comando mostra pacotes, erros e descartes por interface. Execute-o duas vezes, com dez segundos de intervalo, e passa a ter uma taxa em vez de um valor absoluto. A interface e a unidade de serviço podem ter outros nomes no seu sistema, por isso verifique ambos com ip -br link e systemctl list-units --type=service. O aspeto dos pacotes vê-se numa amostra, que deve ser curta, porque uma captura sob carga custa tempo de CPU:

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

Onde a proteção no próprio servidor acaba

Agora a parte honesta. Tudo o que foi descrito até aqui só atua depois de os pacotes terem chegado à sua placa de rede. Um servidor está normalmente ligado a 1 Gbit/s ou a 10 Gbit/s. Com o tamanho de pacote mais pequeno possível, uma linha de 1 Gbit/s transporta cerca de 1,49 milhões de pacotes por segundo e uma linha de 10 Gbit/s cerca de 14,88 milhões. Este é o limite físico, independentemente da CPU, do kernel e da firewall.

Do outro lado estão os ataques reais. Dois exemplos do dia a dia na KernelHost, ambos filtrados em tempo real: um flood UDP contra um gameserver de ARK na porta 7777/UDP com mais de 112,2 Gbit/s e mais de 8,7 milhões de pacotes por segundo, e um ataque multivetor contra um servidor de voz na porta 9987/UDP com mais de 473,4 Gbit/s e mais de 41,5 milhões de pacotes por segundo.

Compare isso com a sua linha: 473,4 Gbit/s são cerca de 470 vezes uma ligação de 1 Gbit/s e ainda cerca de 47 vezes uma ligação de 10 Gbit/s. Por mais correta que a sua regra esteja, nunca chega a ser executada, porque a perda acontece no router que está à frente. E muito antes de a linha encher, a CPU já está no limite: cada pacote custa uma interrupção e uma passagem pela pilha de rede, mesmo que a seguir seja descartado.

É por isso que os dois travões de emergência mais divulgados são ambos insatisfatórios. O null-routing (blackholing) retira da rede o IP atacado e acaba com o ataque, sim, mas também com o seu servidor. E um desvio reativo para um sistema de filtragem consome, no tempo de comutação, exatamente os minutos em que o raid se decide. Só é eficaz uma filtragem que corra de forma permanente na rede, à frente do servidor.

O que a KernelHost coloca à frente

A proteção permanente que corre em cada servidor

A proteção DDoS da KernelHost está construída em dois níveis e mantém-se permanentemente ativa, sem que tenha de ligar seja o que for. O primeiro nível é uma rede global de scrubbing com 17 Tbps de capacidade de mitigação, que trava os ataques volumétricos perto da sua origem, antes de chegarem ao datacenter. O segundo nível é uma filtragem Arbor em tempo real com 3,2 Tbps no próprio local, em Frankfurt am Main, que faz o trabalho fino ao nível do protocolo e descarta os padrões complexos nas camadas 3 a 7.

Há dois pontos decisivos. Primeiro, a filtragem corre de forma permanente, ou seja, não existe um tempo de deteção e de comutação durante o qual os seus jogadores sejam atirados para fora. Segundo, não se recorre a null-routing: o IP atacado permanece na rede e só os pacotes maliciosos caem. A proteção está incluída em cada pacote de servidor sem custo adicional, sem um pacote de proteção à parte e sem qualquer instalação. Os servidores estão no maincubes Premium Datacenter em Frankfurt am Main (Alemanha), certificado TÜV TIER3+ e ligado diretamente ao DE-CIX. O fornecedor é a KernelHost GmbH, com sede em Viena (Áustria). Os jogos e protocolos abrangidos estão listados no artigo Proteção DDoS para gameservers em tempo real.

Advanced DDoS Protection para projetos atacados de forma continuada

Alguns projetos de Rust não são atingidos de vez em quando, mas visados durante semanas, com padrões que vão mudando e sempre em cima do wipe. Para esses casos existe a Advanced DDoS Protection a partir de 50,00 € por mês, em PrePaid e sem prazo mínimo. Traz três coisas que a proteção permanente incluída não oferece desta forma:

  • Um IP de proteção dedicado. O seu servidor é comutado para esse endereço dentro da nossa própria rede, não é preciso alterar nada do seu lado.
  • Regras de proteção geríveis por si, por porta e por protocolo. Define na área de cliente que porta é filtrada com que perfil, por exemplo a 28015/UDP de forma diferente da porta de consulta. As alterações fazem efeito em tempo real, sem ticket e sem tempo de espera.
  • Um perfil de proteção adequado a cada jogo. Para o Rust e também para mais de 40 outros jogos, serviços e protocolos, além de perfis TCP e UDP de atribuição livre para servidores modificados.

Também aqui vale o modelo PrePaid: sem prazo mínimo, sem período de aviso prévio, sem contrato e sem taxa de instalação. Quando a vaga de ataques passar, basta não renovar.

Os dois níveis em comparação

Característica Proteção permanente incluída Advanced DDoS Protection
Preço Incluída em cada pacote de servidor, sem custo adicional a partir de 50,00 € por mês, PrePaid sem prazo mínimo
Ativação Ativa desde o primeiro minuto, não há nada a configurar Encomendar, receber o IP de proteção, o servidor é comutado
Endereço IP IP do servidor, da rede de Frankfurt IP de proteção dedicado adicional
Filtragem 17 Tbps de scrubbing global, mais 3,2 Tbps de filtragem Arbor em tempo real em Frankfurt am Main A mesma filtragem, com regras próprias por porta e por protocolo
Alterar as regras Mantidas pela KernelHost, afinação fina por ticket Por si, na área de cliente, com efeito em tempo real
Perfis de jogo Mais de 40 jogos e protocolos Perfil selecionável por porta, também para servidores modificados
Null-routing durante o ataque Não Não
Indicado para Qualquer servidor, desde o primeiro wipe Projetos atacados de forma dirigida e continuada

Erros frequentes e soluções

O servidor desapareceu do navegador de servidores, mas continua a correr: quase sempre a porta de consulta está bloqueada ou o limite de taxa está demasiado apertado. Verifique com ss -lunp se está à escuta e alivie o limite por etapas. Se a Rust+ ficar muda, normalmente é a app.port que está fechada.

Todos os jogadores têm ping alto e rubberbanding, mas a linha não está cheia: isso aponta para a taxa de pacotes e não para o volume. Veja os pacotes descartados em ip -s link show e os contadores UDP em nstat -az. Um buffer de receção cheio ou um seguimento de ligações esgotado produz exatamente esta imagem.

A regra da firewall está correta e mesmo assim não faz efeito: nesse caso, a linha à frente do servidor está saturada. Uma regra que nunca chega a ser executada, porque o pacote já caiu no router anterior, não consegue mudar nada. A partir desse ponto só ajuda a filtragem na rede.

Depois de ativar a firewall já não há acesso por SSH: inicie sessão pela consola VNC na área de cliente. Por aí consegue desligar a firewall e acrescentar a regra em falta, mesmo quando já não passa nada pela rede.

O ataque para depois de uma mudança de IP e volta ao fim de um ou dois dias: isso é o normal. O próprio servidor publica o novo endereço na lista de servidores assim que volta a estar online. Uma mudança de IP dá horas, não dá uma solução.

Estão a correr comandos de administrador alheios no servidor: não é DDoS, é um acesso RCON comprometido. Mude a palavra-passe de imediato, limite a porta ao seu endereço e verifique a lista de banimentos.

Se está a ser atacado neste momento

Se o seu servidor já corre na KernelHost, a filtragem está permanentemente ativa e não tem de ligar nada. Se mesmo assim notar algo estranho, abra um ticket de suporte para que a nossa equipa reajuste as regras de filtragem do seu IP. Durante um ataque em curso, pode ainda contactar-nos pelo chat de emergência do WhatsApp através do +43 650 8209883.

Indique logo quatro dados: endereço IP, porta, período no seu fuso horário e, em poucas palavras, o que está a ver (jogadores a serem atirados para fora, servidor inacessível, ping alto). Isso poupa uma ronda de perguntas, e essa ronda conta quando o wipe está a decorrer.

Perguntas frequentes

O meu servidor Rust está inacessível neste momento. Isto é um ataque?
Comece pela interface de rede. Se em "ip -s link show" os pacotes descartados e em "nstat -az" os erros UDP subirem com força, enquanto a CPU do processo do Rust se mantém normal, isso aponta para um ataque. Se ambos os valores ficarem calmos e o processo tiver desaparecido, foi um crash.
De que portas precisa mesmo um servidor Rust?
A 28015/UDP para o tráfego de jogo, a porta de consulta (server.queryport, muitas vezes 28017/UDP) para a lista de servidores, a 28016/TCP para o RCON e a 28082/TCP apenas se utilizar a aplicação companion Rust+. O RCON deve ficar limitado ao seu próprio endereço IP, todo o resto fica fechado.
Vale a pena bloquear simplesmente a porta de consulta?
Não, isso prejudica. Sem uma porta de consulta acessível, o seu servidor desaparece do navegador de servidores e passa a constar como offline nos sites de listagem. O que funciona é um limite de taxa por endereço de origem, por exemplo com um meter do nftables ou com o módulo hashlimit do iptables.
Uma mudança de IP ajuda contra o ataque em curso?
Só por pouco tempo. O próprio servidor volta a publicar o novo endereço na lista de servidores assim que fica online. Na prática, o ataque regressa ao fim de um ou dois dias. Uma mudança de IP dá horas, não resolve nada.
Consigo proteger-me com uma firewall no próprio servidor?
Contra ataques pequenos sim, contra os volumétricos não. As suas regras só correm depois de os pacotes terem chegado à placa de rede. Uma linha de 1 Gbit/s transporta cerca de 1,49 milhões de pacotes pequenos por segundo e os ataques reais ficam várias vezes acima disso. A perda passa então a acontecer logo no router que está à frente.
A KernelHost tira o meu IP da rede durante um ataque?
Não. Não se recorre a null-routing nem a blackholing. O IP atacado permanece na rede e só os pacotes maliciosos caem. A filtragem corre de forma permanente, por isso também não existe tempo de comutação no início de um ataque.
O que está incluído na proteção DDoS e quanto custa o nível Advanced?
A proteção permanente de dois níveis está incluída em cada pacote de servidor sem custo adicional: 17 Tbps de capacidade de mitigação na rede global de scrubbing mais 3,2 Tbps de filtragem Arbor em tempo real em Frankfurt am Main. A Advanced DDoS Protection, com IP de proteção dedicado e regras próprias por porta, custa a partir de 50,00 € por mês, em PrePaid, sem prazo mínimo e sem taxa de instalação.
O que devo escrever no ticket enquanto o ataque decorre?
Para começar bastam quatro dados: o endereço IP afetado, a porta, o período no seu fuso horário e, em poucas palavras, o que está a ver. Com isso, as regras de filtragem do seu IP podem ser reajustadas sem uma ronda de perguntas. Em casos urgentes, pode ainda contactar-nos pelo chat de emergência do WhatsApp através do +43 650 8209883.

Servidor Rust Proteção DDoS para Rust Proteção de gameservers Flood UDP Porta de consulta nftables Advanced DDoS Protection Wipe