Proteger servidores CS2 e Source contra ataques DDoS
No Counter-Strike 2 e nos títulos Source, o tráfego de jogo e a consulta de servidor passam pela mesma porta 27015. O que pode proteger por si próprio e a partir de que volume de ataque só ajuda a filtragem na rede.
Um servidor de Counter-Strike raramente cai num momento qualquer. A falha chega na ronda decisiva, mesmo antes da final do torneio ou precisamente quando um jogador banido foi recusado pela segunda vez. Quem está a ser atacado neste preciso momento não precisa de uma discussão de princípios, precisa de uma ordem de trabalhos. Este artigo mostra primeiro o que pode alterar por si próprio no servidor, depois onde essas possibilidades acabam e, no fim, o que tem de acontecer antes disso, na rede.
Porque é que os servidores CS2 e Source são atacados com tanta frequência
O Counter-Strike é um jogo contra o relógio. Uma ronda dura menos de dois minutos, uma partida pouco menos de uma hora, e uma falha dentro dessa hora decide o resultado. A falha deixa assim de ser um simples aborrecimento e passa a ser uma ferramenta: quem está a perder ganha tempo com uma interrupção, e quem gere uma comunidade concorrente sabe que uma noite cheia de timeouts leva os jogadores habituais a procurar outro sítio.
A isto junta-se a arquitetura do motor. Um servidor Source é publicamente localizável através do endereço IP e da porta, e isso é um requisito, não um descuido: sem consulta de servidor respondida, não aparece em nenhuma lista. A pergunta nunca é, portanto, se um atacante encontra o seu endereço, mas apenas o que acontece quando dispara contra ele.
As portas em causa
O Counter-Strike 2, o CS:GO e o Garry's Mod partilham a mesma lógica de portas, e aí está um pormenor que os distingue do Minecraft ou do Rust:
- 27015/UDP, porta de jogo e consulta de servidor ao mesmo tempo (
-port). Por esta única porta passa o tráfego de jogo e ainda a consulta A2S com que a Steam e qualquer site de listagens leem o servidor. Aqui não existe uma porta de consulta separada. - 27015/TCP, RCON. O mesmo número, outro protocolo. Por aí passam os comandos de administração, desde que
rcon_passwordesteja definida. - 27020/UDP, GOTV ou SourceTV (
tv_port). Só é necessária se transmitir de facto. - 27005/UDP, porta do cliente. Parte do jogador e não precisa de qualquer abertura no servidor.
- Com várias instâncias, os números vão subindo (27016, 27017, bem como 27021, 27022 para o GOTV). Se o download rápido dos mapas (
sv_downloadurl) estiver no mesmo host, junta-se ainda 80/TCP ou 443/TCP.
A porta partilhada é o cerne do problema. Um pedido A2S é um pacote de algumas dezenas de bytes, a resposta é um múltiplo disso, e em UDP o endereço de origem pode ser falsificado. Um atacante consegue consultar servidores alheios e encaminhar as respostas para o seu verdadeiro alvo: o seu servidor deixa então de ser apenas vítima e passa também a amplificador. Por isso a Valve acrescentou ao A2S_INFO um challenge prévio, o que atenuou o problema sem o resolver de vez. Como reconhecer um ataque em curso é explicado no artigo Detetar um ataque DDoS no servidor.
O que pode fazer por si próprio antes de gastar dinheiro
A parte que se segue não custa nada e compensa independentemente do sítio onde o seu servidor esteja alojado. Não o livra de um ataque volumétrico, mas faz com que os ataques pequenos e médios se percam sem efeito.
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 o esperado:
ss -lntup
Tudo o que esteja ligado a 127.0.0.1 ou a ::1 não precisa de abertura. Tudo o que esteja à escuta em 0.0.0.0 ou em [::] está acessível a partir da internet, incluindo a base de dados que veio com um addon de estatísticas. Compare isso com a sua linha de arranque:
./game/bin/linuxsteamrt64/cs2 -dedicated \
-port 27015 \
-maxplayers_override 12 \
+game_alias competitive \
+map de_dust2 \
+sv_setsteamaccount O_SEU_TOKEN_GSLT
No CS:GO e no Garry's Mod, a mesma tarefa cabe ao srcds_run. Se a base estiver montada através do SteamCMD, ajuda o artigo Instalar um servidor de jogos com o SteamCMD.
2. Deixar abertas apenas as portas de que o servidor precisa mesmo
Duas portas UDP e uma porta TCP restrita, mais nada. O RCON não tem lugar na internet aberta:
ufw allow 27015/udp comment "Porta de jogo CS2 e A2S"
ufw allow 27020/udp comment "GOTV"
ufw allow from 203.0.113.10 to any port 27015 proto tcp comment "RCON"
Substitua 203.0.113.10 pelo seu próprio endereço. Se este mudar com regularidade, o caminho passa por um túnel SSH em vez de uma abertura permanente.
Um aviso que custa servidores todos os anos: a ordem pela qual ativa uma firewall decide se fica trancado do lado de fora. Essa ordem, com a respetiva via de regresso, está no artigo Configurar a firewall UFW. Se mesmo assim acontecer: os servidores root KVM e os servidores dedicados da KernelHost não têm IPMI nem iDRAC, e chega ao servidor através da consola VNC na área de cliente. Essa consola não depende da stack de rede do sistema convidado.
3. Limitar o tráfego de consultas sem cair da lista de servidores
É aqui que está o erro mais caro de toda esta matéria. Como o tráfego de jogo e a consulta de servidor ocupam a mesma porta, a reação mais óbvia é a errada: quem bloqueia a 27015/UDP ou lhe aplica um rate limit genérico expulsa no mesmo gesto os seus próprios jogadores e termina o ataque por conta própria.
O ponto de partida correto é distinguir entre pacotes de consulta e pacotes de jogo. O motor vê o conteúdo e traz três variáveis de consola para isso:
sv_max_queries_sec 3
sv_max_queries_sec_global 60
sv_max_queries_window 30
A primeira limita as consultas respondidas por endereço de origem, a segunda impõe um teto à soma de todos os endereços e a terceira fixa em segundos a janela de média; find sv_max_queries mostra se a sua build as conhece. Protegem o CPU de gerar respostas inúteis, mas não impedem que os pacotes cheguem.
Um nível abaixo, o tráfego de consultas pode ser separado com rigor. Todos os pacotes sem ligação do motor Source, ou seja, as consultas de servidor e o estabelecimento da ligação, começam com quatro bytes preenchidos (0xffffffff), enquanto o tráfego dos jogadores já ligados não leva esse cabeçalho. É exatamente sobre isso que se pode aplicar um rate limit com o nftables:
table inet cs2 {
chain input {
type filter hook input priority -10; policy accept;
udp dport 27015 @th,64,32 0xffffffff \
meter a2sflood { ip saddr limit rate over 10/second burst 20 packets } drop
}
}
O ficheiro carrega-se com nft -f. A prioridade -10 faz com que a regra atue antes da cadeia de filtragem do UFW, e @th,64,32 lê os primeiros quatro bytes a seguir ao cabeçalho UDP. Com o iptables clássico, a mesma separação consegue-se comparando com o identificador A2S_INFO:
iptables -A INPUT -p udp --dport 27015 \
-m string --algo bm --hex-string "|ffffffff54536f7572636520456e67696e6520517565727900|" \
-m hashlimit --hashlimit-name a2sflood --hashlimit-mode srcip \
--hashlimit-above 10/sec --hashlimit-burst 20 -j DROP
Comece com uma margem generosa e só aperte o limite quando tiver a prova de que as consultas legítimas continuam a passar.
4. 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 pode mudar o mapa, banir todos os jogadores e parar o servidor. Nunca deixe rcon_password vazia nem escolhida a olho, um valor saído de openssl rand -base64 32 chega. Os títulos Source trazem ainda um travão contra tentativas de início de sessão:
sv_rcon_minfailures 3
sv_rcon_maxfailures 5
sv_rcon_minfailuretime 30
sv_rcon_banpenalty 1440
sv_rcon_whitelist_address "203.0.113.10"
Com isto, um endereço fica bloqueado durante um dia após três tentativas falhadas em 30 segundos, enquanto o seu próprio fica de fora dessa regra; find sv_rcon mostra que variáveis a sua build conhece. Ainda assim, a restrição na firewall do passo 2 continua a ser mais eficaz, porque nem sequer deixa a tentativa chegar à aplicação.
5. Aliviar o seguimento de ligações
Este ponto passa muitas vezes despercebido e explica falhas que parecem um ataque volumétrico sem o serem. O kernel cria entradas no seguimento de ligações (conntrack) para o tráfego UDP e, com endereços de origem falsificados, cada endereço significa uma entrada nova. Quando a tabela enche, o kernel descarta pacotes sem distinguir: o ataque e os seus jogadores caem em conjunto. O passo mais eficaz é não deixar sequer que o tráfego de jogo seja seguido, porque o motor gere as suas próprias sessões:
table inet raw {
chain prerouting {
type filter hook prerouting priority raw; policy accept;
udp dport { 27015, 27020 } notrack
}
chain output {
type filter hook output priority raw; policy accept;
udp sport { 27015, 27020 } notrack
}
}
Com o iptables, o equivalente é iptables -t raw -A PREROUTING -p udp --dport 27015 -j NOTRACK e a mesma linha para OUTPUT com --sport. A porta passa depois a precisar de uma abertura explícita, porque sem seguimento deixa de atuar qualquer regra que verifique um estado existente.
6. Buffer de receção e parâmetros do kernel
Se os pacotes chegarem mais depressa do que o processo do servidor os levanta, o buffer de receção transborda. Para os jogadores, isso parece perda de pacotes, ainda que a linha esteja livre. Um ficheiro adicional em /etc/sysctl.d/, aplicado com sysctl -p, dá alguma folga:
net.core.rmem_max = 16777216
net.core.rmem_default = 1048576
net.core.netdev_max_backlog = 16384
Se estes valores são mesmo necessários, é o próprio kernel que o revela: se UdpRcvbufErrors subir em nstat -az, então fazem efeito. Se o contador ficar a zero, o ajuste não muda nada. Isto é reserva, não é proteção.
7. Medidas do lado do anticheat e dos plugins
Uma parte considerável das falhas comunicadas como DDoS não o é. São crashes que um único cliente provoca com umas poucas centenas de pacotes, porque há uma falha aberta no binário do servidor ou numa extensão. Contra isso não ajuda largura de banda, ajuda manutenção:
- Manter o binário do servidor atualizado. Além dos conteúdos de jogo, as atualizações corrigem também erros de rede. Um servidor que está duas versões atrás fica exposto a padrões de crash conhecidos.
- Manter as extensões a condizer com a versão do motor. Para o CS:GO e o Garry's Mod, a base habitual são o Metamod:Source e o SourceMod; para o Counter-Strike 2, o SourceMod ainda não tem a mesma maturidade, e o que ali está difundido são as versões de desenvolvimento do Metamod:Source e o CounterStrikeSharp. Uma extensão desadequada é o motivo mais frequente de crashes depois de uma atualização.
- Menos extensões. Cada plugin é código dentro do mesmo processo, e as extensões com serviços web próprios abrem mais portas e publicam muitas vezes justamente o endereço que quer proteger.
- No Garry's Mod, limitar as mensagens de rede. O autogolo mais conhecido é um menu que fica à escuta em
net.Receivesem qualquer limite: um cliente envia a mensagem em ciclo e trava o servidor sozinho.
local last = {}
net.Receive("o_meu_menu", function(len, ply)
if last[ply] and CurTime() - last[ply] < 0.5 then return end
last[ply] = CurTime()
end)
hook.Add("PlayerDisconnected", "o_meu_menu_cleanup", function(ply)
last[ply] = nil
end)
Também no Garry's Mod: sv_allowcslua 0 impede que os clientes executem código Lua próprio. Os banimentos têm de ficar guardados de forma permanente, caso contrário desaparecem depois do reinício: para isso, os títulos Source conhecem banid e writeid, bem como addip e writeip; o que a sua build traz é mostrado por find ban.
8. Lista de servidores, whitelist e o endereço próprio
Um servidor público de Counter-Strike precisa de um Game Server Login Token, definido através de sv_setsteamaccount. Sem esse token fica por registar e não surge em nenhuma lista pública. Para um grupo fechado, é precisamente isso que resulta: definir sv_password, prescindir do registo e dar o endereço apenas aos jogadores da casa. Para um servidor público não é uma opção: um servidor que ninguém encontra fica tão vazio como um que está offline. Uma whitelist a sério não vem com o motor, essa chega através de extensões.
No endereço do servidor de jogo não há nada a mudar, mas há muito a mudar em tudo o resto à volta: muitas vezes um atacante encontra logo o ambiente inteiro, do servidor web ao host do bot de Discord e até ao acesso ao painel. Esses endereços não têm lugar no mesmo anúncio que o endereço do servidor, nem em registos DNS antigos.
9. Registar dados para não ter de adivinhar durante o ataque
Durante um ataque, a pergunta mais importante é: quanto chega e em que porta. Bastam três comandos:
ip -s link show eth0
nstat -az | grep -i udp
tcpdump -ni eth0 -c 200 "udp port 27015 and udp[8:4] = 0xffffffff"
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 terceira linha mostra exclusivamente os pacotes sem ligação, ou seja, a classe que uma enxurrada de consultas explora. Mantenha essa captura curta, porque sob carga consome ela própria tempo de processamento. Se o contador disparar em segundos enquanto quase ninguém está ligado, já tem a sua resposta.
Onde acaba a proteção feita pelos seus próprios meios
Agora a parte honesta. Tudo o que ficou descrito até aqui só atua depois de os pacotes chegarem à sua placa de rede. 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 do CPU, do kernel e da firewall.
Do outro lado estão os ataques reais. Dois exemplos da operação na KernelHost, ambos filtrados em tempo real: um UDP flood contra um gameserver na 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 9987/UDP com mais de 473,4 Gbit/s e mais de 41,5 milhões de pacotes por segundo.
Faça as contas com a sua linha: 473,4 Gbit/s são cerca de 470 vezes uma ligação de 1 Gbit/s e ainda assim cerca de 47 vezes uma ligação de 10 Gbit/s. Por mais correta que a sua regra seja, nunca chega a ser executada, porque a perda acontece antes, no router que está à frente. E muito antes de a linha encher, o CPU já está no limite, porque cada pacote custa uma passagem pela stack de rede, mesmo que a seguir seja descartado.
Por isso os dois travões de emergência mais divulgados são insatisfatórios. O null-routing (blackholing) retira da rede o IP atacado e termina o ataque, sim, mas termina também com o seu servidor. Um desvio reativo custa, no tempo de comutação, precisamente os minutos em que a partida se decide. Só é eficaz uma filtragem que corra de forma permanente na rede, à frente do servidor.
O que a KernelHost põe do outro lado
A proteção permanente que corre em todos os servidores
A proteção DDoS da KernelHost está montada em dois níveis e 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 interceta os ataques volumétricos perto da sua origem, muito antes de chegarem ao datacenter. O segundo nível é uma filtragem Arbor em tempo real com 3,2 Tbps diretamente no local, em Frankfurt am Main, que faz o trabalho fino e descarta padrões complexos nas camadas 3 a 7.
Há dois pontos decisivos. Primeiro, a filtragem corre de forma permanente, pelo que não existe um tempo de comutação durante o qual os seus jogadores caem. Segundo, não é usado null-routing: o IP atacado mantém-se na rede, apenas os pacotes maliciosos desaparecem. A proteção está incluída em todos os pacotes de servidor sem sobretaxa, sem pacote de proteção em separado e sem configuração. Os servidores estão no maincubes Premium Datacenter em Frankfurt am Main (Alemanha) e o fornecedor é a KernelHost GmbH, com sede em Viena (Áustria). Que jogos e protocolos estão cobertos é o que enumera o artigo Proteção DDoS para servidores de jogos em tempo real.
Advanced DDoS Protection para projetos sob fogo permanente
Alguns projetos são atacados de forma dirigida durante semanas, com padrões que vão mudando e sempre à hora exata da partida. Para esses casos existe a Advanced DDoS Protection a partir de 50,00 € por mês, PrePaid e sem prazo mínimo. Traz três coisas que a proteção permanente incluída não oferece:
- Um IP de proteção dedicado. O seu servidor passa a usar esse endereço dentro da nossa rede, sem que tenha de alterar seja o que for 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 27015/UDP de forma diferente da 27020/UDP. As alterações fazem efeito em tempo real, sem ticket e sem tempo de espera.
- Um perfil de proteção à medida de cada jogo. Para o Counter-Strike 2 e os títulos Source, tal como para mais de 40 outros jogos e protocolos, além de perfis TCP e UDP livres 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 configuração. Quando a onda 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 todos os pacotes de servidor, sem sobretaxa | a partir de 50,00 € por mês, PrePaid e sem prazo mínimo |
| Ativação | Ativa desde o primeiro minuto, nada a configurar | Encomendar, receber o IP de proteção, o servidor é comutado |
| Endereço IP | IP de 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, mais regras próprias por porta e por protocolo |
| Alterar regras | Mantidas pela KernelHost, afinação 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 |
| Indicada para | Qualquer servidor, desde a primeira partida | Projetos atacados de forma permanente e dirigida |
Erros frequentes e soluções
O servidor desapareceu da lista de servidores, mas continua a correr: na maioria dos casos, a 27015/UDP foi bloqueada de forma genérica ou o rate limit ficou demasiado apertado e, como o tráfego de jogo e a consulta partilham a mesma porta, uma regra grosseira atinge os dois. Trabalhe antes com uma comparação sobre os pacotes sem ligação. Se o servidor continuar a faltar apesar de a porta estar acessível, verifique sv_setsteamaccount.
Todos os jogadores têm ping alto, mas a linha não está cheia: isso aponta para taxa de pacotes em vez de volume. Observe os pacotes descartados em ip -s link show e os contadores UDP em nstat -az. Se no log do sistema aparecer nf_conntrack: table full, retire a porta de jogo com notrack.
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á foi descartado no router anterior, não consegue produzir efeito nenhum. A partir daqui só ajuda a filtragem na rede.
Depois de ativar a firewall, deixou de haver acesso SSH: inicie sessão através da consola VNC na área de cliente (não existe IPMI nem iDRAC) e desligue aí a firewall.
O ataque faz uma pausa depois de uma mudança de IP e regressa ao fim de um a dois dias: é o caso normal, porque é o próprio servidor que publica o novo endereço assim que volta a estar registado. Uma mudança de IP dá horas, não dá uma solução.
O servidor cai de forma reproduzível sem que a largura de banda se destaque: na maioria dos casos não é DDoS, é um padrão de crash numa extensão ou uma versão de servidor desatualizada.
Estão a ser executados comandos de administração alheios no servidor: não é DDoS, é um acesso RCON comprometido. Mude a palavra-passe de imediato e restrinja a porta.
Se estiver a ser atacado neste momento
Se o seu servidor já estiver na KernelHost, a filtragem está permanentemente ativa. Se ainda assim notar algo fora do normal, abra um ticket de suporte para que a nossa equipa reajuste as regras de filtragem para o seu IP. Durante um ataque em curso, pode ainda contactar-nos através do chat de emergência do WhatsApp em +43 650 8209883.
Indique logo quatro dados: endereço IP, porta, período de tempo no seu fuso horário e o que está a observar (jogadores a serem expulsos, servidor fora da lista, ping alto). Isso poupa uma ronda de perguntas, e essa ronda conta quando há uma partida a decorrer.
Perguntas frequentes
O meu servidor CS2 desapareceu de repente. É um ataque DDoS?
Posso simplesmente bloquear a porta de consulta?
De que portas precisa mesmo um servidor CS2 ou Source?
Mudar o endereço IP ajuda contra o ataque?
Porque é que a minha regra de firewall não resulta?
Fiquei trancado fora do servidor com a firewall. Como volto a entrar?
A KernelHost coloca o meu endereço IP offline durante um ataque?
Quando é que preciso da Advanced DDoS Protection?
2026 KernelHost GmbH. Todos os direitos reservados. Este guia está protegido por direitos de autor. A sua republicação noutros sites, na íntegra, em parte ou de forma editada, não é permitida sem o nosso consentimento por escrito. Citações com indicação da fonte e ligação são expressamente bem-vindas.

