fail2ban ile Postfix ve Dovecot AUTH Brute-Force Saldırılarını IP Bazında Durdurmak
SASL LOGIN authentication failed satırlarının saniyelik akışı her posta yöneticisinin tanıdığı bir manzaradır. fail2ban'i "kur ve unut" klişesinden çıkarıp doğru log kaynağı, kalibre edilmiş eşikler, recidive katmanı ve nftables ile üretim seviyesinde kuran pratik bir reçete.
EvilMail Team25 Temmuz 202612 dk okuma
Posta sunucusunun mail.log'una tail -f ile bakın; birkaç saniye içinde şu manzarayı görürsünüz:
Bu dağıtık bir credential stuffing ve kullanıcı numaralandırma (user enumeration) operasyonudur. Botnet'ler zayıf SMTP AUTH ve IMAP parolalarını, açık relay'leri ve var olmayan kullanıcıları binlerce IP üzerinden tarar.
iptables -A INPUT -s <ip> -j DROP
kuralını elle yazmak bu ritimde çalışmaz — dakikada onlarca yeni kaynak IP görürsünüz ve kuralları ölçekleyemezsiniz. fail2ban tam da bu döngüyü otomatikleştirir:
log satırını bir filtre regex'iyle eşleştirir, bir zaman penceresinde tekrarı sayar, eşik aşılınca IP'yi güvenlik duvarına ban olarak yazar.
evilmail.pro tarafında port 25/587/465 ve IMAP/POP3 auth yüzeyi kesintisiz taranır; bu bir istisna değil, herkese açık bir altyapının temel durumudur. Aşağıdaki reçete fail2ban'i pasif bir varsayılan kurulumdan gerçekten koruyan bir katmana dönüştürür.
Saldırı yüzeyi: hangi portlar, hangi loglar, hangi imzalar
Doğru filtreyi yazmadan önce neyi savunduğunuzu bilmeniz gerekir. Postfix'in üç girişi ve Dovecot'un servisleri farklı log imzaları üretir:
Port 25 (inbound MTA): Sunucular arası posta teslimi. Burada AUTH açık olmamalıdır — SASL kimlik doğrulaması yalnızca submission portlarına aittir. Port 25'te SASL LOGIN denemesi görüyorsanız ya yanlış yapılandırma vardır ya da bot doğrudan MTA'yı zorluyordur.
Port 587 (submission, STARTTLS) ve 465 (submissions, implicit TLS): Gerçek kullanıcıların posta gönderdiği yer. Brute-force'un asıl hedefi.
Port 143/993 (IMAP/IMAPS) ve 110/995 (POP3/POP3S): Dovecot'un mailbox erişim portları. Parola denemeleri buraya yağar.
Dovecot auth: auth: Info: passwd-file(user,IP): unknown user
Kritik ayrım: port 25 üzerindeki AUTH denemeleri ile submission portlarındaki denemeleri karıştırmayın. En temiz mimari, master.cf'te 587/465'i AUTH için açmak ve main.cf'te port 25 için smtpd_sasl_auth_enable = no bırakmaktır. Böylece port 25'te gördüğünüz her AUTH denemesi tanımı gereği kötü niyetli olur.
fail2ban mimarisi: jail, filter, action nasıl bağlanır
fail2ban üç parçadan oluşur; bu parçaların nasıl birleştiğini anlamadan doğru yapılandırma yazamazsınız:
filter.d/*.conf** — failregex ve datepattern içerir. Log satırını eşleştiren düzenli ifadeler burada yaşar (postfix-sasl.conf, dovecot.conf).
`jail.local` — backend, findtime, maxretry, bantime, action ayarları. `jail.conf`'u asla düzenlemeyin — paket güncellemesinde üzerine yazılır. Tüm özelleştirme jail.local'a gider.
action.d/*.conf** — banlamanın nasıl uygulanacağı (nftables/iptables/ipset).
Modern journald tabanlı sistemlerde backend = systemd kullanın; fail2ban logları dosyadan değil doğrudan journal'dan okur, bu da log rotasyonu senkron sorunlarını ortadan kaldırır.
Deploy etmeden önce filtrenizi mutlaka canlı log'a karşı test edin. fail2ban-regex kaç satırın eşleştiğini söyler:
Çıktının Matched: N satırı sıfırsa filtreniz çalışmıyordur. Bunu jail'i etkinleştirmeden önce görmek, saatlerce süren sessiz bir başarısızlığı önler.
Postfix jail'lerini kurmak: submission ve enumeration
İki Postfix jail'i devreye alıyoruz: postfix-sasl (AUTH brute-force) ve postfix (RCPT reject / enumeration). Kritik nokta eşik seçimidir. Başlangıç değerleri: maxretry = 4, findtime = 10m, bantime = 1h.
Neden maxretry = 1 değil? Çünkü mobil kullanıcılar parola değiştirir, IMAP istemcileri eski kimlik bilgisiyle birkaç kez bağlanmayı dener. 1 deneme = ban politikası meşru kullanıcıyı yakalar (false positive). maxretry = 10 ise çok gevşek — bot rahatça 9 deneme yapar. 3–5 arası pratik dengedir.
Ama dikkat: dağıtık botnet her IP'den yalnızca 1–2 deneme yapar. Tek başına postfix-sasl jail'i findtime penceresinde maxretry'a asla ulaşamaz, çünkü saldırgan bir sonraki IP'ye geçer. Bu yüzden bu jail'i recidive katmanı ve postscreen ile birlikte düşünmek zorundasınız — tek jail dağıtık saldırıya karşı yetmez.
postfix jail'indeki mode = aggressive, RCPT reject ve unknown user satırlarını da yakalar; enumeration yapan botları erken durdurur.
Dovecot jail'i ve auth soket detayı
Dovecot'un daha zengin log üretmesi için 10-logging.conf içinde auth_verbose = yes verin (mail_debug'a gerek yok, o yalnızca gürültü üretir). Filtreniz IMAP, POP3 ve Dovecot SASL'ın Postfix'e auth verdiği submission satırlarını birlikte yakalamalı. Aradığınız imza:
Dovecot yerleşik bir savunma da sunar: auth_failure_delay (varsayılan 2 saniye). Her başarısız denemeden sonra yanıtı geciktirir ve brute-force hızını fiziksel olarak düşürür. Bu, fail2ban'i tamamlayan bir katmandır — IP banlanana kadar geçen sürede saldırganın deneme sayısını sınırlar.
recidive: tekrar edeni katlanarak cezalandır
recidive jail basit ama etkilidir: fail2ban'in kendi log'unu okur ve "son 1 günde 5 kez banlanan" IP'yi çok daha uzun süre — bir hafta — banlar. postfix-sasl bir IP'yi 1 saatliğine banlar, ban düşer, bot geri gelir, tekrar banlanır; beşinci kezden sonra recidive devreye girer ve o IP'yi haftalarca tüm portlarda kilitler.
Posta altyapısında bu kritiktir, çünkü ısrarcı botnet düğümleri günlerce aynı IP havuzundan döner. bantime = 1w, findtime = 1d, maxretry = 5 iyi bir başlangıçtır.
Banların sunucu yeniden başladığında kaybolmaması için persistency şart:
SQLite veritabanı aktif ban listesini diskte tutar; systemctl restart fail2ban sonrası tüm banlar geri yüklenir.
nftables/ipset ile ölçek: binlerce IP'yi ucuza banlamak
fail2ban'in varsayılan iptables action'ı her ban için ayrı bir kural ekler. 5.000 banlı IP'niz olduğunda bu 5.000 ayrı kural demektir ve çekirdek her gelen paket için bu listeyi lineer tarar — CPU'nuz düşük seviyeli bir DoS'a girer. Çözüm set tabanlı banlamadır:
nftables set'i (eski sistemlerde ipset) IP'leri hash tabanlı bir yapıda tutar; lookup O(1)'dir, set'te 50.000 IP olması paket işleme hızını değiştirmez. Aktif ban set'ini görmek için:
bash
nft list ruleset | grep -A20 f2b
# veya doğrudan jail'in set'i:
nft list set inet f2b-table addr-set-postfix-sasl
IPv6 tarafında bir incelik var: bir ISS tipik olarak tek bir müşteriye tam bir /64 subnet atar. Yani IPv6 saldırganını tek bir /128 adresle banlamak işe yaramaz — bot aynı /64 içinde adres değiştirir. nftables action'ınızı IPv6 için /64 blok politikasıyla yapılandırın; IPv4'te tek /32 yeterlidir.
Doğrulama, izleme ve false-positive'den kaçınma
Ban yazdığını görmek "gerçekten çalışıyor" demek değildir. Durumu düzenli olarak okuyun:
bash
fail2ban-client status # tüm jail'ler
fail2ban-client status postfix-sasl # bu jail'de kaç ban, hangi IP'ler
fail2ban-client status recidive
fail2ban-client banned # tüm jail'lerdeki toplam banlı IP
ignoreip listesi false-positive'e karşı ilk savunmanızdır: kendi yönetim IP'niz, monitoring sistemleri ve bilinen-iyi SMTP relay'leriniz mutlaka burada olmalı. Mobil bir kullanıcı yanlışlıkla banlandığında hızlı kurtarma:
bash
fail2ban-client set postfix-sasl unbanip 203.0.113.5
Bir IP'yi manuel banlamak gerekirse:
bash
fail2ban-client set postfix-sasl banip 203.0.113.5
Yapılandırma değişikliğinden sonra tam yeniden başlatma yerine reload tercih edin — mevcut banları korur:
bash
fail2ban-client reload postfix-sasl
Trend izlemek için fail2ban-client banned çıktısını bir cron ile Prometheus/Grafana'ya besleyebilirsiniz; ban sayısındaki ani sıçrama ya yeni bir kampanyayı ya da bir filtre regresyonunu gösterir.
Deploy öncesi ve sonrası checklist
jail.conf'a dokunma; tüm ayarlar jail.local'da. Değişiklikten önce yedek al.
Her filtreyi fail2ban-regex ile canlı log'a karşı test et — Matched sayısı sıfır olmasın.
ignoreip'e yönetim IP'ni, monitoring'i ve known-good relay'leri ekle.
recidive jail aktif ve logpath = /var/log/fail2ban.log doğru.
banaction set tabanlı (nftables-multiport) — asla düz iptables tek kural değil.
dbfile tanımlı; systemctl restart fail2ban sonrası banların korunduğunu doğrula.
Port 25'te smtpd_sasl_auth_enable = no; AUTH yalnızca 587/465'te.
postscreen (postscreen_dnsbl_sites = zen.spamhaus.org) ve reject_unknown_recipient_domain açık — fail2ban'i tamamlıyor.
fail2ban-client status ile jail sayısını, gerçek bir ban gözlemini ve unban prosedürünü doğrula.
Bu bir "kur ve unut" işi değil. Botnet davranışı mevsimsel değişir; mail.log'a ayda bir gerçekten bakıp findtime/maxretry/bantime eşiklerini gördüğünüz saldırı ritmine göre yeniden kalibre etmek, fail2ban kurulumunuz ile bir dosyada çürüyen ölü yapılandırma arasındaki farktır.