В наше время возникла необходимость создать программируемый и настраиваемый роутер, который обеспечивал бы доступ в Интернет домашней сети без геополитических ограничений. Поискав среди старых вещей, обнаружил материнскую плату двадцатилетней давности с твердотельными конденсаторами на борту, которая, издав тихий писк, успешно стартовала. Подробности технических характеристик ПК здесь не важны, поскольку передача пакетов процесс не требовательный к мощности "железа". Докупив дополнительную сетевую карту Wi-Fi стандарта 5G и установив Ubuntu 20.04, настроил устройство согласно руководству на данном сайте, дополнительно подключив и настроил поддержку протоколов IKEv2 и SSTP. Всё работало прекрасно вплоть до неожиданного происшествия — произвольного перезагрузки устройства. Изучив лог-файлы системы после инцидента, обнаружил сообщение ядра Linux о подозрительной активности типа TCP flood.
Кратко разберемся с теорией. В протоколе TCP соединение устанавливается следующим образом: клиент отправляет серверу пакет SYN, ожидая подтверждения от сервера. Однако злоумышленники часто злоупотребляют этим механизмом, отправляя огромное количество таких запросов, заставляя систему расходовать вычислительные ресурсы на обработку несуществующих попыток подключения. Стандартно операционная система не ведет учет соединений.
Изучив проблему подробнее, нашёл подходящий инструмент — модуль hashlimit в фаерволе iptables, поставляемый вместе с операционной системой. Этот модуль позволяет отслеживать частоту новых соединений и сообщать о превышениях лимитов в системный лог. До этого момента я уже использовал утилиту Fail2Ban, которая эффективно блокировала атаки на порт SSH (22). Решил расширить её возможности и применить к защите от SYN-flooding атак. Давайте перейдем от теории к практике.
Создаем новое правило, которое размещается первым в очереди обработки входящих пакетов:
iptables -I INPUT 1 -i интерфейс -p tcp --syn -m hashlimit --hashlimit 200/sec --hashlimit-burst 100 --hashlimit-mode srcip --hashlimit-name synflood -j LOG --log-prefix "[SYNFLOOD] "
Здесь мы задаем фильтрацию по определенному интерфейсу для протокола TCP, отслеживая запросы на установление связи (--syn
). Если частота превышает порог 200 запросов/секунду
, запись фиксируется в журнале системы с отметкой [SYNFLOOD]
. Фильтрация ведется по источнику (ключ srcip
), что позволяет ограничить объем атакующего трафика от конкретного узла.
Fail2Ban получает события из журнала и применяет заданные правила блокировки, автоматически создавая цепочку в таблице фильтрации iptables. Далее настраиваем фильтр для Fail2Ban, создадим файл
/etc/fail2ban/filter.d/synflood.conf
следующего содержания:
[Definition]
failregex = $$SYNFLOOD$$.*SRC=.*PROTO=TCP.*SYN
ignoreregex =
Затем добавляем секцию в конфигурационный файл /etc/fail2ban/jail.local
:
[synflood]
enabled = true
filter = synflood
action = iptables-allports[name=synflood, protocol=tcp]
backend = systemd
maxretry = N # Количество превышений порога за указанный период findtime (целое число)
findtime = 5m
bantime = 1d
Таким образом, Fail2Ban автоматически блокирует узлы, превысившие установленный предел запросов на подключение и разблокирует через время bantime. Перезапускаем службу systemctl restart Fail2Ban.service
и проверяем статус systemctl status Fail2Ban.service
. Для мониторинга и точной настройки смотрим вывод команды iptables -L -n
. После внесения изменений в правила, для удобства управления, создал простой bash-скрипт, позволяющий снять блокировки со всех узлов одновременно, :
#!/bin/bash
jails=$(sudo fail2ban-client status | grep 'Jail list' | cut -d':' -f2 | tr -d ' ' | tr ',' ' ')
for jail in $jails; do
echo "Обрабатываю jail: $jail"
banned_ips=$(sudo fail2ban-client status $jail | grep 'Banned IP list' | cut -d':' -f2)
for ip in $banned_ips; do
if [[ $ip != "" ]]; then
echo "Разбаниваю IP $ip из $jail"
sudo fail2ban-client set $jail unbanip $ip
fi
done
done
echo "Все заблокированные IP были освобождены."
Система работает стабильно уже около трех месяцев, периодически проверяю список забаненных адресов — случайных пользователей среди них не наблюдалось. Надеюсь, мой опыт окажется полезным!
UPD. SYN Cookies: Доп. информация на коммент, дабы читатель в одном месте увидел и решил, что использовать.
Принцип работы:
Клиент >
SYN
> СерверСервер >
SYN-ACK
> Клиент (без сохранения состояния)Клиент >
ACK
> СерверСервер проверяет ACK и восстанавливает состояние
Соединение установлено.
Просмотр в /proc/net/stat/syncookies и вы увидите примерную информацию.
SYNCOOKIES_SENT SYNCOOKIES_RECV SYNCOOKIES_FAILED
123456 120000 3456Типы атак, которые предотвращаются:
SYN Flood - массовая отправка SYN-пакетов
IP Spoofing - подделка адресов
Resource Exhaustion - истощение ресурсов сервера
Как настроить: Добавляем строки в /etc/sysctl.conf:
net.ipv4.tcp_syncookies=1 или 2, если 1 защита включится при превышения порогов, если 2 включена всегда
net.ipv4.tcp_max_syn_backlog=1024 — максимальное количество полунастроенных TCP соединений, которые могут находиться в очереди SYN_RECV, то есть соединений, для которых сервер получил SYN, отправил SYN-ACK и ожидает завершение рукопожатия от клиента. По умолчанию обычно 1024, можно увеличить для серверов с высокой нагрузкой, чтобы не терять соединения при большом числе одновременных запросов. Значение влияет на размер очереди запросов на установку TCP соединений.
net.ipv4.tcp_synack_retries=5 — количество повторных отправок TCP SYN-ACK пакетов сервером при установлении соединения, если подтверждение не приходит. Значение по умолчанию — 3 или 5 (зависит от версии ядра). Меньшее значение уменьшит время ожидания подтверждения SYN от клиента, но может привести к отказу в соединении при потере пакетов
net.core.somaxconn=128 — максимальное количество установленных TCP соединений в очереди accept, то есть длина очереди ожидающих принятия соединений (ESTABLISHED очередь). Обычно по умолчанию 128, рекомендуется увеличить для высоконагруженных серверов.
net.ipv4.tcp_max_tw_buckets=180000 — максимальное количество TCP сокетов в состоянии TIME_WAIT (полузакрытое состояние); при превышении лишние закрываются. По умолчанию 180000, можно увеличить для нагруженных серверов.
После внесённых изменений, перечитайте конфигурацию
sysctl -p
В этом методе не все так радужно есть и ограничения.
Потеря параметров TCP:
Не поддерживаются опции TCP
SACK— позволяет получателю явно сообщать отправителю, какие именно сегменты данных получены, а какие отсутствуют, что помогает эффективно восстанавливать только потерянные пакеты без необходимости повторять уже полученные. Это улучшает работу TCP при потере пакетов и снижает лишнюю нагрузку на сеть.
Timestamps— расширение для точного измерения времени прохождения пакетов (RTT), улучшая оценку времени ожидания и обработку потерь пакетов. Опция увеличивает точность для адаптации размеров окон и предотвращения старых пакетов ("дождевых капель").
Window Scaling — позволяет увеличить максимальный размер окна TCP (объем данных, который может быть отправлен без подтверждения) с 64 Кб (обычное 16-битное поле окна) до примерно 1 гигабайта, используя коэффициент масштабирования (степень двойки), который договаривается при установлении соединения. Это значительно повышает производительность на высокоскоростных и высокозадержанных сетях.
Используется только базовое MSS - — максимально допустимый размер сегмента TCP в байтах, который может быть передан без фрагментации. Он согласовывается в процессе установления соединения и обычно зависит от MTU канала передачи (например, при MTU 1500 MSS около 1460). MSS задает ограничение на размер полезной части TCP пакета, чтобы избежать фрагментации на уровне IP.
Вычислительная нагрузка:
Требуется вычисление хэшей для каждого ACK
Дополнительная нагрузка на CPU
Совместимость:
Некоторые экзотические опции TCP могут не работать
Всем успехов!