fail2ban nginx ayarı: Ubuntu 24.04'te wp-login.php ve xmlrpc.php kaba kuvvet saldırılarını engelleme
Ubuntu 24.04'te fail2ban nginx ayarıyla wp-login.php ve xmlrpc.php'ye gelen kaba kuvvet isteklerini eklentiye gerek kalmadan sunucu katmanında durdurun. Yazıda limit_req ile istek sınırı, nftables ile engelleme, fail2ban-regex ile filtre testi ve kendinizi kilitlememek için alınacak önlemler adım adım anlatılıyor.
sunucu.com.tr Ekibi10 dk okuma
WordPress sitenizin CPU kullanımı sebepsiz yere tavan yapıyorsa nedeni çoğu zaman wp-login.php ve xmlrpc.php dosyalarına gelen binlerce parola denemesidir. Doğru bir fail2ban nginx ayarı bu istekleri PHP'ye ulaşmadan keser. Böylece eklentiye bağlı kalmadan saldırıyı sunucu katmanında durdurursunuz. Bu rehber Ubuntu 24.04 üzerinde Nginx ve PHP-FPM ile WordPress ya da benzeri bir PHP sitesi çalıştıran sistem yöneticileri için hazırlandı.
wp-login.php ve xmlrpc.php saldırıları sunucuyu neden yorar
Her giriş denemesi PHP-FPM'de bir işçi süreci başlatır, WordPress çekirdeğini yükler ve veritabanında kullanıcı sorgusu çalıştırır. Parola karşılaştırması da bilinçli olarak yavaş tasarlanmış bir hash fonksiyonuyla yapılır. Tek bir istek önemsizdir. Ancak dakikada yüzlerce istek gelince PHP-FPM havuzu dolar ve gerçek ziyaretçiler beklemeye başlar.
xmlrpc.php daha da tehlikelidir, çünkü system.multicall yöntemiyle tek bir HTTP isteğinde çok sayıda parola denenebilir. Güvenlik eklentileri bu saldırıyı yakalayabilir ama eklenti de PHP içinde çalışır, yani yük zaten oluşmuştur. Saldırıyı Nginx ve güvenlik duvarı seviyesinde kesmek çok daha ucuzdur.
Strateji iki katmanlı:
- Nginx
limit_req: Tek bir IP'nin giriş sayfalarına saniyede gönderebileceği istek sayısını sınırlar ve fazlasına PHP çalıştırmadan 429 döner. - fail2ban: Access log'u izler, eşiği aşan IP'yi belirli bir süre nftables ile tamamen engeller.
Ön koşullar
- Ubuntu 24.04 LTS, Nginx ve PHP-FPM (Ubuntu 24.04 deposunda PHP 8.3 gelir).
- sudo yetkili bir kullanıcı.
- Nginx access log'unun varsayılan
combinedbiçiminde olması. - Sağlayıcınızın web konsolu ya da VNC erişimi. SSH bağlantınız koparsa sunucuya buradan girersiniz.
Kendinizi kilitlemeyin: Bu rehberde güvenlik duvarına kural ekleyen bir araç kuruyorsunuz. Ubuntu'nun fail2ban paketi SSH için de bir jail'i varsayılan olarak etkin getirir. Kendi yönetim IP'nizi
ignoreipsatırına eklemeden kuralları devreye alırsanız SSH erişiminizi kaybedebilirsiniz. UFW kullanıyorsanız etkinleştirmeden önceufw allow OpenSSHile SSH'ye izin verin. Değişikliklerden önce konsol erişiminizin çalıştığını doğrulayın.
Kendi sunucunuz yoksa aynı adımları bir bulut sunucu üzerinde de uygulayabilirsiniz.
Saldırıyı Nginx access.log'da tespit etme
Önlem almadan önce saldırının gerçekten var olduğunu ve hangi dosyayı hedeflediğini görün. Aşağıdaki komut combined biçimindeki log'da wp-login.php ve xmlrpc.php'ye POST gönderen IP'leri sayar. Ubuntu 24.04 ve Debian 12 için hazırlandı. Yalnızca okuma yapar, geri alınacak bir değişiklik yoktur.
# combined log biçiminde: $1 = istemci IP, $6 = "METOD, $7 = istek yolu, $9 = durum kodu
sudo awk '$6 == "\"POST" && $7 ~ /(wp-login|xmlrpc)\.php/ {print $1, $7, $9}' /var/log/nginx/access.log \
| sort | uniq -c | sort -rn | head -20
Çıktıda aynı IP'nin wp-login.php için yüzlerce kez 200 aldığını görüyorsanız bu başarısız giriş demektir. WordPress başarılı girişte 302 ile yönlendirir, başarısız girişte formu tekrar 200 ile gösterir. xmlrpc.php ise hata olsa bile genellikle 200 döner. Filtreyi bu davranışa göre yazacağız.
Nginx limit_req ile giriş sayfalarına istek sınırı koyma
İlk katman Nginx'te. limit_req_zone yönergesi http bağlamında tanımlanmalıdır. Ubuntu'nun nginx.conf dosyası /etc/nginx/conf.d/*.conf dosyalarını http bloğu içinde yüklediği için bu dizin uygun yerdir.
/etc/nginx/conf.d/wp-giris-limit.conf dosyasını oluşturun:
# Her istemci IP'si için dakikada 6 istek; durum bilgisi 10 MB'lık paylaşımlı bellekte tutulur
limit_req_zone $binary_remote_addr zone=wpgiris:10m rate=6r/m;
# Sınırı aşan isteklere 503 yerine 429 dön; fail2ban bu kodu da yakalayacak
limit_req_status 429;
Ardından sitenizin server bloğuna (ör. /etc/nginx/sites-available/example.com) giriş sayfası için ayrı bir location ekleyin. Genel location ~ \.php$ bloğundan önce, tam eşleşme olarak tanımlanması gerekir:
location = /wp-login.php {
# 3 isteklik kısa bir patlamaya izin ver, sonrası 429
limit_req zone=wpgiris burst=3 nodelay;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
# Jetpack ya da mobil uygulama kullanmıyorsanız XML-RPC'yi tamamen kapatın
location = /xmlrpc.php {
deny all;
}
Jetpack veya WordPress mobil uygulaması gibi XML-RPC'ye ihtiyaç duyan bir bileşen kullanıyorsanız deny all yerine wp-login.php bloğundaki gibi limit_req uygulayın. Değişikliği uygulamadan önce sözdizimini mutlaka kontrol edin:
# Önce sözdizimi testi; yalnız başarılıysa yeniden yükle
sudo nginx -t && sudo systemctl reload nginx
Geri almak için eklediğiniz location bloklarını silip conf.d dosyasını taşıyın ve aynı komutu tekrar çalıştırın. Değerler hakkında ayrıntılı bilgi Nginx limit_req modülü belgesinde yer alıyor.
limit_req tek başına yeterli değildir. Saldırgan dakikada 6 istek sınırına uyarak saatlerce deneme yapabilir. Kalıcı çözüm, tekrar eden IP'yi tamamen engelleyen fail2ban'dır.
Ubuntu 24.04'te fail2ban nginx kurulumu ve jail yapılandırması
Aşağıdaki betik şunları yapar:
fail2banvenftablespaketlerini kurar.jail.daltında genel ayarları içeren bir dosya yazar:ignoreipve nftables banaction.- WordPress filtresini ve jail'i oluşturur.
- Yapılandırmayı test eder, ardından servisi yeniden başlatır.
Ubuntu 24.04 ve Debian 12 için hazırlandı. İdempotent çalışır: dosya içeriği aynıysa dokunmaz, farklıysa eski dosyayı tarih damgalı bir yedeğe kopyalar. Var olan jail.local dosyanıza dokunmaz. Çalıştırmadan önce YONETIM_IP değerini kendi ofis veya VPN adresinizle değiştirin. Örnekteki 198.51.100.0/24 belge amaçlı bir adrestir.
#!/usr/bin/env bash
# fail2ban + nftables ile wp-login.php ve xmlrpc.php koruması
# Ubuntu 24.04 ve Debian 12 için hazırlandı. root ya da sudo ile çalıştırın.
set -euo pipefail
# ---- Değiştirilecek değerler ----
YONETIM_IP="198.51.100.0/24" # ORNEK: kendi sabit IP'niz ya da VPN aralığınız
NGINX_LOG="/var/log/nginx/access.log" # Site başına ayrı log kullanıyorsanız bu yolu değiştirin
# ---------------------------------
TEMEL_DOSYA="/etc/fail2ban/jail.d/00-temel.local"
JAIL_DOSYA="/etc/fail2ban/jail.d/nginx-wordpress.local"
FILTRE_DOSYA="/etc/fail2ban/filter.d/nginx-wordpress.conf"
if [[ ${EUID} -ne 0 ]]; then
echo "Bu betik root ya da sudo ile çalıştırılmalı." >&2
exit 1
fi
if [[ ! -f "${NGINX_LOG}" ]]; then
echo "Log dosyası bulunamadı: ${NGINX_LOG}" >&2
exit 1
fi
# Paketler zaten kuruluysa apt yalnızca güncel olduklarını doğrular
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y fail2ban nftables
# Standart girdiden gelen içeriği hedef dosyaya yazar.
# İçerik aynıysa dokunmaz; farklıysa önce eski dosyanın yedeğini alır.
yaz() {
local hedef="$1" gecici
gecici="$(mktemp)"
cat > "${gecici}"
if [[ -f "${hedef}" ]] && cmp -s "${gecici}" "${hedef}"; then
rm -f "${gecici}"
echo "Değişiklik yok: ${hedef}"
return 0
fi
if [[ -f "${hedef}" ]]; then
cp -a "${hedef}" "${hedef}.yedek-$(date +%Y%m%d%H%M%S)"
fi
install -m 0644 "${gecici}" "${hedef}"
rm -f "${gecici}"
echo "Yazıldı: ${hedef}"
}
# 1) Genel ayarlar: beyaz liste ve nftables tabanlı engelleme
yaz "${TEMEL_DOSYA}" <<EOF
[DEFAULT]
# Bu adresler hiçbir jail tarafından engellenmez. Kendi IP'nizi mutlaka ekleyin.
ignoreip = 127.0.0.1/8 ::1 ${YONETIM_IP}
banaction = nftables-multiport
banaction_allports = nftables-allports
EOF
# 2) Filtre: wp-login.php ve xmlrpc.php'ye giden, 200/403/429 alan POST istekleri
# Not: fail2ban tarih kısmını satırdan çıkarır, bu yüzden köşeli parantez içi boş da eşleşmeli.
yaz "${FILTRE_DOSYA}" <<'EOF'
[Definition]
failregex = ^<HOST> \S+ \S+ \[[^\]]*\] "POST /(?:[^" ]*/)?(?:wp-login|xmlrpc)\.php[^"]*" (?:200|403|429)
ignoreregex =
EOF
# 3) Jail: 10 dakikada 5 eşleşme olursa 1 saat engelle
yaz "${JAIL_DOSYA}" <<EOF
[nginx-wordpress]
enabled = true
filter = nginx-wordpress
port = http,https
logpath = ${NGINX_LOG}
# Dosya tabanlı log için backend'i açıkça belirt; systemd backend dosyayı okumaz
backend = auto
maxretry = 5
findtime = 10m
bantime = 1h
EOF
# 4) Yapılandırmayı test et; hata varsa servisi yeniden başlatmadan çık
fail2ban-client -t
systemctl enable fail2ban
systemctl restart fail2ban
sleep 2
fail2ban-client status nginx-wordpress
Betiği wp-fail2ban.sh adıyla kaydedip sudo bash wp-fail2ban.sh ile çalıştırabilirsiniz.
Geri alma: /etc/fail2ban/jail.d/00-temel.local, /etc/fail2ban/jail.d/nginx-wordpress.local ve /etc/fail2ban/filter.d/nginx-wordpress.conf dosyalarını başka bir dizine taşıyın ya da varsa .yedek- uzantılı kopyalarını geri koyun. Ardından sudo systemctl restart fail2ban çalıştırın. fail2ban yeniden başlarken kendi nftables tablosunu temizler, bu yüzden elle kural silmeniz gerekmez.
Filtrenin mantığı
Filtre üç durum kodunu yakalar:
- 200:
wp-login.php'de başarısız giriş,xmlrpc.php'de her türlü çağrı. - 429:
limit_reqsınırına takılan istek. - 403:
deny allile kapattığınızxmlrpc.php'yi zorlamaya devam eden istemci.
Başarılı girişler 302 döndüğü için kendi oturum açmalarınız sayılmaz. GET istekleri de sayılmaz, yani giriş sayfasını açmak tek başına engelleme sebebi değildir.
banaction olarak nftables kullanma ve kuralları doğrulama
Ubuntu 24.04'te iptables komutu arka planda nftables'a çevrilir. Yine de fail2ban'ın doğrudan nftables kullanması kural yönetimini sadeleştirir. nftables-multiport eylemi engellenen IP'leri bir set'e ekler ve yalnız jail'de tanımlı portları (80 ve 443) kapatır. SSH portu bu jail tarafından engellenmez.
Durumu ve kuralları şu komutlarla kontrol edin:
# Etkin jail listesi
sudo fail2ban-client status
# WordPress jail'inin sayaçları ve engellenen IP'ler
sudo fail2ban-client status nginx-wordpress
# fail2ban'ın oluşturduğu nftables tablosu ve içindeki set'ler
sudo nft list table inet f2b-table
Yanlışlıkla engellenen bir adresi kaldırmak için (örnekteki adres belge amaçlıdır):
sudo fail2ban-client set nginx-wordpress unbanip 203.0.113.25
fail2ban-regex ile filtreyi test etme
Jail'in hiç IP engellememesinin en sık nedeni, filtrenin log satırlarıyla eşleşmemesidir. fail2ban-regex aracı filtreyi gerçek log üzerinde çalıştırır ve kaç satırın eşleştiğini gösterir:
# Filtreyi mevcut access log üzerinde dene; özet satırlarında "matched" sayısına bakın
sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-wordpress.conf
Çıktıdaki Failregex: N total satırı sıfırsa iki ihtimal vardır: log'da gerçekten POST isteği yoktur ya da log biçiminiz combined değildir. İlk adımdaki awk komutu eşleşme gösterip fail2ban-regex sıfır döndürüyorsa sorun biçimdedir.
Uçtan uca test için sunucuya ait olmayan ve ignoreip listesinde bulunmayan bir makineden birkaç yanlış giriş gönderin. Bu makinenin IP'si bir saat boyunca engellenecektir:
# Test makinesinden çalıştırın; example.com yerine kendi alan adınızı yazın
for i in $(seq 1 8); do
curl -s -o /dev/null -w "%{http_code}\n" \
-X POST -d "log=test&pwd=yanlis" https://example.com/wp-login.php
done
İlk istekler 200, sonrakiler 429 döner. Eşik aşıldıktan sonra bağlantı zaman aşımına uğrar. Sunucuda sudo tail -f /var/log/fail2ban.log ile Ban satırını izleyebilirsiniz.
Sık yapılan hatalar
JSON ya da özel log biçimi
Log'u Loki veya Elasticsearch'e göndermek için log_format ile JSON biçimine geçtiyseniz yukarıdaki regex hiçbir satırı yakalamaz. İki seçenek var: JSON biçimine uygun ayrı bir filtre yazmak ya da fail2ban için ikinci bir access_log satırıyla combined biçimli bir log daha tutmak. İkincisi daha az hataya açıktır.
Yanlış logpath
Her site için ayrı log kullanıyorsanız (ör. /var/log/nginx/example.com.access.log) jail'deki logpath bu dosyayı göstermelidir. Birden fazla dosyayı satır satır yazabilir ya da /var/log/nginx/*access.log gibi joker karakter kullanabilirsiniz. CloudPanel gibi panellerde loglar site kullanıcısının dizinindedir. Hazır panelli bir web sitesi sunucusu kullanıyorsanız log yolunu panelin site ayarlarından kontrol edin.
Backend'in systemd olarak kalması
Ubuntu ve Debian'da SSH jail'i journald'dan okuyacak biçimde ayarlanabilir. Bu ayar [DEFAULT] bölümüne taşınırsa dosya tabanlı jail'ler log dosyasını hiç okumaz. Betikte jail'e backend = auto yazmamızın nedeni budur.
Cloudflare veya ters vekil arkasında gerçek IP
Site Cloudflare arkasındaysa Nginx log'unda ziyaretçinin değil Cloudflare'in IP'si görünür. Bu durumda fail2ban Cloudflare sunucularını engeller ve tüm siteyi erişilemez hale getirebilir. Önce Nginx'te gerçek IP'yi geri kazanın:
# /etc/nginx/conf.d/cloudflare-gercek-ip.conf
# Güncel aralıklar: https://www.cloudflare.com/ips/ (listeyi oradan eksiksiz kopyalayın)
set_real_ip_from 173.245.48.0/20;
# ... diğer Cloudflare IPv4 ve IPv6 aralıkları ...
real_ip_header CF-Connecting-IP;
Bu, log'daki IP'yi düzeltir. Ancak trafik hâlâ Cloudflare üzerinden geldiği için sunucudaki nftables kuralı saldırganı durdurmaz. Engellemenin işe yaraması için fail2ban'ın Cloudflare API'sini çağıran cloudflare eylemini kullanmanız ya da kuralı Cloudflare tarafında WAF ile tanımlamanız gerekir. limit_req ise gerçek IP'ye göre çalışmaya devam eder.
ignoreip'i unutmak
Ofisinizdeki herkes aynı NAT IP'sinden çıkıyorsa birkaç çalışanın parola hatası tüm ofisi bir saat siteden uzak tutabilir. Sabit ofis IP'nizi ignoreip satırına ekleyin. SSH tarafında ek sertleştirme için OpenSSH yamalarını doğrulama ve sshd_config sertleştirme rehberimize göz atabilirsiniz.
fail2ban mı CrowdSec mi
fail2ban tek bir sunucuda, yerel log üzerinden çalışır ve her sunucu kendi engelleme listesini ayrı tutar. Bir veya birkaç sunucunuz varsa yeterli ve sadedir.
CrowdSec ise algılama ile engellemeyi ayırır. Merkezi bir ajan logları analiz eder, "bouncer" adı verilen bileşenler de güvenlik duvarı, Nginx veya Cloudflare tarafında kararı uygular. Topluluk tarafından paylaşılan kötü IP listelerinden de yararlanabilirsiniz. Kurulum ve bouncer seçenekleri için CrowdSec'in kendi resmi belgelerine bakabilirsiniz.
Geçişi düşünmek için makul işaretler şunlar: aynı saldırganın sitelerinizi barındıran birden fazla sunucuyu sırayla denemesi, engelleme kararlarını tek yerden yönetme ihtiyacı ve Cloudflare ile sunucu arasında ortak bir engelleme listesi isteği. Tek sunuculu bir WordPress kurulumu için fail2ban ile limit_req birlikteliği genellikle yeterlidir.
Sık sorulan sorular
xmlrpc.php'yi tamamen kapatmak siteyi bozar mı?
Çoğu sitede bozmaz. Jetpack, WordPress mobil uygulaması ve bazı eski uzaktan yayınlama araçları XML-RPC kullanır. Bunlardan birini kullanıyorsanız deny all yerine limit_req uygulayın. Bu durumda fail2ban filtresi 200 ve 429 yanıtlarını yakalamaya devam eder.
bantime ve maxretry için hangi değerler uygun?
Başlangıç için 10 dakikada 5 deneme ve 1 saat engelleme dengeli bir seçimdir. Aynı IP'ler sürekli geri dönüyorsa fail2ban'ın bantime.increment = true ayarıyla tekrar eden saldırganların engelleme süresini otomatik artırabilirsiniz. Çok düşük maxretry parolasını unutan gerçek kullanıcıları da engeller.
UFW kullanıyorum, nftables banaction çakışır mı?
Ubuntu 24.04'te UFW de nftables altyapısı üzerinde çalışır. fail2ban kendi f2b-table tablosunu oluşturur ve UFW kurallarını değiştirmez. Engellenen IP'yi fail2ban tablosu düşürür, diğer trafik UFW kurallarına göre işlenir. UFW'yi ilk kez etkinleştirecekseniz önce SSH'ye izin vermeyi unutmayın.
fail2ban yeniden başlatılınca engeller kaybolur mu?
fail2ban engelleri kendi SQLite veritabanında tutar ve servis yeniden başladığında süresi dolmamış engelleri geri yükler. nftables tablosu yeniden oluşturulur. Sunucuyu yeniden başlattıktan sonra fail2ban-client status nginx-wordpress ile listeyi kontrol edebilirsiniz.
Eklenti kullanmaya devam etmeli miyim?
Sunucu katmanındaki koruma yükü keser, eklenti ise iki aşamalı doğrulama gibi uygulama içi özellikler ekler. İkisi birbirini tamamlar. Ancak eklentinin kendi giriş kilitlemesini çok sıkı ayarlamanız gerekmez, çünkü saldırı trafiğinin büyük kısmı PHP'ye hiç ulaşmayacaktır.
Sonuç
limit_req her isteği PHP'ye ulaşmadan sınırlar, fail2ban ise inatçı IP'leri nftables ile tamamen keser. Sıralama önemlidir: önce ignoreip ile kendi erişiminizi güvenceye alın, ardından filtreyi fail2ban-regex ile doğrulayın ve en son başka bir makineden uçtan uca test yapın. Cloudflare arkasındaysanız engellemenin Cloudflare tarafında yapılması gerektiğini unutmayın. Bu ayarlar devreye girdikten sonra wp-login.php ve xmlrpc.php kaynaklı CPU dalgalanmalarının belirgin biçimde azalması gerekir.
- #fail2ban
- #nginx
- #wordpress
- #kaba kuvvet
- #nftables
- #ubuntu 24.04
- #xmlrpc

