Защита CS2- и Source-серверов от DDoS-атак
У Counter-Strike 2 и игр на Source игровой трафик и серверный запрос идут через один и тот же порт 27015. Что вы можете закрыть сами и с какого объёма атаки помогает только фильтрация в сети перед сервером.
Counter-Strike-сервер редко падает в произвольный момент. Сбой приходит в решающем раунде, незадолго до финала турнира или ровно тогда, когда забаненного игрока во второй раз не пустили на сервер. Тому, кого обстреливают прямо сейчас, нужны не рассуждения о принципах, а порядок действий. В этой статье сначала разобрано, что вы можете изменить на самом сервере, затем то, где эти возможности заканчиваются, и в конце то, что должно происходить в сети перед сервером.
Почему CS2- и Source-серверы атакуют так часто
Counter-Strike — игра на время. Раунд длится меньше двух минут, матч около часа, и сбой внутри этого часа решает исход. Значит, простой не просто раздражает, он работает как инструмент: тот, кто проигрывает, выигрывает время за счёт прерванного матча, а тот, кто держит конкурирующее сообщество, знает, что вечер сплошных таймаутов уводит постоянных игроков к другим.
К этому добавляется устройство самого движка. Source-сервер можно публично найти по IP-адресу и порту, и это условие работы, а не недосмотр: без ответа на серверный запрос он не попадёт ни в один браузер серверов. Поэтому вопрос никогда не стоит так, найдёт ли атакующий ваш адрес. Вопрос только в том, что произойдёт, когда он по нему ударит.
Порты, о которых идёт речь
У Counter-Strike 2, CS:GO и Garry's Mod одна и та же логика портов, и в ней есть особенность, которая отличает их от Minecraft или Rust:
- 27015/UDP, одновременно игровой порт и серверный запрос (
-port). Через этот единственный порт идёт игровой трафик и вдобавок A2S-запрос, которым Steam и любой сайт со списками серверов считывают состояние сервера. Отдельного query-порта здесь нет. - 27015/TCP, RCON. Тот же номер, другой протокол. По нему идут административные команды, если задан
rcon_password. - 27020/UDP, GOTV или SourceTV (
tv_port). Нужен только тогда, когда вы действительно ведёте трансляцию. - 27005/UDP, клиентский порт. Он открывается со стороны игрока, и разрешающее правило на сервере ему не требуется.
- При нескольких экземплярах номера идут по возрастанию (27016, 27017, а для GOTV 27021, 27022). Если быстрая загрузка карт (
sv_downloadurl) лежит на том же хосте, добавляется 80/TCP или 443/TCP.
Общий порт и есть суть проблемы. A2S-запрос — это пакет в несколько десятков байт, а ответ на него в разы больше, при этом в UDP адрес отправителя легко подделать. Атакующий может опрашивать чужие серверы и направлять ответы на свою настоящую цель: тогда ваш сервер не только жертва, но и усилитель. Именно поэтому Valve добавила к A2S_INFO предварительный обмен challenge, что смягчило проблему, но не сняло её. По каким признакам распознать идущую атаку, разбирает статья Как определить DDoS-атаку на сервере.
Что вы можете сделать сами, прежде чем тратить деньги
Следующий раздел не стоит ничего и полезен независимо от того, у какого провайдера стоит ваш сервер. От объёмной атаки он не избавит, но небольшие и средние атаки после него уходят в пустоту.
1. Инвентаризация: что на самом деле слушает сеть
Прежде чем писать хоть одно правило, выясните, какие службы доступны снаружи. На игровом сервере, который развивался постепенно, их почти всегда больше, чем вы ожидаете:
ss -lntup
Всё, что привязано к 127.0.0.1 или ::1, разрешающего правила не требует. Всё, что слушает на 0.0.0.0 или [::], доступно из интернета, в том числе база данных, которую притащил с собой аддон статистики. Сравните это со своей строкой запуска:
./game/bin/linuxsteamrt64/cs2 -dedicated \
-port 27015 \
-maxplayers_override 12 \
+game_alias competitive \
+map de_dust2 \
+sv_setsteamaccount ВАШ_GSLT_ТОКЕН
В CS:GO и Garry's Mod ту же задачу выполняет srcds_run. Если основа ставится через SteamCMD, поможет статья Установка игрового сервера через SteamCMD.
2. Оставьте открытыми только те порты, которые серверу действительно нужны
Два UDP-порта и один ограниченный TCP-порт, больше ничего. RCON в открытом интернете не место:
ufw allow 27015/udp comment "CS2 игровой порт и A2S"
ufw allow 27020/udp comment "GOTV"
ufw allow from 203.0.113.10 to any port 27015 proto tcp comment "RCON"
Замените 203.0.113.10 на свой собственный адрес. Если он регулярно меняется, идите через SSH-туннель, а не через постоянно открытое правило.
Замечание, которое каждый год стоит кому-то доступа к серверу: порядок команд при включении файрвола решает, закроете ли вы доступ самому себе. Он вместе с запасным путём описан в статье Настройка файрвола UFW без потери доступа. Если это всё же случилось: у KVM root-серверов и выделенных серверов KernelHost нет ни IPMI, ни iDRAC, и до сервера вы добираетесь через VNC-консоль в личном кабинете. Она не зависит от сетевого стека гостевой системы.
3. Ограничьте трафик запросов, не вылетев из списка серверов
Здесь кроется самая дорогая ошибка во всей этой теме. Игровой трафик и серверный запрос занимают один и тот же порт, поэтому напрашивающаяся реакция оказывается неверной: тот, кто закрывает 27015/UDP или огульно ограничивает на нём интенсивность, тем же движением выбрасывает собственных игроков и доводит атаку до конца за атакующего.
Правильная точка приложения сил — различать пакеты запросов и игровые пакеты. Движок видит их содержимое и приносит для этого три консольные переменные:
sv_max_queries_sec 3
sv_max_queries_sec_global 60
sv_max_queries_window 30
Первая ограничивает число обработанных запросов на один адрес отправителя, вторая ставит потолок для суммы по всем адресам, третья задаёт окно усреднения в секундах; find sv_max_queries покажет, знает ли их ваша сборка. Они берегут CPU от бессмысленной генерации ответов, но не мешают самим пакетам доходить до сервера.
Уровнем ниже трафик запросов отделяется чисто. Все пакеты Source-движка без установленного соединения, то есть серверные запросы и подключение к серверу, начинаются с четырёх выставленных байт (0xffffffff), а у трафика уже подключённых игроков такого заголовка нет. Именно на этот признак и вешается ограничение интенсивности через 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
}
}
Файл загружается командой nft -f. Приоритет -10 обеспечивает срабатывание правила перед цепочкой фильтрации UFW, а @th,64,32 читает первые четыре байта за UDP-заголовком. В классическом iptables то же самое разделение даёт сравнение с сигнатурой 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
Начинайте с запасом и затягивайте порог только тогда, когда убедились, что легитимные запросы проходят.
4. Защитите RCON
Открытый RCON-порт со слабым паролем — это не проблема DDoS, а захват сервера: у кого есть RCON, тот меняет карту, банит всех игроков и останавливает сервер. Никогда не оставляйте rcon_password пустым и не придумывайте его на ходу, достаточно значения из openssl rand -base64 32. У игр на Source есть вдобавок собственный тормоз против перебора паролей:
sv_rcon_minfailures 3
sv_rcon_maxfailures 5
sv_rcon_minfailuretime 30
sv_rcon_banpenalty 1440
sv_rcon_whitelist_address "203.0.113.10"
После трёх неудачных попыток за 30 секунд адрес блокируется на сутки, а ваш собственный остаётся исключением; find sv_rcon покажет, какие переменные знает ваша сборка. Ограничение на файрволе из шага 2 всё равно действеннее, потому что оно не пропускает попытку до самого приложения.
5. Разгрузите отслеживание соединений
Этот пункт часто упускают, а он объясняет сбои, которые выглядят как объёмная атака, но ею не являются. Для UDP-трафика ядро заводит записи в таблице отслеживания соединений (conntrack), и при подделанных адресах отправителя каждый адрес означает новую запись. Когда таблица заполнена, ядро отбрасывает пакеты без разбора: вместе с атакой вылетают и ваши игроки. Действеннее всего вообще не отслеживать игровой трафик, ведь свои сессии движок ведёт сам:
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
}
}
В iptables этому соответствует iptables -t raw -A PREROUTING -p udp --dport 27015 -j NOTRACK и та же строка для OUTPUT с --sport. После этого порту нужно явное разрешающее правило, потому что без отслеживания перестаёт работать любое правило, которое проверяет уже установленное состояние.
6. Буферы приёма и параметры ядра
Если пакеты приходят быстрее, чем серверный процесс успевает их забирать, буфер приёма переполняется. Для игроков это выглядит как потеря пакетов, хотя канал свободен. Отдельный файл в /etc/sysctl.d/, применённый через sysctl -p, даёт запас:
net.core.rmem_max = 16777216
net.core.rmem_default = 1048576
net.core.netdev_max_backlog = 16384
Нужны ли эти значения, подскажет само ядро: если UdpRcvbufErrors в выводе nstat -az растёт, они пригодятся. Если счётчик стоит на нуле, настройка ничего не изменит. Это запас прочности, а не защита.
7. Меры со стороны анти-чита и плагинов
Заметная часть сбоев, о которых сообщают как о DDoS, никаким DDoS не является. Это падения, которые один-единственный клиент вызывает парой сотен пакетов, потому что в бинарнике сервера или в расширении открыта уязвимость. Полоса пропускания тут не поможет, помогает уход за сервером:
- Держите бинарник сервера в актуальном состоянии. Обновления закрывают не только игровое содержимое, но и сетевые ошибки. Сервер, отставший на две версии, открыт для уже известных схем падения.
- Держите расширения в соответствии с версией движка. Для CS:GO и Garry's Mod обычной основой служат Metamod:Source и SourceMod; для Counter-Strike 2 SourceMod пока не достиг той же зрелости, там распространены Metamod:Source в сборках для разработчиков, а также CounterStrikeSharp. Неподходящее расширение — самая частая причина падений после обновления.
- Меньше расширений. Каждый плагин — это код в том же процессе, а расширения с собственными веб-службами открывают дополнительные порты и часто публикуют как раз тот адрес, который вы хотите защитить.
- В Garry's Mod ограничьте сетевые сообщения. Самый известный выстрел себе в ногу — это меню, которое слушает
net.Receiveбез всякого ограничения: клиент шлёт сообщение в цикле и в одиночку тормозит сервер.
local last = {}
net.Receive("моё_меню", function(len, ply)
if last[ply] and CurTime() - last[ply] < 0.5 then return end
last[ply] = CurTime()
end)
hook.Add("PlayerDisconnected", "моё_меню_очистка", function(ply)
last[ply] = nil
end)
Тоже про Garry's Mod: sv_allowcslua 0 не даёт клиентам выполнять собственный Lua-код. Баны нужно записывать на диск, иначе после перезагрузки их не останется: у игр на Source для этого есть banid и writeid, а также addip и writeip; что из этого умеет ваша сборка, покажет find ban.
8. Список серверов, белый список и собственный адрес
Публичному Counter-Strike-серверу нужен Game Server Login Token, который задаётся через sv_setsteamaccount. Без этого токена сервер остаётся незарегистрированным и не попадает ни в один публичный список. Для закрытой постоянной группы это как раз работает: задайте sv_password, откажитесь от регистрации и раздайте адрес только своим игрокам. Для публичного сервера такой вариант отпадает: сервер, которого никто не находит, пуст ровно так же, как выключенный. Настоящего белого списка в движке нет, он появляется только через расширения.
Адрес игрового сервера изменить нельзя, зато можно изменить всё, что находится рядом: часто атакующий находит сразу всё окружение, от веб-сервера и хоста Discord-бота до доступа к панели. Этим адресам не место ни в том же объявлении, что и адрес сервера, ни в старых DNS-записях.
9. Ведите измерения, чтобы во время атаки не гадать
Во время атаки главный вопрос звучит так: сколько приходит и на какой порт. Хватает трёх команд:
ip -s link show eth0
nstat -az | grep -i udp
tcpdump -ni eth0 -c 200 "udp port 27015 and udp[8:4] = 0xffffffff"
Первая команда показывает пакеты, ошибки и отброшенное по каждому интерфейсу; выполните её дважды с интервалом в десять секунд, и вы получите интенсивность вместо абсолютного значения. Третья строка показывает исключительно пакеты без установленного соединения, то есть тот класс, которым злоупотребляет поток запросов. Держите такой захват коротким, потому что под нагрузкой он и сам стоит процессорного времени. Если счётчик набирается за секунды, а подключённых почти нет, ответ у вас есть.
Где самостоятельная защита заканчивается
Теперь честная часть. Всё описанное выше начинает работать только тогда, когда пакеты уже дошли до вашей сетевой карты. При минимально возможном размере пакета канал 1 Гбит/с несёт около 1,49 миллиона пакетов в секунду, канал 10 Гбит/с около 14,88 миллиона. Это физический потолок, и он не зависит ни от CPU, ни от ядра, ни от файрвола.
Против этого стоят реальные атаки. Два примера из эксплуатации в KernelHost, оба отфильтрованы в реальном времени: UDP-флуд по игровому серверу на 7777/UDP с более чем 112,2 Гбит/с и более чем 8,7 миллиона пакетов в секунду, а также многовекторная атака по голосовому серверу на 9987/UDP с более чем 473,4 Гбит/с и более чем 41,5 миллиона пакетов в секунду.
Сопоставьте это со своим каналом: 473,4 Гбит/с — это примерно в 470 раз больше подключения на 1 Гбит/с и всё ещё примерно в 47 раз больше подключения на 10 Гбит/с. Насколько бы верным ни было ваше правило, оно просто не будет выполнено, потому что потери возникают раньше, на маршрутизаторе перед сервером. И задолго до того, как канал заполнится, закончится CPU, ведь каждый пакет стоит одного прохода через сетевой стек, даже если после этого его отбрасывают.
Поэтому оба распространённых стоп-крана не устраивают. Null-routing (blackholing) убирает атакуемый IP-адрес из сети и заканчивает атаку, а вместе с ней и работу вашего сервера. Реактивное перенаправление трафика съедает на переключении ровно те минуты, в которые решается исход матча. Работает только фильтрация, которая идёт постоянно в сети перед сервером.
Что KernelHost этому противопоставляет
Постоянная защита, которая работает на каждом сервере
Защита от DDoS в KernelHost построена в два уровня и активна постоянно, включать её вам не нужно. Первый уровень — глобальная scrubbing-сеть с ёмкостью митигации 17 Тбит/с, которая перехватывает объёмные атаки близко к их источнику, задолго до того, как они дойдут до дата-центра. Второй уровень — фильтрация Arbor в реальном времени на 3,2 Тбит/с непосредственно на месте во Франкфурте-на-Майне, которая берёт на себя тонкую работу и отбрасывает сложные схемы на уровнях Layer 3-7.
Решают два момента. Во-первых, фильтрация работает постоянно, поэтому нет времени переключения, в которое ваши игроки вылетают. Во-вторых, null-routing не применяется: атакуемый IP-адрес остаётся в сети, отбрасываются только вредоносные пакеты. Защита входит в каждый серверный тариф без доплаты, без отдельного пакета защиты и без настройки. Серверы стоят в maincubes Premium Datacenter во Франкфурте-на-Майне (Германия), провайдер — KernelHost GmbH со штаб-квартирой в Вене (Австрия). Какие игры и протоколы покрыты, перечисляет статья Защита игровых серверов от DDoS в реальном времени.
Advanced DDoS Protection для проектов под постоянным обстрелом
Некоторые проекты атакуют прицельно неделями, с меняющимися схемами и всегда точно ко времени матча. Для таких случаев есть Advanced DDoS Protection от 50,00 € в месяц, по модели PrePaid и без минимального срока. Она даёт три вещи, которых нет во включённой постоянной защите:
- Выделенный защищённый IP-адрес. Ваш сервер переводится на этот адрес внутри нашей сети, перестраивать что-либо на своей стороне не нужно.
- Самостоятельно управляемые правила защиты по портам и протоколам. В личном кабинете вы задаёте, какой порт фильтруется по какому профилю, например 27015/UDP иначе, чем 27020/UDP. Изменения вступают в силу в реальном времени, без тикета и без ожидания.
- Профиль защиты под конкретную игру. Для Counter-Strike 2 и игр на Source, как и для более чем 40 других игр и протоколов, плюс свободные TCP- и UDP-профили для модифицированных серверов.
Здесь тоже действует модель PrePaid: ни минимального срока, ни срока расторжения, ни договора, ни платы за подключение. Когда волна атак пройдёт, вы просто не продлеваете услугу.
Сравнение двух вариантов защиты
| Характеристика | Включённая постоянная защита | Advanced DDoS Protection |
|---|---|---|
| Цена | Входит в каждый серверный тариф без доплаты | от 50,00 € в месяц, PrePaid без минимального срока |
| Активация | Работает с первой минуты, настраивать нечего | Заказать, получить защищённый IP-адрес, сервер переводится на него |
| IP-адрес | IP-адрес сервера из франкфуртской сети | Дополнительный выделенный защищённый IP-адрес |
| Фильтрация | 17 Тбит/с глобального scrubbing плюс фильтрация Arbor в реальном времени на 3,2 Тбит/с во Франкфурте-на-Майне | Та же фильтрация плюс собственные правила по портам и протоколам |
| Изменение правил | Ведёт KernelHost, тонкая настройка через тикет | Самостоятельно в личном кабинете, действует в реальном времени |
| Игровые профили | Более 40 игр и протоколов | Профиль выбирается по портам, в том числе для модифицированных серверов |
| Null-routing во время атаки | Нет | Нет |
| Подходит для | Любого сервера, с первого же матча | Проектов под постоянным прицельным обстрелом |
Частые ошибки и решения
Сервер пропал из браузера серверов, но продолжает работать: обычно 27015/UDP закрыли целиком или ограничили на нём интенсивность слишком жёстко, а поскольку игровой трафик и запрос делят один порт, грубое правило бьёт по обоим сразу. Работайте вместо этого со сравнением по пакетам без установленного соединения. Если сервера нет в списке, хотя порт доступен, проверьте sv_setsteamaccount.
У всех игроков высокий пинг, а канал при этом не заполнен: это указывает на интенсивность пакетов, а не на объём. Посмотрите отброшенные пакеты в ip -s link show и счётчики UDP в nstat -az. Если в системном логе стоит nf_conntrack: table full, выведите игровой порт из отслеживания через notrack.
Правило файрвола написано верно и всё равно не работает: значит, канал перед сервером насыщен. Правило, которое никогда не выполняется, потому что пакет уже отброшен на маршрутизаторе перед сервером, ничего изменить не может. С этого места помогает только фильтрация в сети.
После включения файрвола пропал доступ по SSH: войдите через VNC-консоль в личном кабинете (IPMI или iDRAC не предусмотрены) и отключите файрвол оттуда.
После смены IP-адреса атака затихает и через один-два дня возвращается: это обычное дело, потому что новый адрес ваш сервер публикует сам, как только снова зарегистрируется. Смена IP-адреса даёт несколько часов, но не решение.
Сервер воспроизводимо падает, а расход полосы при этом не выделяется: обычно это не DDoS, а схема падения в каком-то расширении или устаревшая версия сервера.
На сервере выполняются чужие административные команды: это не DDoS, а скомпрометированный доступ по RCON. Немедленно смените пароль и ограничьте порт.
Если вас атакуют прямо сейчас
Если ваш сервер уже работает в KernelHost, фильтрация активна постоянно. Если вы всё же замечаете странности, откройте тикет в поддержку, чтобы наша команда подстроила правила фильтрации под ваш IP-адрес. Во время идущей атаки с нами дополнительно можно связаться через экстренный чат WhatsApp по номеру +43 650 8209883.
Укажите сразу четыре вещи: IP-адрес, порт, период в вашем часовом поясе и то, что вы наблюдаете (игроков выбрасывает, сервера нет в браузере, высокий пинг). Это избавит от круга уточняющих вопросов, а он дорого стоит, когда идёт матч.
Частые вопросы
Мой CS2-сервер внезапно пропал. Это DDoS-атака?
Можно ли просто закрыть query-порт?
Какие порты действительно нужны CS2- или Source-серверу?
Помогает ли смена IP-адреса против атаки?
Почему моё правило файрвола ничего не даёт?
Я закрыл себе доступ файрволом. Как вернуться на сервер?
Убирает ли KernelHost мой IP-адрес из сети во время атаки?
Когда нужна Advanced DDoS Protection?
2026 KernelHost GmbH. Все права защищены. Эта инструкция охраняется авторским правом. Публикация на других сайтах, в том числе частично или в изменённом виде, без нашего письменного согласия не разрешается. Цитирование с указанием источника и активной ссылкой мы приветствуем.

