Maildir yedeklemesi: snapshot al, ama asıl işi doğrulanabilir geri yükleme yapsın
Maildir yedeklemek kolay görünür: her mesaj ayrı, değişmez bir dosya. Ama restore günü açılmayan bir kutu, hiç yedeğiniz olmamasından beterdir. Bu yazıda snapshot + offsite şifreli depoyu kurup asıl değeri sağ tarafa koyuyoruz: her hafta sandbox'a otomatik geri yüklenip doveadm ile kanıtlanan bir kurtarma hattı ve "son doğrulanmış restore" metriği.
EvilMail Team30 Temmuz 202614 dk okuma
Maildir yedeği "aldım" sanıp kutuyu bozmanın üç yolu
Maildir yedeklemesi teoride en kolay iştir: her mesaj kendi dizininde, kendi başına, bir daha asla değişmeyecek tek bir dosya. Ne kilit var, ne binary bir mbox'ın ortasına yazma derdi. "rsync -a yeter" diye düşünürsünüz ve aylarca yeşil yanan bir cron işiyle içiniz rahat eder. Sorun restore gününde çıkar: kutuyu geri koyarsınız, kullanıcı IMAP'e bağlanır ve okunmuş binlerce mesaj tekrar okunmamış görünür, ya da Dovecot kutuyu açarken UID çakışmasından yakınıp yeniden indeksler, ya da hiç açılmaz.
Üç somut başarısızlık senaryosu var, ve üçünü de gerçekten yaşanmış hâliyle sayıyorum:
Canlı Maildir üzerinden crash-consistent snapshot. Teslimat ya da bir IMAP oturumu tam ortasındayken alınan snapshot, dovecot-uidlist dosyasını cur/ içindeki gerçek dosyalarla tutarsız yakalar. Restore edilen kutuda UID eşlemesi kayar.
Maildir Yedekleme ve Doğrulanabilir Geri Yükleme Testi | evilmail.pro — EvilMail Blog
`tmp/` ve indekslerin körü körüne kopyalanması. Yarım teslimatlar ve yeniden kurulabilir indeksler yedeğe girer; restore'da hem çakışma hem gereksiz şişme yaratır.
Hiç test edilmemiş depo. En tehlikelisi. Yedek yeşil görünür ama içinde ne olduğunu, açılıp açılmayacağını kimse bilmez.
Bu yazının sözü bir "yedek işi" kurmak değil. Söz şu: her hafta otomatik olarak bir sandbox'a geri yüklenen, `doveadm` ile kanıtlanan ve "en son ne zaman gerçekten restore edildi?" sorusuna bir sayıyla cevap veren bir kurtarma hattı kurmak. Snapshot ve offsite depo herkesin bildiği kısım; asıl değer sağ yarıda.
Maildir anatomisi: neyi yedekle, neyi atla
Bir Maildir kutusu üç dizinden ibaret gibi görünür ama asıl kritik olan, yanındaki meta dosyalardır. Teslimat atomiktir: MDA önce mesajı tmp/ altına yazar, fsync eder, sonra new/ altına rename eder. IMAP istemcisi görünce mesaj cur/ altına taşınır ve dosya adının sonuna bayrak eklenir.
Bayraklar dosya adının içinde kodlanır. :2, bilgi alanından sonrası: :2,S = Seen, :2,RS = Replied+Seen, :2,FS = Flagged+Seen, :2,T = silinmek üzere işaretli, :2,D = taslak. Bu son ek kaybolursa kutunun tüm okundu/yanıtlandı durumu sıfırlanır — kullanıcı için felaket, sizin için "yedek çalıştı ama yanlış çalıştı" davası.
Yedeklenmesi zorunlu olan meta dosyalar:
dovecot-uidlist — UID ile dosya adı eşlemesi. Bu olmadan istemciler her mesajı yeni sanar.
dovecot-keywords — özel etiketlerin adları.
maildirsize — Maildir++ kota sayacı.
subscriptions — IMAP klasör abonelikleri.
Atlanacaklar (yeniden kurulabilir, restore edilirse zarar verir):
dovecot.index, dovecot.index.cache, dovecot.index.log, dovecot.index.log.2 — Dovecot bunları uidlist ve mesaj dosyalarından yeniden üretir. Restore ederseniz eski indeks yeni UID'lerle çakışır.
tmp/ — her zaman hariç. İçinde yarım teslimatlar var.
Snapshot katmanı: crash-consistent yetmez, app-consistent lazım
Dosya sistemi snapshot'ı tek başına eksiktir. Maildir'in atomik teslimatı cur/ ve new/ içindeki mesaj dosyalarını güvende tutar, ama dovecot-uidlist bir teslimat ya da EXPUNGE ortasında güncellenirken snapshot alınırsa, uidlist ile fiziksel dosyalar arasında bir an için uyumsuzluk yakalanabilir. Sonuç: restore'da yeniden indekslenen bir kutu ve kaybolmuş UID sürekliliği.
İki doğru yaklaşım var:
App-consistent kopya çıkar, snapshot'ı onun üstünden al. Dovecot'un dsync altyapısı bunun için var:
bash
# Kutuyu uidlist ile uyumla (indeksi tazeler)
doveadm force-resync -u [email protected] '*'
# Ya da tutarlı, bağımsız bir kopya üret
doveadm backup -u [email protected] maildir:/backup/staging/test
ZFS/Btrfs snapshot + doveadm senkronu. Canlı vmail dataset'inde önce resync yapıp hemen ardından snapshot almak pratikte tutarlı bir görüntü verir. Snapshot atomiktir ve saniyeler sürer — canlı trafiği durdurmaz.
LVM tarafında lvcreate -L 5G -s -n vmail_snap /dev/vg0/vmail, Btrfs tarafında btrfs subvolume snapshot -r /var/mail /var/mail/.snap/$(date +%F) aynı işi görür. Hangisi olursa olsun kural sabit: snapshot'ı restic'e verilecek tutarlı bir kaynak olarak kullan, canlı Maildir'e doğrudan restic salma.
Offsite şifreli depo: restic ile dedup + retention
3-2-1 kuralı: en az 3 kopya, 2 farklı ortam, 1'i offsite. Snapshot yerelde ilk iki kopyayı verir; offsite şifreli depo üçüncüyü. Milyonlarca küçük dosyadan oluşan bir Maildir'de restic (ya da borg) çıplak rsync'e göre iki net üstünlük sağlar: blok seviyesinde dedup ve şifreleme. Aynı mesaj birden fazla kutuda ya da bir taşımadan sonra iki yerde duruyorsa disk üzerinde bir kez saklanır.
Depo bütünlüğünü düzenli tara. restic check metadata'yı doğrular; asıl bit-çürümesini yakalamak için veriyi de oku, ama her gece hepsini okumak pahalı olduğundan haftaya böl:
bash
# Her gün verinin 1/7'sini oku; hafta sonunda tüm depo taranmış olur
restic check --read-data-subset=1/7
Otomasyonu systemd timer ile kur
Cron çalıştırır ama gözlemlemez. systemd timer başarısızlığı journald'a yazar, OnFailure= ile alarm ünitesi tetikler ve Persistent=true ile makine kapalıyken kaçan çalıştırmayı telafi eder. Sıralı hat tek scriptte: doveadm force-resync → ZFS snapshot → restic backup → restic forget --prune.
Çoklu domain ortamında her kutu için ayrı iş kurmayın; tek vmail dataset'ini bütün olarak yedekleyin. /var/mail/vhosts/{domain}/{user}/ yapısı tek bir kaynağın altında olduğundan snapshot bütün domainleri tutarlı bir anda yakalar, restic dedup zaten tekrarları eler.
Asıl mesele: haftalık otomatik ve doğrulanabilir geri yükleme
İşte makalenin çekirdeği. Yukarıdaki her şey "yedek almak"tı. Şimdi onu her hafta gerçekten geri yüklüyoruz — canlı sistemi kirletmeden, ayrı bir sandbox yolunda.
bash
restic restore latest \
--target /var/mail/restore-test \
--include /var/mail/vhosts/evilmail.pro/test
# restic hedef dizinin altında tam yolu korur:
SANDBOX=/var/mail/restore-test/var/mail/vhosts/evilmail.pro/test
Doğrulama üç katman, hepsi yeşil olmadan test başarılı sayılmaz:
1. Mesaj sayısı. Kaynak snapshot'la restore edilenin dosya sayısı birebir tutmalı:
bash
find "$SANDBOX"/{cur,new} -type f | wc -l
# Snapshot içindeki dosya sayısıyla karşılaştır
2. İçerik checksum manifesti. Yedek alınmadan önce üretilmiş sha256 listesini restore sonrası doğrula. Tek bir bozuk mesaj bile yakalanır:
bash
# Yedek öncesi (kaynak kutunun içinde):
cd /var/mail/vhosts/evilmail.pro/test
find cur new -type f -exec sha256sum {} + > manifest.sha256
# Restore sonrası (sandbox'ta, aynı göreli yollarla):
cd "$SANDBOX" && sha256sum -c manifest.sha256
3. Uygulama doğrulaması. En önemlisi. Dosyalar sağlam olabilir ama Dovecot yine de açamayabilir. Doğrulamanın canlı kutuya bulaşmaması için mail konumunu sandbox'a sabitleyin (-o mail=maildir:...); force-resync hatasız dönmeli ve bilinen bir mesaj gerçekten çekilebilmeli:
bash
doveadm -o mail=maildir:"$SANDBOX" force-resync -u verify@localhost '*' # çıkış kodu 0
doveadm -o mail=maildir:"$SANDBOX" search -u verify@localhost ALL | wc -l # beklenen sayı
doveadm -o mail=maildir:"$SANDBOX" fetch -u verify@localhost 'guid text' mailbox INBOX uid 1
Üçü de geçtiyse restore kanıtlanmıştır. Test biter bitmez /var/mail/restore-test sandbox'ını temizleyin ki bir sonraki koşu temiz başlasın.
Kanıtı ölç: son doğrulanmış geri yükleme metriği ve alarm
Yedeğiniz yeşil yanabilir ama tek önemli soru şudur: "en son ne zaman gerçekten restore edilip doğrulandı?" Bunun cevabı bir cümle değil, bir sayı olmalı. Doğrulama scripti üç katmanı da geçtiğinde bir zaman damgası yazsın:
Prometheus tarafında alarmın kritik özelliği, sadece bozuk restore'u değil doğrulamanın hiç yapılmamasını da yakalamasıdır. Sessiz başarısızlık en tehlikeli senaryodur çünkü hiçbir hata mesajı üretmez:
# Doğrulama 8 günden eskiyse tetikle (haftalık + 1 gün pay)
time() - maildir_restore_verified_timestamp > 691200
RPO hedefiniz günlük snapshot'la 24 saat. RTO'yu tahmin etmeyin, ölçün: restore + force-resync süresini script'te zamanlayıp bir metrik olarak yazın. Felaket anında "ne kadar sürer?" sorusunun cevabı elinizde hazır olsun.
Felaket provası checklist
[ ] Yedek doveadm ile app-consistent kopyadan mı alınıyor (canlı Maildir'e doğrudan restic yok)?
[ ] tmp/ ve dovecot.index* dışlanıyor mu (--exclude)?
[ ] dovecot-uidlist, dovecot-keywords, maildirsize, subscriptions yedeğe dahil mi?
[ ] restic check --read-data-subset son 7 günde tüm depoyu taradı mı?