Asosiy kontentga o‘tish

Maqolalar 27 daqiqa 39 ko‘rish

fail2ban: parol terib ko'rayotgan botlarni avtomatik bloklash

Firewall barcha portlarni yopib qo‘ya olmaydi — SSH, nginx yoki login sahifasi kabi xizmatlar doim ochiq turishi kerak. Muammo shundaki, bunday ochiq nuqtalarni botlar doimiy ravishda tekshiradi va parol topishga urinadi. Fail2ban loglarni kuzatib, bir IP manzildan ko‘p marta muvaffaqiyatsiz urinish bo‘lsa, uni vaqtincha firewall orqali bloklaydi. Bu bo‘limda Fail2ban’ni real serverda sozlash, jail’lar bilan ishlash, o‘z IP’ingizni tasodifan bloklab qo‘ymaslik hamda nginx va Django login sahifalari uchun maxsus filtrlar yozish ko‘rib chiqiladi.

linux security devops

Mundarija

UFW haqidagi maqolada serverdagi keraksiz portlarni yopib chiqqandik. Lekin ayrim portlar doim ochiq turishi kerak: SSH — serverga o‘zimiz kirishimiz uchun, 80 va 443 esa sayt ishlashi uchun.

Muammo shundaki, botlar ham aynan shu ochiq portlarni tinimsiz tekshiradi. Ayniqsa SSH’ga turli login va parollar bilan qayta-qayta kirishga urinadi.

Buni ko‘rish uchun SSH loglarini ochib qaraymiz:

Terminal
sudo journalctl -u ssh --since -1h | grep -c "Failed password"
# 312

Bir soatning o‘zida 312 ta urinish. Har safar boshqa parol, ko‘pincha esa turli IP manzillardan.

Parol bilan kirish o‘chirib qo‘yilgan bo‘lsa, ular serverga kira olmaydi. Lekin urinishlarning o‘zi ham bekorga ketmaydi: CPU ishlaydi, loglar to‘lib boradi, sshd esa har bir so‘rovni qayta ishlaydi.

nginx’da ham vaziyat o‘xshash. Botlar /wp-login.php, /.env, /admin/ kabi manzillarni qidirib yuradi va bir kunda minglab 404 javob qoldirishi mumkin. Agar Django’da login sahifangiz bo‘lsa, unga ham doimiy ravishda kirishga urinib ko‘rishadi.

fail2ban aynan shunday holatlar uchun kerak. U loglarni kuzatib boradi va bir IP manzildan qisqa vaqt ichida bir necha marta muvaffaqiyatsiz urinish bo‘lsa, o‘sha IP’ni firewall orqali vaqtincha bloklaydi.

Masalan, bot 5 marta urinib ko‘rdi — 10 daqiqaga bloklanadi. Keyin yana qaytsa, blok muddati bir kungacha uzayishi mumkin. Bularning hammasi avtomatik ishlaydi, siz har safar qo‘lda aralashishingiz shart emas.

Bu maqola ufw maqolasining bevosita davomi: u yerdagi ikkita SSH sessiya odati, provayder konsolining joyi, ss -tlnp va qoidalar tartibi shu yerda ham kerak bo'ladi. Muhit ham o'sha — Ubuntu 22.04/24.04 va Debian 12; farq bor joyda alohida aytaman.

fail2ban qanday ishlaydi#

Zanjir shunday:

Sxema
bot → SSH'ga noto'g'ri parol → sshd log'ga yozadi → fail2ban log'ni o'qiydi
    → 5-urinishda → firewall'ga "shu IP'ni yop" qoidasi → bot 1 soat jim

Bu zanjirda uchta tushuncha bor, butun fail2ban shunga quriladi:

  • filter — log qatorini qanday tanib olish. Bu regex (matn qolipi): masalan, sshd filtri "Failed password for ... from " ko'rinishidagi qatorlarni taniydi va undan IP'ni ajratib oladi;

  • jail ("qamoqxona") — filter + qaysi log + nechta urinish + qancha muddat, hammasi bitta paketda. [sshd] — bu jail;

  • action — chegaradan oshganda aynan nima qilish. Bizda bu "ufw'ga deny qoidasi qo'shish", muddat tugagach — olib tashlash.

fail2ban o'zi alohida firewall emas. U firewallga kerakli qoidani qo'shib yoki olib tashlab turadi. Shuning uchun ikkisi bir-birini to'ldiradi: ufw'da doimiy qoidalarni siz yozasiz, fail2ban esa vaqtinchalik qoidalarni loglar asosida o'zi yozib-o'chirib turadi.

O‘rnatish va birinchi tekshiruv#

Terminal
sudo apt update && sudo apt install -y fail2ban
systemctl status fail2ban

Bu yerda Ubuntu bilan Debian yo'llari ajraladi.

Ubuntu 22.04/24.04 da active (running) chiqadi — paket o'rnatilishi bilan sshd jail'i yoqilgan holda ishlay boshlaydi.

Debian 12 da esa katta ehtimol bilan failed. Bu qo'llanmani Ubuntu'da yozib, Debian'da o'qiganlarning klassik "menda ishlamadi"si. Sababini o'z ko'zingiz bilan ko'ring:

Terminal
sudo journalctl -u fail2ban -e --no-pager | tail -20
# ERROR  Failed during configuration: Have not found any log file for sshd jail

fail2ban'ning standart sshd jail'i /var/log/auth.log faylini o'qimoqchi, lekin minimal Debian 12 da rsyslog yo'q va bu fayl umuman mavjud emas — SSH loglari faqat systemd journal ichida. Yechim — jail'ga faylni emas, journal'ni o'qishni aytish: backend = systemd. Buni keyingi bo'limdagi jail.local da yozamiz; Ubuntu'da ham shu qatorni yozamiz — u yerda ham ishlaydi va sozlamangiz ikkala tizimda bir xil bo'ladi.

Eski qo'llanmalardagi logpath = /var/log/auth.log

Buni ko'r-ko'rona ko'chirmang. Fayl yo'q tizimda jail ishga tushmaydi, fayl bor tizimda esa backend = systemd baribir to'g'riroq — log rotatsiyasi, fayl ruxsatlari kabi masalalardan xoli.

Xizmatni reboot'da ham ko'tariladigan qilib qo'yamiz (Debian'da hozircha failed bo'lsa ham — sozlagach ishlaydi):

Terminal
sudo systemctl enable --now fail2ban

Sozlamalarni o'zgartirgandan keyin ikki yo'l bor: sudo systemctl restart fail2ban — to'liq qayta ishga tushirish; sudo fail2ban-client reload — faqat konfiguratsiyani qayta o'qish, banlar ro'yxati saqlanib qoladi. Ikkalasidan oldin sozlamani sinab ko'rish odat bo'lsin:

Terminal
sudo fail2ban-client -t
# OK: configuration test is successful

Xizmat ishlayotgan bo'lsa, jail'larni ko'ramiz:

Terminal
sudo fail2ban-client status
# Status
# |- Number of jail:      1
# `- Jail list:   sshd

sudo fail2ban-client status sshd
Natija
Status for the jail: sshd
|- Filter
|  |- Currently failed: 3
|  |- Total failed:     47
|  `- Journal matches:  _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
   |- Currently banned: 2
   |- Total banned:     2
   `- Banned IP list:   45.xx.xx.xx 185.xx.xx.xx

O'qish: Currently failed — hozir "hisobda" turgan, hali chegaraga yetmagan IP'lar; Total failed — jail ishga tushganidan beri ko'rilgan jami xato urinish; Currently banned / Banned IP list — ayni paytda bloklanganlar. Journal matches qatori — jail loglarni journal'dan o'qiyotganining belgisi. Server internetga ochiq bo'lsa, bir necha soatdan keyin bu ro'yxatda IP'lar paydo bo'lishi odatiy hol: demak, fail2ban loglarni ko'ryapti va qoidalar ishlayapti.

Sozlash: jail.conf ga tegilmaydi#

Asosiy sozlamalar /etc/fail2ban/jail.conf da — bu paketning fayli, yangilanishda ustidan yoziladi. O'z sozlamalaringiz jail.local da turadi va jail.conf ustidan qo'llanadi:

INI
# /etc/fail2ban/jail.local
[DEFAULT]
# ENG MUHIM QATOR: o'zingizni hech qachon bloklamang
# (lokal manzillar, ofis/uy IP, xususiy tarmoq — probel bilan, CIDR ham bo'ladi)
ignoreip = 127.0.0.1/8 ::1 198.51.100.42 10.0.0.0/24

# 10 daqiqa ichida 5 ta muvaffaqiyatsiz urinish → 1 soatga ban
# (muddat formatlari: 10m, 1h, 1d, 1w)
findtime  = 10m
maxretry  = 5
bantime   = 1h

# qayta-qayta kelganlar uchun ban muddati o'sib boradi: 1h, 2h, 4h ... 1 haftagacha
bantime.increment = true
bantime.factor    = 2
bantime.maxtime   = 1w

# bloklashni ufw orqali qilamiz — qoidalar `ufw status` da ko'rinadi
banaction = ufw

# log fayllar fayldan o'qiladi (nginx, ilova). Ubuntu 24.04 paketi standartni
# "systemd" qilib qo'ygan — bu qatorsiz nginx jail'lari faylni o'qimaydi
backend = auto

[sshd]
enabled = true
# loglarni auth.log faylidan emas, systemd journal'dan o'qish —
# Ubuntu'da ham, Debian 12 da ham bir xil ishlaydi
backend = systemd
# SSH portini o'zgartirgan bo'lsangiz: port = 2222
port    = ssh
mode    = aggressive

Har qator nima uchun:

  • ignoreip — birinchi va eng muhim. Siz ham xato parol terishingiz mumkin (yoki deploy skripti noto'g'ri kalit bilan urinishi) — shunda o'zingizni serverdan bir soatga yopib qo'yasiz. Statik IP'ingiz va xususiy tarmoqni shu yerga yozing; o'z IP'ingizni bilish — kompyuterda curl ifconfig.me. Keyingi bo'limdagi testda o'zingizni banlab qo'yishingiz aniq, va bu ignoreipsiz juda noqulay bo'ladi.

  • backend = systemd — faqat [sshd] da, [DEFAULT] da esa auto: nginx jail'lari (pastda) loglarni fayldan o'qiydi, ularga journal kerak emas. [DEFAULT] dagi backend = auto ni tashlab ketmang. Ubuntu 24.04 da paketning /etc/fail2ban/jail.d/defaults-debian.conf fayli standart backend'ni systemd qilib qo'ygan. Bu qatorsiz nginx jail'lari ham journal'ni o'qiydi, logpath e'tiborsiz qoladi va jail hech qanday xato bermay, hech kimni ushlamaydi. Tekshirish: sudo fail2ban-client -d | grep "'add'" — har jail yonida qaysi backend ishlayotgani chiqadi.

  • port = ssh — ssh so'zi /etc/services dan 22 ga tarjima qilinadi. banaction = ufw IP'ni hamma portdan yopadi, shuning uchun bu yerda port hal qiluvchi emas; lekin boshqa action'lar (iptables-multiport) faqat shu portni yopadi — port o'zgargan bo'lsa yozish odat bo'lsin.

  • findtime / maxretry / bantime — "qancha vaqtda, nechta, qanchaga". 5 ta 10 daqiqada — botlar uchun juda kam, odam uchun yetarli.

  • bantime.increment — "recidiv" mantig'i: bir marta bloklangan IP qayta kelsa, ikki barobar uzoqroq yopiladi. Doimiy skanerlar bir necha kunda bir haftalik banga tushadi va tinchiydi.

  • banaction = ufw — standart holatda fail2ban to'g'ridan-to'g'ri iptables/nftables'ga yozadi (Ubuntu 24.04 paketida standart — nftables) va bu qoidalar ufw status da ko'rinmaydi. ufw action'i bilan hammasi bir joyda.

Ishga tushiramiz va tekshiramiz:

Terminal
sudo fail2ban-client -t && sudo systemctl restart fail2ban
sudo fail2ban-client status
# Jail list:   sshd
sudo ufw status numbered
# [ 1] Anywhere   REJECT IN   45.xx.xx.xx   # fail2ban qo'shgan, muddati tugasa o'zi o'chadi
# [ 2] OpenSSH    LIMIT IN    Anywhere

Restart'da xato bersa, sabab konfiguratsiyada. Tez-tez uchraydiganlari: bo'lim nomi [DEFAULT] katta harflarda emas; bantime.increment juda eski fail2ban'da (0.10.x) yo'q — fail2ban-client version bilan ko'ring, 0.11 dan past bo'lsa o'sha uch qatorni vaqtincha o'chiring; banaction = ufw yozilgan-u, ufw o'rnatilmagan. Aniq xabar har doim shu yerda: sudo journalctl -u fail2ban -e --no-pager | tail -20.

mode = aggressive nima

sshd filter'ining oddiy rejimi faqat "Failed password" ni sanaydi. aggressive rejim ulanishni parolgacha yetkazmay uzib ketadigan skanerlarni ham ("Connection closed by ... [preauth]", noto'g'ri protokol versiyasi) hisobga oladi. Amalda ban soni 2–3 barobar ortadi va hammasi haqiqiy botlar.

recidive jail — eski usul

Eski qo'llanmalarda bantime.increment o'rniga [recidive] jail'i uchraydi: u fail2ban'ning o'z log'ini o'qib, istalgan jail'da bir kunda 3 marta banlangan IP'ni bir haftaga yopadi (logpath = /var/log/fail2ban.log, findtime = 1d, maxretry = 3, bantime = 1w). bantime.increment yoqilgan bo'lsa u ortiqcha — ikkalasidan bittasini tanlang; birga ishlasa ham zarar qilmaydi, shunchaki dublikat.

Amaliyot: hujumni o'zimiz qilib ko'ramiz#

Nazariya tugadi — endi tizim rostdan ishlashini isbotlaymiz. Test serverida, production'da emas.

Test sxemasi. Sizga serverdan tashqaridagi, boshqa IP'li mashina kerak. Hozir SSH qilib turgan kompyuteringizdan test qilsangiz va uning IP'si ignoreip da bo'lmasa — o'zingizni banlaysiz: mavjud sessiya uzilmaydi (ban faqat yangi ulanishlarni to'sadi), lekin qayta ulanolmaysiz. Agar IP'ingiz ignoreip da bo'lsa — banlanmaysiz, lekin ban ham ko'rmaysiz: fail2ban bunday IP'ni sanamaydi ham (Total failed o'zgarmaydi), faqat /var/log/fail2ban.log ga Ignore <IP> by ip deb yozib qo'yadi. Eng oson yo'l: telefonni hotspot qilib noutbukni shu tarmoqqa ulash — sizda boshqa IP bo'ladi.

Hujum. Hotspot'dagi mashinadan:

Terminal
ssh notreal_user@SERVER_IP
# parol so'raganda istalgan narsani terib Enter — 3 marta
# ssh chiqib ketadi; qayta ishga tushiring, yana 3 marta

Beshinchi-oltinchi xato urinishdan keyin ssh'ning javobi o'zgaradi: parol so'rashni ham to'xtatib, Connection refused beradi. Ban ishladi.

Serverda parol bilan kirish o'chirilgan bo'lsa, parol umuman so'ralmaydi — ssh darhol Permission denied (publickey) deb chiqadi. Bu ham normal: sshd logga Invalid user notreal_user deb yozadi va fail2ban uni ham sanaydi. Shunchaki ssh notreal_user@SERVER_IP ni 5–6 marta takrorlang.

Server tomonda nima bo'lgani — asosiy (banlanmagan) sessiyangizda uchta joydan tekshiring, bu real diagnostika mashqi:

Terminal
# 1) fail2ban nima dedi
sudo tail -20 /var/log/fail2ban.log
# NOTICE  [sshd] Found 203.0.113.99 - 2026-08-29 03:12:07
# NOTICE  [sshd] Ban 203.0.113.99          ← mana u

# 2) jail hisobotida
sudo fail2ban-client status sshd
# Banned IP list:   203.0.113.99

# 3) ufw'da — banaction = ufw qilganimiz uchun ko'rinadi
sudo ufw status numbered
# [ 1] Anywhere    REJECT IN    203.0.113.99

E'tibor bering: ban qoidasi ro'yxatning 1-o'rniga tushdi. Bu ufw maqolasidagi tartib mantig'i: ban sizning allow 22 ingizdan yuqorida turishi shart, aks holda paket avval ruxsatga mos kelib o'tib ketardi.

Testni tugatgach o'zingizni oching:

Terminal
sudo fail2ban-client set sshd unbanip 203.0.113.99

Ban bo'ldi, lekin ufw status da yo'q

fail2ban + ufw haqida eng ko'p beriladigan savol. banaction = ufw yozmagan bo'lsangiz, fail2ban standart iptables-multiport action'ini ishlatadi: iptables'da o'zining f2b-sshd zanjirini yaratadi va ufw bundan bexabar (ufw faqat o'zi yozganini ko'radi). Ban ishlayapti, faqat boshqa "daftar"da: sudo iptables -L f2b-sshd -n --line-numbers. Ikkalasi ham to'g'ri ishlaydi; ufw action'i butun firewall holatini bitta ufw status da ko'rish uchun qulay, juda ko'p ban bo'ladigan yuqori yuklamali serverlarda esa nftables-multiport samaraliroq. CentOS/RHEL/Alma/Rocky'da (ufw yo'q, firewalld bor) — banaction = firewallcmd-rich-rules; qolgan hamma sozlama bir xil.

Bloklangan IP'ni ko'rish, ochish, tekshirish#

Terminal
# kim bloklangan
sudo fail2ban-client status sshd

# ma'lum IP'ni qo'lda ochish (masalan, hamkasb o'zini bloklab qo'ydi)
sudo fail2ban-client set sshd unbanip 203.0.113.77
# takrorlanmasin: ishonchli IP bo'lsa jail.local'dagi ignoreip'ga qo'shib,
sudo fail2ban-client reload      # reload banlar ro'yxatini saqlab qoladi

# qo'lda bloklash (log kutmasdan)
sudo fail2ban-client set sshd banip 45.xx.xx.xx

# nima bo'layotganini jonli kuzatish
sudo tail -f /var/log/fail2ban.log
# [sshd] Found 45.xx.xx.xx - 2026-08-29 03:12:07
# [sshd] Ban 45.xx.xx.xx
# [sshd] Unban 45.xx.xx.xx        ← bantime tugadi

Filter'ning o'zini tekshirish — regex haqiqatan log qatorlarini tanimoqdami:

Terminal
# systemd journal'dan sshd loglarini sshd filter'i bilan sinash
sudo fail2ban-regex systemd-journal /etc/fail2ban/filter.d/sshd.conf
# Lines: 1842 lines, 0 ignored, 611 matched, 1231 missed

matched noldan katta bo'lsa — filter ishlayapti. Bu buyruq keyinroq o'z filtrlaringizni yozganda asosiy vositangiz bo'ladi.

nginx: skanerlarni ham yopamiz#

SSH'dan keyin ikkinchi eng ko'p urinadigan joy — web. fail2ban nginx uchun tayyor filtrlar bilan keladi (/etc/fail2ban/filter.d/nginx-*.conf). Uchtasi amalda foydali:

INI
# /etc/fail2ban/jail.local (davomi)

# HTTP Basic Auth (masalan, /admin/ oldidagi parol) — noto'g'ri parol
[nginx-http-auth]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/error.log

# nginx'ning o'z limit_req'i ishga tushgan so'rovlar — DoS urinishlari
# (nginx'da limit_req sozlanmagan bo'lsa, bu jail hech narsa ko'rmaydi)
[nginx-limit-req]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/error.log
maxretry = 20

# bot skanerlari: /wp-login.php, /phpmyadmin, webmail — 404 yomg'iri
[nginx-botsearch]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/access.log
maxretry = 10
findtime = 5m

nginx-botsearch ehtiyotkorlik talab qiladi: oddiy foydalanuvchi ham 404 oladi (eski havola, xato manzil). Shuning uchun maxretry yuqoriroq (10) va findtime qisqa (5 daqiqa) — 5 daqiqada 10 ta bot manziliga urinish odamdan chiqmaydi. Filtr har qanday 404 ni emas, faqat ma'lum yo'llarni sanaydi: wp-login.php, wp-admin.php, phpMyAdmin, webmail, cgi-bin, mysqladmin.

Botlar eng ko'p qidiradigan /.env va /.git/ bu ro'yxatda yo'q. Ularni qo'shish uchun paket faylini tahrirlamaymiz — yonida .local fayl ochamiz:

INI
# /etc/fail2ban/filter.d/botsearch-common.local
[Init]
block = \/?(<webmail>|<phpmyadmin>|<wordpress>|cgi-bin|mysqladmin|\.env|\.git)[^,]*

Tekshirish: access log'da GET /.env ... 404 qatori bo'lsa, sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-botsearch.conf uni matched qatoriga qo'shadi.

Ikki nozik joy. Birinchisi — backend: nginx loglarini journal'ga emas, to'g'ridan-to'g'ri faylga yozadi, shuning uchun bu jail'larda logpath shart va backend auto bo'lishi kerak — aynan shu sababdan [DEFAULT] da backend = auto, systemd esa faqat [sshd] da. Have not found any log file chiqsa — nginx o'rnatilmagan yoki log boshqa joyda (saytlar uchun alohida log qilgan bo'lsangiz, logpath = /var/log/nginx/*error.log kabi wildcard ishlaydi). Ikkinchisi — Docker: nginx konteynerda bo'lsa, loglari /var/log/nginx/ da emas, docker logs da, va host'dagi fail2ban ularni ko'rmaydi. Yechim — konteyner log papkasini host'ga volume qilib chiqarish va logpath da o'sha faylni ko'rsatish.

Terminal
sudo fail2ban-client -t && sudo systemctl restart fail2ban
sudo fail2ban-client status
# Jail list:   nginx-botsearch, nginx-http-auth, nginx-limit-req, sshd

Django login uchun o'z filtrimiz#

Sayt admin paneli yoki login sahifasi bo'lsa, undagi muvaffaqiyatsiz urinishlarni ham bloklash mumkin. fail2ban buni bilmaydi — ilova o'zi logga yozishi kerak. Django'da eng sodda yo'l — muvaffaqiyatsiz loginni alohida logger bilan faylga yozish:

Python
# config/settings/base.py — LOGGING'ga qo'shimcha handler va logger
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "auth": {"format": "%(asctime)s %(message)s"},
    },
    "handlers": {
        "auth_file": {
            "class": "logging.FileHandler",
            "filename": "/var/log/myapp/auth.log",  # papkani pastda yaratamiz
            "formatter": "auth",
        },
    },
    "loggers": {
        # faqat shu logger faylga yozadi — qolgan loglar tegmaydi
        "myapp.auth": {"handlers": ["auth_file"], "level": "INFO", "propagate": False},
    },
}

Django bu papkani o'zi yaratmaydi — bo'lmasa, ilova start paytida FileNotFoundError bilan yiqiladi. Papkani oldindan ochib, ilova ishlaydigan foydalanuvchiga beramiz (systemd qismida bu deploy edi):

Terminal
sudo mkdir -p /var/log/myapp
sudo chown deploy:deploy /var/log/myapp
Python
# apps/accounts/signals.py — Django'ning o'z signali, view'ga tegilmaydi
import logging

from django.contrib.auth.signals import user_login_failed
from django.dispatch import receiver

log = logging.getLogger("myapp.auth")


def client_ip(request) -> str:
    # nginx orqasidamiz: REMOTE_ADDR har doim 127.0.0.1. Haqiqiy IP'ni nginx
    # X-Real-IP sarlavhasiga o'zi yozadi — mijoz yuborgan qiymat ustidan yoziladi
    return request.META.get("HTTP_X_REAL_IP") or request.META.get("REMOTE_ADDR", "-")


@receiver(user_login_failed)
def log_failed_login(sender, credentials, request, **kwargs):
    if request is None:
        return
    # fail2ban shu qatorni o'qiydi — format qat'iy: "LOGIN FAILED from <IP>"
    log.info("LOGIN FAILED from %s user=%s", client_ip(request), credentials.get("username", "-"))

Signal fayli o'z-o'zidan yuklanmaydi — uni ilova ishga tushganda import qilamiz, aks holda @receiver hech qachon ulanmaydi va log bo'sh qoladi:

Python
# apps/accounts/apps.py
from django.apps import AppConfig


class AccountsConfig(AppConfig):
    name = "apps.accounts"

    def ready(self):
        from . import signals  # noqa: F401 — import'ning o'zi signalni ulaydi

Ilovani qayta ishga tushiring (sudo systemctl restart mysite-web) va login sahifasida bir marta ataylab xato parol tering. FileHandler faylni ilova start paytida yaratadi, xato login esa unga birinchi qatorni yozadi:

Terminal
sudo tail -1 /var/log/myapp/auth.log
# 2026-08-29 03:12:07,123 LOGIN FAILED from 203.0.113.99 user=admin

Bu qadamni o'tkazib yubormang: jail yoqilganda log fayli hali yo'q bo'lsa, fail2ban Have not found any log file for django-login jail deb butunlay ishga tushmaydi — u bilan birga sshd himoyasi ham to'xtaydi.

Endi fail2ban uchun filter — bitta regex, <HOST> — fail2ban IP'ni oladigan joy:

INI
# /etc/fail2ban/filter.d/django-login.conf
[Definition]
failregex = ^.* LOGIN FAILED from <HOST> user=.*$
ignoreregex =
INI
# /etc/fail2ban/jail.local (davomi)
[django-login]
enabled  = true
port     = http,https
logpath  = /var/log/myapp/auth.log
filter   = django-login
maxretry = 5
findtime = 10m
bantime  = 1h

Tekshiruv — avval filter, keyin jonli:

Terminal
# regex log faylga mos keladimi
sudo fail2ban-regex /var/log/myapp/auth.log /etc/fail2ban/filter.d/django-login.conf
# Lines: 12 lines, 0 ignored, 12 matched, 0 missed

sudo systemctl restart fail2ban
sudo fail2ban-client status django-login

matched — regex ushlagan qatorlar. 0 chiqsa, regex log formatiga mos emas — o'z log'ingizdan real bir qatorni olib regex bilan solishtiring (eng ko'p sabab: sana formati yoki qator boshidagi qo'shimcha maydon). Juda katta chiqsa — regex haddan ortiq keng, oddiy foydalanuvchilarni ham ushlayapti. Shu test filterni jonli ishga tushirishdan oldin majburiy: noto'g'ri regex yo hech kimni ushlamaydi, yo qonuniy foydalanuvchilarni banlaydi.

Endi login sahifasida 10 daqiqada 5 marta xato parol tergan IP bir soatga serverning hamma portlaridan yopiladi (banaction = ufw butun IP'ni rad etadi). Bu ataylab qattiq: login'ni buzmoqchi bo'lgan odamning SSH'ga ham keragi yo'q.

Nega X-Forwarded-For emas

Ko'p qo'llanmalarda IP X-Forwarded-For ning birinchi qiymatidan olinadi. fail2ban uchun bu xavfli: $proxy_add_x_forwarded_for mijoz yuborgan sarlavhani o'chirmaydi, faqat oxiriga haqiqiy IP'ni qo'shadi. Hujumchi X-Forwarded-For: 198.51.100.42 yuborsa, birinchi qiymat — uning o'zi yozgan soxta IP. Natijada u o'zi banga tushmaydi, buning o'rniga istalgan begona IP'ni, masalan sizning ofisingizni, butun serverdan bloklatadi. X-Real-IP ni esa nginx har safar $remote_addr bilan qayta yozadi. Faqat nginx konfiguratsiyasida shu qator borligini tekshiring (nginx maqolasida bor): proxy_set_header X-Real-IP $remote_addr;

Bir hafta kuzatuv va ertalabki tekshiruv#

Sozlab bo'lgach bir hafta o'tib ko'ring — raqamlar fikrni o'zgartiradi:

Terminal
sudo fail2ban-client status sshd | grep -E "Total (failed|banned)"
# |  `- Total failed:     14 806
#    |- Total banned:     1 231

Bir hafta ichida 14 ming urinish, 1 200 ta ban — bitta oddiy VPS'da. Bu "kimdir meni buzmoqchi" degani emas: bu internetning fon shovqini, har bir ochiq IP shuni oladi. fail2ban bu shovqinni sshd'ga yetkazmay to'xtatadi.

Shu raqamlar bilan birga eng muhim tekshiruv — fail2ban bermaydigani: muvaffaqiyatli kirishlar.

Terminal
sudo journalctl -u ssh --since -7d --grep='Accepted' --no-pager | tail
# auth.log bor tizimda: sudo grep 'Accepted' /var/log/auth.log | tail

fail2ban xato urinishlarni to'sadi; lekin bu ro'yxatda notanish IP ko'rsangiz — bu allaqachon insident: parol yoki kalit o'g'irlangan bo'lishi mumkin. Ertalabki besh daqiqalik tekshiruv shundan iborat: ufw status verbose (firewall joyidami), fail2ban-client status sshd (raqamlar odatiy diapazondami) va shu Accepted ro'yxati.

ufw bilan birga: kim nima qiladi#

ufw fail2ban
Qoida turi doimiy, siz yozgan vaqtinchalik, log asosida avtomatik
Savol "qaysi port kimga ochiq?" "ochiq portni kim suiiste'mol qilyapti?"
Misol 5432 faqat app serverdan 22 ni 5 marta xato tergan IP — 1 soat yopiq
O'zaro banaction = ufw bilan fail2ban qoidalari ufw status da ko'rinadi

Tartib: avval ufw (ochiq portlar minimal), keyin fail2ban (ochiq qolganlar himoyalangan). Ikkinchisi birinchisiz kam foyda beradi — 5432 ochiq bo'lsa, Postgres loglari uchun jail yozishdan ko'ra portni yopish osonroq.

Uchinchi qatlam — bu ikkisidan tashqarida, lekin shu paketda yuradi: SSH'da parol o'rniga kalit (PasswordAuthentication no, PermitRootLogin no). fail2ban parol terishni sekinlashtiradi, kalit esa uni ma'nosiz qiladi — ikkalasi birga ideal.

Shu xatolar chiqsa#

  • O'zimni bloklab qo'ydim.

    Boshqa IP'dan (telefon interneti) yoki provayder konsolidan kirib: sudo fail2ban-client set sshd unbanip SIZNING_IP. Keyin ignoreip ga shu IP'ni qo'shing — bu birinchi bo'lib qilinishi kerak edi.

  • fail2ban-client status — "Failed to access socket".

    Xizmat ishlamayapti: sudo systemctl status fail2ban va sudo journalctl -u fail2ban -n 30 — ko'pincha jail.local dagi xato (mavjud bo'lmagan logpath, noto'g'ri bo'lim nomi).

  • Jail yoqilgan, lekin hech kimni bloklamayapti.

    fail2ban-regex bilan filter'ni tekshiring — 0 matched bo'lsa, log formati regex'ga mos emas (masalan, nginx log formatini o'zgartirgansiz). Yoki ignoreip juda keng (0.0.0.0/0 yozib qo'yilgan holatni ko'rganman).

  • Ban bor, lekin IP baribir ulanyapti.

    Uch sabab: SSH nostandart portda-yu jail'da port yozilmagan (iptables-multiport action'ida ban faqat jail portiga qo'llanadi); banaction noto'g'ri; yoki Docker — konteyner portlari ufw'ni aylanib o'tadi (ufw maqolasidagi 5-ssenariy). sudo ufw status da REJECT qatori bormi — tekshiring.

  • Debian'da o'rnatgan zahoti failed, logda Have not found any log file for sshd.

    auth.log yo'q — [sshd] da backend = systemd. Bu maqoladagi jail.local shunday yozilgan.

  • nginx jail'i uchun Have not found any log file.

    nginx yo'q, log boshqa joyda yoki Docker ichida. To'g'ri logpath; hozircha kerak bo'lmasa — o'sha jail'da enabled = false.

  • Restart'da xato, fail2ban-client -t o'tmayapti.

    Sintaksis: [DEFAULT] katta harfda; eski versiyada bantime.increment yo'q (fail2ban-client version); banaction = ufw bor-u ufw yo'q.

  • Reboot'dan keyin himoya yo'q.

    sudo systemctl enable fail2ban qilinmagan (ufw uchun ufw enable ning o'zi yetadi).

  • Restart'dan keyin banlar yo'qoldi.

    Odatda bunday bo'lmasligi kerak: fail2ban banlarni /var/lib/fail2ban/fail2ban.sqlite3 da saqlaydi va bantime tugamagan bo'lsa restart'dan keyin qayta qo'llaydi. Yo'qolgan bo'lsa — dbpurgeage yoki baza fayli ruxsatlari; lekin bunga ko'p vaqt sarflash shart emas, botlar baribir qaytib keladi va yana bloklanadi.

  • Loglar juda tez to'lyapti.

    loglevel = NOTICE qiling (default INFO) — shunda faqat ban/unban yoziladi. Faylda bo'lim sarlavhasi shart: usiz fail2ban qatorni jimgina e'tiborsiz qoldiradi, -t esa baribir OK deydi.

INI
# /etc/fail2ban/fail2ban.local
[Definition]
loglevel = NOTICE

logrotate fail2ban uchun paket bilan keladi.

Yakuniy ro'yxat#

  1. apt install fail2ban — sshd jail'i darrov ishlaydi.
  2. jail.local yarating; ignoreip ga o'z IP va xususiy tarmoqni yozing.
  3. banaction = ufw, bantime.increment = true.
  4. [sshd] — mode = aggressive, port o'zgargan bo'lsa — yozing.
  5. nginx: nginx-http-auth, nginx-limit-req, nginx-botsearch.
  6. Ilova login'i bo'lsa: log qatori → filter.d/*.conf → jail; fail2ban-regex bilan tekshirish.
  7. Bir haftadan keyin status — raqamlarga qarab maxretry/bantime ni sozlash; Accepted ro'yxatida notanish IP yo'qligini tekshirish.
  8. Zaxira: jail.local + o'z filtrlaringiz (filter.d/django-login.conf) — butun himoya sozlamangiz shu sanoqli faylda; yangi serverga ko'chirishga shu yetadi.

Shu qismning buyruqlari#

Shpargalka
sudo apt install -y fail2ban
sudo fail2ban-client statusjail'lar ro'yxati
sudo fail2ban-client status sshdbloklanganlar, sanoq
sudo fail2ban-client set sshd unbanip IPochish
sudo fail2ban-client set sshd banip IPqo'lda bloklash
sudo fail2ban-client -tsozlamani sinash (restart'dan oldin)
sudo fail2ban-client reloadjail.local o'zgargach, banlar saqlanadi
sudo fail2ban-client version
sudo fail2ban-regex LOG FILTER.conffilter'ni sinash
sudo fail2ban-regex systemd-journal /etc/fail2ban/filter.d/sshd.conf
sudo tail -f /var/log/fail2ban.logjonli kuzatish
sudo systemctl status fail2ban
sudo journalctl -u fail2ban -e --no-pagerxizmat ishga tushmasa — sabab shu yerda
sudo ufw status numberedREJECT qatorlari — fail2ban banlari
sudo iptables -L f2b-sshd -n --line-numbersbanaction ufw bo'lmasa — banlar shu yerda
sudo journalctl -u ssh --since -7d --grep='Accepted'kim MUVAFFAQIYATLI kirgan

fail2ban firewall'ning "aqlli" yarmi: ufw eshiklarni yopdi, fail2ban ochiq eshiklarga suqilib kelayotganlarni haydab yuradi. Ikkalasi o'n daqiqalik ish — va shundan keyin serverning loglari haqiqatan sizning loglaringizga aylanadi, botlarniki emas.

Shu ikki maqoladan keyin siz firewall policy'sini tushunasiz, jail sozlaysiz, o'z filtringizni yozib test qilasiz va "ishlamayapti"ni qatlam-baqatlam diagnostika qilasiz. Bundan yuqoriga yo'l: nftables ni to'g'ridan-to'g'ri o'rganish (ufw ostidagi dvigatel); fail2ban action'lariga Telegram xabarnoma ulash (action.d/ ichini o'rganib); CrowdSec bilan tanishish — fail2ban'ning "jamoaviy reputatsiya"li avlodi; va bir nechta serverni Ansible bilan bir xil sozlash.

27 daqiqa 39 marta o‘qildi