Политика хранения писем: автоудаление, архивирование и баланс между compliance и потребностями
«Храним всё навсегда» — это не бонус для клиента, а необработанный риск и юридическая мина. Разбираем, как построить многоуровневую retention-политику на Dovecot: doveadm expunge по классам почты, сжатый архивный стор, legal hold и temp-email как предельный случай осознанно короткого TTL.
EvilMail Team4 августа 2026 г.12 мин чтения
Почему «хранить всё» — это не политика, а её отсутствие
Знакомая картина: корпоративный ящик разросся до 40 ГБ, doveadm search по нему думает секундами, бэкап INBOX тянется всю ночь, а на входящий запрос об удалении персональных данных по GDPR Art. 17 ответить нечем — переписка человека размазана между INBOX, десятком папок, Archive и Trash без единого правила. Никто не решал «хранить вечно». Просто никто не решал ничего, и «вечно» получилось по умолчанию.
В этом и корень: отсутствие retention-политики маскируется под заботу о клиенте («вдруг понадобится»), а на деле это накопленный риск. Каждое письмо, которое вы храните дольше необходимого, — это данные, которые могут утечь, которые придётся отдать по судебному запросу и которые вы обязаны найти и стереть по требованию субъекта. Хранение — это обязательство, а не актив.
Рабочая retention-политика декларативна: описана как набор правил, применяется автоматически без участия человека и различается по классам почты. Дальше — три силы, которые тянут эти правила в разные стороны, и инженерия, которая примиряет их на связке Dovecot + doveadm + Sieve.
Три противоречащих режима хранения
Единого «правильного срока» не существует, потому что на политику давят три несводимые силы.
Первая — юридический compliance, и он двусторонний. GDPR Art. 5(1)(e) вводит принцип storage limitation: персональные данные хранятся «не дольше, чем необходимо для целей обработки». Art. 17 закрепляет право на удаление. Оба пункта давят *удалять*. Но налоговое и бухгалтерское право тянет в обратную сторону: во многих юрисдикциях первичные документы и связанная переписка хранятся 5–10 лет, кадровые документы — годы после увольнения. Одно и то же законодательство одновременно требует стереть и сохранить — разные данные, разные основания.
Вторая — стоимость и производительность стора. Горячий IMAP-стор на быстрых дисках стоит денег, а размер ящика напрямую бьёт по скорости поиска, индексации и времени бэкапа.
Третья — реальные потребности пользователя, которые почти всегда скромнее, чем «оставьте мне всё на всякий случай».
Свести их к одному числу нельзя. Примиряют разделением почты на классы:
Ключевая колонка матрицы — «основание». На каждый класс должно быть записано, *почему* именно этот срок: «уведомления — 60 дней, оперативная надобность, персональных данных нет»; «финансовая переписка — 10 лет, п. такой-то налогового закона». Без основания срок — выдумка, которую не защитить ни перед регулятором, ни перед субъектом данных.
Retention как код: doveadm expunge и cron вместо ручной чистки
Ручная чистка не масштабируется и невоспроизводима. Retention должна быть кодом. На Dovecot базовый инструмент — doveadm expunge с критерием savedbefore.
Деталь, на которой спотыкаются: savedbefore смотрит на INTERNALDATE — дату *попадания письма в ящик*, а sentbefore берёт заголовок Date:, которым отправитель управляет и может подделать. Для retention корректен именно savedbefore — вы удаляете по возрасту хранения, а не по заявленной дате отправки.
Всегда сначала search (сухой прогон), потом expunge:
bash
# 1. Сухой прогон — что именно попадёт под нож
doveadm search -u [email protected] mailbox INBOX savedbefore 90d
# 2. Само удаление писем старше 90 дней
doveadm expunge -u [email protected] mailbox INBOX savedbefore 90d
# 3. Отдельное, более агрессивное правило для Trash
doveadm expunge -u [email protected] mailbox Trash savedbefore 30d
Trash — всегда отдельное правило с коротким сроком: корзина по определению временна, держать в ней письма 90 дней бессмысленно.
Обёртка в cron с логированием и проверкой кода возврата:
bash
#!/usr/bin/env bash
set -euo pipefail
LOG=/var/log/mail-retention/$(date +%F).log
exec >>"$LOG" 2>&1
echo "=== retention run $(date -Is) ==="
while IFS=: read -r user days; do
echo "-- $user : INBOX savedbefore ${days}d"
# исключаем письма под legal hold (см. ниже)
doveadm expunge -u "$user" mailbox INBOX savedbefore "${days}d" NOT keyword '$Hold' \
|| echo "WARN: expunge failed for $user (rc=$?)"
done < /etc/mail-retention/policy.tab
echo "=== done $(date -Is) ==="
Два предупреждения для больших сторов. Первое: в формате mdbox expunge только *помечает* письма удалёнными — место на диске не освобождается, потому что mdbox складывает несколько писем в один файл-storage. Реальное освобождение делает отдельный проход doveadm purge -u user@domain. Второе: при массовом прогоне поднимите mail_max_lock_timeout и гоняйте операции через doveadm-server, иначе упрётесь в блокировки индексов.
Автоархивирование: тёплый стор и разделение горячих данных от холодных
«Удалить» и «убрать из горячего стора» — разные операции, и путать их дорого. Письмо двухлетней давности по договору удалять нельзя, но и держать его на быстром NVMe рядом с сегодняшним INBOX незачем. Оно должно уехать в холодный сжатый стор.
Сжатие включается на уровне plugin — новые письма пишутся в gzip, чтение старых форматов (gz/bz2/zstd) остаётся прозрачным:
ALT= задаёт альтернативный (дешёвый, медленный) стор. Перенос холодных данных без удаления — altmove:
bash
# Всё, что старше полугода, физически переезжает в /cold без удаления
doveadm altmove -u [email protected] savedbefore 180d
Правило раскладки можно строить и на Sieve — раскладывать входящее сразу по папкам-архивам через fileinto; для условий по дате понадобятся расширения date и variables.
Компромисс, о котором забывают: архив ≠ забыть. Данные в холодном сторе так же попадают под retention и под GDPR-запросы. Архивирование снижает стоимость и ускоряет INBOX, но не выводит письма из-под ваших обязательств. У архива должен быть собственный срок и собственное правило expunge — просто с бóльшим порогом.
Legal hold: когда удаление обязано остановиться
У автоматического удаления должно быть исключение. Если на ящик наложен судебный или регуляторный hold, письма нельзя трогать, даже если формальный срок retention истёк, — иначе это уничтожение доказательств.
Механика на Dovecot — кастомный keyword, который cron-скрипт обязан пропускать:
bash
# Наложить hold на конкретные письма (по отправителю, теме, дате)
doveadm flags add -u [email protected] '$Hold' mailbox INBOX from [email protected]
# С этого момента expunge с 'NOT keyword $Hold' их не тронет
Юридический нюанс: legal hold законно перебивает право на удаление — регулятор или суд имеют приоритет над запросом субъекта данных. Но это перекрытие нужно документировать: кто наложил hold, когда, на каком основании, когда снял. Аудит-лог наложения и снятия hold — не бюрократия, а ваша защита, если субъект оспорит отказ в удалении.
Temp-email как предельный случай retention
Одноразовый ящик evilmail.pro — противоположный полюс той же шкалы. TTL здесь не годы, а минуты и часы. Инструмент буквально тот же — savedbefore и expunge, — но порог измеряется минутами, а запуск идёт не ночным cron, а агрессивно и постоянно.
Осознанно короткий срок жизни — это приватность по умолчанию: минимум поверхности для утечки, отсутствие накопленного массива, который имело бы смысл взламывать. Нельзя украсть переписку, которой уже нет. Temp-email наглядно доказывает главный тезис статьи: правильный срок хранения диктуется *назначением* ящика, а не привычкой хранить. Ящик для одного кода подтверждения не должен пережить сам код.
Что должно быть в вашей политике: чеклист
Классификация почты по срокам — минимум 4 класса (транзакционная / переписка / финансово-кадровая / temp), а не единое число.
Задокументированное правовое основание на каждый класс — почему именно этот срок.
Автоматизация `expunge`/`altmove` с обязательным сухим прогоном doveadm search и логами каждого запуска.
Отдельное, более короткое правило для Trash — 30 дней, не общий срок.
Механизм legal hold через keyword с NOT keyword $Hold в скриптах и аудит-логом наложения и снятия.
Процедура ответа на GDPR-запрос — выгрузка и форсированное удаление в обход срока, включая архив.
`doveadm purge` в расписании — иначе mdbox не вернёт место после expunge.
Согласование с бэкапами — слепая зона: письмо, удалённое по Art. 17, но лежащее в бэкапе 12 месяцев, — это невыполненное обязательство. Ротация бэкапов должна догонять политику удаления.
Ежегодный пересмотр сроков — законы и потребности меняются, вчерашние 90 дней сегодня могут оказаться неверными.
Retention — это не галочка в чеклисте юриста и не «удалим, когда закончится место». Это код: декларативные правила по классам, применяемые автоматически, с исключением на hold и с честным ответом на вопрос «почему именно столько». Всё остальное — это «вечно» по умолчанию, то есть риск, который однажды предъявят к оплате.