Bounce Mail Analizi: Teslimat Hatalarını Okuma ve Çözme

Bir gün patronunuz size gelip “Müşterilere gönderdiğimiz kampanya mailleri gitmiyor, ne olduğunu bul” dediğinde, elinizdeki tek ipucu genellikle bir bounce mailidir. O küçük, teknik dolu mesaj aslında size çok şey anlatır, yeter ki nasıl okuyacağınızı bilin. Mail teslimat sorunları sysadmin hayatının en sinir bozucu kısımlarından biridir çünkü sorun bazen sizin sunucunuzda, bazen karşı tarafta, bazen DNS’te, bazen de tamamen rastgele görünen bir kara listede olur. Bu yazıda bounce mail analizini başından sonuna kadar ele alacağız.

Bounce Mail Nedir ve Neden Önemlidir

Bounce mail, göndericiye iletilen teslimat başarısızlık bildirimidir. Teknik adıyla NDR (Non-Delivery Report) veya DSN (Delivery Status Notification) olarak geçer. SMTP protokolünün bir parçası olup RFC 3464 standardında tanımlanmıştır.

Bounce mailleri iki ana kategoriye ayrılır:

  • Hard Bounce: Kalıcı teslimat hatası. Adres yok, domain yok, hesap kapatılmış. Tekrar denemenin anlamı yok.
  • Soft Bounce: Geçici teslimat hatası. Posta kutusu dolu, sunucu geçici olarak erişilemez, bant genişliği sınırı aşıldı. Belirli süre sonra tekrar denenebilir.

Neden önemli? Çünkü bounce analizi yapmadan mail altyapınızın gerçek durumunu bilemezsiniz. Yüzde kaçı gidiyor, neden gitmiyor, hangi domain’ler sorunlu, IP’niz kara listede mi? Bunların hepsini bounce mailleri size söyler.

Bounce Mailin Anatomisi

Bir bounce mail genellikle üç bölümden oluşur: insan tarafından okunabilir açıklama, orijinal mesaj başlıkları ve makine tarafından okunabilir DSN bölümü. Postfix kullanan bir sistemden gelen örnek bounce’a bakalım:

# Bir bounce mailin ham içeriğini görmek için
cat /var/mail/admin | grep -A 100 "Return-Path: <>"

Tipik bir bounce mailin yapısı şöyle görünür:

From: Mail Delivery Subsystem <[email protected]>
To: [email protected]
Subject: Undeliverable: Toplanti daveti

This is the mail system at host mail.ornekdomain.com.

Your message could not be delivered to one or more recipients.

   <[email protected]>: host mail.karsialan.com[192.168.1.10] said:
   550 5.1.1 The email account that you tried to reach does not exist.
   Please try double-checking the recipient's email address for typos or
   unnecessary spaces. (in reply to RCPT TO command)

------ This is a copy of the message, including all the headers. ------

Bu mesajda dikkat etmeniz gereken kısımlar şunlar:

  • 550: SMTP yanıt kodu
  • 5.1.1: Detaylı hata kodu (Enhanced Status Code)
  • Hata mesajının hangi SMTP komutuna yanıt olarak geldiği

SMTP Hata Kodlarını Anlamak

SMTP hata kodları üç haneli sayılardır ve her rakam bir anlam taşır. İlk rakam sonucu belirtir:

  • 2xx: Başarılı
  • 4xx: Geçici hata, tekrar dene
  • 5xx: Kalıcı hata, tekrar deneme

En sık karşılaşılan kodlar:

  • 421: Servis geçici olarak kullanılamıyor
  • 450: Posta kutusu geçici olarak erişilemez
  • 451: İşlem iptal edildi, yerel hata
  • 452: Depolama alanı yetersiz
  • 500: Sözdizimi hatası
  • 501: Parametre sözdizimi hatası
  • 503: Hatalı komut sırası
  • 550: Posta kutusu bulunamadı veya erişilemez
  • 551: Kullanıcı bu sunucuda değil
  • 552: Depolama kotası aşıldı
  • 553: Geçersiz adres formatı
  • 554: İşlem başarısız, spam veya güvenlik politikası

Enhanced Status Code’lar ise X.Y.Z formatındadır ve daha fazla detay verir. Örneğin 5.7.1 “mesaj reddedildi, güvenlik politikası” anlamına gelir.

Postfix Log Analizi

Bounce mailleri anlamak için sadece bounce mesajına bakmak yetmez, sunucu loglarına da bakmanız gerekir. Postfix’te mail logları genellikle /var/log/mail.log veya /var/log/maillog dosyasındadır.

# Belirli bir alıcıya giden maillerin durumunu kontrol et
grep "[email protected]" /var/log/mail.log | tail -50

# Bounce olan mailleri filtrele
grep "status=bounced" /var/log/mail.log | tail -100

# Bugünkü tüm hataları say
grep "$(date +%b %e)" /var/log/mail.log | grep -c "status=bounced"

Postfix log satırı şöyle görünür:

Jan 15 14:23:45 mailserver postfix/smtp[12345]: ABC123: to=<[email protected]>,
relay=mail.karsialan.com[203.0.113.10]:25, delay=2.3, delays=0.1/0/1.8/0.4,
dsn=5.1.1, status=bounced (host mail.karsialan.com[203.0.113.10] said:
550 5.1.1 <[email protected]>: Recipient address rejected: User unknown
in virtual mailbox table (in reply to RCPT TO command))

Bu satırı parçalara ayıralım:

  • ABC123: Postfix kuyruk ID’si
  • relay: Bağlandığımız sunucu ve IP
  • dsn=5.1.1: Enhanced status code
  • status=bounced: Mail iletilmedi
  • Parantez içindeki kısım: Karşı sunucunun verdiği yanıt
# Kuyruk ID'sine göre tüm log satırlarını bul
grep "ABC123" /var/log/mail.log

# En çok bounce alan domain'leri listele
grep "status=bounced" /var/log/mail.log | 
  grep -oP "to=<[^>]+>" | 
  grep -oP "@[^>]+" | 
  sort | uniq -c | sort -rn | head -20

Gerçek Dünya Senaryosu 1: Kampanya Mailleri Gitmiyordu

Bir e-ticaret müşterisinin mail sistemini yönetiyorduk. Pazarlama ekibi 50.000 kişilik bir listeye kampanya maili gönderdi ve sabah baktıklarında bounce oranının yüzde 35’e çıktığını gördüler. Panik havasında bana geldiler.

İlk adım logları incelemekti:

# Son 24 saatin bounce istatistikleri
grep "$(date +%b %e)" /var/log/mail.log | 
  grep "status=bounced" | 
  grep -oP "dsn=d.d.d" | 
  sort | uniq -c | sort -rn

Çıktı şöyle geldi:

2847  dsn=5.7.1
8234  dsn=5.1.1
6891  dsn=5.7.26

dsn=5.7.26 dikkatimi çekti. Bu kod SPF veya DMARC başarısızlığını gösterir. Mail sunucusunun SPF kaydına baktım:

# SPF kaydını kontrol et
dig TXT ornekdomain.com | grep "v=spf1"

# Daha detaylı sorgu
host -t TXT ornekdomain.com

Sorun ortaya çıktı. Kampanya mailleri üçüncü parti bir mail servisinden gönderiliyordu ama o servisin IP aralıkları SPF kaydına eklenmemişti. Hızlı çözüm:

# Mevcut SPF kaydını güncelle
# DNS panelinden şu kaydı ekle:
# v=spf1 include:ornekdomain.com include:mailchimp-provider.com ~all
# Değişikliği doğrula
dig TXT ornekdomain.com +short

Gerçek Dünya Senaryosu 2: Kara Liste Sorunu

Başka bir senaryoda sunucumuzdan gönderilen maillerin büyük telekom operatörlerinin mail sistemlerine gittiğini ama küçük hosting firmaları ve bazı kurumsal mail sunucularına gidemediğini fark ettik.

# Mail kuyruğundaki bekleyen mailleri kontrol et
mailq | head -50

# Postfix kuyruğunu daha detaylı gör
postqueue -p | grep -E "^^|^[A-F0-9]"

# Kuyruktaki belirli domain'lere ait mailleri say
postqueue -p | grep "@hedefdomain.com" | wc -l

Bounce mesajlarının içeriğine baktım:

550 5.7.1 Message rejected due to: IP reputation check failed.
Visit https://blacklistcheck.ornekrbl.com for more information.

IP’nin kara listede olup olmadığını kontrol etmek için:

# Manuel kara liste kontrolü
MAILIP="203.0.113.50"

# MXToolbox alternatifi olarak komut satırından
for rbl in zen.spamhaus.org bl.spamcop.net dnsbl.sorbs.net b.barracudacentral.org; do
  result=$(dig +short "${MAILIP##*.}.${MAILIP%.*}.${MAILIP%.*.*}.${MAILIP%%.*}.${rbl}")
  if [ -n "$result" ]; then
    echo "LISTED in $rbl: $result"
  else
    echo "Clean in $rbl"
  fi
done

IP’nin Spamhaus’ta listelendiğini gördük. Sorunun kaynağını bulmak için o IP’den o gün gönderilen mailleri inceledim:

# Belirli IP'den gönderilen mailleri logda bul
grep "client=[203.0.113.50]" /var/log/mail.log | 
  grep "$(date +%b %e)" | head -50

# Saatlik gönderim yoğunluğunu analiz et
grep "$(date +%b %e)" /var/log/mail.log | 
  grep "status=sent" | 
  awk '{print $1, $2, $3}' | 
  cut -d: -f1 | sort | uniq -c

Gece 3’te anormal bir spike vardı. Sunucuda bir hesap ele geçirilmişti ve spam gönderilmişti. Hesabı kapattık, IP’yi kara liste kaldırma işlemine soktuk ve rate limiting ekledik.

Bounce Mail Header’larını Elle Analiz Etme

Bazen otomatik araçlar yetersiz kalır ve bounce’u başından sonuna elle okumak gerekir. Bir bounce mailin tam header’ı şöyle görünür:

# Procmail veya mutt ile bir bounce'u kaydet ve incele
# Veya doğrudan mailbox dosyasından çıkar
formail -s cat < /var/mail/admin | grep -B5 -A200 "mailer-daemon"

Tipik bir bounce header analizi:

Return-Path: <>
Received: from mail.ornekdomain.com (mail.ornekdomain.com [203.0.113.1])
  by mx.hedefdomain.com with ESMTP id abc123
  for <[email protected]>; Mon, 15 Jan 2024 14:23:45 +0300

Content-Type: multipart/report; report-type=delivery-status;
  boundary="===============1234567890=="

--===============1234567890==
Content-Type: message/delivery-status

Reporting-MTA: dns; mail.ornekdomain.com
Action: failed
Status: 5.1.1
Diagnostic-Code: smtp; 550 5.1.1 User unknown

Content-Type: multipart/report olan maillerde üç MIME bölümü bulunur:

  • Birinci bölüm: İnsan okunabilir açıklama
  • İkinci bölüm: message/delivery-status, makine okunabilir DSN bilgisi
  • Üçüncü bölüm: message/rfc822, orijinal mailin kopyası

Postfix ile Bounce Yönetimi

Postfix bounce davranışını özelleştirmek için birkaç önemli konfigürasyon parametresi var:

# main.cf içindeki bounce ilgili ayarları gör
postconf | grep -E "bounce|notify|maximal_queue"

# Önemli parametreler
postconf bounce_notice_recipient
postconf notify_classes
postconf bounce_queue_lifetime
postconf maximal_queue_lifetime

Bounce bildirimleri yönetimi için main.cf ayarları:

# /etc/postfix/main.cf düzenlemesi
# Bounce bildirimlerini admin'e de gönder
notify_classes = bounce, delay, policy, protocol, resource, software
bounce_notice_recipient = [email protected]

# 2FA bounce süresi (varsayılan 5 gün)
bounce_queue_lifetime = 2d
maximal_queue_lifetime = 5d

# Değişiklikleri uygula
postfix reload

Bounce Analizi için Script Yazmak

Manuel analiz zaman alıcıdır. Günlük bounce raporları için basit bir script işinizi çok kolaylaştırır:

#!/bin/bash
# bounce_report.sh - Günlük bounce raporu

LOG="/var/log/mail.log"
TARIH=$(date +%b %e)
RAPOR="/tmp/bounce_raporu_$(date +%Y%m%d).txt"

echo "=== Bounce Raporu: $(date) ===" > $RAPOR
echo "" >> $RAPOR

# Toplam bounce sayısı
TOPLAM=$(grep "$TARIH" $LOG | grep -c "status=bounced")
echo "Toplam bounce: $TOPLAM" >> $RAPOR

# Hata koduna göre dağılım
echo "" >> $RAPOR
echo "--- Hata Kodları ---" >> $RAPOR
grep "$TARIH" $LOG | grep "status=bounced" | 
  grep -oP "dsn=K[d.]+" | 
  sort | uniq -c | sort -rn >> $RAPOR

# En çok bounce alan domain'ler
echo "" >> $RAPOR
echo "--- En Çok Bounce Alan Domainler (Top 10) ---" >> $RAPOR
grep "$TARIH" $LOG | grep "status=bounced" | 
  grep -oP "to=<[^>]+>" | 
  grep -oP "@K[^>]+" | 
  sort | uniq -c | sort -rn | head -10 >> $RAPOR

# Kara liste ipucları
echo "" >> $RAPOR
echo "--- Muhtemel Kara Liste Redleri ---" >> $RAPOR
grep "$TARIH" $LOG | grep "status=bounced" | 
  grep -iE "blacklist|blocked|spam|rbl|reputation" | 
  wc -l >> $RAPOR

cat $RAPOR
# Opsiyonel: Mail ile gönder
# mail -s "Günlük Bounce Raporu" [email protected] < $RAPOR

DMARC Raporlarını Okuma

DMARC raporları da aslında bir tür bounce analizi aracıdır. XML formatında gelirler ve SPF/DKIM başarısızlıklarını raporlarlar:

# DMARC raporunu aç ve parse et
# Önce zip'i aç
unzip dmarc_rapor.zip -d /tmp/dmarc/

# XML'i insan okunabilir hale getir
cat /tmp/dmarc/*.xml | python3 -c "
import sys
import xml.dom.minidom
content = sys.stdin.read()
print(xml.dom.minidom.parseString(content).toprettyxml(indent='  '))
" | grep -A5 -B5 "fail"

DMARC raporundaki kritik alanlar:

  • source_ip: Maili gönderen IP
  • count: Bu IP’den gönderilen mail sayısı
  • disposition: Alıcı sunucunun ne yaptığı (none, quarantine, reject)
  • dkim result: DKIM doğrulama sonucu
  • spf result: SPF doğrulama sonucu

Bounce Loop Tuzağından Kaçınmak

Bounce loop, bir bounce mailin kendisinin de bounce yemesi durumudur. Bu durum sunucuyu felç edebilir. Postfix bunu Return-Path: ile önler ama yanlış konfigürasyonlarda sorun çıkabilir:

# Bounce loop olup olmadığını kontrol et
grep "mailer-daemon" /var/log/mail.log | 
  grep "$(date +%b %e)" | wc -l

# Kuyruktaki mailer-daemon kaynaklı mailleri gör
postqueue -p | grep "MAILER-DAEMON"

# Eğer loop varsa kuyruğu temizle (dikkatli!)
postsuper -d ALL deferred

Exchange ve Office365 Bounce Kodları

Eğer hedef sunucu Exchange veya Office365 ise, bounce mesajları biraz farklı görünür:

550 5.4.1 Recipient address rejected: Access denied.
AS(201806281)

Bu gibi durumlarda Microsoft’un hata kod veritabanına başvurmak gerekir. Sık görülen Office365 kodları:

  • 5.4.1: SPF veya sender policy başarısızlığı
  • 5.7.64: TenantAttribution. Tenant’ın kimlik doğrulama sorunu
  • 5.7.124: Gönderici bu dağıtım listesine izinli değil
  • 5.1.10: Alıcı adresi geçersiz veya yok
# Office365'e giden maillerin durumunu izle
grep "@onmicrosoft.com|@outlook.com|@hotmail.com" /var/log/mail.log | 
  grep "status=bounced" | 
  grep -oP "said: K.*" | 
  sort | uniq -c | sort -rn | head -20

Bounce Oranını Düşürmek için Pratik Adımlar

Bounce analizi yapıp sorunu tespit ettikten sonra ne yapmalısınız? İşte pratik adımlar:

  • Liste hijyeni: Hard bounce alan adresleri derhal listeden çıkarın. Bounce oranı yüzde 2’yi geçmeye başlarsa alarm verin.
  • SPF kaydı: Tüm mail gönderen IP ve servislerinizin SPF kaydında olduğundan emin olun.
  • DKIM imzalama: Her domain için DKIM key oluşturun ve tüm mailleri imzalayın.
  • DMARC politikası: Önce p=none ile başlayın, raporları analiz edin, sonra quarantine ve reject‘e geçin.
  • Rate limiting: Dakikada belirli sayının üzerinde mail gönderimini engelleyin.
  • Warm-up: Yeni IP’lerden gönderim yaparken yavaş başlayıp kademeli artırın.
# Postfix'te rate limiting
# main.cf
# smtpd_client_connection_rate_limit = 100
# smtpd_client_message_rate_limit = 200

# Mevcut rate limitlerini kontrol et
postconf | grep -i rate_limit

# Anlık SMTP bağlantı sayısını izle
ss -tn | grep :25 | wc -l

Bounce Takip Sistemi Kurmak

Manuel takip uzun vadede sürdürülebilir değil. Basit bir veritabanı tabanlı takip sistemi kurmak için:

#!/bin/bash
# bounce_tracker.sh - Bounce'ları SQLite'a kaydet

DB="/var/lib/mail-stats/bounces.db"

# Veritabanını oluştur
sqlite3 $DB "CREATE TABLE IF NOT EXISTS bounces (
  tarih TEXT,
  alici TEXT,
  domain TEXT,
  hata_kodu TEXT,
  hata_mesaji TEXT,
  kayit_zamani DATETIME DEFAULT CURRENT_TIMESTAMP
);"

# Bugünkü bounce'ları parse edip ekle
grep "$(date +%b %e)" /var/log/mail.log | 
grep "status=bounced" | while read line; do
  ALICI=$(echo "$line" | grep -oP "to=<K[^>]+")
  DOMAIN=$(echo "$ALICI" | cut -d@ -f2)
  KOD=$(echo "$line" | grep -oP "dsn=K[d.]+")
  MESAJ=$(echo "$line" | grep -oP "said: K[^)]+")
  TARIH=$(echo "$line" | awk '{print $1, $2, $3}')

  sqlite3 $DB "INSERT INTO bounces (tarih, alici, domain, hata_kodu, hata_mesaji)
    VALUES ('$TARIH', '$ALICI', '$DOMAIN', '$KOD', '${MESAJ//'/''}');"
done

echo "Kayıt tamamlandı."
sqlite3 $DB "SELECT domain, COUNT(*) as sayi FROM bounces
  WHERE kayit_zamani > datetime('now', '-1 day')
  GROUP BY domain ORDER BY sayi DESC LIMIT 10;"

Sonuç

Bounce mail analizi bir sanatır; ham veriyi okuyup sorunun kaynağına inebilmek için hem SMTP protokolünü hem de sisteminizin davranışını iyi tanımanız gerekir. En önemli nokta şudur: bir bounce mesajı sizi şikayet etmez, size bilgi verir. O bilgiyi doğru okuyabilirseniz çözüm genellikle çok uzakta değildir.

Pratik olarak yapmanız gerekenler şunlardır: Her gün bounce loglarını gözlemleyin, yüzde 2’nin üzerinde hard bounce oranı görürseniz hemen müdahale edin, SPF/DKIM/DMARC üçlüsünü mutlaka kurun ve çalışır hale getirin, kara liste durumunu haftalık kontrol edin ve liste temizliğini asla ihmal etmeyin.

Mail teslimat sorunları genellikle tek bir noktada değil, birden fazla katmanda birleşen küçük sorunlardan kaynaklanır. Bounce analizi o katmanları tek tek soymanızı sağlar. Sabırlı olun, logları okuyun ve her bounce mesajına “bu bana ne söylüyor?” diye sorun.

Benzer Konular

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir