fail2ban Performans Optimizasyonu: Büyük Log Dosyalarıyla Başa Çıkma

Yoğun trafik alan bir sunucuda fail2ban’ın yavaşladığını fark ettiğinizde, genellikle ilk şüphelendiğiniz yer yanlış olur. Çoğu sysadmin doğrudan jail konfigürasyonuna bakar, oysa asıl sorun büyük log dosyalarının işlenmesinde gizlidir. Günde milyonlarca satır üreten bir nginx veya SSH log dosyasıyla çalışırken fail2ban’ın CPU kullanımının %80’e çıktığını gördüyseniz, bu yazı tam size göre.

Sorun Neden Ortaya Çıkar?

fail2ban özünde bir log takip aracıdır. Her jail için tanımladığınız filtreler, düzenli ifadeler (regex) kullanarak log dosyalarını tarar. Küçük bir VPS’te bu süreç saniyeler içinde tamamlanır, fakat yüksek trafikli bir sunucuda durum farklıdır.

Tipik problemler şunlardır:

  • Log dosyası büyümesi: /var/log/auth.log dosyası günde 500MB’a ulaşabilir
  • Regex karmaşıklığı: Kötü yazılmış regex ifadeleri her satır için aşırı CPU harcar
  • Polling gecikmesi: fail2ban’ın dosyayı ne sıklıkla kontrol ettiği kritik öneme sahiptir
  • Veritabanı yükü: SQLite veritabanına yapılan sürekli yazma işlemleri I/O darboğazı yaratır
  • Aşırı jail sayısı: 20-30 aktif jail aynı anda çalışıyorsa bellek kullanımı ciddi biçimde artar

Bir prodüksiyon örneği verelim: E-ticaret sitesi barındıran bir sunucuda SSH brute force saldırıları sırasında auth.log dakikada 10.000 satır üretiyordu. fail2ban bu yükü kaldıramayıp 45 saniye gecikmeyle ban işlemi yapıyordu. O 45 saniyede saldırgan yüzlerce deneme daha yapabiliyordu.

Önce Mevcut Durumu Ölçün

Optimize etmeden önce neyi optimize ettiğinizi bilmeniz gerekir. fail2ban’ın performansını izlemek için birkaç pratik yöntem var.

# fail2ban'ın CPU ve bellek kullanımını izle
top -p $(pgrep fail2ban)

# Daha detaylı kaynak kullanımı için
pidstat -p $(pgrep fail2ban) 1 10

# fail2ban log dosyasındaki hata ve uyarıları say
grep -c "WARNING|ERROR" /var/log/fail2ban.log

# Son 100 satırdaki ban işlem sürelerini incele
tail -100 /var/log/fail2ban.log | grep "Ban|Found"

fail2ban-client ile de hızlı durum kontrolü yapabilirsiniz:

# Tüm jail'lerin durumunu listele
fail2ban-client status

# Spesifik bir jail için detay
fail2ban-client status sshd

# fail2ban'ın ping süresini ölç (gecikme testi)
time fail2ban-client ping

Eğer time fail2ban-client ping komutu 1 saniyeden uzun sürüyorsa, ciddi bir performans sorununuz var demektir. Normal şartlarda bu işlem 50ms altında tamamlanmalıdır.

Log Rotasyonu ve fail2ban Entegrasyonu

En temel ama en çok ihmal edilen konudan başlayalım: log rotasyonu. Büyük log dosyaları fail2ban’ı direkt etkiler çünkü her yeniden başlatmada tüm dosya yeniden taranır.

# /etc/logrotate.d/custom-fail2ban-aware dosyasını oluşturun
cat > /etc/logrotate.d/nginx-fail2ban << 'EOF'
/var/log/nginx/access.log
/var/log/nginx/error.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    sharedscripts
    postrotate
        # Nginx'e yeni log dosyası açmasını söyle
        [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
        # fail2ban'a log rotasyonu olduğunu bildir
        fail2ban-client set nginx-req-limit unbanip 0.0.0.0 2>/dev/null || true
    endscript
}
EOF

fail2ban kendi ayarlarında log rotasyonunu nasıl ele aldığını jail.local içinde belirtebilirsiniz:

# /etc/fail2ban/jail.local
[DEFAULT]
# Log rotasyonundan sonra fail2ban'ın davranışı
# false = rotasyondan sonra dosyayı yeniden okumaya başla
# true = sadece yeni satırları oku (büyük dosyalar için önerilir)
logtarget = /var/log/fail2ban.log

# findtime değerini makul tutun - çok büyük yaparsanız daha fazla log taranır
findtime = 600

# Veritabanı temizleme süresi
dbpurgeage = 86400

Performans Kritik Ayarlar: jail.local Optimizasyonu

# /etc/fail2ban/jail.local - Performans odaklı konfigürasyon

[DEFAULT]
# Backend seçimi performans için kritiktir
# systemd > pyinotify > polling sıralamasını takip edin
backend = systemd

# Polling backend kullanıyorsanız kontrol aralığını artırın
# Varsayılan 1 saniyedir, yüksek yük altında 5-10 saniye yapın
pollinterval = 5

# findtime: Ne kadar geriye bakılacak (saniye)
# Büyük loglar için bu değeri küçük tutun
findtime = 300

# maxretry: Kaç denemeden sonra ban
maxretry = 5

# bantime: Ban süresi - uzun tutmak tekrar tarama yükünü azaltır
bantime = 3600

# Incremental ban: Her tekrar banlamada süre katlansın
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 604800

# Veritabanı yolu - SSD üzerinde olmasına dikkat edin
dbfile = /var/lib/fail2ban/fail2ban.sqlite3

# Veritabanı güncellemelerini belleğe al, diske toplu yaz
# Bu I/O yükünü dramatik biçimde azaltır
dbpurgeage = 86400

backend seçimi burada en kritik parametredir:

  • systemd: journald ile entegre çalışır, en verimli yöntemdir
  • pyinotify: Kernel’ın inotify mekanizmasını kullanır, polling’den iyidir
  • polling: Dosyayı periyodik olarak kontrol eder, en yavaş yöntemdir
  • auto: Sisteme göre otomatik seçim yapar, genellikle iyi çalışır

Regex Optimizasyonu: En Büyük Hız Kazanımı

Kötü yazılmış regex ifadeleri fail2ban performansını on kat yavaşlatabilir. Her log satırı için çalışan bir regex, milyonlarca satırda ciddi gecikme yaratır.

Önce mevcut filterlerinizin ne kadar sürdüğünü test edin:

# Bir filtrenin belirli bir log dosyasındaki performansını test et
time fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

# Daha detaylı analiz için
fail2ban-regex --print-all-matched /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf 2>&1 | head -50

# Sadece istatistikleri göster
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf | tail -20

Şimdi kötü ve iyi regex örneklerini karşılaştıralım:

# /etc/fail2ban/filter.d/nginx-custom.conf - Kötü regex örneği
[Definition]
# Bu regex gereksiz yere karmaşık ve yavaş
# .* kullanımı backtracking'e neden olur
failregex = ^.*- -.*"(GET|POST|PUT|DELETE|HEAD|OPTIONS|PATCH) .* HTTP/.*" (4[0-9]{2}|5[0-9]{2}) .*$

# İyi regex örneği - daha spesifik, daha hızlı
[Definition]
# IP adresini başa al, gereksiz grup yakalamayı kaldır
# (?:...) kullanarak yakalamayan grup tanımla
failregex = ^<HOST> - S+ [S+ +d{4}] "(?:GET|POST) S+ HTTP/d.d" (?:400|401|403|404|429) d+

Regex yaratırken dikkat edilmesi gerekenler:

  • .* kullanımını minimumda tutun: Greedy matching gereksiz CPU harcar
  • etiketini doğru konuma koyun: IP adresi genellikle satırın başındadır
  • (?:...) kullanın: Yakalamayan gruplar (...) gruplarından daha hızlıdır
  • Alternation’ı sınırlayın: (a|b|c|d|e) yerine karakter sınıfı [abcde] kullanın

Büyük Log Dosyaları için systemd Journal Kullanımı

Eğer servisleriniz systemd altında çalışıyorsa, dosya tabanlı log yerine doğrudan journald’den okuma yapın. Bu yöntem özellikle yüksek yük altında çok daha verimlidir.

# /etc/fail2ban/jail.local içinde SSH için systemd backend
[sshd]
enabled = true
backend = systemd
journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd
maxretry = 3
findtime = 300
bantime = 3600
# journald'nin fail2ban ile doğru çalıştığını doğrula
journalctl -u sshd --since "1 hour ago" | grep "Failed password" | wc -l

# fail2ban'ın journal'dan okuduğunu kontrol et
fail2ban-client status sshd | grep "Currently banned"

# systemd backend ile fail2ban'ı yeniden başlat ve log'u izle
systemctl restart fail2ban
journalctl -fu fail2ban

Veritabanı Optimizasyonu

fail2ban SQLite kullandığı için, veritabanı boyutu büyüdükçe yavaşlar. Özellikle bantime.increment kullanıyorsanız veritabanı hızla büyüyebilir.

# Veritabanı boyutunu kontrol et
ls -lh /var/lib/fail2ban/fail2ban.sqlite3

# SQLite içine bağlanıp tablo boyutlarını incele
sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 << 'EOF'
.tables
SELECT name, COUNT(*) as kayit_sayisi FROM bans GROUP BY name;
SELECT COUNT(*) as toplam_ban FROM bans;
SELECT COUNT(*) as toplam_ip FROM bips;
.quit
EOF

# Eski kayıtları temizle (30 günden eski)
sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 
  "DELETE FROM bans WHERE timeofban < strftime('%s', 'now') - 2592000;"

# Veritabanını optimize et (VACUUM işlemi)
sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 "VACUUM;"

Bu işlemi otomatikleştirmek için bir cron job oluşturun:

# /etc/cron.weekly/fail2ban-db-cleanup
cat > /etc/cron.weekly/fail2ban-db-cleanup << 'EOF'
#!/bin/bash
DB="/var/lib/fail2ban/fail2ban.sqlite3"

if [ -f "$DB" ]; then
    # 30 günden eski kayıtları sil
    sqlite3 "$DB" "DELETE FROM bans WHERE timeofban < strftime('%s', 'now') - 2592000;" 2>/dev/null
    
    # Veritabanını optimize et
    sqlite3 "$DB" "VACUUM;" 2>/dev/null
    
    echo "fail2ban veritabani temizlendi: $(date)" >> /var/log/fail2ban-maintenance.log
fi
EOF

chmod +x /etc/cron.weekly/fail2ban-db-cleanup

Çok Sayıda Jail ile Çalışma Stratejisi

20’den fazla aktif jail çalıştırıyorsanız, öncelik sıralaması yapmanız gerekir. Her jail ayrı bir thread’de çalışır ve bellek tüketir.

# Kaç aktif jail var?
fail2ban-client status | grep "Jail list" | tr ',' 'n' | wc -l

# Her jail'in ban sayısını görüntüle
for jail in $(fail2ban-client status | grep "Jail list" | sed 's/.*Jail list:s*//' | tr ',' ' '); do
    banned=$(fail2ban-client status $jail 2>/dev/null | grep "Currently banned" | awk '{print $NF}')
    echo "$jail: $banned banned IP"
done

Az kullanılan jail’leri tespit edip kapatın:

# Son 7 günde hiç ban yapmamış jail'leri bul
for jail in $(fail2ban-client status | grep "Jail list" | sed 's/.*Jail list:s*//' | tr ',' ' '); do
    total=$(fail2ban-client status $jail 2>/dev/null | grep "Total banned" | awk '{print $NF}')
    if [ "$total" = "0" ]; then
        echo "Devre disi birakilabilir: $jail"
    fi
done

ipset ile Firewall Performansını Artırma

Çok sayıda IP banladığınızda, iptables kuralları uzar ve her paket için tarama süresi artar. ipset kullanmak bu sorunu çözer.

# ipset kurulu mu kontrol et
which ipset || apt-get install ipset -y

# fail2ban'ın ipset kullanmasını sağla
# /etc/fail2ban/action.d/iptables-ipset-proto6.conf dosyasını kullanın

# ipset action'ı varsayılan yap
cat >> /etc/fail2ban/jail.local << 'EOF'

[DEFAULT]
# Standart iptables yerine ipset kullan
banaction = iptables-ipset-proto6
banaction_allports = iptables-ipset-proto6-allports
EOF

# Değişikliği uygula
systemctl restart fail2ban

# ipset tablosunun oluşturulduğunu doğrula
ipset list | grep "fail2ban"

# Kaç IP banlandığını hızlıca görüntüle
ipset list fail2ban-sshd | grep "Number of entries"

Gerçek Dünya Senaryosu: WordPress Sitesi Altında Ezilmek

Bir müşterinin 5 WordPress sitesi barındıran sunucusunda yaşanan problemi anlatalım. Sunucu özellikleri: 4 vCPU, 8GB RAM, HDD disk.

Problem: fail2ban sürekli %95 CPU kullanıyordu. access.log günde 2GB büyüyordu.

# İlk teşhis
tail -f /var/log/fail2ban.log | grep "ERROR|WARNING"
# Çıktı: WARNING: Lots of fails detected...

# Log boyutunu kontrol et
du -sh /var/log/nginx/access.log
# Çıktı: 1.8G /var/log/nginx/access.log

# fail2ban-regex ile mevcut filtreyi test et
time fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-http-auth.conf
# Çıktı: real 4m32.156s - 4.5 dakika! Kabul edilemez.

Uygulanan çözümler sıralamasıyla:

# 1. Log rotasyonunu günlük bazdan saatlik bazına al
cat > /etc/logrotate.d/nginx << 'EOF'
/var/log/nginx/*.log {
    hourly
    rotate 24
    compress
    delaycompress
    missingok
    notifempty
    sharedscripts
    postrotate
        nginx -s reopen
    endscript
}
EOF

# 2. Logrotate'i saat başı çalıştır
echo "0 * * * * root /usr/sbin/logrotate /etc/logrotate.d/nginx" > /etc/cron.d/nginx-rotate

# 3. fail2ban konfigürasyonunu optimize et
# findtime'ı 1 saatten 5 dakikaya indir
# Bu sayede fail2ban daha az log taramak zorunda kalır
sed -i 's/findtime = 3600/findtime = 300/' /etc/fail2ban/jail.local

# 4. polling yerine pyinotify kullan
sed -i 's/backend = polling/backend = pyinotify/' /etc/fail2ban/jail.local

# Sonucu test et
systemctl restart fail2ban
sleep 30
time fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-http-auth.conf
# Yeni çıktı: real 0m8.432s - 8 saniye. 30 kat iyileşme!

İzleme ve Alarm Sistemi Kurma

Optimize ettiğiniz sistemi izlemek de bir o kadar önemlidir. Basit bir izleme scripti:

#!/bin/bash
# /usr/local/bin/fail2ban-monitor.sh

THRESHOLD_CPU=50
THRESHOLD_BANNED=1000
LOG_FILE="/var/log/fail2ban-monitor.log"

# fail2ban PID'ini bul
F2B_PID=$(pgrep fail2ban-server)

if [ -z "$F2B_PID" ]; then
    echo "$(date): KRITIK - fail2ban calismıyor!" | tee -a $LOG_FILE
    systemctl start fail2ban
    exit 1
fi

# CPU kullanımını kontrol et
CPU_USAGE=$(ps -p $F2B_PID -o %cpu --no-headers | awk '{print int($1)}')

if [ "$CPU_USAGE" -gt "$THRESHOLD_CPU" ]; then
    echo "$(date): UYARI - fail2ban CPU kullanimi: %$CPU_USAGE" | tee -a $LOG_FILE
fi

# Toplam ban sayısını kontrol et
TOTAL_BANNED=0
for jail in $(fail2ban-client status 2>/dev/null | grep "Jail list" | sed 's/.*Jail list:s*//' | tr ',' ' '); do
    banned=$(fail2ban-client status $jail 2>/dev/null | grep "Currently banned" | awk '{print $NF}')
    TOTAL_BANNED=$((TOTAL_BANNED + banned))
done

echo "$(date): Toplam banlanan IP: $TOTAL_BANNED, CPU: %$CPU_USAGE" >> $LOG_FILE

# Cron ile her 5 dakikada bir çalıştır
# */5 * * * * root /usr/local/bin/fail2ban-monitor.sh
chmod +x /usr/local/bin/fail2ban-monitor.sh

# Cron'a ekle
echo "*/5 * * * * root /usr/local/bin/fail2ban-monitor.sh" > /etc/cron.d/fail2ban-monitor

Sonuç

fail2ban performans optimizasyonu tek seferlik bir iş değildir. Sunucunuzun trafiği büyüdükçe, saldırı örüntüleri değiştikçe ayarlarınızı da güncellemek zorundasınız.

Öncelik sırasıyla yapmanız gerekenler şöyle özetlenebilir:

  • Önce ölçün: fail2ban-regex ve top ile mevcut durumu belgeleyin
  • Backend’i optimize edin: Mümkünse systemd, değilse pyinotify kullanın
  • Log rotasyonunu düzeltin: Büyük log dosyaları en yaygın performans katilidir
  • findtime değerini küçültün: Ne kadar az geriye bakılırsa o kadar az tarama yapılır
  • Regex’lerinizi gözden geçirin: .* kullanımını minimuma indirin
  • ipset’e geçin: Çok sayıda ban kuralı için zorunludur
  • Veritabanını düzenli temizleyin: SQLite şişmeye meyillidir
  • İzleme kurun: Sorun büyümeden haberdar olun

Gerçek dünyada hiçbir zaman “mükemmel” bir konfigürasyon yoktur. Her sunucunun trafik profili, saldırı yüzeyi ve donanım kapasitesi farklıdır. Bu yüzden buradaki teknikleri kendi ortamınıza uyarlayın, test edin ve ölçün. Körü körüne kopyala-yapıştır yapmak bazen işe yarar, ama çoğunlukla yeni problemler yaratır.

Yorum yapın