Внешняя пересылка через alias в mailcow не идёт через мой SMTP relay, а пересылка из SOGo — идёт: почему
Если внешняя пересылка через 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.
Где технически расходятся маршруты — 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:588Alias же обрабатывается на уровне 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 уходил на порт 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 в ящике получателя.
Когда 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.
Источники
- mailcow docs: Relayhosts — Механизм sender-dependent transports: настройка релейхоста в Configuration and Details → Routing и привязка к домену через Mail setup → Domains → Sender-dependent transports. https://docs.mailcow.email/manual-guides/Postfix/u_e-postfix-relayhost/
- mailcow-dockerized: эталонный dovecot.conf — Дословные значения sieve_redirect_envelope_from = recipient и submission_host = postfix:588 в эталонной конфигурации Dovecot проекта. https://github.com/mailcow/mailcow-dockerized/blob/master/data/conf/dovecot/dovecot.conf
- mailcow community: External email forwarding why not alias? — Обсуждение и встроенная в интерфейс mailcow рекомендация использовать Sieve/SOGo-форвардинг вместо classic-alias для внешней пересылки. https://community.mailcow.email/d/1233-external-email-forwarding-why-not-alias
- mailcow-dockerized: data/conf/postfix/main.cf — Выбор транспорта через sender_dependent_default_transport_maps (по конвертному отправителю), relayhost пуст по умолчанию; порт 588 в master.cf. https://github.com/mailcow/mailcow-dockerized/blob/master/data/conf/postfix/main.cf
- Postfix: sender_dependent_default_transport_maps — Поиск транспорта по адресу конвертного отправителя и @domain. https://www.postfix.org/postconf.5.html#sender_dependent_default_transport_maps



