SMTP DDoS'unu Throttling ile Durdurmak: Bağlantı Katmanında Savunma Zinciri
Bir MX sunucusunu diz çöktüren şey spam içeriği değil, bağlantı katmanıdır. nftables'tan postscreen'e, anvil'den fail2ban'a uzanan bir savunma zinciriyle slowloris, connection flood ve SASL brute-force'u içerik taramasına hiç ulaşmadan kesin.
EvilMail Team27 Temmuz 202613 dk okuma
Dört çekirdekli bir MX düşünün. master.cf içinde smtp servisinin process limiti varsayılan 100'de duruyor. Bir saldırgan 300 farklı IP'den 25/tcp'ye bağlanıyor, EHLO gönderiyor ve sonra susuyor. Veri yok, MAIL FROM yok, sadece açık kalan bir oturum. Yirmi dakika sonra sunucuya bakıyorsunuz:
bash
$ ss -tan state established '( dport = :25 )' | wc -l
100
$ postqueue -p | tail -1
-- 0 Kbytes in 0 Requests.
Kuyruk boş, CPU rölantide, bant genişliği neredeyse sıfır — ama tek bir meşru posta içeri girmiyor. Çünkü 100 process'in hepsi, hiçbir şey söylemeyen 100 yarı açık oturum tarafından rehin alınmış durumda. Bu bir bant genişliği saldırısı değil; process açlığı saldırısı. Ve bu yüzden "spam filtresi kur" tavsiyesi burada işe yaramaz — filtre daha çalışacak bir bağlantı bile bulamıyor.
Doğru refleks, savunmayı bir maliyet piramidi olarak kurmaktır: ucuz olan paketi çekirdekte erken düşür, pahalı olan içerik taramasını en sona sakla. Bir bağlantıyı ne kadar geç elerseniz o kadar çok kaynak harcarsınız. Amaç, her kademede bir öncekinden daha az bağlantıyı bir sonrakine geçirmektir.
Saldırı yüzeyi: neden bağlantı katmanı, içerik değil
Postfix'te her aktif SMTP oturumu bir smtpd process'i tutar. master.cf içindeki servis satırının yedinci alanı (process limit) bu havuzun tavanını belirler; smtp için varsayılan 100'dür. Bir process ne kadar süre meşgulse, havuzdan o kadar uzun süre çıkmaz. anvil ise bu process'lerin arasında oturan, hangi istemci IP'sinin kaç bağlantı/mesaj/alıcı harcadığını bellekte sayan yerleşik muhasebecidir. Sertleştirmenin çoğu, bu iki mekanizmayı ayarlamaktan ibarettir.
Kaynağı tüketen üç ayrı vektör var ve üçü de aynı yerden çözülmez:
Eşzamanlılık tükenmesi (slowloris): Az sayıda IP, çok sayıda oturumu açık tutup process'leri kilitler. Çözüm: timeout kısaltma (Katman 4).
Yeni bağlantı hızı (connection-rate flood): Bir IP saniyede onlarca yeni TCP oturumu açar. Çözüm: nftables + anvil (Katman 1 ve 3).
587 üzerinde SASL brute-force ve TLS handshake CPU tüketimi: Kimlik denemeleri ve pahalı TLS handshake'leri işlemciyi yakar. Çözüm: fail2ban + TLS rate limit (Katman 5 ve 3).
En ucuz paket burada düşer, çünkü conntrack tablosu şişip Postfix'e hiç danışmadan bağlantı reddedilir. Kritik nokta sıralamadır: önce fazlalığı düşüren kuralları yaz, kabul kuralını en sona koy. Kabul kuralını başa koyarsanız per-IP limitler hiç değerlendirilmez.
nft
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
# Per-IP eşzamanlı bağlantı tavanı (25/tcp)
tcp dport 25 ct state new ct count over 20 drop
# Per-IP yeni bağlantı hızı
tcp dport 25 ct state new \
meter smtp_rate { ip saddr limit rate over 30/minute } drop
# Global SYN emniyeti: eşiği aşan yeni bağlantıyı düşür
ct state new tcp dport { 25, 587 } \
limit rate over 60/second burst 20 packets drop
tcp dport { 25, 587 } accept
}
}
Uyarı: eşiği çok kısmayın. Meşru bir MTA reddedilince tekrar dener; NAT arkasındaki bütün bir kurumsal ağ tek IP olarak görünebilir. 25/tcp'de per-IP ~20 eşzamanlı bağlantı makul bir başlangıçtır; 587'de submission trafiği daha gevşek tutulmalıdır, çünkü orada gerçek kullanıcılarınızın istemcileri oturur.
Katman 2 — postscreen ile ön eleme
postscreen, bağlantıyı gerçek bir smtpd process'ine devretmeden önce ucuz testler yapan bir kapıcıdır. Zombi botların büyük kısmını, pahalı SMTP durum makinesine hiç ulaşmadan eler.
İki test asıl işi görür. Pregreet: RFC uyumlu bir istemci, banner'ı görmeden konuşmaz; postscreen banner'ı birkaç saniye geciktirir ve erken konuşan botu yakalar. DNSBL skorlaması: birden fazla kara listeyi ağırlıklandırarak eşiğe göre karar verir.
postscreen_cache_map kritiktir: bir kez temiz çıkan IP önbelleğe alınır, böylece meşru gönderenler her seferinde yeniden testten geçmez. master.cf tarafında postscreen, smtpd pass soketi, dnsblog ve tlsproxy servislerinin tanımlı olması gerekir. Not: postscreen'in derin protokol testleri (bare newline, non-SMTP command) bağlantıyı kesip istemciyi tekrara zorlar; bu retry maliyeti meşru MTA'lar için sorun değildir ama agresif ayarlarsanız ilk teslimatı geciktirir.
Katman 3 — anvil: Postfix'in yerleşik rate limiter'ı
İşin kalbi burasıdır. anvil, anvil_rate_time_unit (varsayılan 60s) penceresinde her istemci IP'sinin davranışını sayar ve tavanı aşanı reddeder. Varsayılanların çoğu ya çok gevşektir ya da tamamen kapalıdır (0 = sınırsız).
smtpd_client_event_limit_exceptions satırı pazarlık konusu değil. Kendi relay'lerinizi, backup MX'inizi ve dahili ağınızı buraya koymazsanız, kendi altyapınız anvil eşiğine ilk çarpan taraf olur. İki nüansı unutmayın: anvil sayaçları bellek içidir ve process başına değil, tek bir anvil daemon'ı üzerinden merkezileşir. Birden fazla MX'iniz varsa her biri kendi anvil'ini ayrı sayar, yani gerçek dünyadaki per-IP toplam limitiniz MX sayısıyla çarpılır. Cluster ölçeğinde mükemmel değildir; farkında olun.
Katman 4 — master.cf process havuzu ve timeout sertleştirme
Slowloris'i asıl burada öldürürsünüz. Process limitini yükseltme dürtüsüne direnin — havuzu büyütmek saldırgana daha çok hedef vermekten başka işe yaramaz. Doğru refleks, bağlantı başına ömrü kısaltmaktır.
smtpd_timeout 300s'den 30s'ye inince, hiçbir şey söylemeyen bir oturum yarım dakikada koparılır. smtpd_junk_command_limit = 3, NOOP/RSET spam'i ile oturumu canlı tutmaya çalışan botu üç komuttan sonra keser. smtpd_error_sleep_time = 5s ise bir tarpit etkisi yaratır: kötü davranan istemciyi her hatada beklettiğiniz için saldırının hızını içeriden düşürürsünüz.
Submission (587) ve smtps (465) portlarında ayrı -o override'ları uygulayın. Orada AUTH zorunlu olduğundan, kimliksiz oturum daha erken kesilmeli:
# master.cf
submission inet n - y - - smtpd
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_connection_rate_limit=10
-o smtpd_timeout=30
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
Değişikliklerden sonra postfix reload yeterli; tam restart gerekmez.
Katman 5 — SASL brute-force ve fail2ban (587/465)
587 ve 465, kimlik denemelerinin yağdığı yerdir. Postfix her başarısız denemeyi loglar:
recidive jail'i, gün içinde defalarca banlanıp geri dönen inatçı IP'leri bir haftalığına susturur. Bunu Dovecot tarafında auth_failure_delay = 5s ile birleştirin — her başarısız kimlikte gelen zorunlu gecikme, brute-force'un deneme hızını tek başına dizginler.
Ölçüm: attığınız taş ürküttüğü kurbağaya değiyor mu
Eşik ayarını körlemesine yapmayın. Anvil saatte bir istatistik loglar; gerçek tavanlarınızın ne olduğunu bu satırlar söyler:
bash
# anvil'in gördüğü en yüksek per-IP değerler
$ grep 'statistics:' /var/log/mail.log
... statistics: max connection rate 34/60s for (smtp:203.0.113.44)
... statistics: max connection count 21 for (smtp:203.0.113.44)
# anvil reddi gerçekleşiyor mu
$ grep 'too many connections' /var/log/mail.log | wc -l
# postscreen cache isabet oranı (PASS OLD = önbellekten)
$ grep -c 'postscreen.*PASS OLD' /var/log/mail.log
$ grep -c 'postscreen.*PASS NEW' /var/log/mail.log
# anlık eşzamanlı bağlantı
$ ss -tan state established '( dport = :25 )' | wc -l
PASS OLD sayısı PASS NEW'den çok yüksekse önbellek çalışıyor ve meşru gönderenleri tekrar tekrar sınamıyorsunuz demektir. max connection count sürekli sizin limitinize dayanıyorsa eşik ya doğru ya da fazla kısık — logdaki reddedilen IP'lerin meşru mu zombi mi olduğuna bakmadan gevşetmeyin.
Deliverability uyarısı: Eşikleri aşırı kısarsanız Gmail ve Outlook gibi büyük gönderenleri cezalandırırsınız. Bunlar tek IP'den yüksek eşzamanlılıkla teslimat yapar ve anvil'e connection flood gibi görünür. Meşru postanın gecikmemesi için postscreen whitelist önbelleği ve smtpd_client_event_limit_exceptions ile bu ağları muaf tutmak şarttır. Port 25 zaten proxy'lenemez — bu yüzden postscreen + anvil kombinasyonu doğru ayarlandığında, pahalı üçüncü parti "DDoS koruması" ürünlerinin çoğunu 25/tcp için gereksiz kılar.
Sertleştirme checklist'i
nftables: 25/tcp için ct count over 20 drop + meter ... limit rate over 30/minute drop; ct state new ... limit rate over 60/second drop global SYN emniyeti; kabul kuralı en sonda.
postscreen:greet_action = enforce, dnsbl_threshold = 3, üç ağırlıklı DNSBL, postscreen_cache_map btree önbellek.