0887 371 498 support@itservice-bg.net
Откриване на DDoS по IPFIX: малка машина до рутера, която различава атака от сваляне
07.10.2026 · Самуил Арсов · MikroTik, Киберсигурност, Рутери

Откриване на DDoS по IPFIX: малка машина до рутера, която различава атака от сваляне

Граничният ни рутер (AS47453) вече не пуска трафик от хиляди познати зловредни мрежи — това го върши наборът blackhole маршрути, за който писах по-рано. Но blackhole спира само онова, за което вече знаеш, че е лошо. Срещу внезапен флуд от свеж адрес, който до преди минута е бил чист, списък не помага. Трябва някой да гледа трафика в реално време и да каже „ето, сега нещо не е наред“. Точно това добавихме миналата седмица — откриване на обемни атаки по NetFlow, без да пипаме самия рутер.

Защо не на рутера

RouterOS може да експортира IPFIX (това е NetFlow версия 10) за целия трафик, който минава през него. Но самият анализ не му е работа — това е чист рутер, чиято единствена задача е да препраща пакети, без connection tracking и без firewall (защо точно така — в статията за blackhole по-долу).

Затова сложихме отделна малка виртуална машина до него: четири ядра, 8 GB памет, Ubuntu. На нея върви FastNetMon Community — безплатната версия на познат инструмент за детекция на DDoS. Рутерът праща IPFIX към машината, машината смята и не връща нищо обратно. Ако утре спре, рутерът продължава да работи все едно нищо не е станало.

Инсталиране

Community версията се слага с официалния инсталатор — един скрипт, който тегли пакета и вдига услугата:

wget https://install.fastnetmon.com/installer -O installer
chmod +x installer
sudo ./installer

Инсталаторът иска имейл (за маркетинг от производителя) и без подаден такъв се върти в безкраен цикъл — подава му се наведнъж и готово. След него услугата е вдигната, но по подразбиране не стартира сама при рестарт — това се включва с systemctl enable fastnetmon.

Конфигурацията

Три файла вършат цялата работа:

  • /etc/fastnetmon.conf — настройките;
  • /etc/networks_list — твоите мрежи, тези, които се наблюдават;
  • /etc/networks_whitelist — адреси, които никога да не влизат в списъка (сървъри, шлюзове, самата машина).

В networks_list се изреждат собствените префикси, по един на ред — това казва на FastNetMon кое е „наше“ и си струва да се пази:

203.0.113.0/24
198.51.100.0/23

А това е самият /etc/fastnetmon.conf — параметрите, които реално решават поведението. „По подразбиране“ е стойността във файла, който слага официалният инсталатор; удебеленото сме сменили ние.

Откъде идват данните

Параметър Какво прави По подразбиране При нас
netflow Приема NetFlow/IPFIX от рутера off on
netflow_host / netflow_port На кой адрес и порт слуша 0.0.0.0 / 2055 0.0.0.0 / 2055
netflow_sampling_ratio Колко пакета представлява един отчетен (1 = всеки пакет) 1 1 — рутерът праща без sampling
average_calculation_time Прозорецът в секунди, за който се смята средната скорост на всеки адрес. Трябва да е по-дълъг от времената на рутера (виж по-долу) 5 30
speed_calculation_delay През колко секунди се преизчисляват скоростите 1 1
check_period През колко секунди се сравнява с праговете 1 1
process_incoming_traffic / process_outgoing_traffic Гледа входящия / изходящия трафик on / on on / on
networks_list_path Файлът с нашите мрежи — само те се следят /etc/networks_list същият
white_list_path Адреси, които никога не се банват /etc/networks_whitelist същият — сървъри, рутери, статичните адреси на VIP клиент
enable_connection_tracking Брои нови потоци в секунда за всеки адрес (нужно за прага по потоци) on on

Кога е бан — общите прагове за един адрес

Параметър Какво прави По подразбиране При нас
enable_ban Изобщо да банва (IPv4) on on — blackhole през GoBGP при дребни пакети
enable_ban_ipv6 Същото за IPv6 (по адрес, не цяла мрежа) on on
ban_for_pps + threshold_pps Бан над X пакета в секунда on, 20 000 on, 20 000
ban_for_bandwidth + threshold_mbps Бан над X мегабита в секунда on, 1 000 on, 1 000
ban_for_flows + threshold_flows Бан над X нови потока в секунда off, 3 500 off, 3 500

Прагове по протокол

Параметър Какво прави По подразбиране При нас
ban_for_icmp_pps + threshold_icmp_pps Бан при ICMP над X пакета в секунда off, 100 000 on, 1 000
ban_for_tcp_pps / ban_for_udp_pps Бан при TCP / UDP над X пакета в секунда off, 100 000 off, 100 000
ban_for_tcp_bandwidth и сродните Същото, но в мегабити off, 100 000 off, 100 000

Какво става при бан

Параметър Какво прави По подразбиране При нас
ban_time Колко секунди трае банът 1 900 600
unban_only_if_attack_finished След изтичане отбанва само ако трафикът е паднал под прага, иначе удължава on on
ban_details_records_count Колко записа на потоци събира за файла с подробностите 20 20
notify_script_path Скрипт, който се вика при бан и отбан /usr/local/bin/notify_about_attack.sh същият — с нашата класификация
collect_attack_pcap_dumps Пази pcap от атаката (при NetFlow пакети няма, безполезно) off off
gobgp / exabgp Обявява бана по BGP — тоест реален blackhole off / off on / off
gobgp_announce_host Обявява единичния адрес (/32) on off — обявява скриптът
gobgp_community_host BGP community, по която рутерът разпознава бана 65001:666 65001:666
gobgp_next_hop Next hop в обявата (рутерът така или иначе го прави blackhole) 0.0.0.0 адресът на машината
gobgp_announce_host_ipv6 Същото за IPv6 on off — обявява скриптът

Други

Параметър Какво прави По подразбиране При нас
disable_usage_report Телеметрия към производителя off on — изключена
monitor_local_ip_addresses Следи и собствените адреси на машината on on
enable_api Локален API, през който работи конзолният клиент on on
sort_parameter / max_ips_in_list Подредба и брой на най-натоварените адреси в конзолата packets / 7 packets / 7

Две неща, които таблицата не казва сама. Няма отделен праг за SYN — SYN flood се хваща от общия праг по пакети. И типът на атаката, който FastNetMon пише във файла си, не влияе на бана: той банва само по праговете отгоре, а типът е етикет.

Първите дни това беше само детекция: FastNetMon записваше кой е минал прага, без да блокира. От 11 октомври банът е истински blackhole на рутера през вградения GoBGP, а от 12 октомври blackhole има само при дребни пакети, тоест при атака. Как е вързано, е описано по-долу.

Рутерът праща NetFlow

Откъм рутера е буквално в два реда — включваш експорта и казваш накъде да го праща (адресът на машината, порт 2055, версия IPFIX):

/ip traffic-flow set enabled=yes active-flow-timeout=10s
/ip traffic-flow target add dst-address=198.51.100.10 port=2055 version=ipfix
MikroTik Traffic Flow — експорт на IPFIX към FastNetMon
Същото и през WebFig: IP → Traffic Flow, с Active Flow Timeout 10 секунди.

Едно нещо изненада в началото: NetFlow пробите скачаха — ту 100 хиляди пакета в секунда, ту 800 хиляди, без това да е реално. Причината е, че рутерът праща потоците на вълни. Първо скъсихме времето, за което държи отворен поток (горният active-flow-timeout=10s), и усреднихме за десет секунди. Общият трафик седна върху реалния. По отделен адрес обаче не съвсем: едно голямо сваляне беше отчетено като 465 Mbps и 39 000 пакета в секунда, а от самите записи на потоците излизаше около три пъти по-малко. Документацията на FastNetMon казва, че прозорецът за усредняване трябва да е по-дълъг и от active, и от inactive timeout на рутера (тук 10 и 15 секунди), иначе скоростите излизат завишени и на пикове. Същото описват и колеги във форума на MikroTik. На 11 октомври го вдигнахме на 30 секунди. Дали това слага край на фалшивите скокове — ще кажем след седмица наблюдение.

Как се гледа в терминала

Community версията няма уеб интерфейс, но има жив изглед в терминала — fastnetmon_client. Показва в реално време кои адреси колко теглят и колко качват, подредени по натоварване, а най-отдолу — списъка с адресите, минали прага:

fastnetmon_client показва трафика по адрес и бан листата в терминала
Живият изглед на машината — входящ и изходящ трафик по адрес, подреден по натоварване. IP адресите са скрити.

Същият изглед за IPv6 е с fastnetmon_client --ipv6. Числата тук веднага се сверяват с броячите на рутера — ако се разминават силно, значи NetFlow не е настроен както трябва.

Прагът сам по себе си лъже

FastNetMon работи на праг: ако към един наш адрес влязат повече от определен брой пакети в секунда, обявява го за атака. Звучи просто, но има уловка. Голямо легитимно сваляне също праща десетки хиляди пакети в секунда. През първия ден машината „засече“ 97 събития над прага — и нито едно не беше атака. Всичките бяха хора, които теглят нещо по HTTPS, с пакети по 1100–1500 байта.

Разликата е в размера на пакета. При истинска атака пакетите са дребни — SYN flood е 60 байта, UDP flood също е малък, защото целта е да задръсти, не да пренесе нещо. При сваляне пакетите са пълни, близо до 1500. Затова не вярваме сляпо на прага. Всяко събитие над него минава през собствена класификация:

Вид По какво се познава
SYN flood предимно SYN пакети и среден пакет под 200 байта
UDP amplification UDP от отразяващи портове — DNS 53, NTP 123, memcached 11211 и подобни
UDP flood UDP с дребни пакети
ICMP flood предимно ICMP
Фрагменти много фрагментирани пакети
Над прага, не атака всичко друго — почти винаги сваляне

Само първите пет броим за атака. Останалото се записва, но не вдига тревога. Между другото, FastNetMon има и собствено разпознаване на вида, но то гледа само какъв дял от пакетите е от даден тип, без размера — затова само то нарича голямото сваляне „SYN flood“. Не му вярваме, ползваме горната таблица.

Какво идва в Telegram

Известия по Telegram имаме отдавна от рутерите; закачихме и това за същия бот. Скриптът от конфига по-горе (notify_script_path) получава от FastNetMon адреса, посоката и подробностите за атаката, минава ги през класификацията и праща съобщение. Пращаме само когато клиент е блокиран, тоест има blackhole. Сваляне над прага отива само в лога. Видът е написан в съобщението:

🚨 FastNetMon: входяща атака — SYN flood
IP: 203.0.113.66
85000 pps / 41 mbps, среден пакет 60 B
Действие: blackhole анонсиран към R1

Когато банът изтече, идва второ съобщение, че адресът е свален и blackhole-ът е оттеглен. Така, ако клиент се обади, че няма интернет, отговорът е в групата.

Telegram известия от FastNetMon за входяща SYN flood DDoS атака
Известията в групата — за всяка атака едно 🚨 и после ✅ при спиране. Адресите са скрити; последното в 23:13 е тестовата SYN flood.

Същото и публично, но без адресите

Тези данни показваме и на публичната страница с инфраструктурата — колко атаки е имало, кога и от какъв вид, седем дни назад по часове. Адресите не ги показваме. Те са наши и няма смисъл публична страница да подсказва на нападателя дали ударът му е минал. По същата причина числата там са с половин час закъснение.

Страницата показва и свалянията, но с друг цвят. В графиката по часове жълтото е „над прага, сваляне“: голям трафик с едри пакети, който не е атака и не се блокира. Червеното е атака по класификацията. В плочката „Вид на атаките“ има и ред „Над прага (pps)“. Той показва колко от всички събития за 30 дни са просто превишен праг. През първите дни бяха 99%. Таблицата отдолу остава само за истинските атаки.

Адресът не излиза, но дали е IPv4 или IPv6 се вижда. В подсказката на всяко квадратче и в таблицата с атаките пише „IPv4“ или „IPv6“, а в „Вид на атаките“ има ред „IPv4 / IPv6“ с бройките за 30 дни. Тук се натъкнахме на дребен бъг във FastNetMon Community. Във файла с подробностите за IPv6 бан полето IP: е 0.0.0.0. Истинският адрес е само в името на файла, затова го взимаме от там.

Секцията DDoS детекция: жълто е над прага (сваляне), червено е атака
Секцията „DDoS детекция“ на публичната страница — същите данни, без адресите. Жълто: над прага, сваляне без блокиране. Червено: атака.

Как се тества, без да удряш собствената мрежа

Остава един въпрос: откъде знаеш, че цялата верига работи, ако още никой не те е нападнал? Да пуснеш истински флуд по собствения си uplink само за да се самопровериш е глупаво — товариш мрежата и може да събориш точно това, което пазиш.

Затова написах малък скрипт, който праща изкуствен IPFIX поток право към машината по локалния интерфейс — описание на атака, но без нито един реален пакет по мрежата. За FastNetMon изглежда като 90 000 пакета в секунда SYN към тестов адрес. Пуснахме го и проследихме цялата верига: засече го, класифицира го като SYN flood със среден пакет 56 байта, прати 🚨 в Telegram, появи се и на страницата.

Едно нещо ни отне време. Кратък изблик само „засича“ и спира дотам. FastNetMon първо събира проба от около двайсет записа за дъмпа на атаката и чак тогава финализира. Ако потокът спре преди това, тревогата увисва и съобщение не тръгва. Трябва да тече непрекъснато поне половин минута — което всъщност обяснява защо реалните сваляния минават докрай, а първият ни кратък тест заседна.

Blackhole през GoBGP

Дотук машината само гледаше. На 11 октомври банът стана истински. Когато FastNetMon реши, че адрес е над прага, и пакетите са дребни, рутерът спира целия трафик към него за десет минути. После го пуска сам, ако трафикът е паднал.

Връзката между двете е BGP. Във FastNetMon Community има вграден GoBGP. Това е BGP демон, който не пипа маршрутите на самия Linux. Само говори BGP с рутера: обявява „този адрес, с такава community“ и после го оттегля. Хубавото на BGP е, че го разбира всеки рутер, без скриптове, които влизат по SSH и пишат команди.

На машината

GoBGP идва с инсталатора, в /opt/fastnetmon-community/libraries/gobgp_*. Услуга за него няма, затова я пишем сами. Конфигът е кратък: нашият частен AS, адресът на машината и рутерът като единствен съсед:

# /etc/gobgpd.conf
[global.config]
  as = 65001
  router-id = "198.51.100.10"
  local-address-list = ["198.51.100.10"]

[[neighbors]]
  [neighbors.config]
    neighbor-address = "198.51.100.1"
    peer-as = 47453
  [[neighbors.afi-safis]]
    [neighbors.afi-safis.config]
      afi-safi-name = "ipv4-unicast"
  [[neighbors.afi-safis]]
    [neighbors.afi-safis.config]
      afi-safi-name = "ipv6-unicast"

В systemd услугата има един важен ред. BindsTo=fastnetmon.service връзва GoBGP към FastNetMon. Ако FastNetMon спре или се срине, спира и GoBGP. BGP сесията пада и рутерът сам маха всички blackhole маршрути. Няма как да остане забравен бан на клиент, защото детекторът е умрял.

# /etc/systemd/system/gobgpd.service
[Unit]
Description=GoBGP for FastNetMon
After=network-online.target
BindsTo=fastnetmon.service
Before=fastnetmon.service

[Service]
ExecStart=/opt/fastnetmon-community/libraries/gobgp_3_12_0/gobgpd -f /etc/gobgpd.conf --api-hosts=127.0.0.1:50051
Restart=on-failure

[Install]
WantedBy=fastnetmon.service

API-то слуша само локално. Порт 179 е отворен в защитната стена само за адреса на рутера. Във fastnetmon.conf остава да се включи модулът:

gobgp = on
gobgp_next_hop = 198.51.100.10
gobgp_next_hop_host_ipv4 = 198.51.100.10
gobgp_announce_host = off   # обявява скриптът, по размера на пакета
gobgp_community_host = 65001:666
gobgp_announce_host_ipv6 = off   # и IPv6 го обявява скриптът

На рутера

Откъм MikroTik е един BGP съсед и един филтър. Филтърът е по-важната част. Той пази рутера не от атаките, а от самата машина. Ако тя по грешка или защото е пробита обяви нещо друго, примерно default маршрут или мрежата със сървърите, рутерът трябва да го откаже:

/routing bgp connection add name=fastnetmon as=47453 \
    local.address=198.51.100.1 local.role=ebgp \
    remote.address=198.51.100.10 remote.as=65001 \
    afi=ip,ipv6 input.filter=fnm-in \
    input.limit-process-routes-ipv4=100 input.limit-process-routes-ipv6=100 \
    output.filter-chain=fnm-out output.redistribute=""

/routing filter rule add chain=fnm-out rule="reject;"
/routing filter rule add chain=fnm-in rule="\
if (dst in 203.0.113.0/25 || dst in 2001:db8:0:2::/64) { reject; }\n\
if (dst-len == 32 && bgp-communities includes 65001:666 \
    && (dst in 203.0.113.0/24 || dst in 198.51.100.0/23)) {\n\
    set blackhole yes;\n\
    append bgp-communities no-export;\n\
    accept;\n\
}\n\
if (dst-len == 128 && bgp-communities includes 65001:666 \
    && dst in 2001:db8::/48) {\n\
    set blackhole yes;\n\
    append bgp-communities no-export;\n\
    accept;\n\
}\n\
reject;"

Редовете на филтъра по ред:

  • Първият е бялата листа: сървъри, рутери и статичните адреси на един VIP клиент. Те законно правят повече от праговете и blackhole за тях не се приема никога. За IPv6 това е мрежата на сървърите. Същата листа я има и във FastNetMon, тук е втора преграда.
  • Вторият приема само единичен адрес (/32) от нашите мрежи, с нашата community. Слага го като blackhole и го маркира no-export, за да не излезе към доставчиците.
  • Третият е същото за IPv6: единичен адрес (/128) от нашия IPv6 блок.
  • Всичко останало се отказва.

IPv6 по същата сесия

За IPv6 не вдигнахме втора BGP сесия. BGP е многопротоколен (MP-BGP): една сесия, вдигната върху IPv4, носи и ipv6-unicast, стига двете страни да го обявят. В GoBGP това е второ afi-safi към съседа, а в RouterOS 7 е afi=ip,ipv6 на връзката. Параметърът там се казва afi, не address-families.

Едно нещо все пак трябваше: машината да има собствен IPv6 адрес в мрежата на рутера. Иначе GoBGP праща IPv6 маршрута с next-hop от вида ::ffff:198.51.100.10, а с истински адрес няма какво да се гадае как рутерът ще го приеме. Сложихме статичен адрес и по същия случай записахме статично IPv6 съседите в сървърната мрежа на рутера, с коментар кой е кой. В таблицата със съседи имаше много неуспешни записи за адреси, които никой не ползва: някой сканира мрежата.

Проверката беше като за IPv4. Ръчно обявен тестов адрес излезе на рутера като blackhole. Адрес от сървърната мрежа беше отказан. Към доставчиците не тръгна нищо. Скриптът мина бан и unban с IPv6 адрес. Изкуственият IPFIX е само за IPv4, затова пълен тест с трафик за IPv6 още няма.

Към машината рутерът не праща нищо (fnm-out), тя няма нужда от маршрути. Таванът от 100 префикса пази, ако нещо започне да обявява масово.

Две неща проверихме на живо, защото ни тревожеха от blackhole статията. Blackhole маршрутите от BGP не гасят fast path на рутера, той остава активен. И не пишат по ред в лога за всеки маршрут, както правят ръчно добавените. Тогава точно това ни беше изтрило историята на лога.

Тестът

Първо ръчно, без FastNetMon. С gobgp global rib add обявихме свободен адрес от нашите мрежи и адрес от бялата листа. Първият се появи на рутера като blackhole, вторият беше отказан. Към доставчиците не тръгна нищо. После с изкуствения IPFIX от предишната секция: от засичането до blackhole на рутера минаха около четири секунди, а при изтичане на бана маршрутът изчезна сам.

Решението, което не е техническо

Праговете не различават атака от много бързо сваляне. Още първия час след включването blackhole получи клиент, който сваляше игра от Steam: кеш сървърите им в София дават над 240 Mbps на една машина. Десет минути без интернет за него. Първо избрахме всеки бан да е blackhole, с праг 30 000 пакета в секунда, за да не може един клиент да вземе ресурса на хилядата около него.

На следващия ден го направихме по-точно. FastNetMon Community не комбинира прагове: банва, ако мине pps или Mbps, условие „и“ няма. Затова FastNetMon вече само казва „над прага“, а blackhole решава скриптът по средния размер на пакета. Атаките, които товарят рутери и сървъри, са с дребни пакети: SYN около 60 байта, ICMP около 50. Свалянията са с пакети от 1200 до 1500 байта. Границата е 400 байта, а прагът се върна на 20 000 пакета в секунда.

Към един адрес Резултат
над 20 000 pps, среден пакет под 400 B blackhole + съобщение
над 20 000 pps, среден пакет над 400 B само запис, трафикът минава
UDP amplification, фрагменти blackhole и при едри пакети
ICMP над 1 000 pps blackhole
if [ "$AVG" -lt 400 ] || [ "$KIND" = amp ] || [ "$KIND" = frag ] || [ "$KIND" = icmp ]; then
    gobgp global rib -a ipv4 add "$IP/32" nexthop 198.51.100.10 community 65001:666
fi

Размерът на пакета е по-добър от праг по Mbps, защото расте заедно с атаката. При 30 000 pps границата от 400 байта е около 100 Mbps. Но атака от 80 000 pps и 150 Mbps е с пакети от 234 байта. Праг „под 100 Mbps“ би я пропуснал, а размерът я хваща.

В Telegram идва само блокиран клиент. Ако някой се обади, че няма интернет, отговорът е там.

Едно ограничение остава. Доставчиците ни не приемат blackhole от нас, така че атаката пак стига до граничния рутер. Пазим вътрешната мрежа и останалите клиенти, но не и самия uplink.

Какво следва

Няколко дни наблюдение, преди да се пипа нещо друго. Дали усредняването за 30 секунди наистина маха фалшивите скокове. Дали границата от 400 байта пуска свалянията и хваща атаките. И първата истинска IPv6 атака, за да видим веригата докрай.

По темата: Blackhole маршрути вместо firewall — как същият граничен рутер блокира познатите зловредни мрежи, без да пипа firewall.