Прилітає сьогодні зранку алерт від моніторингу, що блог впав:
Ну, думаю – знов якийсь DDoS, не перший раз.
Зміст
Investigating the issue
Йду в Cloudflare, вмикаю Under Attack Mode, і починаю розбиратись.
Дивлюсь запити:
Ага, думаю, ващє фігня – з одного IP запити, зараз його забаню, і готово.
Додаю нове правило з Action = Block в Cloudflare Security Rules, і пішов глянути – що з IP такий?
Whois каже, що якийсь хост з DigitalOcean:
$ whois 46.101.201.123 ... inetnum: 46.101.128.0 - 46.101.255.255 abuse-c: AD10778-RIPE netname: DIGITALOCEAN ...
Там часто всякі боти запускаються, нічого незвичного.
Далі, вирішив з nmap глянути що за сервіси є на тому атакуючому хості:
# nmap -sS 46.101.201.123 ... PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 443/tcp open https ...
Хм, думаю – дивно, що за бот такий, що має 80 і 443 порти?
Відкриваю https://46.101.201.123 в браузері, і… попадаю на власний блог 🙂
Шта?…
Перевіряю, які IP в мене в DigitalOcean, і:
Тобто – да, 46.101.201.123 – це Droplet IP сервера, на якому хоститься RTFM.
Хоча взагалі на DNS в IN A для rtfm.co.ua використовується DigitalOcean Reserved IP, який можна переключати між дроплетами:
Тобто:
- NS для
rtfm.co.ua– Cloudflare - на них IN A 67.207.75.157
- Droplet IP 46.101.201.123 не вказаний ніде
- але запити йдуть на нього
Окей…
Тут ще буде окреме питання – чому в CloudFlare показувались запити від 46.101.201.123 – але про це в кінці.
SYN flood та підключення в SYN_RECV
Пішов глянути що взагалі на сервері в нетворкінгу, які активні конекти?
А там…
Купа підключень в статусі SYN_RECV – класичний SYN flood: клієнт нам відправляє TCP-пакет з флагом SYN, ми йому відповіли з SYN-ACK, і чекаємо на ACK від нього – але він не приходить, а ресурси CPU/RAM сервера на очікування зайняті (див. TCP handshake, нещодавно писав).
Mitigating the issue
Так як підключення йдуть не через CloudFalre – то і його Security Rules нам не допоможуть.
А Network Firewall в Digital Ocean, як і Security Groups в AWS вміють тільки в Allow правила – але не в Deny (в AWS можна зробити Deny через правила у VPC NACL – Network Access Control List).
Linux Kernel TCP tuning
В першу черги тюнимо параметри TCP-стеку ядра:
# sysctl -w net.ipv4.tcp_syncookies=1 net.ipv4.tcp_syncookies = 1 # sysctl -w net.ipv4.tcp_max_syn_backlog=4096 net.ipv4.tcp_max_syn_backlog = 4096 # sysctl -w net.ipv4.tcp_synack_retries=2 net.ipv4.tcp_synack_retries = 2
Тут:
net.ipv4.tcp_syncookies: вмикаємо SYN cookies – ядро може не тримати стан TCP-підключення, коли backlog переповненийnet.ipv4.tcp_max_syn_backlog: збільшуємо розмір беклогу дляSYN/SYN-ACK, аби реальні клієнти не відвалювались- дефолт 256
net.ipv4.tcp_synack_retries: обмежуємо кількість спроб ядра відповісти наSYN– скільки раз шлемоSYN-ACK, якщо клієнт не повернувACK- дефолт 5
Вже стало краще:
Далі можна на DigitalOcean firewall дозволити доступ тільки з мереж Cloudflare – але вони змінюються, а робити якусь авторизацію зараз влом.
Iptables та DROP by Source Address
Можна, звісно, банити атакуючі мережі – на момент перевірки була одна 177.36.16.0/20:
# netstat -anp | grep 46.101.201.123 | grep SYN_RECV tcp 0 0 46.101.201.123:443 177.36.16.214:15795 SYN_RECV - tcp 0 0 46.101.201.123:443 177.36.16.152:43548 SYN_RECV - tcp 0 0 46.101.201.123:443 177.36.17.0:43309 SYN_RECV - tcp 0 0 46.101.201.123:443 177.36.17.237:47283 SYN_RECV -
Додаємо правило з DROP і логуванням для перевірки:
# iptables -I INPUT -s 177.36.16.0/20 -j LOG --log-prefix "DROP 177.36.16.0/20 "
Дивимось, чи працює правило:
# journalctl -k | grep "DROP 177.36.16.0/20" | head Jan 02 08:34:35 setevoy-do-2023-09-02 kernel: DROP 177.36.16.0/20 IN=eth0 OUT= MAC=de:71:8d:d9:82:55:fe:00:00:00:01:01:08:00 SRC=177.36.16.242 DST=46.101.201.123 LEN=52 TOS=0x00 PREC=0x00 TTL=54 ID=40235 DF PROTO=TCP SPT=17587 DPT=443 WINDOW=65535 RES=0x00 SYN URGP=0 Jan 02 08:34:37 setevoy-do-2023-09-02 kernel: DROP 177.36.16.0/20 IN=eth0 OUT= MAC=de:71:8d:d9:82:55:fe:00:00:00:01:01:08:00 SRC=177.36.16.193 DST=46.101.201.123 LEN=52 TOS=0x00 PREC=0x00 TTL=56 ID=8752 DF PROTO=TCP SPT=45940 DPT=443 WINDOW=65535 RES=0x00 SYN URGP=0 ...
Залишаємо правило, але прибираємо запис в лог, бо це зайве навантаження на диск і систему:
# iptables -R INPUT 1 -s 177.36.16.0/20 -j DROP
Вже краще – але, очікувано, пішли підключення з інших адрес:
# netstat -anp | grep 46.101.201.123 | grep SYN_RECV tcp 0 0 46.101.201.123:443 45.94.171.239:48242 SYN_RECV - tcp 0 0 46.101.201.123:443 146.103.26.224:30654 SYN_RECV - tcp 0 0 46.101.201.123:443 91.124.63.174:45287 SYN_RECV - tcp 0 0 46.101.201.123:443 194.116.228.226:15311 SYN_RECV -
Iptables та DROP by Destination Address
Ну і насправді тут є дуже просте рішення:
- валідні запити мають йти тільки на Reserved IP, який вказаний в Cloudflare DNS
- запити на Droplet IP на порт 443 взагалі не мають приходити
Тому просто банимо їх з iptables:
# iptables -A INPUT -p tcp -d 46.101.201.123 --dport 443 -j DROP
Тепер жодного SYN_RECV не залишилось.
Зберігання правил з iptables-persistent
Перевіряємо правила зараз:
# iptables -L INPUT -n --line-numbers Chain INPUT (policy ACCEPT) num target prot opt source destination 1 DROP 0 -- 177.36.16.0/20 0.0.0.0/0 2 DROP 6 -- 0.0.0.0/0 46.101.201.123 tcp dpt:443
Аби зберігати їх при ребутах системи – встановлюємо iptables-persistent:
# apt install iptables-persistent
Під час установки він запропонує зберегти правила в файл /etc/iptables/rules.v4:
Перевіряємо що там:
# cat /etc/iptables/rules.v4 | grep 46.101.201.123 -A INPUT -d 46.101.201.123/32 -p tcp -m tcp --dport 443 -j DROP
Готово.
Але це буде працювати, поки не почнуть флудити на сам Reserved IP 67.207.75.157.
Тоді вже доведеться робити через дозвіл тільки з Cloudflare IPs.
Bonus: WordPress, Cloudflare та запити від Droplet IP
Але після того, як із SYN flood начебто розібрався – графіки кількості запитів в Cloudflare не зменшились.
І це логічно – бо SYN взагалі йшов напряму до сервера на IP 46.101.201.123, а не через Cloudflare, а там ці запити в Clodflare взагалі не трекались.
При цьому в логах Cloudflare від IP 46.101.201.123 всюди був один і той самий Path до файлу /wp-content/uploads/2025/11/freebsd_logo1.jpg:
Графіки Cloudflare за останні 6 годин виглядали так – в топі Source IPs зліва внизу саме 46.101.201.123:
- SYN flood почався близько 10 ранку за Києвом
- в цей жеж час є спайк запитів до Cloudflare від самого сервера RTFM
Тобто виглядало так, ніби на сервері якийсь код постійно звертається до одного і того самого URL на самому сервері.
Відключив Cloudflare WordPress плагін – ні, запити не спадають.
Відключив WP_CRON – теж не допомогло.
WTF is going on? – судорожно думав я, і додумався, що пора б включити і подивитись NGINX access logs і подивитись, що взагалі на сервер приходить.
А access logs побачив купу записів виду:
... [02/Jan/2026:11:07:35 +0000] "GET /en/freebsd-home-nas-part-1-configuring-zfs-mirror-raid1/ HTTP/1.1" 200 35451 "-" "HackerNews/1536 CFNetwork/3860.200.71 Darwin/25.1.0" [02/Jan/2026:11:07:42 +0000] "GET /en/freebsd-home-nas-part-1-configuring-zfs-mirror-raid1/ HTTP/1.1" 200 35462 "https://news.ycombinator.com/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36" [02/Jan/2026:11:07:43 +0000] "GET /wp-json/pvc/v1/increase/33806 HTTP/1.1" 200 99 "https://rtfm.co.ua/en/freebsd-home-nas-part-1-configuring-zfs-mirror-raid1/" "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36" ...
HackerNews, ycombinator? Вау…
А друге – сама причина 46.101.201.123 в логах Clodflare: “GET /wp-json/pvc/v1/increase/” – це запит до WordPress-плагіна Page View Count, який не так давно включив. А “https://rtfm.co.ua/en/freebsd-home-nas-part-1-configuring-zfs-mirror-raid1″ – звідки запит був зроблений.
Тобто, плагін на сторінці поста робить запит, аби збільшити лічильник переглядів – при цьому підставляючи Referrer у вигляді тої сторінки, звідки він запит робить.
Cloudflare жеж бачить, що запит йде від Origin – і використовує в логах Droplet IP.
Ну а далі вже проста перевірка – що коїться взагалі з постом FreeBSD: Home NAS, part 1 – configuring ZFS mirror (RAID1):
Це при тому, що зазвичай переглядів кілька десятків, ну максимум 100-200.
І перевірка в гуглі вже показала причину такого напливу:
А трапилось те, що я сьогодні вранці перший раз запостив лінк на https://lobste.rs, звідки його перепостили на Hacker News, і я отримав “Hacker News hug of death” – див. Surviving the Hug of Death, де у людини була схожа ситуація.
Після відключення плагіну Page View Count – Droplet IP 46.101.201.123 в Cloudflare зник.
![]()









