SPF Flattening: 10 DNS Sorgu Limitini Aşmadan PermError'dan Kurtulmak
SPF kaydındaki 10 DNS sorgu limitini aşınca tüm gönderiminiz sessizce PermError'a düşer. Include düzleştirme (flattening) bunu çözer ama otomasyon olmadan bir zaman bombasıdır. Önce sorguyu ölçmeyi, doğru budamayı ve düzleştirmeyi ne zaman gerçekten yapmanız gerektiğini anlatıyoruz.
EvilMail Team17 Temmuz 202611 dk okuma
Sorun: 11. sorgu SPF'inizi tamamen çökertir
Pazarlama ekibi altıncı ESP'yi devreye alır, kimse SPF kaydına bakmaz. Kayıt hâlâ ~all ile bitiyor, sözdizimi kusursuz, dig temiz TXT döndürüyor. Ama o gün gönderilen kampanyaların yarısı Promotions yerine Spam'e düşmeye başlar. Sebep sözdiziminde değil: kaydı çözümlemek için gereken DNS sorgusu sayısı sessizce 10'u aştı.
RFC 7208, bir SPF kaydını değerlendirirken yapılabilecek DNS sorgusunu 10 ile sınırlar. Bu bir öneri değil, alıcının uygulaması gereken katı bir tavan. On birinci sorguyu tetiklediğiniz anda değerlendirme durur ve sonuç pass, softfail ya da neutral değil — PermError olur.
Kritik yanlış anlama tam burada: PermError, "fail" demek değildir. "Bu politikayı değerlendiremiyorum" demektir. Gmail, Outlook ve büyük alıcılar PermError gören bir SPF'i geçersiz sayar. Sonuç DMARC katmanına taşınır: DMARC'ın SPF ayağı
pass
üretemez, hizalama (alignment) kurulamaz, ve
p=none
bile olsa itibar sinyaliniz çöker. Kayıt "var" ama işe yaramaz durumdadır. Üstelik hiçbir bounce, hiçbir hata logu görmezsiniz — sadece açılma oranı düşer.
SPF sorguları nasıl sayılır (ve neyi saymaz)
Kural mekanizma bazında nettir. Şu altı mekanizmanın her biri 1 DNS sorgusu harcar:
include
a
mx
ptr
exists
redirect modifier'ı
Şunlar hiç saymaz:
ip4
ip6
all
Düzleştirmenin bütün mantığı bu satırda gizli: include'ları, sorgu maliyeti sıfır olan ip4/ip6 bloklarına çevirmek.
En çok gözden kaçan nokta, include'ların özyinelemeli sayılmasıdır. include:_spf.google.com kaydınızda tek satır görünür, ama Google bu kaydı içeride _netblocks.google.com, _netblocks2.google.com ve _netblocks3.google.com'a açar. Yani o tek satır 4 sorgu harcar: birincisi _spf.google.com'un kendisi, üçü iç içe (nested). Kaydınıza altı include yazdığınızda gördüğünüz "6" sayısı, gerçekte 15-16'ya karşılık gelebilir.
İki ek limit daha vardır ve ikisi de PermError üretir:
Void lookup limiti: NXDOMAIN veya boş yanıt dönen en fazla 2 sorguya izin verilir. Silinmiş bir alt alanı işaret eden ölü bir include, sessizce bu sayacı yer.
`mx` ve `ptr` başına 10 A/AAAA çözümü: Bir mx mekanizması 10'dan fazla MX kaydını çözmek zorunda kalırsa, yine PermError.
ptr mekanizmasına gelince: kullanmayın. Yavaş, ters DNS'e bağımlı, güvenilmez ve void lookup riskini artırıyor. RFC 7208 bile açıkça önermiyor. Kayıtlarınızda ptr görürseniz, silmek neredeyse her zaman doğru harekettir.
Mevcut kaydınızı ölçün: kaç lookup harcıyorsunuz
Düzleştirmeye geçmeden önce sayının gerçekten 10'u aştığını kanıtlamanız gerekir. Çoğu "SPF too many lookups" vakası aslında şişkinlikten ibarettir, gerçek bir kapasite sorunundan değil.
Elle saymak yerine otomatik bir doğrulayıcı kullanın. checkdmarc (Python CLI) ham lookup sayısını, void lookup'ları ve sözdizimi hatalarını tek komutta verir:
Düzleştirme (flattening), her include satırını, o include'ın o an çözümlediği tüm ip4/ip6 CIDR bloklarıyla birebir değiştirmektir. Sorgu maliyeti yüksek mekanizmaları, maliyeti sıfır olan literal IP bloklarıyla ikame edersiniz.
Burada iki fiziksel sınıra çarpacaksınız. Bir TXT string'i en fazla 255 karakter olabilir; düzleştirilmiş bir kayıt kolayca bunu aşar. Çözüm, kaydı birden çok tırnaklı parçaya bölmektir — DNS bu parçaları otomatik birleştirir:
İkinci sınır: tek bir TXT yanıtının pratikte 512 baytlık klasik UDP çerçevesine sığması tercih edilir. Aşarsanız çözümleyiciler TCP'ye ya da EDNS'e düşer; çalışır ama bazı katı ağ ortamlarında sorun çıkarır. Kayıt yüzlerce CIDR'e ulaşıyorsa, bu tek başına düzleştirmenin yanlış araç olduğunun sinyalidir.
Düzleştirdiğinizde ~all yerine -all'a geçmeyi de değerlendirin. Artık kaydınız gönderen tüm IP'leri açıkça listeliyor; belirsizlik bırakmanın gerekçesi zayıflar. Ama bunu ancak listenin eksiksiz ve güncel olduğundan eminseniz yapın — ki bu bizi bir sonraki bölüme getiriyor.
Neden manuel düzleştirme bir zaman bombasıdır
Google'ın resmi dokümantasyonu açık: "SPF kaydınızda Google'ın IP aralıklarını değil, _spf.google.com include'ını kullanın; bu aralıklar önceden haber verilmeden değişir." Microsoft, spf.protection.outlook.com için aynı şeyi söyler. Düzleştirme, bu sağlayıcıların size açıkça "yapmayın" dediği şeyi yapmaktır.
Include'ın var oluş amacı canlı bir gösterge (indirection) olmasıdır: sağlayıcı yeni bir /24 eklediğinde kendi SPF kaydını günceller, sizinki otomatik takip eder. Düzleştirilmiş kayıt donuktur. Sağlayıcı yeni bir gönderim bloğu devreye aldığı gün, o bloktan çıkan mailleriniz SPF'ten geçemez — ve bunu hiçbir hata üretmeden yapar. Kayıt teknik olarak geçerlidir, sadece artık eksiktir.
Buna TTL ve yayılma gecikmesini de ekleyin: yüksek TTL'li donuk bir kayıt, sağlayıcı IP'lerini değiştirdiğinde günlerce bayat kalabilir. Sonuç kaçınılmaz: manuel düzleştirme, arkasına yenileme otomasyonu koymadığınız sürece savunulamaz. "Bir kez düzleştir ve unut", birkaç ay sonra sebebini kimsenin hatırlamadığı bir deliverability düşüşüdür.
Otomasyon hattı: düzleştirmeyi canlı tutmak
Düzleştirmeyi meşru kılan tek şey, include'ın sağladığı canlılığı bir cron job ile geri getirmektir. Fikir basit: include'ları düzenli olarak yeniden çözümle, sonucu yayınlanmış düz kayıtla karşılaştır, fark varsa DNS API'siyle güncelle ve operatörü uyar.
Kavramsal iskelet:
bash
#!/usr/bin/env bash
set -euo pipefail
SOURCES=(_spf.google.com spf.protection.outlook.com sendgrid.net \
mailgun.org _spf.salesforce.com servers.mcsv.net)
# 1) include'ları özyinelemeli çöz, CIDR'leri normalize et
resolve_spf() { python3 flatten_spf.py "$@"; } # ip4/ip6 CIDR listesi döndürür
NEW=$(resolve_spf "${SOURCES[@]}" | sort -u)
OLD=$(cat /var/lib/spf/current_cidrs.txt)
# 2) diff'le
if ! diff <(echo "$OLD") <(echo "$NEW") >/dev/null; then
# 3) değişim var → yeni TXT kaydı üret ve DNS API ile yayınla
build_txt "$NEW" | dns_api_update evilmail.pro TXT
echo "$NEW" > /var/lib/spf/current_cidrs.txt
alert "SPF düz kaydı güncellendi: $(diff <(echo "$OLD") <(echo "$NEW"))"
fi
Kurulum notları: TXT TTL'ini 300–3600 saniye aralığında tutun; donuk kaydın penceresini küçültür. Cron'u günlük çalıştırmak çoğu için yeterlidir, agresif ESP'ler için saatlik. Diff mantığını CIDR sınırlarını normalize ederek yapın (örneğin 64.233.160.0/19 ile üst üste binen daha küçük blokları birleştirin) — yoksa her küçük değişimde gereksiz güncelleme yayınlarsınız.
Otomasyonun üstüne bir de bağımsız izleme koyun: haftalık bir checkdmarc evilmail.pro çalıştırıp dns_lookups sayısını, void lookup'ları ve kaydın hâlâ geçerli olduğunu doğrulayan bir uyarı. Düzleştirme boru hattınız bozulursa, bunu müşteriler değil siz fark edersiniz.
Düzleştirmeden önce denenecek daha temiz çözümler
Sahada gördüğüm PermError vakalarının çoğu düzleştirme gerektirmiyor — sadece budama gerektiriyor. Şu dördünü sırayla deneyin:
Ölü include'ları budayın. İki yıl önce test edilip bırakılan bir ESP'nin include'ı hâlâ kayıtta duruyor olabilir. Her include'ı gerçek gönderim loglarınızla eşleştirin; kullanılmayanı silin. Genelde bu tek başına sizi 10'un altına indirir.
Alt alan adı ayrımı yapın. SPF limiti kayıt başınadır, alan başına değil. İşlemsel maili mail.evilmail.pro, pazarlamayı news.evilmail.pro üzerinden gönderin. Her alt alanın kendi bağımsız 10-lookup bütçesi olur. Altı ESP'yi ikiye bölmek çoğu zaman düzleştirmeye olan ihtiyacı tamamen ortadan kaldırır — ve bonus olarak itibarı ayrıştırır: pazarlama şikayetleri işlemsel teslimatınızı etkilemez.
Envelope-from (Return-Path) segmentasyonu. SPF, From: başlığını değil zarf gönderenini kontrol eder. ESP'leri farklı bounce domainlerine dağıtırsanız, her biri kendi SPF kaydını kullanır ve ana domaininizin bütçesini hiç harcamaz.
Gereksiz `a` ve `mx` mekanizmalarını temizleyin. Birçok kayıt, alan adının kendisi hiç mail göndermediği hâlde alışkanlıktan a mx taşır. Sunucunuz doğrudan gönderim yapmıyorsa bunları silin; iki lookup geri kazanırsınız.
Düzleştirme moda bir teknik, ama çoğu kayıt aslında düzleştirmeye muhtaç değil — sadece şişkin. Önce ölçün, sonra budayın, ayrıştırın; düzleştirmeyi gerçekten 4+ aktif ESP'niz olduğunda ve arkasına bir cron hattı koyabildiğinizde son çare olarak saklayın.
Kontrol listesi
Lookup sayısını ölçtün mü — checkdmarc ile gerçekten 10'un üstünde mi?
Ölü ve kullanılmayan include'ları gönderim loglarıyla eşleştirip budadın mı?
Alt alan ayrımını (mail. / news.) ve Return-Path segmentasyonunu değerlendirdin mi?
Gereksiz a / mx / tüm ptr mekanizmalarını temizledin mi?
Düzleştirdiysen: cron + diff + DNS API güncelleme + operatör uyarısı hattını kurdun mu?
TXT TTL'ini 300–3600 saniyeye indirdin mi?
Kayıt 255 karakter string ve pratikteki 512 bayt yanıt sınırlarını aşmıyor mu, gerekiyorsa doğru parçalanmış mı?
-all mı ~all mı — bilinçli seçtin mi ve düz kaydın eksiksiz olduğundan emin misin?