SYN-флуд: что видно в метриках и что вы реально можете сделать
Сайт вдруг перестаёт принимать новые подключения: уже открытые сессии работают нормально, процессор и память в порядке, а новые клиенты просто не могут достучаться. Похоже на баг в приложении, но причина часто в другом — кто-то заваливает сервер SYN-пакетами, не собираясь ничего устанавливать. Разберём, как это выглядит в метриках, что решается штатными средствами Linux на одном сервере, а что требует вмешательства выше — на уровне сети или провайдера.
Содержание
Как работает SYN-флуд: атака на состояние, а не на канал
TCP-соединение устанавливается через three-way handshake: клиент шлёт SYN, сервер отвечает SYN-ACK и кладёт запрос в очередь полуоткрытых соединений, а завершается всё финальным ACK от клиента. Если хотите разобрать этот обмен по шагам — есть отдельный разбор рукопожатия TCP, здесь коротко: пока ACK не пришёл, запись о соединении занимает место в SYN-очереди слушающего сокета.
SYN-флуд эксплуатирует именно этот момент. Атакующий шлёт множество SYN-пакетов, часто с подделанным (spoofed) обратным адресом, и никогда не отправляет финальный ACK. Сервер честно отвечает SYN-ACK на каждый и ждёт — обычно несколько секунд, с ретраями по таймауту. Пока ждёт, запись держит место в SYN-очереди. Если скорость входящих SYN превышает скорость, с которой очередь освобождается по таймауту, очередь переполняется, и новые легитимные SYN начинают либо тихо отбрасываться, либо — в зависимости от настроек — сервер отвечает RST. Реальные пользователи в этот момент видят зависшее подключение или отказ, хотя сервер как будто «жив»: CPU не в потолке, диск не насыщен, обычные графики нагрузки выглядят почти спокойно.
Это принципиально отличает SYN-флуд от волюметрической атаки, забивающей канал мусорным трафиком. Здесь бьют не по полосе, а по конечному ресурсу ядра — размеру очереди полуоткрытых соединений. Поэтому небольшой по объёму, но частый поток SYN-пакетов может положить приём новых подключений на сервере с многогигабитным каналом и свободным процессором. Если хотите увидеть общую картину действий при DDoS любого типа, до этой статьи стоит прочитать первые действия при DDoS-атаке — здесь мы сосредоточимся конкретно на протокольной атаке SYN-флудом.
Что вы увидите в netstat -s и ss
Первый и самый быстрый индикатор — количество соединений в состоянии SYN_RECV. Посчитать их можно так:
ss -n state syn-recv | wc -l
В обычной жизни это число единицы или низкие десятки — соединения проскакивают это состояние за доли секунды. Если вы видите сотни или тысячи записей в SYN_RECV, которые держатся стабильно (а не разово, при обычном всплеске интереса), это уже повод присмотреться внимательнее. Полезно посмотреть, с каких адресов идёт основная масса:
ss -n state syn-recv | awk '{print $4}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -20
Если адреса разнообразные и не повторяются — вероятен спуфинг обратного адреса, классика для SYN-флуда. Если наоборот — небольшая группа адресов шлёт основную массу, это можно фильтровать точечно уже на уровне iptables/nftables.
Второй источник — агрегированная статистика стека, netstat -s (или его современная замена nstat, если пакет iproute2 собран с ней):
netstat -s | grep -iE 'SYN|listen|overflow'
Здесь стоит обратить внимание на несколько конкретных счётчиков (названия зависят от версии ядра и net-tools, но суть одна):
- SYNs to LISTEN sockets dropped — сколько входящих SYN отброшено, потому что очередь была переполнена. Растущее число при живом трафике — прямой признак переполнения SYN-очереди.
- times the listen queue of a socket overflowed — то же самое, но применительно к accept-очереди (уже установленным, но не забранным
accept()соединениям); не путайте с SYN-очередью — это разные структуры, и переполняться они могут по разным причинам. Подробно про разницу между ними и про параметрыtcp_max_syn_backlog/somaxconnесть отдельная статья про предел backlog и SYN — если давно не разбирались, что там за две очереди у одного сокета, стоит заглянуть туда. - TCPSynRetrans — сколько раз ядро повторно отправляло SYN-ACK, не дождавшись ACK. Аномальный рост этого счётчика синхронно с ростом
SYN_RECVподтверждает, что дело именно в незавершённых рукопожатиях, а не в чём-то ещё.
Третий полезный источник — журнал ядра. Linux умеет сам сообщать о подозрении на SYN-флуд, если включены SYN cookies (о них — в следующем разделе):
dmesg -T | grep -i "possible SYN flooding"
Строка вида possible SYN flooding on port 443. Sending cookies. — это ядро прямым текстом говорит вам, что произошло и что оно уже среагировало. Стоит завести на неё алерт в мониторинге логов (например, через journalctl или агент, который умеет grep по dmesg/kern.log) — это один из немногих случаев, когда ядро само диагностирует конкретный тип атаки, и грех этим не пользоваться.
Если снимаете метрики регулярно, а не только в момент инцидента, стоит собирать SYN_RECV-счётчик и SYNs to LISTEN sockets dropped в Zabbix/Prometheus как отдельные ряды: сравнение «было — стало» на графике куда нагляднее, чем разовый снимок netstat -s в панике.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверКак отличить SYN-флуд от легитимной нагрузки
Резкий рост SYN_RECV бывает и без злого умысла — например, при флеш-толпе после публикации в популярном СМИ или при агрессивном ретрае клиентов из-за проблем сети на их стороне. Разница обычно видна по нескольким признакам сразу, а не по одному:
- Разнообразие источников. Легитимный всплеск — это тысячи разных людей с разных провайдеров, но с более-менее ожидаемой географией для вашей аудитории. SYN-флуд со спуфингом — случайные, часто не маршрутизируемые в реальности адреса, разбросанные без всякой логики.
- Завершаемость рукопожатий. У обычных пользователей за SYN почти всегда следует ACK и переход в
ESTABLISHED. При флуде подавляющее большинство «зависает» вSYN_RECVи там же тайм-аутится. - Соотношение с прикладными логами. Если веб-сервер (nginx, Apache) не видит соответствующего роста в access-логе — запросы просто не долетают до уровня приложения, — а на уровне TCP всплеск огромный, это симптоматично именно для атаки на рукопожатие, а не на приложение.
- Профиль по портам. Легитимная нагрузка идёт на реальные открытые сервисы (80, 443, ваш SSH-порт). Если SYN сыплются вперемешку по случайным портам, это, скорее, сканирование или широкий флуд без разбора цели.
Полезно держать под рукой baseline — сколько SYN_RECV и какая скорость SYN в секунду для вас нормальны в пиковые часы. Без этого baseline любое число выглядит одинаково тревожным, а с ним сразу видно отклонение.
SYN cookies: как ядро защищается само
Главный встроенный механизм защиты в Linux — SYN cookies. Идея в том, чтобы вообще не тратить память на SYN-очередь для не подтверждённых соединений. Вместо того чтобы хранить состояние полуоткрытого соединения, сервер кодирует всю необходимую информацию (упрощённо: часть параметров соединения и таймстамп) прямо в поле sequence number ответного SYN-ACK — в виде криптографической «печеньки». Когда приходит ACK от клиента, сервер проверяет cookie, восстанавливает из неё параметры и создаёт соединение — без необходимости держать запись всё это время в очереди.
Проверить и включить:
sysctl net.ipv4.tcp_syncookies
# 0 — выключено, 1 — включать при переполнении очереди (по умолчанию в большинстве дистрибутивов), 2 — всегда
sysctl -w net.ipv4.tcp_syncookies=1
# постоянно, через sysctl.conf
echo 'net.ipv4.tcp_syncookies = 1' >> /etc/sysctl.d/99-syn-protection.conf
sysctl -p /etc/sysctl.d/99-syn-protection.conf
В большинстве современных дистрибутивов (Ubuntu, Debian, AlmaLinux) значение 1 стоит по умолчанию — то есть ядро само включает cookies в момент, когда SYN-очередь начинает переполняться, а в спокойное время работает обычным путём. Это и есть основная причина, почему одиночный SYN-флуд умеренной интенсивности часто вообще не замечают пользователи: ядро отбивается само, вы узнаёте об этом постфактум по логам и графикам.
У SYN cookies есть цена, и о ней стоит сказать честно. Чтобы уместить нужные данные в 32-битное поле sequence number, приходится жертвовать частью TCP-опций — в частности, для cookie-соединений не передаются некоторые расширения (например, часть параметров window scaling и SACK может быть недоступна или согласовывается по ограниченному набору значений, которые сервер помнит заранее). На практике для веб-трафика это почти никогда не критично, но для соединений с большой задержкой и объёмом (спутниковые каналы, трансатлантические линки) деградация масштабирования окна теоретически может ощущаться как более медленный разгон соединения в первые секунды. Также cookies не помогают, если атака идёт не на SYN-очередь, а забивает канал целиком, — тут они бессильны, потому что проблема не в ядре, а в физической полосе.
Что ещё настроить на одном сервере
Кроме включённых по умолчанию cookies, есть несколько параметров и приёмов, которые имеет смысл проверить и подстроить заранее, а не в момент атаки:
# Размер SYN-очереди (сколько полуоткрытых соединений держим до cookies/сброса)
sysctl -w net.ipv4.tcp_max_syn_backlog=4096
# Сколько раз повторять SYN-ACK, если ACK не пришёл — по умолчанию 5,
# каждая попытка увеличивает время жизни записи в очереди
sysctl -w net.ipv4.tcp_synack_retries=2
# Backlog очереди на уровне сокета (второй параметр listen()) —
# должен быть согласован с тем, что просит приложение/веб-сервер
sysctl -w net.core.somaxconn=4096
Снижение tcp_synack_retries с дефолтных 5 до 2-3 сокращает время, которое запись о полуоткрытом соединении проводит в очереди при отсутствии ответа — при таймаутах с экспоненциальной задержкой это заметно ускоряет освобождение места под легитимные подключения. Плата — при реальных, но временных проблемах в сети у клиента (потерянный пакет SYN-ACK) шанс, что соединение всё же установится с первой попытки без участия клиента, немного ниже. Компромисс разумный: клиентские TCP-стеки и так переотправляют SYN сами, если не дождались ответа.
Для точечной фильтрации по скорости SYN с одного адреса пригодится nftables:
# Ограничить входящие SYN на 443 порт: не больше 20 новых соединений в секунду с одного IP,
# с кратковременным бёрстом до 40
nft add rule inet filter input tcp dport 443 tcp flags syn tcp option maxseg size 1-1360 counter drop
nft add rule inet filter input tcp dport 443 tcp flags & (syn|ack) == syn limit rate over 20/second burst 40 packets drop
Второе правило — рабочий лимитер: он не блокирует конкретный IP по чёрному списку, а просто отбрасывает избыточный поток SYN на порт целиком, если он превышает разумную скорость. Для одиночного сервера с умеренным легитимным трафиком это снижает эффект флуда на пределе backlog, но не спасает от массированной распределённой атаки — она просто равномерно размажется по разным адресам и обойдёт лимит per-IP или общий лимит по порту незаметно.
Отдельно стоит проверить ulimit/файловые дескрипторы и лимиты conntrack, если у вас stateful firewall: сама таблица conntrack — тоже конечный ресурс, и массовый SYN-флуд способен исчерпать её раньше, чем SYN-очередь TCP-стека, если размер таблицы занижен по умолчанию.
Где заканчиваются возможности одного сервера
Всё, что описано выше, работает, пока атака помещается в возможности вашего сервера и его канала — то есть пока проблема действительно в переполнении SYN-очереди, а не в исчерпании полосы или процессора на обработку самого потока пакетов. У этого подхода есть жёсткий потолок: сервер физически может обработать конечное число пакетов в секунду и конечную пропускную способность интерфейса. Если атакующие направляют трафик, превышающий аплинк — неважно, 100 Мбит/с или несколько гигабит, — пакеты забивают канал ещё до того, как долетят до вашего файрвола и правил nftables. Никакие sysctl на самом сервере эту проблему не решают, потому что решать её уже физически негде: место кончилось раньше, чем ваши настройки успевают сработать.
Здесь начинается зона ответственности провайдера или внешнего сервиса фильтрации (scrubbing-центра). У upstream-провайдера канал и оборудование на порядок мощнее вашего, и объёмную атаку он способен отфильтровать до того, как трафик доберётся до вашего сервера — например, через BGP-анонс на время атаки, который перенаправляет трафик через центр очистки, либо через встроенную анти-DDoS фильтрацию на границе сети. Если у вашего хостинга есть такая опция — стоит знать заранее, как её включить, а не искать в панике посреди инцидента. Подробнее про весь спектр защит на выделенном сервере, включая уровень провайдера, есть отдельный разбор в статье про защиту от DDoS на выделенном сервере.
Практическое правило простое: если netstat -s показывает переполнение SYN-очереди, но канал и процессор в порядке — вы, скорее всего, справитесь настройками на сервере. Если растёт входящий трафик на интерфейсе, а vnstat/iftop показывают, что канал близок к насыщению, — это уже разговор с поддержкой хостинга, а не повод искать ещё один sysctl-параметр.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
SYN cookies снижают производительность при обычной нагрузке?
Нет, пока очередь не переполнена, ядро использует обычный механизм установления соединений, cookies включаются в дело только в момент переполнения (при значении net.ipv4.tcp_syncookies = 1). Постоянное принудительное использование (значение 2) на практике почти никогда не нужно.
Можно ли полностью отключить SYN-флуд одним параметром?
Нет универсального выключателя. Комбинация SYN cookies, разумного tcp_max_syn_backlog и лимитов по скорости в nftables закрывает атаки, которые помещаются в ваш канал. От объёмной атаки это не защищает — тут нужна фильтрация выше по сети.
Как понять, что атака идёт именно SYN-флудом, а не HTTP-флудом или чем-то ещё?
Смотрите на состояние SYN_RECV и счётчик SYNs to LISTEN sockets dropped в netstat -s. Если они растут, а веб-сервер в access-логе почти ничего не видит — запросы не доходят до уровня приложения, значит, атака бьёт именно по рукопожатию, а не по прикладной логике.
Нужно ли увеличивать tcp_max_syn_backlog «про запас», даже без атак?
Разумное значение (2048-4096 для среднего сервера) не повредит и помогает справляться с легитимными всплесками тоже, но само по себе не спасает от целенаправленного флуда — при достаточной интенсивности атакующий заполнит и увеличенную очередь. Это буфер прочности, а не защита.
Спасает ли смена SSH-порта или закрытие лишних сервисов от SYN-флуда?
Нет, это снижает площадь атаки для сканирований и брутфорса, но SYN-флуд обычно бьёт по открытым публичным портам (80/443), которые и так должны быть доступны — их не закроешь без потери сервиса.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →