Na mapi napada stoji gusta mrlja nad jednom državom i samo se nameće pravilo „zabraniti je u celini". Ono neće dati gotovo ništa: promena države izlaza košta napadača nekoliko minuta, a pod zabranu će pasti vaši sopstveni posetioci i vi sami na putu. Još gore, vidljivi obilazač koji dolazi iz jednog oblaka posle blokiranja preseliće se na kućne adrese i postaće nerazlučiv od ljudi.

Ispod je pet koraka koji na istom tom serveru zaista daju rezultat: preseći skenere, ograničiti tempo, ne preseći ono što je potrebno i — ako je ipak neophodno — blokirati ispravno. Sve se može proveriti i podesiti u jednom navratu.

Korak 1. Videti ko zapravo stvara mrlju

Najpre pronađite dnevnik zahteva. Na serveru sa više sajtova access.log neretko je prazan, dok zapisi odlaze u dnevnik konkretnog virtuelnog hosta:

sudo ls -lS /var/log/apache2/*access*.log /var/log/apache2/*requests*.log 2>/dev/null

U svim narednim komandama ubacuje se datoteka koja se našla na vrhu tog spiska. Najpre vodeće adrese za dan, zatim ono što tačno traži ona na vrhu:

sudo awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
sudo grep '^203\.0\.113\.' /var/log/apache2/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20

Druga komanda odgovara na glavno pitanje. Ako su u izlazu desetine različitih stranica vašeg sajta, to je sakupljač sadržaja: bezopasan je i dovoljno je ne računati ga kao posetioca. Ako su tamo /wp-login.php, /.env, /phpmyadmin i druge stvari koje nemate, to je skener i njime se bavi korak 2. Kod nginx-a dnevnici leže u /var/log/nginx/, polja su ista.

Korak 2. Jail za zahteve prema onome što nemate

Putanja zahteva ne može se falsifikovati — ona i jeste namera, za razliku od adrese i User-Agent-a. Filter /etc/fail2ban/filter.d/web-probes.local:

[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD) /(wp-login\.php|wp-admin|xmlrpc\.php|\.env|\.git/|phpmyadmin|administrator/)
ignoreregex =

Jail /etc/fail2ban/jail.d/web-probes.local:

[web-probes]
enabled  = true
port     = http,https
filter   = web-probes
logpath  = /var/log/apache2/*access.log
maxretry = 1
findtime = 1h
bantime  = 1d

Ovde je maxretry = 1 opravdano: slučajnih obraćanja tuđoj administraciji nema. Primeniti i uveriti se da filter hvata zapise u vašem dnevniku:

sudo fail2ban-regex /var/log/apache2/access.log /etc/fail2ban/filter.d/web-probes.local
sudo systemctl reload fail2ban && sudo fail2ban-client status web-probes

Korak 3. Ograničiti tempo

Skener i obilazač odaju se brzinom, nezavisno od toga iz koje su države. Trideset stranica u minutu sa jedne adrese jeste prag koji ne dotiče čoveka koji prelistava katalog:

[http-flood]
enabled  = true
port     = http,https
filter   = apache-overflows
logpath  = /var/log/apache2/site_access.log
maxretry = 30
findtime = 60
bantime  = 1h

Ključni detalj jeste koji je dnevnik naveden u logpath. Ako pod masku padne dnevnik samog upravljačkog panela ili administracije, blokiraćete sami sebe: jedna njegova stranica povlači desetak zahteva za stilove i skripte, a trideset se skupi u dva klika. Zato se panelu daje posebna datoteka dnevnika, koja se ne poklapa sa maskom jail-a, a statičkim datotekama dug rok keširanja, da ih pregledač ne traži iznova pri svakom prelasku. Znak, ako se to ipak dogodilo: HTML stiže, oblikovanje ne, stranica visi. Skidanje blokade:

sudo fail2ban-client set http-flood unbanip 203.0.113.10

Korak 4. Zaštititi ono što se ne sme preseći

Pre nego što uključite blokade, upišite izuzetke — inače će prvo pod njih pasti ono što je potrebno:

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.0/24 66.249.64.0/19

Šta obavezno mora da stoji u tom redu i zašto:

  • Vaša sopstvena adresa ili podmreža kancelarije. Najčešći uzrok gubitka pristupa serveru jeste sopstveno pravilo, a ne napad;
  • Opseg 66.249.64.0/19. Googleov pretraživački robot prepoznaje se po User-Agent-u, ali onaj njegov deo koji izvršava JavaScript dolazi kao običan Chrome/Linux i ne predstavlja se. Svako pravilo tipa „blokirati saobraćaj iz data-centara" pogađa upravo njega, nakon čega stranice tiho ispadaju iz indeksa. Sumnjiva adresa može se proveriti obrnutim upitom: dig -x 66.249.66.1 +short treba da vrati ime u zoni googlebot.com;
  • Adrese obaveštenja o uplatama. Webhook-ovi platnih sistema stižu sa AWS i Azure servera. Ako te oblake zatvorite u celini, potvrde uplata prestaće da stižu, a saznaćete to onda kada klijent napiše da je novac skinut, a pristupa nema. Opsezi se uzimaju iz dokumentacije vašeg pružaoca platnih usluga.

Korak 5. Ako je blokada ipak potrebna — blokirajte podmrežu, a ne državu

Postojano zgušnjavanje iz jedne mreže, koje traje danima i prati ga istovrsni niz zahteva, jeste jedan izvršilac koji ide po spisku. Takav slučaj blokira se tačkasto i na određeno vreme:

sudo ufw deny from 198.51.100.0/22 to any
sudo ufw status numbered

Drugom komandom zapamtite broj pravila: blokadu će trebati skinuti upravo preko njega, sudo ufw delete <broj>. Dogovorite se sa samim sobom o roku — na primer mesec dana — i upišite skidanje u kalendar. Pravila postavljena „zauvek" posle godinu dana pretvaraju se u spisak koji niko ne sme da dira, jer se niko ne seća zbog čega su te adrese tamo dospele.

Koju podmrežu blokirati, može se saznati po adresi iz prvog koraka: whois 198.51.100.17 | grep -iE 'route|cidr|netname'.

Šta se menja posle ovih pet koraka

Skeneri se presecaju po nameri, a ne po poreklu, i hvataju se na prvom zahtevu. Obilazači udaraju u ograničenje tempa. Pretraživački robot i obaveštenja o uplatama i dalje prolaze. Jedina blokada koja je ostala široka jeste tačkasta, sa poznatim rokom i jasnim razlogom.

Mapa pritom nikuda ne nestaje, ali joj se uloga menja: ona nije tu da zatvara države, nego da samo zgušnjavanje uopšte primeti i uporedi ga sa onim od pre nedelju dana. Za to je potrebna istorija, a ne snimak tekućeg dana. Kako takva mapa izgleda zajedno sa dnevnikom blokada i spiskom otvorenih portova, pokazuju demo stranice ispod.