fail2ban ile Nginx ve Apache Sunucularını Koruma Altına Alma

Sunucunu kurup internete açtığın an, logları izlemeye başlarsan göreceksin: dakikalar içinde brute force denemeleri, tarayıcı botları ve kötü niyetli istekler akmaya başlıyor. Özellikle Nginx ve Apache gibi web sunucularına yönelik saldırılar hem çok yaygın hem de otomatize edilmiş durumda. İşte bu noktada fail2ban devreye giriyor. Fail2ban, log dosyalarını izleyerek belirlediğin kurallara uyan IP adreslerini otomatik olarak yasaklayan, hafif ama son derece etkili bir araç.

Bu yazıda fail2ban’ı sıfırdan kurup hem Nginx hem de Apache için gerçek dünya senaryolarına uygun şekilde nasıl yapılandıracağını adım adım göstereceğim. Sadece “şunu kopyala yapıştır” değil, her ayarın ne işe yaradığını da anlayarak ilerleyeceğiz.

Fail2ban Nedir ve Nasıl Çalışır

Fail2ban Python ile yazılmış bir daemon. Temel mantığı şu: log dosyalarını düzenli aralıklarla tarar, belirlediğin regex patternlerine uyan başarısız giriş denemelerini veya şüpheli aktiviteleri sayar, belirlediğin eşiği aşan IP adreslerini ise iptables, nftables veya ufw gibi firewall araçlarına banlar.

Birkaç temel kavramı baştan netleştirelim:

  • Filter: Log satırlarını analiz eden regex tanımları. Hangi satırların “başarısız deneme” sayılacağını belirler.
  • Jail: Bir filter ile action’ı birleştiren yapılandırma bloğu. “Bu log için şu filtreyi kullan, eşiği aşarsa şu action’ı çalıştır” der.
  • Action: Ban işlemini gerçekleştiren komut. Genellikle iptables kuralı ekler.
  • bantime: IP’nin kaç saniye yasaklı kalacağı.
  • findtime: Başarısız denemelerin kaç saniye içinde sayılacağı.
  • maxretry: findtime içinde kaç başarısız denemeden sonra ban uygulanacağı.

Kurulum

Debian/Ubuntu tabanlı sistemlerde:

sudo apt update
sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

RHEL/CentOS/Rocky Linux sistemlerde:

sudo dnf install epel-release -y
sudo dnf install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Kurulumdan sonra fail2ban’ın düzgün çalıştığını doğrula:

sudo systemctl status fail2ban
sudo fail2ban-client status

Temel Yapılandırma Felsefesi

Fail2ban’ın yapılandırma dosyaları /etc/fail2ban/ altında. Burada önemli bir kural var: /etc/fail2ban/jail.conf dosyasını doğrudan düzenleme. Bu dosya paket güncellemelerinde üzerine yazılır. Bunun yerine jail.local dosyası oluştur:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Ya da sıfırdan da oluşturabilirsin, daha temiz olur:

sudo nano /etc/fail2ban/jail.local

Aynı kural filter dosyaları için de geçerli. /etc/fail2ban/filter.d/ altındaki .conf dosyaları yerine .local uzantılı dosyalar oluşturmalısın.

Global Ayarlar

jail.local dosyasının başına global varsayılan değerleri tanımla. Bu değerler tüm jail’ler için geçerli olur, jail bazında override edebilirsin:

[DEFAULT]
# Hiçbir zaman banlanmayacak IP'ler (kendi IP'n, monitoring sistemlerin)
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 10.0.0.0/8

# Ban süresi: 1 saat (saniye cinsinden veya zaman suffixleri)
bantime = 3600

# Bu süre içinde maxretry kadar başarısız deneme olursa ban
findtime = 600

# Kaç başarısız denemede ban
maxretry = 5

# Backend: systemd, auto, pyinotify, polling
backend = systemd

# Email bildirimleri için (opsiyonel)
destemail = [email protected]
sendername = Fail2Ban
mta = sendmail

# Varsayılan action: ban + email veya sadece ban
action = %(action_)s

ignoreip ayarı kritik. Kendi IP adresini mutlaka buraya ekle, yoksa yanlışlıkla kendini kilitleyebilirsin. VPN kullanıyorsan VPN IP bloğunu da ekle.

bantime için -1 değeri kalıcı ban anlamına gelir. Özellikle agresif saldırılar için kullanılabilir ama dikkatli ol.

Nginx Yapılandırması

Nginx HTTP Auth Koruması

Nginx’te basic auth veya giriş sayfası koruması için önce filter dosyasını oluştur:

sudo nano /etc/fail2ban/filter.d/nginx-http-auth.local
[Definition]
failregex = ^ [error] d+#d+: *d+ user "(?:[^"]+|.*?)":? (?:password mismatch|was not found in "[^"]*"), client: <HOST>, server: S*, request: "S+ S+ HTTP/d+", host: "S+"
            ^ [error] d+#d+: *d+ no user/password was provided for basic authentication, client: <HOST>, server: S*, request: "S+ S+ HTTP/d+", host: "S+"

ignoreregex =

Şimdi jail.local dosyasına bu jail’i ekle:

[nginx-http-auth]
enabled  = true
filter   = nginx-http-auth
port     = http,https
logpath  = /var/log/nginx/error.log
maxretry = 3
bantime  = 3600
findtime = 300

Nginx 4xx Hata Koruması

Scanner’lar ve botlar genellikle var olmayan sayfalara, admin panellerine, /wp-admin, /.env, /config.php gibi path’lere istek gönderir. Bu istekler 404 döndürür. Çok fazla 404 isteği gönderen IP’leri banlamak için:

sudo nano /etc/fail2ban/filter.d/nginx-4xx.conf
[Definition]
failregex = ^<HOST> -.*"(GET|POST|HEAD).*HTTP.*" (400|401|403|404|405|444) .*$

ignoreregex = ^<HOST> -.*"(GET|POST) /health.*HTTP.*" 404
              ^<HOST> -.*"GET /favicon.ico HTTP.*" 404

ignoreregex kısmına healthcheck endpoint’leri ve favicon gibi meşru 404’leri ekle, yoksa monitoring araçların veya tarayıcılar banlanabilir.

jail.local‘e ekle:

[nginx-4xx]
enabled  = true
filter   = nginx-4xx
port     = http,https
logpath  = /var/log/nginx/access.log
maxretry = 20
bantime  = 7200
findtime = 60

Bu jail için maxretry‘yi biraz yüksek tuttum çünkü 404 meşru kullanıcılarda da oluşabilir. 60 saniye içinde 20 tane 404 alan birinin büyük ihtimalle scanner olduğunu söyleyebiliriz.

Nginx Rate Limit Koruması

Nginx’te limit_req_zone kullandıysan bu zone’u aşan istekler error log’a düşer. Bunu da fail2ban ile yakalayabilirsin:

sudo nano /etc/fail2ban/filter.d/nginx-req-limit.conf
[Definition]
failregex = limiting requests, excess:.* by zone.*client: <HOST>

ignoreregex =
[nginx-req-limit]
enabled  = true
filter   = nginx-req-limit
port     = http,https
logpath  = /var/log/nginx/error.log
maxretry = 10
bantime  = 7200
findtime = 60

Apache Yapılandırması

Apache Auth Koruması

Apache’de zaten apache-auth filter’ı hazır geliyor, sadece jail tanımlaması yapman yeterli:

[apache-auth]
enabled  = true
filter   = apache-auth
port     = http,https
logpath  = /var/log/apache2/error.log
maxretry = 3
bantime  = 3600
findtime = 300

Apache Overflows ve Scanner Koruması

Apache’ye yönelik overflow denemelerini ve bilinen kötü niyetli bot davranışlarını yakalamak için:

[apache-overflows]
enabled  = true
filter   = apache-overflows
port     = http,https
logpath  = /var/log/apache2/error.log
maxretry = 2
bantime  = 86400
findtime = 600

[apache-noscript]
enabled  = true
filter   = apache-noscript
port     = http,https
logpath  = /var/log/apache2/access.log
maxretry = 6
bantime  = 86400
findtime = 600

[apache-badbots]
enabled  = true
filter   = apache-badbots
port     = http,https
logpath  = /var/log/apache2/access.log
bantime  = 172800
maxretry = 1
findtime = 86400

apache-badbots için maxretry = 1 ayarladım çünkü bilinen kötü niyetli bot user-agent’larını kullanan biri için tek bir istek bile yeterli sebep. Bu filter /etc/fail2ban/filter.d/apache-badbots.conf içinde zaten tanımlı geliyor, içine bakarak hangi botları kapsadığını görebilirsin.

Özel Apache 4xx Filter

Nginx için yaptığımız gibi Apache için de 4xx bazlı bir filter oluşturalım:

sudo nano /etc/fail2ban/filter.d/apache-4xx.conf
[Definition]
failregex = ^<HOST> -.*"(GET|POST|HEAD).*HTTP.*" (400|401|403|404|405) .*$

ignoreregex = ^<HOST> -.*"GET /server-status.*HTTP.*" 403
              ^<HOST> -.*"GET /favicon.ico HTTP.*" 404
              ^<HOST> -.*"GET /.well-known/.*HTTP.*" 404

.well-known path’ini ignoreregex’e eklemeyi unutma, Let’s Encrypt validasyonları bu path’i kullanır ve 404 dönebilir.

[apache-4xx]
enabled  = true
filter   = apache-4xx
port     = http,https
logpath  = /var/log/apache2/access.log
maxretry = 15
bantime  = 7200
findtime = 60

Log Format Kontrolü

Filter’ların düzgün çalışması için web sunucunun log formatının fail2ban’ın beklediği formata uyması gerekiyor. Nginx’in varsayılan combined log formatı genellikle sorunsuz çalışır:

# Nginx log formatı kontrol
sudo tail -5 /var/log/nginx/access.log

# Apache log formatı kontrol
sudo tail -5 /var/log/apache2/access.log

Eğer custom log format kullanıyorsan filter regex’ini buna göre ayarlaman gerekebilir. Filter’ı test etmeden önce bu adımı atlama.

Filter Test Etme

Jail’leri aktif etmeden önce filter’larını test et. Bu hem hata yakalamak hem de gerçekten istediğin satırları yakalayıp yakalamadığını görmek için çok önemli:

# Belirli bir filter'ı log dosyasına karşı test et
sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-4xx.conf

# Detaylı çıktı için
sudo fail2ban-regex --print-all-matched /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-4xx.conf

Çıktıda kaç satırın eşleştiğini, kaç satırın ignore edildiğini göreceksin. Eğer hiç eşleşme yoksa ya log formatın farklıdır ya da regex yanlış yazılmıştır.

Yapılandırmayı Uygulama ve Yönetim

Tüm değişikliklerden sonra fail2ban’ı yeniden başlatmak yerine reload et, daha temiz:

sudo fail2ban-client reload

# Belirli bir jail'i reload etmek için
sudo fail2ban-client reload nginx-4xx

# Tüm aktif jail'leri listele
sudo fail2ban-client status

# Belirli bir jail'in durumunu gör
sudo fail2ban-client status nginx-4xx

# Banlı IP'leri listele
sudo fail2ban-client status nginx-4xx | grep "Banned IP"

Bir IP’yi manuel olarak banlamak veya ban kaldırmak:

# Manuel ban
sudo fail2ban-client set nginx-4xx banip 192.168.1.100

# Ban kaldırma (unban)
sudo fail2ban-client set nginx-4xx unbanip 192.168.1.100

# Tüm jail'lerde belirli bir IP'yi unban et
for jail in $(sudo fail2ban-client status | grep "Jail list" | sed 's/.*://;s/,//g'); do
    sudo fail2ban-client set $jail unbanip 192.168.1.100 2>/dev/null
done

Son komutu bir script olarak kaydedip kullanmak, özellikle false positive durumlarında hayat kurtarır.

Gerçek Dünya Senaryosu: WordPress Saldırısı

Diyelim ki WordPress çalıştıran bir sunucun var ve /wp-login.php ile /xmlrpc.php üzerinden sürekli brute force geliyor. Bu çok yaygın bir senaryo.

sudo nano /etc/fail2ban/filter.d/wordpress.conf
[Definition]
failregex = ^<HOST> -.*"POST /(wp-login|xmlrpc).php HTTP.*" (200|403|429) .*$
            ^<HOST> -.*"GET /wp-login.php?.*HTTP.*" (200|302) .*$

ignoreregex =
[wordpress]
enabled  = true
filter   = wordpress
port     = http,https
logpath  = /var/log/nginx/access.log
maxretry = 5
bantime  = 86400
findtime = 300

Burada ilginç bir nokta: başarılı login’ler de 200 döndürür. Ama 300 saniye içinde 5 kez login denemesi yapan biri zaten brute force yapıyordur. Meşru bir kullanıcı bu kadar hızlı deneme yapmaz.

Jail Durumunu İzleme ve Log Takibi

# Fail2ban logunu canlı takip et
sudo tail -f /var/log/fail2ban.log

# Son banlanan IP'leri göster
sudo grep "Ban " /var/log/fail2ban.log | tail -20

# IP bazlı ban sayılarını say
sudo grep "Ban " /var/log/fail2ban.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head -20

Son komut hangi IP’nin kaç kez banlandığını gösterir. Çok tekrar eden IP’ler için kalıcı ban veya ISP’ye abuse bildirimi düşünebilirsin.

Recidive Jail: Tekrar Banlananlara Daha Sert Muamele

Bir IP defalarca banlanıp açılıyorsa, muhtemelen bot döngüsü içinde. recidive jail’i bu IP’leri daha uzun süre banlar:

[recidive]
enabled   = true
filter    = recidive
logpath   = /var/log/fail2ban.log
action    = %(action_)s
bantime   = 604800  ; 1 hafta
findtime  = 86400   ; 1 günlük sürede
maxretry  = 3       ; 3 kez banlanırsa 1 hafta yasak

recidive filter’ı fail2ban kendi log dosyasını izler. Başka bir jail tarafından 24 saat içinde 3 kez banlanan bir IP 1 hafta boyunca yasaklanır. Bu özellikle persistent saldırganlar için çok etkili.

Action Özelleştirme

Varsayılan action sadece iptables ban uygular. Bunu genişletebilirsin. Örneğin hem ban uygula hem de email gönder:

[nginx-http-auth]
enabled  = true
filter   = nginx-http-auth
port     = http,https
logpath  = /var/log/nginx/error.log
maxretry = 3
bantime  = 3600
action   = %(action_mwl)s

%(action_)s: Sadece ban %(action_mw)s: Ban + whois + email %(action_mwl)s: Ban + whois + log satırları + email

Email bildirimleri için mail sunucusunun kurulu olması gerekiyor. Basit bir test için mailutils veya sendmail yeterli.

Sistemin Sağlığını Kontrol Etme

Fail2ban düzgün çalışıyor mu, gerçekten koruma sağlıyor mu? Bunu doğrulamak için:

# Aktif iptables kurallarını kontrol et
sudo iptables -L f2b-nginx-4xx -n --line-numbers

# Fail2ban'ın oluşturduğu tüm zincirleri gör
sudo iptables -L -n | grep f2b

# Istatistikleri çek
sudo fail2ban-client status | grep "Number of jail"

# Her jail için ban sayısını özetle
for jail in $(sudo fail2ban-client status | grep "Jail list" | sed 's/.*://;s/,//g'); do
    banned=$(sudo fail2ban-client status $jail | grep "Currently banned" | awk '{print $NF}')
    total=$(sudo fail2ban-client status $jail | grep "Total banned" | awk '{print $NF}')
    echo "$jail: Aktif ban=$banned, Toplam ban=$total"
done

Performans İpuçları

Fail2ban yoğun trafik alan sunucularda zaman zaman CPU spike’ına neden olabilir. Bunu minimize etmek için:

  • backend = systemd kullan: journald üzerinden log okumak dosya polling’den daha verimli.
  • findtime değerini gereğinden uzun tutma: Ne kadar uzun olursa o kadar fazla log satırı memory’de tutulur.
  • Çok sayıda jail yerine daha akıllı filter’lar yaz: 10 jail yerine 4-5 iyi yazılmış jail daha iyi çalışır.
  • dbpurgeage değerini ayarla: Fail2ban SQLite veritabanında ban geçmişini tutar, eski kayıtları temizlemesi için bu değeri ayarla.
[DEFAULT]
dbpurgeage = 648000  ; 7.5 gün

Sık Yapılan Hatalar

Fail2ban kurarken en çok karşılaşılan hatalar:

  • Kendini banlamak: ignoreip içine kendi IP’ni eklemeden yapılandırma test ederken. Çözüm: Her zaman önce ignoreip ayarla.
  • Log path yanlışlığı: Jail’deki logpath gerçek log dosyasına işaret etmiyor. sudo ls -la /var/log/nginx/ ile doğrula.
  • jail.conf üzerinde çalışmak: Güncelleme sonrası tüm değişiklikler gider. Her zaman .local dosyaları kullan.
  • Filter test etmemek: Aktif etmeden önce fail2ban-regex ile test etmemek. Çalışmayan filter anlamsız bir kaynak tüketimidir.
  • maxretry’yi çok düşük tutmak: maxretry = 1 gibi aşırı kısıtlayıcı değerler meşru kullanıcıları da etkiler.

Sonuç

Fail2ban, doğru yapılandırıldığında web sunucularına yönelik otomatik saldırıların büyük çoğunluğunu durduran güçlü bir araç. Kurulumu basit, bakımı az, etkisi ise oldukça yüksek.

Özetleyecek olursam: Her zaman .local dosyalarında çalış, ignoreip ile kendi IP’ni koru, filter’ları aktive etmeden önce fail2ban-regex ile test et ve log formatlarının beklenenle uyuştuğundan emin ol. Recidive jail’i mutlaka aktive et çünkü persistent saldırganları otomatik olarak daha sert cezalandırması büyük kolaylık sağlıyor.

Fail2ban tek başına yeterli bir güvenlik çözümü değil elbette. WAF, mod_security, rate limiting ve düzenli güvenlik taramaları ile birlikte kullanıldığında gerçek anlamda katmanlı bir savunma oluşturursun. Ama bu araçlar arasında fail2ban’ın kurulum/fayda oranı son derece yüksek, bu yüzden her web sunucusunda bulunması gereken temel bir güvenlik katmanı olduğunu düşünüyorum.

Yorum yapın