Защита Rust-сервера от DDoS-атак

Опубликовано 14 мин. чтения

Rust-серверы атакуют почти всегда к вайпу или в разгар рейда. Что вы можете закрыть сами, где самостоятельная защита упирается в физику канала и что должно происходить перед сервером, в сети.

Rust-сервер редко уходит в офлайн случайно. Момент почти всегда выдаёт мотив: атака начинается ровно в минуту вайпа или в разгар рейда. Тому, кого обстреливают, нужна не дискуссия о принципах, а порядок действий. В этой статье сначала показано, что вы можете изменить на самом сервере, затем, где эти возможности заканчиваются, и в конце, что должно происходить перед сервером, в сети.

Почему именно Rust-серверы атакуют так часто

Rust — это игра, в которой прогресс жёстко привязан ко времени. Рейд длится минуты, цикл вайпа — недели. Поэтому простой здесь стоит дороже, чем почти в любой другой игре: тот, кто защищается, выигрывает время, если сервер падает. Тот, кто нападает, не даёт противнику зайти в игру. А тот, кто держит конкурирующее сообщество, прекрасно знает, что первый вечер вайпа определяет онлайн на весь месяц.

К этому добавляется обстоятельство, которое нельзя убрать настройками: Rust-сервер публично находится по IP-адресу и порту, иначе на него никто не зайдёт. В отличие от сайта за прокси, игровой сервер обязан публиковать свой настоящий адрес. Поэтому вопрос никогда не в том, найдёт ли атакующий ваш IP, а только в том, что произойдёт, когда он по нему ударит.

О каких портах идёт речь

В обычной конфигурации Rust-сервер занимает четыре порта:

  • 28015/UDP, игровой порт (server.port). Через него идёт весь игровой трафик. В UDP нет установки соединения, каждый пакет существует сам по себе, а адрес отправителя легко подделать. Для атакующего это означает две вещи: его не отследить, и при этом каждый пакет создаёт работу вашему серверу.
  • Query-порт (server.queryport), тоже UDP. Через него сервер отвечает на запросы Steam A2S_INFO, A2S_PLAYERS и A2S_RULES, без него он не появится ни в одном списке серверов. Если значение не задано явно, порт оказывается прямо рядом с игровым, а во многих строках запуска он выставлен на 28017/UDP. Посмотрите свою собственную строку запуска, а не полагайтесь на значение по умолчанию.
  • 28016/TCP, RCON (rcon.port), при rcon.web 1 в варианте WebSocket.
  • 28082/TCP, приложение-компаньон Rust+ (app.port).

Query-порт из этой четвёрки самый неприятный, потому что ответ A2S заметно больше запроса. Атакующий может опрашивать чужие игровые серверы с подделанным адресом отправителя и направлять ответы на свою настоящую цель. Тогда ваш сервер уже не только жертва, но и усилитель против третьих лиц. Именно поэтому Valve дополнила A2S_INFO запросом-челленджем, что смягчило проблему, но не закрыло её. По каким признакам распознать атаку, разобрано в статье Как определить DDoS-атаку на сервере.

Что вы можете сделать сами, прежде чем тратить деньги

Следующая часть не стоит ничего и окупается независимо от того, где стоит ваш сервер. От объёмной атаки она вас не избавит, но сделает так, что мелкие атаки останутся без эффекта, а в серьёзной ситуации вам не придётся гадать.

1. Инвентаризация: что на самом деле слушает

Прежде чем писать хоть одно правило, выясните, какие службы доступны снаружи. На игровом сервере, который разрастался месяцами, их почти всегда больше, чем вы ожидаете:

ss -lntup

Всё, что привязано к 127.0.0.1 или ::1, открывать не нужно. Всё, что висит на 0.0.0.0 или [::], доступно из интернета, в том числе служба базы данных, которую притащил за собой плагин. Сравните вывод со своей строкой запуска:

./RustDedicated -batchmode -nographics \
  +server.port 28015 \
  +server.queryport 28017 \
  +server.identity "wipe" \
  +server.maxplayers 150 \
  +rcon.port 28016 \
  +rcon.web 1 \
  +rcon.password "ВАШ-ДЛИННЫЙ-СЛУЧАЙНЫЙ-ПАРОЛЬ"

Если Rust у вас установлен через SteamCMD, по базовой части поможет статья Установка игрового сервера через SteamCMD.

2. Оставить открытыми только те порты, которые Rust действительно нужны

Четыре порта, и не больше. RCON не место в открытом интернете, его следует ограничить вашим адресом, а Rust+ открывать только тогда, когда вы пользуетесь приложением-компаньоном:

ufw allow 28015/udp comment "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"

Замените 203.0.113.10 на свой собственный адрес. Если адрес у вас меняется, путь идёт через SSH-туннель.

Замечание, которое каждый год стоит кому-то сервера: порядок действий при включении файрвола решает, запрёте вы себя снаружи или нет. Он вместе с путём назад описан в статье Настройка файрвола UFW. Если это всё-таки случилось: у KVM root-серверов и выделенных серверов KernelHost доступ к серверу остаётся через VNC-консоль в личном кабинете. Она не подключена к сетевому стеку гостевой системы, и правило файрвола внутри гостя её заблокировать не может.

3. Защитить query-порт, не вылетев из списка серверов

Напрашивающийся рефлекс закрыть query-порт — самая дорогая ошибка в этой теме. Без него ваш сервер исчезает из браузера серверов, показывает неверное число игроков, а сайты со списками помечают его как офлайн. Атаку вы в этом случае доведёте до конца сами.

Правильный путь — ограничение частоты запросов на каждый адрес отправителя. Настоящий клиент при пролистывании списка опрашивает сервер несколько раз в секунду, инструмент для reflection-атак делает это тысячами. В nftables это оформляется в отдельной таблице, которая обрабатывается раньше цепочки фильтрации:

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

Файл загружается командой nft -f. Приоритет -10 обеспечивает срабатывание правила раньше цепочки фильтрации, которую UFW создаёт с приоритетом 0. В классическом iptables того же добивается модуль 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

Начинайте со щедрого лимита и затягивайте его только тогда, когда убедились, что легитимные запросы проходят. Иначе слишком узкий порог обнаружится только в день вайпа.

4. Разгрузить отслеживание соединений

Этот пункт упускают почти всегда, а именно он объясняет простои, которые выглядят как объёмная атака, но ею не являются. Для UDP-трафика ядро заводит записи в таблице отслеживания соединений (conntrack), и при подделанных адресах отправителя каждый новый адрес означает новую запись. Когда таблица заполнена, ядро отбрасывает пакеты без разбора: вместе с атакой вылетают и ваши игроки. В системном логе тогда появляется nf_conntrack: table full, dropping packet. Проверить это можно так:

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

Самый действенный шаг — вообще не отслеживать игровой трафик. Ядру не нужно вести состояние соединений за Rust, свои сессии он ведёт сам:

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

В iptables соответствие выглядит так:

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

И только после этого имеет смысл повышать nf_conntrack_max. Кто сначала увеличивает таблицу, тот лишь отодвигает проблему на несколько минут и расходует на это оперативную память.

5. Буфер приёма и параметры ядра

Если пакеты приходят быстрее, чем процесс Rust успевает их забирать, буфер приёма сокета переполняется. Для игроков это выглядит как потеря пакетов, хотя канал при этом свободен:

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

Положите значения в /etc/sysctl.d/ и активируйте их командой sysctl -p. Нужны ли они, скажет само ядро: если в nstat -az растёт UdpRcvbufErrors или в ss -lunp постоянно что-то висит в очереди приёма, значит они работают. Если оба показателя остаются на нуле, настройка ничего не изменит. Это запас, а не защита.

6. Защитить RCON

Открытый RCON-порт со слабым паролем — это не проблема DDoS, а захват сервера: у кого есть RCON, тот может банить, разбанивать и останавливать игру. Никогда не оставляйте rcon.password пустым и не придумывайте его наугад, значение из openssl rand -base64 32 генерируется за пять секунд. И не открывайте порт публично, а ограничьте его своим адресом.

7. Меры на стороне анти-чита и плагинов

Значительная часть простоев, о которых владельцы сообщают как о DDoS, к DDoS отношения не имеет. Это падения, которые один-единственный клиент вызывает парой сотен пакетов, потому что в серверном бинарнике или в плагине открыта дыра. Против этого помогает уход за сервером, а не полоса пропускания:

  • Держите серверный бинарник в актуальном состоянии. Ежемесячное обновление, которое вынуждает делать вайп, одновременно является обновлением безопасности. Кто его откладывает, остаётся наедине с уже известными ошибками.
  • Держите фреймворк плагинов в актуальном состоянии. Oxide/uMod и Carbon подтягиваются после каждого обновления Rust. Фреймворк, не соответствующий версии сервера, чаще всего и оказывается причиной падений в вечер вайпа.
  • Меньше плагинов. Каждый плагин — это дополнительный код в том же процессе. Плагины с собственными веб-службами (просмотр карты, страницы статистики) открывают новые порты и заодно нередко публикуют ровно тот адрес, который вы хотите защитить.
  • Ведите бан-листы. Повторяющиеся попытки подключения с одного и того же аккаунта пресекаются штатными средствами. Владельцев и модераторов Rust хранит в server/<identity>/cfg/users.cfg, блокировки в server/<identity>/cfg/bans.cfg. Блокировка через banid переживает перезагрузку.

Белого списка в самом Rust нет, он появляется через фреймворк плагинов. Для приватного сервера или сервера сообщества он действительно работает. Для публичного вайп-сервера это не вариант: сервер, на который никто не может зайти, точно так же пуст, как и тот, что лежит в офлайне.

8. Список серверов и собственный адрес

Публичный IP игрового сервера изменить нельзя, а вот всё, что находится рядом с ним, вполне. Часто атакующий сразу находит всё окружение: веб-сервер с магазином, хост Discord-бота, сервер резервных копий, доступ к панели. Этим адресам не место ни в одном объявлении вместе с игровым, ни в старых DNS-записях. Раз в квартал проверяйте, какой поддомен куда указывает.

9. Вести измерения, чтобы во время атаки не гадать

Во время атаки важен один вопрос: сколько приходит и на какой порт. Хватает трёх команд:

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

Первая команда показывает пакеты, ошибки и отброшенное по каждому интерфейсу. Выполните её дважды с интервалом в десять секунд, и вместо абсолютного значения вы получите скорость. Интерфейс и unit службы у вас могут называться иначе, проверьте и то и другое через ip -br link и systemctl list-units --type=service. Как выглядят сами пакеты, покажет выборка, а короткой она должна оставаться потому, что запись трафика под нагрузкой отнимает процессорное время:

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

Где заканчивается самостоятельная защита

Теперь честная часть. Всё описанное выше начинает работать только тогда, когда пакеты уже дошли до вашей сетевой карты. Сервер обычно подключён на 1 Гбит/с или на 10 Гбит/с. При минимально возможном размере пакета канал на 1 Гбит/с пропускает около 1,49 миллиона пакетов в секунду, канал на 10 Гбит/с — около 14,88 миллиона. Это физический потолок, независимо от CPU, ядра и файрвола.

Против этого стоят реальные атаки. Два примера из боевой эксплуатации в KernelHost, оба отфильтрованы в реальном времени: UDP-flood на игровой сервер ARK по порту 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-сеть с мощностью mitigation 17 Тбит/с, которая перехватывает объёмные атаки близко к источнику, ещё до того как они дойдут до дата-центра. Второй уровень — фильтрация Arbor в реальном времени на 3,2 Тбит/с прямо на месте, во Франкфурте-на-Майне: она берёт на себя тонкую работу на уровне протоколов и отбрасывает сложные схемы на уровнях Layer 3-7.

Решающих момента два. Во-первых, фильтрация работает постоянно, поэтому нет ни времени на обнаружение, ни времени на переключение, за которое ваших игроков успевает выкинуть. Во-вторых, null-routing не применяется: атакуемый IP остаётся в сети, отсеиваются только вредоносные пакеты. Защита входит в каждый серверный тариф без доплаты, без отдельного пакета защиты и без настройки. Серверы стоят в maincubes Premium Datacenter во Франкфурте-на-Майне (Германия), дата-центр сертифицирован по TÜV TIER3+ и подключён напрямую к DE-CIX. Провайдер — KernelHost GmbH со штаб-квартирой в Вене (Австрия). Какие игры и протоколы покрыты, перечислено в статье Защита игровых серверов от DDoS в реальном времени.

Advanced DDoS Protection для проектов под постоянным обстрелом

Некоторые Rust-проекты атакуют не время от времени, а неделями и целенаправленно, со сменой схем и всегда ровно к вайпу. Для таких случаев есть Advanced DDoS Protection от 50,00 € в месяц, PrePaid и без минимального срока. Она даёт три вещи, которых включённая постоянная защита в таком виде не предлагает:

  • Выделенный защищённый IP-адрес. Сервер переключается на него внутри нашей собственной сети, перестраивать что-либо на вашей стороне не нужно.
  • Правила защиты по каждому порту и протоколу, которыми вы управляете сами. В личном кабинете вы задаёте, какой порт фильтруется по какому профилю, например 28015/UDP иначе, чем query-порт. Изменения вступают в силу в реальном времени, без тикета и без ожидания.
  • Профиль защиты под конкретную игру. Как для Rust, так и более чем для 40 других игр, служб и протоколов, плюс свободно настраиваемые профили TCP и UDP для модифицированных серверов.

Здесь тоже действует модель PrePaid: без минимального срока, без срока уведомления об отказе, без договора и без платы за подключение. Когда волна атак прошла, вы просто не продлеваете.

Сравнение двух уровней

Характеристика Включённая постоянная защита Advanced DDoS Protection
Цена Входит в каждый серверный тариф, без доплаты от 50,00 € в месяц, PrePaid без минимального срока
Активация Активна с первой минуты, настраивать нечего Заказать, получить защищённый IP, сервер переключается
IP-адрес IP сервера из франкфуртской сети Дополнительный выделенный защищённый IP
Фильтрация 17 Тбит/с глобального scrubbing плюс 3,2 Тбит/с фильтрации Arbor в реальном времени во Франкфурте-на-Майне Та же фильтрация, дополнительно собственные правила по каждому порту и протоколу
Изменение правил Ведутся командой KernelHost, тонкая настройка через тикет Самостоятельно в личном кабинете, действуют в реальном времени
Игровые профили Более 40 игр и протоколов Профиль выбирается для каждого порта, в том числе для модифицированных серверов
Null-routing во время атаки Нет Нет
Кому подходит Любому серверу, начиная с первого вайпа Проектам под постоянным целенаправленным обстрелом

Частые ошибки и решения

Сервер исчез из браузера серверов, но продолжает работать: почти всегда query-порт закрыт или слишком жёстко ограничен по частоте запросов. Проверьте через ss -lunp, слушает ли он, и ослабляйте порог постепенно. Если молчит Rust+, обычно закрыт app.port.

У всех игроков высокий пинг и рывки персонажа (rubberbanding), но канал не забит: это указывает на пакетную нагрузку, а не на объём. Посмотрите отброшенные пакеты в ip -s link show и счётчики UDP в nstat -az. Такую картину даёт переполненный буфер приёма или исчерпанная таблица отслеживания соединений.

Правило файрвола верное, но всё равно не действует: значит, канал перед сервером насыщен. Правило, которое никогда не выполняется, потому что пакет упал ещё на маршрутизаторе перед ним, ничего изменить не может. С этого момента помогает только фильтрация в сети.

После включения файрвола пропал доступ по SSH: войдите через VNC-консоль в личном кабинете. Через неё можно выключить файрвол и дописать недостающее правило, даже если по сети уже ничего не проходит.

После смены IP атака делает паузу и через один-два дня возвращается: это обычное дело. Ваш сервер сам публикует новый адрес в списке серверов, как только снова оказывается онлайн. Смена IP даёт часы, а не решение.

На сервере выполняются чужие админские команды: это не DDoS, а скомпрометированный доступ к RCON. Немедленно смените пароль, ограничьте порт своим адресом, проверьте бан-лист.

Если вас атакуют прямо сейчас

Если ваш сервер уже работает в KernelHost, фильтрация активна постоянно и включать вам ничего не нужно. Если вы всё же замечаете странности, откройте тикет в поддержку, чтобы наша команда подстроила правила фильтрации под ваш IP-адрес. Во время идущей атаки с нами дополнительно можно связаться через экстренный чат WhatsApp по номеру +43 650 8209883.

Укажите сразу четыре вещи: IP-адрес, порт, промежуток времени в вашем часовом поясе и коротко, что вы наблюдаете (игроков выкидывает, сервер недоступен, высокий пинг). Это избавляет от круга уточняющих вопросов, а он дорого обходится, когда идёт вайп.

Частые вопросы

Мой Rust-сервер сейчас недоступен: это атака?
Сначала посмотрите на сетевой интерфейс. Если в «ip -s link show» резко растут отброшенные пакеты, а в «nstat -az» ошибки UDP, при этом загрузка CPU процессом Rust остаётся обычной, это говорит об атаке. Если оба показателя спокойны, а процесса нет, значит это было падение.
Какие порты должны быть открыты у Rust-сервера?
28015/UDP для игрового трафика, query-порт (server.queryport, часто 28017/UDP) для списка серверов, 28016/TCP для RCON и 28082/TCP только тогда, когда вы пользуетесь приложением-компаньоном Rust+. RCON следует ограничить вашим собственным IP-адресом, всё остальное остаётся закрытым.
Есть ли смысл просто заблокировать query-порт?
Нет, это только вредит. Без доступного query-порта ваш сервер исчезает из браузера серверов, а сайты со списками помечают его как офлайн. Работает другое: ограничение частоты запросов на каждый адрес отправителя, например через meter в nftables или через модуль hashlimit в iptables.
Поможет ли смена IP против идущей атаки?
Только ненадолго. Ваш сервер сам публикует новый адрес в списке серверов, как только снова оказывается онлайн. На практике атака возвращается через один-два дня. Смена IP даёт часы, но ничего не решает.
Можно ли защититься файрволом на самом сервере?
От мелких атак да, от объёмных нет. Ваши правила начинают работать только тогда, когда пакеты уже дошли до сетевой карты. Канал на 1 Гбит/с пропускает около 1,49 миллиона мелких пакетов в секунду, реальные атаки превышают это в разы. Потери возникают тогда уже на маршрутизаторе перед сервером.
Убирает ли KernelHost мой IP из сети во время атаки?
Нет. Ни null-routing, ни blackholing не применяются. Атакуемый IP остаётся в сети, отсеиваются только вредоносные пакеты. Фильтрация работает постоянно, поэтому в начале атаки нет и времени на переключение.
Что входит в защиту от DDoS и сколько стоит уровень Advanced?
Двухуровневая постоянная защита входит в каждый серверный тариф без доплаты: 17 Тбит/с мощности mitigation в глобальной scrubbing-сети плюс 3,2 Тбит/с фильтрации Arbor в реальном времени во Франкфурте-на-Майне. Advanced DDoS Protection с выделенным защищённым IP и собственными правилами по каждому порту стоит от 50,00 € в месяц, PrePaid, без минимального срока и без платы за подключение.
Что писать в тикет, когда атака идёт прямо сейчас?
Для начала хватит четырёх пунктов: затронутый IP-адрес, порт, промежуток времени в вашем часовом поясе и коротко, что вы наблюдаете. Этого достаточно, чтобы подстроить правила фильтрации под ваш IP без круга уточняющих вопросов. В острых случаях с нами дополнительно можно связаться через экстренный чат WhatsApp по номеру +43 650 8209883.

Rust-сервер Rust-DDoS-защита Защита игровых серверов UDP-flood Query-порт nftables Advanced DDoS Protection Вайп