Dovecot Kota Yönetimi: Kullanıcı ve Alan Adı Bazında Posta Kutusu Sınırları ve Aşım Uyarıları
Dolu posta kutusuna gelen mesajı kuyruk sonrası bir bounce olmaktan çıkarıp, daha kuyruğa girmeden Postfix'te reddedilen bir politika kararına dönüştürüyoruz. quota ve quota-status eklentilerini birlikte kurup %80/%95 eşiğinde uyarı yollayan, %100'de SMTP seviyesinde 552 döndüren ve alan adı başına havuz sınırı dayatan gerçek bir üretim yapılandırması.
EvilMail Team9 Temmuz 202614 dk okuma
Sorun: kota kuyruğun yanlış tarafında uygulanıyor
Klasik hatalı kurulumu tarif edelim. Kota yalnızca LMTP teslim anında kontrol ediliyor. Dolu kutuya bir mesaj geldiğinde Postfix mesajı kabul ediyor, kuyruğa alıyor, LMTP'ye teslim etmeyi deniyor ve Dovecot 552 5.2.2 Mailbox is full diyerek geri çeviriyor. Bu noktada Postfix'in elinde bir NDR üretip özgün göndericiye geri yollamaktan başka seçenek kalmıyor. Gönderen adresi sahteyse — spam ve phishing'de neredeyse her zaman öyledir — bu geri sekme masum bir üçüncü tarafa gidiyor. Backscatter tam olarak budur ve IP itibarınızı bunun için yakarsınız.
İkinci sorun gecikme. Mesaj kuyrukta oturuyor, birkaç retry deneniyor, saatler sonra NDR üretiliyor. Meşru gönderen "teslim edildi mi, edilmedi mi" belirsizliğinde kalıyor.
Doğru mimari iki katmanlıdır ve görev paylaşımı nettir:
`quota` eklentisi teslim ve IMAP işlemleri sırasında kullanımı sayar, kuralları uygular ve eşik uyarılarını tetikler.
`quota-status` politika servisi Postfix'e RCPT TO
Dovecot Kota Yapılandırması: Kullanıcı ve Alan Adı Bazlı Sınırlar | evilmail — EvilMail Blog
anında, mesaj daha kuyruğa girmeden "bu kutu dolu, reddet" cevabı verir.
İlki muhasebeyi tutar, ikincisi kararı kuyruğun doğru tarafına — pre-queue'ya — taşır. Aşağıdaki diyagram bu ayrımı gösteriyor.
Backend seçimi: neden maildir++ değil de count
Dovecot kotayı birkaç farklı backend ile sayabilir ve seçim üretimde ciddi fark yaratır.
quota = maildir:User quota maildir++ backend'idir. Her kutunun kökünde maildirsize adında bir dosya tutar ve kullanımı buradan okur. Sorun şu: bu dosya bozulduğunda — ki eşzamanlı erişim, kesilen teslimler veya elle dosya oynamalarında bozulur — Dovecot tüm maildir'i tarayıp yeniden hesaplamak zorunda kalır. Milyonlarca dosyalı bir kutuda bu recalc dakikalarca sürer ve o sırada teslim yavaşlar.
quota = count:User quota ise sayımı posta kutusu index'i / dict üzerinden yapar. quota_vsizes = yes ile birlikte mesajların vsize'ını (RFC822 boyutu) sayar ve pratikte recalc ihtiyacını sıfıra indirir çünkü sayaç index ile birlikte tutulur. Üretim için önerim net: count.
Dikkat: Dovecot conf dosyalarında yüzde işareti %% olarak kaçışlanır. quota_rule2 çöp kutusuna ana kotanın üstünde ek %10 tanır — böylece kullanıcı sildiği mesajı Trash'e taşıyarak yer açabilir. Spam:ignore ile spam klasörü kotaya sayılmaz. Bu iki kural olmadan kullanıcı "sildim ama hâlâ dolu" tuzağına düşer, çünkü silme işlemi Trash'i şişirir.
count yerine dict backend'ini yalnızca paylaşımlı sayıma ihtiyacınız varsa — örneğin birden fazla Dovecot düğümü aynı kotayı görmeliyse — kullanırsınız: quota = dict:User quota:%%{user}:proxy::quotadict. Tek düğümlü çoğu kurulumda buna gerek yoktur.
Kullanıcı başına kotayı yapılandırmak
Eklentiyi iki yerde etkinleştirin. Teslim/mail tarafı:
90-quota.conf'taki quota_rule = *:storage=2G statik varsayılandır. Gerçek dünyada her kullanıcının kotası farklıdır — planına göre değişir. Bunu userdb üzerinden, SQL'den override edersiniz. virtual_users tablosuna bytes cinsinden bir quota kolonu (BIGINT) ekleyin ve user_query'de bir quota_rule alanı döndürün:
sql
-- dovecot-sql.conf.ext içindeki user_query
SELECT
CONCAT('/var/mail/vhosts/', maildir) AS home,
5000 AS uid, 5000 AS gid,
CONCAT('*:bytes=', quota) AS quota_rule
FROM virtual_users
WHERE email = '%u';
Birleşme mantığı şudur: userdb'den gelen quota_rule, 90-quota.conf'taki statik *:storage=... kuralını override eder; ama quota_rule2 (Trash) ve quota_rule3 (Spam) gibi ek kurallar yerinde kalır. Yani her kullanıcı için ana limiti veritabanından çekersiniz, davranışsal kuralları config'ten. quota kolonu NULL ise kullanıcı statik 2G varsayılanına düşer — bu güvenli bir davranıştır.
Aşım uyarıları: %80 ve %95 eşiklerinde otomatik mail
Kullanıcı kutusu dolmadan haberdar olmalı. Dovecot bunu quota_warning ayarlarıyla yapar: bir eşik geçildiğinde belirlediğiniz komutu çalıştırır.
Dovecot eşikleri en yüksek aşılandan başlayarak tek bir tanesini tetikler: kullanım %80'i geçince quota_warning2, %95'i geçince quota_warning devreye girer. Script'e yüzde ve kullanıcı adı argüman olarak geçilir:
bash
#!/bin/bash
# /usr/local/bin/quota-warning.sh
PERCENT=$1
USER=$2
cat <<EOF | /usr/lib/dovecot/dovecot-lda -d "$USER" -o "plugin/quota=count"
From: [email protected]
Subject: Posta kutunuz %${PERCENT} doluluğa ulaştı
Content-Type: text/plain; charset=UTF-8
Merhaba,
${USER} posta kutunuz kapasitesinin %${PERCENT} seviyesine ulaştı.
%100'e ulaştığında yeni gelen mesajlar reddedilecektir.
Yer açmak için gereksiz ve büyük ekli mesajları silip Çöp kutusunu boşaltın.
evilmail
EOF
Buradaki kritik ayrıntı -o plugin/quota=count. Uyarı mesajını kullanıcının kutusuna teslim ediyorsunuz; bu teslim tekrar kota kontrolünü tetikler ve teslimi açık bir backend ile çalıştırmazsanız uyarı akışı kendi içinde tökezleyebilir. Teslimi doğru backend ile çalıştırmak ve service bloğunu user = vmail ile izinlendirmek bu tuzağı kırar. Script'in vmail olarak çalışması ve /var/mail/vhosts altına yazma iznine sahip olması gerekir.
Aşağıdaki merdiven, üç mekanizmanın — iki uyarı eşiği, hard limit ve grace — tek bir kutuda nasıl örtüştüğünü gösteriyor.
quota-status: Postfix'e pre-queue red
Asıl deliverability kazancı bu bölümde. quota-status Dovecot ile gelen bağımsız bir politika servisidir; Postfix'in policy protokolünü konuşur ve "bu alıcının kutusu dolu mu" sorusuna RCPT TO anında cevap verir.
DUNNO Postfix'e "benden bir itiraz yok, restriction zincirine devam et" demektir — kutu dolu değilse veya kullanıcı yoksa (kullanıcı yokluğu kararını başka bir mekanizmaya bırakırsınız) böyle döner. Kutu doluysa 552 5.2.2 ile kalıcı red döner.
Sıralama kritik. check_policy_service mutlaka reject_unauth_destination'dan sonra gelmeli. Aksi halde relay etmediğiniz alan adları için de Dovecot'a kota sorarsınız — hem gereksiz yük, hem de bir bilgi sızıntısı vektörü. Önce "bu maili ben kabul ediyor muyum" kararını verin, sonra "kutu dolu mu" diye sorun.
Sonuç: dolu kutuya gelen mesaj artık RCPT TO cevabında reddediliyor. Kuyruk yok, NDR yok, backscatter yok. Gönderen 552'yi anında görüyor ve bu net bir teslim-edilemez sinyali.
Kalıcı 552 yerine geçici 452 4.2.2 da döndürebilirsiniz. Fark davranışsaldır: 552 göndericiye "vazgeç" der, 452 "sonra tekrar dene" der. Kullanıcının birazdan yer açacağını varsaydığınız senaryolarda (örneğin premium müşteriler için) 452 retry penceresi bırakır; standart öneri kalıcı doluluk için 552'dir.
Alan adı başına kota: Dovecot'un yapmadığı şey
Açık konuşalım: Dovecot'ta "domain quota" diye bir ayar yoktur. Kota her zaman posta kutusu düzeyindedir. Bir alan adının toplam tüketimine tavan koymak istiyorsanız bunu Dovecot dışında kurgulamanız gerekir. İki pratik strateji var.
1. Provizyon anında dayatma. En temiz yol. Yeni bir posta kutusu oluşturulurken veya kotası büyütülürken, o alan adının mevcut kotalar toplamının plan havuzunu aşıp aşmayacağını uygulama katmanında kontrol edin:
sql
-- Yeni kutuya X byte kota vermeden önce çalıştır
SELECT
d.quota_limit AS pool_limit,
COALESCE(SUM(u.quota), 0) AS pool_used
FROM domains d
LEFT JOIN virtual_users u ON u.domain_id = d.id
WHERE d.name = 'evilmail.pro'
GROUP BY d.quota_limit;
-- pool_used + yeni_kota > pool_limit ise reddet
evilmail'de kullandığımız yaklaşım budur: domain.quota_limit alanı DB'de tutulur, kontrol provizyon katmanında yapılır. Kutuya atanan kotaların toplamı domenin havuzunu aşamaz. Bu, aşırı-taahhüdü (over-provisioning) baştan engeller.
2. Gerçek zamanlı toplam. Provizyon kontrolü kotaların *tahsisini* sınırlar ama fiili *kullanımı* değil. Fiili kullanımı domain düzeyinde gözlemek isterseniz, periyodik bir cron ile doveadm çıktısını domaine göre toplayın ve aşım halinde bir bayrak set edin:
bash
# Domain başına fiili kullanımı topla
doveadm quota get -A -f flow | awk -F@ '/STORAGE/ {print $2}'
Buradan quota_over_flag set edip yeni kutu açılışını dondurabilir veya uyarı yollayabilirsiniz. Ama unutmayın: quota-status yalnızca kutu düzeyinde çalışır — domain havuzu kararı her zaman sizin uygulama/DB katmanınızda kalır.
Doğrulama ve operasyon
Yapılandırmadan sonra tek tek doğrulayın. Tek kullanıcının kotası:
bash
doveadm quota get -u [email protected]
# Quota name Type Value Limit %
# User quota STORAGE 148213 2097152 7
# User quota MESSAGE 1240 - 0
Sayaç ile disk uyuşmuyorsa yeniden hesaplayın (count backend'de nadiren gerekir):
bash
doveadm quota recalc -u [email protected]
doveadm quota get -A # tüm kullanıcılar
Asıl testi Postfix tarafında canlı yapın. postmap -q kota politikasını test etmez; gerçek bir SMTP oturumu açmanız gerekir. swaks ile dolu bir kutuya RCPT gönderin ve 552'yi görün:
bash
swaks --to [email protected] --server mx.evilmail.pro --quit-after RCPT
# ... <** 552 5.2.2 Mailbox is full
Bunu göremiyorsanız restriction sırası yanlıştır veya listener kapalıdır. Log izleme:
bash
journalctl -u dovecot | grep -i quota
En sık düşülen tuzaklar:
`quota_vsizes = yes` unutulur. Sayım vsize ile yapılmadığında limit ile fiili kullanım tutmaz; kullanıcı "kutum boş ama dolu diyor" der.
Trash/Spam kuralları eksiktir.Spam:ignore ve Trash:storage=+10%% yoksa silme işlemi Trash'i şişirir ve kullanıcı yer açamaz.
Uyarı script'i açık backend ile çağrılmaz.-o plugin/quota=count olmadan uyarı teslimi yanlış backend'e düşebilir.
Postfix chroot. smtpd chroot'ta çalışıyorsa inet_listener yerine /var/spool/postfix/private/ altında bir unix_listener gerekebilir; check_policy_service unix:private/quota-status ile bağlayın.
Kota katmanı sağlamlaştığında sıradaki halka gönderim tarafındadır: alan adı başına outbound rate-limit ve DKIM imzalama ile MTA'nızı hem içeri hem dışarı disipline etmek.