Mailcow: пересылка через alias не идёт через relay
АйТи Фреш
Linux, Docker и DevOps

Внешняя пересылка через alias в mailcow не идёт через мой SMTP relay, а пересылка из SOGo — идёт: почему

Автор: , директор ООО «АйТи-Фреш» · · ~12 мин чтения
Пересылка письма через alias в mailcow обходит SMTP relay, а пересылка через Sieve и SOGo идёт через тот же relay
Одно и то же письмо, два разных маршрута — потому что это два разных механизма пересылки.

Если внешняя пересылка через alias в mailcow идёт напрямую в интернет мимо вашего SMTP relay, а такая же по смыслу пересылка, настроенная через SOGo, честно уходит через relay — это не баг, а архитектурная разница двух механизмов. Разбираю, почему alias и Sieve redirect ведут себя по-разному, и как настроить внешнюю пересылку так, чтобы она гарантированно шла через нужный маршрут.

Кейс: «Афиша Групп» и письма, которые не долетали до партнёра

«Афиша Групп» — рекламное агентство на 40 рабочих мест, у них почта на mailcow, которую мы обслуживаем. У бухгалтерии настроена внешняя пересылка копий всех счетов на почту бухгалтера на аутсорсе — партнёрской компании, которая ведёт учёт по договору. Пересылку сделали через alias в mailcow: адресат внутри плюс внешний адрес бухгалтера в списке получателей одного алиаса.

У агентства на домене настроен sender-dependent relayhost — все исходящие письма идут через внешний smarthost на порту 2525, а не напрямую с сервера mailcow на порт 25 получателя. Причина обычная: у хостинг-провайдера ограничена прямая отправка с IP сервера, плюс так проще следить за репутацией и логами исходящей почты в одном месте.

Через несколько недель партнёрская бухгалтерия сообщила, что часть писем до них не доходит — без предсказуемой закономерности. При этом автоматическая пересылка, которую для пробы включили в настройках SOGo у одного из ящиков бухгалтерии, долетала стабильно. В логах Postfix картина прояснилась быстро: копии по алиасу уходили не на smarthost, а напрямую на MX получателя по порту 25, который у провайдера фильтруется. Разница в поведении для одинаковой на первый взгляд задачи и стала поводом разобраться, что происходит на самом деле.

Два разных механизма пересылки в mailcow — и это не оговорка в интерфейсе

В mailcow есть подсказка в разделе Alias, которая часто ускользает от внимания при первой настройке: для внешней пересылки рекомендуется использовать не classic-alias, а Sieve-фильтры или встроенную пересылку SOGo. Это не стилистическая рекомендация, а указание на разное поведение двух механизмов на уровне транспорта письма.

Alias в mailcow на уровне Postfix — это классическое расширение списка получателей в момент приёма письма (virtual alias maps): Postfix видит адрес алиаса и заменяет его списком реальных адресов, внутренних и внешних. Ключевой момент — конвертный отправитель (MAIL FROM) при этом не меняется: это по-прежнему внешний отправитель, например поставщик, приславший счёт. А mailcow выбирает sender-dependent transport через параметр Postfix sender_dependent_default_transport_maps, то есть по адресу и домену конвертного отправителя. Домена поставщика в mailcow нет, транспорт не находится, и копия уходит обычной доставкой — напрямую на MX получателя по порту 25.

Пересылка через Sieve (в том числе через встроенную опцию пересылки в SOGo, которая сохраняется как Sieve-скрипт ящика) работает иначе: директива redirect отправляет письмо заново через Postfix, и конвертным отправителем становится адрес вашего ящика — так настроен Dovecot в mailcow. Для Postfix это уже исходящее письмо вашего домена, поэтому к нему применяется выбранный для домена sender-dependent transport, то есть ваш relayhost. Заголовок From при этом остаётся исходным — меняется только конверт.

Разница между ними — ровно та же, что и между обычной серверной фильтрацией почты через Sieve и списком рассылки на уровне транспорта: одно живёт на уровне почтового ящика и его правил, другое — на уровне таблицы маршрутизации адресов, которую видит Postfix ещё до того, как письмо вообще становится «принадлежащим» конкретному пользователю mailcow.

Сравнение маршрутов доставки при пересылке через classic alias и через Sieve redirect в mailcow
Alias минует submission_host и relayhost, Sieve redirect — нет.

Где технически расходятся маршруты — dovecot.conf и submission_host

В эталонной конфигурации Dovecot, которую использует mailcow (data/conf/dovecot/dovecot.conf), задан параметр sieve_redirect_envelope_from = recipient — при пересылке через Sieve конвертным отправителем становится адрес получателя, то есть вашего ящика, а не исходный внешний отправитель. Там же задан submission_host = postfix:588 — это означает, что письма, сформированные Sieve-скриптом (в том числе через redirect), Dovecot передаёт не напрямую в интернет, а внутреннему Postfix через порт службы submission на 588, где к ним и применяются все правила транспорта данного mailcow, включая relayhost конкретного домена или ящика.

# фрагмент эталонного dovecot.conf, за который отвечает поведение redirect
sieve_redirect_envelope_from = recipient
submission_host = postfix:588

Alias же обрабатывается на уровне Postfix ещё до того, как письмо вообще попадает в Dovecot, — submission_host и sieve_redirect_envelope_from к нему не имеют никакого отношения. Postfix через virtual alias maps просто подменяет адрес получателя списком адресов, а конверт письма оставляет прежним. Дальше для внешнего адреса из этого списка Postfix ищет транспорт по конвертному отправителю — и, не найдя домена отправителя среди своих, доставляет письмо напрямую.

Именно поэтому alias-пересылка на внешний адрес логически ближе к «ещё одному получателю входящего письма», а не к «новому исходящему письму от имени вашего домена» — и это ключевая причина, почему sender-dependent transport, который в mailcow привязывается к домену отправителя, к ней не применяется: у такого письма отправитель в конверте чужой. У письма, прошедшего через Sieve redirect и submission, отправитель в конверте — ваш ящик.

Отдельно стоит отметить: это не значит, что alias-пересылка обязательно сломана или всегда обходит relay — на части конфигураций, где relayhost задан не как sender-dependent, а как единственный transport по умолчанию для всего сервера, разницы в маршруте может не быть вовсе. Но как только relayhost привязан к домену через Sender-dependent transports (а в интерфейсе mailcow он настраивается именно так, по домену), alias-копии писем от внешних отправителей под это правило не попадают. Письма, которые сотрудники пишут на алиас изнутри, под него при этом попадут — отсюда ощущение «часть доходит, часть нет».

Таблица сравнения alias и Sieve redirect в mailcow по уровню обработки, применению relayhost и сохранению письма
Три ключевых отличия объясняют, почему письма расходятся разными маршрутами.

Подтверждение на форуме: alias уходил на порт 25 напрямую

На форуме сообщества mailcow в теме «External email forwarding why not alias?» автор начинает с подсказки из самого интерфейса mailcow: в разделе Alias администраторам рекомендуют использовать для пересылки Sieve или встроенный форвардинг SOGo вместо классического alias именно для внешних адресов — это зафиксированная в самом продукте рекомендация, а не случайная находка одного пользователя.

Один из участников той же темы описывает ровно наш симптом: в интерфейсе всё выглядело правильно, но внешние копии по алиасу не уходили. В логах он видел, что они направлялись на порт 25, заблокированный его провайдером, тогда как sender-dependent transport был настроен на 2525. Пересылка, включённая в SOGo, заработала сразу — он называет это «исправлением за 30 секунд» и предполагает, что проблема в неприменении sender-dependent transport к пересылке. Разбор выше объясняет, почему так: транспорт выбирается по конвертному отправителю.

Логика здесь симметрична тому, что видел администратор «Афиша Групп»: пересылка через SOGo (то есть фактически через Sieve redirect) с самого начала уходила через relay стабильно, а alias — нет. Это не совпадение и не случайная удача одной настройки, а прямое следствие разницы в транспортном пути, который проходит письмо в каждом из двух случаев.

Похожая путаница встречается и в обратную сторону — когда администратор, наоборот, ожидает, что письмо гарантированно пройдёт проверку на open relay на внешнем шлюзе просто потому, что ушло с корпоративного домена. Оба случая — про одно и то же: транспортные правила применяются не к смыслу письма, а к конкретному техническому пути, которым оно фактически передаётся между серверами.

Как настроить внешнюю пересылку так, чтобы она шла через relay

Правильная замена classic-alias на внешний адрес — Sieve-правило внутри самого ящика с директивой redirect, а не alias на уровне домена. В SOGo это делается без единой строчки кода: в настройках почты есть раздел пересылки (Forward), где включается пересылка на внешний адрес с сохранением копии в ящике; для условной пересылки — фильтр с действием перенаправления. Названия пунктов немного отличаются между версиями SOGo, но результат один — Sieve-скрипт ящика.

require ["copy"];

redirect :copy "buhgalter-partner@example.com";

Ключевая деталь синтаксиса — модификатор :copy: без него redirect отправляет письмо во внешний ящик и одновременно прекращает дальнейшую обработку внутри mailcow, то есть письмо не остаётся во внутреннем ящике получателя. С :copy исходное письмо продолжает обычную доставку во внутренний ящик, а наружу параллельно уходит его копия — именно такое поведение нужно для сценария «переслать копию бухгалтеру, не теряя письмо у себя».

После перехода с alias на такой Sieve-фильтр письмо, попадающее к получателю, формируется как новое исходящее сообщение конкретного ящика, с вашим ящиком в качестве конвертного отправителя передаётся Dovecot через submission_host = postfix:588 и дальше маршрутизируется Postfix по тем же правилам transport и relayhost, что и любое обычное письмо, отправленное пользователем вручную — включая настроенный sender-dependent relayhost на порту 2525.

Одна оговорка, о которой я предупреждаю заранее. После redirect получатель видит письмо с исходным From (например, поставщика), а конверт и IP — ваши. SPF у получателя будет проверяться по вашему домену и relay, так что его нужно держать корректным; а вот подпись DKIM исходного отправителя сохраняется только если письмо по пути не изменили. Поэтому пересылку я проверяю не только по логу Postfix, но и по заголовкам Authentication-Results в ящике получателя.

Чек-лист из четырёх шагов настройки внешней пересылки письма через Sieve-фильтр в SOGo вместо alias
Четыре шага — и пересылка гарантированно идёт тем же маршрутом, что и вся исходящая почта.

Когда classic-alias всё же уместен

Не стоит делать вывод, что alias в mailcow — «неправильный» механизм в принципе. Для пересылки между внутренними ящиками на одном домене или для групповых адресов вида info@domain.ru, которые разворачиваются в несколько внутренних сотрудников, alias работает ровно так, как задуман, и никакой разницы в маршрутизации там нет — все получатели внутренние, вопрос relayhost для входящей почты на них попросту не встаёт.

Разница проявляется именно на внешних адресах в списке получателей alias, да ещё и при активном sender-dependent relayhost на домене. Если у вас нет настроенного relayhost и вся исходящая почта и так уходит напрямую с сервера — поведение alias и Sieve redirect с точки зрения маршрута практически не отличается, и решение можно принимать исходя из удобства администрирования, а не транспортной логики.

Практическое правило, которым я пользуюсь: любую пересылку на внешний адрес, где важна гарантия конкретного маршрута (репутация IP, лимиты провайдера, логирование через smarthost), настраиваю через Sieve-фильтр в ящике, а alias оставляю для группировки писем строго внутри своего домена. Это правило легко проверить один раз на новом клиенте и больше к нему не возвращаться — в отличие от ситуации, когда несоответствие маршрута всплывает случайно, через жалобу партнёра на пропавшие письма.

Итог: что изменилось у «Афиша Групп»

Мы перевели пересылку счетов бухгалтеру-партнёру с alias на Sieve-фильтр с модификатором :copy через SOGo. После перехода письма стали идти по тому же маршруту, что и обычная исходящая почта агентства — через relay на порту 2525, с теми же логами и той же репутацией отправителя, что заметно упростило разбор любых будущих проблем с доставкой в одном месте.

За месяц наблюдения после перехода ни одно из пересылаемых писем не потерялось и не ушло по непредсказуемому маршруту — переменная, из-за которой раньше периодически терялась часть писем, просто перестала существовать: транспорт стал одним и тем же для всей исходящей почты домена, независимо от того, обычное это письмо или пересылка.

Если у вас есть внешняя пересылка через alias и настроен sender-dependent relayhost — стоит один раз проверить, действительно ли эта пересылка идёт через тот же маршрут, что и остальная исходящая почта. Проверка занимает несколько минут, а последствия невидимой потери писем клиенту или партнёру обычно обходятся куда дороже.

Частые вопросы

Почему alias для внешнего адреса не использует настроенный relayhost?

Alias разворачивается в Postfix на этапе приёма письма, а конвертный отправитель остаётся внешним. mailcow выбирает sender-dependent transport по домену конвертного отправителя (sender_dependent_default_transport_maps); чужой домен в нём не найден, и копия уходит напрямую на MX получателя.

Почему пересылка через SOGo работает по-другому?

Пересылка SOGo сохраняется как Sieve-скрипт ящика с redirect. Dovecot отправляет такое письмо через submission_host (postfix:588), а из-за sieve_redirect_envelope_from = recipient конвертным отправителем становится ваш ящик — поэтому срабатывает транспорт вашего домена, то есть relayhost.

Как правильно настроить внешнюю пересылку копии письма, не теряя оригинал?

Через Sieve-правило внутри ящика с redirect и сохранением копии (в скрипте — redirect :copy или redirect плюс keep). В SOGo это пересылка в настройках почты с отметкой о сохранении копии либо фильтр с действием перенаправления. Без копии исходное письмо не останется во внутреннем ящике.

Означает ли это, что classic-alias в mailcow не нужно использовать вообще?

Нет, для пересылки между внутренними ящиками одного домена или для групповых адресов alias работает штатно и без расхождений в маршруте. Разница проявляется только на внешних адресах при активном sender-dependent relayhost.

Как проверить, что пересылка действительно уходит через relay, а не напрямую?

Отправьте тестовое письмо с внешнего ящика на alias и на ящик с Sieve-пересылкой и посмотрите в логе Postfix поле relay= у строк доставки: `docker compose logs --since 30m postfix-mailcow | grep relay=`. При работе через relay там будет адрес smarthost и его порт (например, :2525), а не MX получателя на :25.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи