Два сайта — два SMTP-аккаунта у одного провайдера: как научить Postfix не путать логины
Ситуация, которую я разбираю у клиентов регулярно: на одном сервере живут два сайта, у каждого свой домен и свой почтовый ящик у одного и того же провайдера, а Postfix упрямо авторизуется под одним логином. Половина писем улетает не от того отправителя, вторая половина отбивается с «Sender address rejected». Ниже — как это устроено внутри, рабочая конфигурация, разбор конкретного случая с цифрами и список мест, где всё ломается из раза в раз.
Почему одного relayhost и одного пароля не хватает
Типовая отправляющая конфигурация выглядит так: relayhost = [smtp.provider.ru]:587, в /etc/postfix/sasl_passwd одна-единственная строка с логином и паролем, smtp_sasl_auth_enable = yes. Это работает ровно до момента, пока с сервера пишет один домен. Как только на той же машине появляется второй сайт со своим ящиком у того же провайдера, конструкция ломается тихо и некрасиво.
Механика простая, если прочитать документацию буквально. Параметр smtp_sasl_password_maps описан как «lookup tables with one username:password entry per sender, remote hostname or next-hop domain» — то есть ключом поиска может быть отправитель, имя хоста или домен следующего перехода. Но дальше идёт ключевая оговорка: «Per-sender lookup is done only when sender-dependent authentication is enabled». По умолчанию smtp_sender_dependent_authentication = no, и поиск по отправителю не выполняется вообще. Ключ — только назначение. А назначение у обоих сайтов одно и то же: [smtp.provider.ru]:587. Одна запись в таблице, один логин, и Postfix даже не подозревает, что вы хотели чего-то другого.
Стоит отдельно проговорить, чего в этой схеме нет. Postfix не смотрит на то, какой сайт сгенерировал письмо, не различает пользователей php-fpm и не заглядывает в заголовки. Для маршрутизатора существует ровно одна характеристика письма, по которой можно принять решение об учётке, — адрес отправителя конверта. Всё остальное надо к этому адресу привести, и обычно именно на этом шаге и застревают: конфиг Postfix правильный, а приложение отдаёт не тот конверт.
Наружу это выходит двумя способами. Строгий провайдер отбивает письмо на этапе MAIL FROM с ответом вида 553 5.7.1 Sender address rejected: not owned by user — и вы хотя бы видите проблему в логе. Провайдер помягче молча переписывает конверт в адрес аутентифицированного аккаунта: письма магазина уходят от имени корпоративного ящика, ответы клиентов падают не туда, суточный лимит одного ящика съедается за двоих, а статистику доставки по сайтам собрать невозможно в принципе. Второй вариант хуже — он не болит, пока кто-нибудь не спросит, почему клиенту ответили с чужого адреса.
- Письма второго сайта уходят от адреса первого — клиент отвечает не туда.
- `553 5.7.1 Sender address rejected` или `550 5.7.60 SMTP; Client does not have permissions to send as this sender`.
- Суточный лимит отправки провайдера выбирается вдвое быстрее и без объяснений.
- DMARC-отчёты показывают чужой домен в конверте — SPF-выравнивание разъезжается.
- Разбор «какой сайт отправил это письмо» превращается в гадание по логам приложения.
Три параметра и две таблицы: рабочий конфиг
Postfix умеет отдельные аккаунты для отдельных отправителей начиная с версии 2.3 — то есть с 2006 года. Никакой экзотики, никаких патчей, всё в штатном SASL_README. Нужны три параметра в main.cf и две таблицы. Вот база, от которой я всегда стартую:
# /etc/postfix/main.cf
smtp_sasl_auth_enable = yes
smtp_sender_dependent_authentication = yes
sender_dependent_relayhost_maps = hash:/etc/postfix/sender_relay
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt
smtp_sasl_tls_security_options = noanonymous
relayhost = [smtp.provider.ru]:587Дальше — таблицы. В sender_relay мы говорим, куда сдавать письмо для конкретного отправителя, в sasl_passwd — под каким логином. Обратите внимание на последнюю строку sasl_passwd: это учётка для общего relayhost, она нужна для всего, что не попало ни в одно правило по отправителю.
# /etc/postfix/sender_relay
shop@shop.example.ru [smtp.provider.ru]:587
@shop.example.ru [smtp.provider.ru]:587
@corp.example.ru [smtp.provider.ru]:587
# /etc/postfix/sasl_passwd
shop@shop.example.ru shop@shop.example.ru:PasswordOne
@shop.example.ru shop@shop.example.ru:PasswordOne
@corp.example.ru info@corp.example.ru:PasswordTwo
# логин для общего relayhost
[smtp.provider.ru]:587 info@corp.example.ru:PasswordTwoПорядок поиска описан в мануале однозначно и его стоит держать в голове: «the Postfix SMTP client will search the SASL password file by sender address before it searches that same file by destination», а демон trivial-rewrite ищет relayhost по отправителю и использует глобальный relayhost «only as a final resort». Обе таблицы просматриваются по полному адресу конверта и по @domain — поэтому строка вида @shop.example.ru закрывает все ящики домена одним правилом, и это тот случай, когда я предпочитаю доменную запись поимённой. Отдельная тонкость: результат DUNNO в sender_dependent_relayhost_maps (Postfix 2.6 и новее) прекращает поиск, не переопределяя глобальный relayhost — удобно, когда для одного адреса надо явно вернуться к дефолту.
С конкретными провайдерами есть нюанс с портами, и его лучше решить до правки таблиц. У Яндекс Почты официальные параметры отправки — smtp.yandex.ru, порт 465 с SSL, либо 587, если клиент начинает соединение без шифрования и поднимает его через STARTTLS; логин — полный адрес ящика, пароль — не от аккаунта, а созданный пароль приложения. У Почты Mail.ru в справке для почтовых программ указан smtp.mail.ru, порт 465 с SSL/TLS, и тоже пароль для внешнего приложения вместо основного. Порт 587 работает с обычной схемой из примера выше (smtp_tls_security_level = encrypt, STARTTLS). Порт 465 — это не STARTTLS, а TLS с первого байта, и Postfix подключается к нему только с smtp_tls_wrappermode = yes (Postfix 3.0 и новее), который по документации требует smtp_tls_security_level = encrypt или строже. Без wrappermode соединение с 465 просто висит до таймаута и падает в deferred с невнятным lost connection.
# Вариант 1: всё через Яндекс на 587 (STARTTLS) — схема из примера выше
relayhost = [smtp.yandex.ru]:587
smtp_tls_security_level = encrypt
# Вариант 2: провайдер только на 465 (Mail.ru) — SUBMISSIONS/wrappermode
relayhost = [smtp.mail.ru]:465
smtp_tls_security_level = encrypt
smtp_tls_wrappermode = yes
# /etc/postfix/sasl_passwd — ключ повторяет форму relayhost буква в букву
[smtp.yandex.ru]:587 info@corp.example.ru:ПарольПриложения
@shop.example.ru shop@shop.example.ru:ПарольПриложения2Если в одном сервере смешиваются отправители на 587 и на 465, глобальный smtp_tls_wrappermode = yes ломает первых. Тогда я завожу в master.cf отдельный транспорт, например smtps-out unix - - n - - smtp -o smtp_tls_wrappermode=yes -o smtp_tls_security_level=encrypt, и направляю в него нужных отправителей через sender_dependent_default_transport_maps со значением вида smtps-out:[smtp.mail.ru]:465. Ключ в sasl_passwd при этом остаётся по адресу отправителя, а для назначения — строго [smtp.mail.ru]:465.
- `postmap /etc/postfix/sender_relay && postmap /etc/postfix/sasl_passwd` — обязательно после каждой правки.
- `chown root: /etc/postfix/sasl_passwd* && chmod 0600 /etc/postfix/sasl_passwd*` — пароли лежат открытым текстом.
- `postfix reload` — иначе изменения подхватятся «через минуту или около того».
- Тип таблицы (`hash:`, `lmdb:`, `cdb:`) проверяйте командой `postconf default_database_type`, в сборках он разный.
Главная ловушка: envelope sender — это не то, что в заголовке From
Девять из десяти обращений «я всё настроил по инструкции, а логин по-прежнему один» упираются в это. Обе таблицы ищутся по адресу отправителя конверта (envelope sender, он же MAIL FROM), а не по заголовку From:, который видит человек в почтовом клиенте. Сайт может рисовать в письме сколь угодно красивый From: Магазин <shop@shop.example.ru> — если в конверте стоит www-data@srv01.localdomain, Postfix честно не найдёт совпадения ни в sender_relay, ни в sasl_passwd и уедет на общий relayhost с общим логином.
Кто портит конверт чаще всего: PHP-функция mail() без пятого аргумента (-f), скрипты из cron, у которых отправитель собирается из имени юзера и hostname, самописные рассыльщики через sendmail -t, и любой софт, где в настройках есть только поле «От кого» без отдельного «Return-Path». В WordPress та же история: wp_mail() вызывает PHPMailer::setFrom() с третьим аргументом false, то есть меняет только заголовок, а конверт остаётся за sendmail_path и настройками PHP; в Bitrix отправка тоже идёт через mail() или собственный SMTP-модуль, и конверт надо проверять отдельно. Проверяется одной строкой в логе — смотрите поле from= у записи cleanup/qmgr, там именно конверт:
# что реально стоит в конверте
grep -E 'from=<' /var/log/mail.log | tail -20
# отправитель писем, застрявших в очереди
mailq | head
postcat -q <QUEUEID> | head -40Лечится в порядке предпочтения. Первое и правильное — заставить приложение подставлять нужный конверт: в PHP это mail.force_extra_parameters = -f shop@shop.example.ru в отдельном пуле php-fpm, или sendmail_path = /usr/sbin/sendmail -t -i -f shop@shop.example.ru. Второе, если до приложения не дотянуться, — sender_canonical_maps, но с открытыми глазами: он переписывает и конверт, и заголовки отправителя, то есть меняет видимого автора письма. Третье, самое грязное, но иногда единственное, — разнести сайты по разным системным пользователям и переписать отправителя по локальной части через регулярку. Я хожу этим путём только когда первые два закрыты.
- `sender_dependent_relayhost_maps` и `smtp_sasl_password_maps` ищутся по конверту и по `@domain`, не по заголовку.
- `postmap -q "shop@shop.example.ru" hash:/etc/postfix/sender_relay` — проверка ключа ровно так, как его ищет Postfix.
- Пустой конверт (`from=<>`) — это bounce, он всегда уйдёт по общему relayhost.
- `sender_canonical_maps` обрабатывается до `canonical_maps` и трогает заголовки — учитывайте при отладке.
Разбор из практики: магазин пряжи «Шерсть и нить», два сайта, один провайдер
Клиент — магазин пряжи «Шерсть и нить» на 48 рабочих мест: розничные точки, склад, интернет-магазин и офис; обслуживаем инфраструктуру целиком. На отдельном VPS (Debian 12, Postfix 3.7.11 из репозитория, nginx + php-fpm 8.2) живут два сайта: корпоративный сайт с оптовым прайсом и мастер-классами на corp.example.ru и интернет-магазин пряжи на shop.example.ru. Почта у обоих у одного провайдера, ящики разные, лимит — 500 писем в сутки на ящик. Обращение было сформулировано так: «покупатели не получают подтверждения заказов, а в почту бухгалтерии сыплются ответы про мотки и доставку».
Картина за трое суток по логам: 1 240 отказов 553 5.7.1 Sender address rejected от релея провайдера, очередь deferred разбухла до 900+ писем, суточный лимит корпоративного ящика выбирался к 15:00, после чего сыпалось 450 4.7.1 Too many messages. В main.cf был единственный relayhost = [smtp.provider.ru]:587 и одна строка в sasl_passwd — с логином бухгалтерии, потому что исторически сначала настроили корпоративный сайт. Магазин, добавленный годом позже, всё это время отправлял под чужой учёткой; провайдер до какого-то момента переписывал конверт молча, а после ужесточения политики начал отбивать.
Развернул схему из раздела выше: два правила по @domain в обеих таблицах плюс запись для общего relayhost. И тут же наступил на две классические грабли. Первая: в relayhost был порт :587, а в sasl_passwd я по инерции написал [smtp.provider.ru] без порта — мануал на этот счёт предельно ясен, «если вы указали [ и ] или нестандартный порт в relayhost, то же самое должно быть в smtp_sasl_password_maps», и без совпадения записи просто не находятся. Вторая: правки внёс, postmap не сделал — Postfix продолжал читать старый .db, и десять минут я смотрел в неизменившийся лог. Обе ошибки диагностировались одной командой postmap -q.
Отдельно вылез конверт. Магазин слал письма через mail() без -f, поэтому в конверте стоял www-data@yarn-web — ни одно правило по отправителю не срабатывало даже после исправления скобок. Добавил в пул php-fpm магазина php_admin_value[mail.force_extra_parameters] = -f shop@shop.example.ru, перезапустил пул. Итог через неделю наблюдения: отказов 553 — ноль, письма обоих доменов уходят под своими логинами, в логе у каждой доставки видно нужный sasl_username, лимиты разъехались (магазин 380–410 писем/сутки, корпоративный сайт 35–45), очередь пустая. Суммарно работа заняла около двух часов, из которых полтора — поиск того самого www-data.
- Было: 1 240 отказов за 72 часа, очередь 900+ писем, лимит выбран к 15:00.
- Стало: 0 отказов, два независимых потока 380–410 и 35–45 писем в сутки.
- Правки: 3 параметра в `main.cf`, 2 таблицы по 4 строки, 1 строка в пуле php-fpm.
- Потерянного времени на грабли: несовпавший порт в `sasl_passwd` и забытый `postmap`.
Отладка: пять команд, которыми я закрываю вопрос
Отладка тут скучная и полностью детерминированная — никакой магии, просто последовательность проверок. Сначала смотрю, что вообще включено, потом — находятся ли ключи, потом — что уходит в сеть.
# 1. что включено на самом деле (не то, что в файле, а то, что применилось)
postconf -n | grep -E 'sender_dependent|sasl|relayhost'
# 2. находится ли ключ ровно так, как его ищет Postfix
postmap -q "shop@shop.example.ru" hash:/etc/postfix/sender_relay
postmap -q "@shop.example.ru" hash:/etc/postfix/sasl_passwd
postmap -q "[smtp.provider.ru]:587" hash:/etc/postfix/sasl_passwd
# 3. тестовое письмо с явным конвертом
sendmail -f shop@shop.example.ru -t <<'EOF'
From: Shop <shop@shop.example.ru>
To: proverka@example.com
Subject: sender-dependent test
test
EOF
# 4. смотрим, куда и с каким результатом ушло
tail -f /var/log/mail.log | grep -E 'from=<|relay=|status=|SASL'
# при необходимости — временно видеть AUTH-диалог с релеем
postconf -e 'debug_peer_list = smtp.provider.ru' && postfix reload
# 5. пересборка таблиц и применение
postmap /etc/postfix/sender_relay /etc/postfix/sasl_passwd && postfix reloadПункт 2 — самый недооценённый. postmap -q спрашивает таблицу тем же способом, что и сам Postfix: если команда молчит, значит записи нет, и никакие рассуждения «но она же там написана» не помогают. Чаще всего молчание означает лишний пробел вместо табуляции в неожиданном месте, несовпадающие скобки, отсутствующий порт или то, что вы забыли postmap и спрашиваете свежий текстовый файл при старом индексе.
Пункт 4 даёт финальное доказательство. Пункт 4 даёт финальное доказательство, но с оговоркой: SMTP-клиент Postfix не пишет в строку доставки имя учётки — поле sasl_username= в логе ставит только smtpd для входящих подключений. Поэтому для исходящей почты я смотрю связку from=<…> у qmgr и relay=/status= у smtp той же очереди, а логин подтверждаю двумя способами: Return-Path и заголовки провайдера в тестовом письме, пришедшем на внешний ящик, либо временный debug_peer_list с именем релея — тогда в логе виден весь AUTH-диалог. Если вместо status=sent стоит 530 5.7.0 Authentication required — Postfix не нашёл пароль и пошёл без аутентификации; если SASL authentication failed; server … said: 535 — пароль нашёлся, но не подошёл. После отладки debug_peer_list обязательно очистить.
Есть ещё один приём, который экономит время на боевом сервере: не гонять тестовые письма наружу, а посмотреть решение маршрутизатора напрямую. Решение о relayhost принимает демон trivial-rewrite, и напрямую его не спросить, но ключи его таблиц проверяются тем же postmap -q, а для рискованных правок надёжнее поднять отдельный тестовый инстанс через postmulti -e create с копией конфигурации и гонять проверки там. Особенно это выручает, когда сервер обслуживает боевую почту компании и «поэкспериментировать полчасика» на нём нельзя: любая ошибка в sender_relay немедленно кладёт весь исходящий поток в очередь.
И маленькая привычка, которая окупается: после каждой правки сохраняйте вывод postconf -n в файл рядом с конфигом и коммитьте вместе с main.cf. Через полгода, когда письма внезапно поедут не туда, вы за минуту увидите разницу между «как было» и «как стало» — вместо того чтобы восстанавливать историю по памяти и датам изменения файлов.
- `postconf -n` показывает применённое, а не задуманное — начинайте с него.
- `postmap -q` молчит = записи нет, точка.
- `sasl_username=` в логе пишет только `smtpd` для входящих; логин исходящего письма подтверждайте заголовками тестового письма или временным `debug_peer_list`.
- `postfix reload` после `postmap` — не ритуал, а способ не ждать минуту.
Где ломается из раза в раз
Список ниже — концентрат того, что я вижу на чужих серверах. Первые четыре пункта закрывают, по ощущениям, девять случаев из десяти; остальные — редкие, но злые.
Отдельно про производительность. Включение smtp_sender_dependent_authentication = yes документировано как отключающее кэширование SMTP-соединений — «disables SMTP connection caching to ensure that mail from different senders will use the appropriate credentials». Логика понятна: переиспользовать сессию, открытую под другим логином, нельзя. На обычном корпоративном потоке — десятки, сотни писем в сутки — вы этого не заметите. А вот если сервер гонит массовую рассылку в несколько тысяч писем, каждое письмо получит собственный TCP+TLS+AUTH хендшейк, и время выгребания очереди вырастет заметно. Это ровно тот случай, когда стоит вынести рассылку в отдельный инстанс Postfix или в API провайдера, а sender-dependent оставить транзакционным письмам.
И про пароли. До Postfix 3.9 разделителем логина и пароля жёстко служит двоеточие, поэтому пароль с двоеточием внутри в sasl_passwd не записать никак — только менять пароль. В 3.9 и новее появился smtp_sasl_password_result_delimiter, которым разделитель можно переназначить на любой символ, не встречающийся в имени пользователя. На Debian 12 с Postfix 3.7 этого параметра ещё нет — проверяйте postconf -d | grep result_delimiter, прежде чем закладываться.
- Скобки и порт в `sasl_passwd` не совпадают с `relayhost`/`sender_relay` — запись не находится.
- Забыт `postmap` после правки — читается старый индекс.
- Ключ ищется по заголовку `From`, а в конверте `www-data@hostname`.
- Нет строки для общего `relayhost` — всё, что не попало в правила, уходит без аутентификации.
- `smtp_sasl_security_options` оставлен по умолчанию (`noplaintext, noanonymous`) при работе без TLS → `SASL authentication failure: No worthy mechs found`. Ставьте `smtp_tls_security_level = encrypt` и `smtp_sasl_tls_security_options = noanonymous`. Та же ошибка бывает, когда в системе нет модулей PLAIN/LOGIN для Cyrus SASL — в Debian/Ubuntu это пакет `libsasl2-modules`.
- Права на `sasl_passwd` и `sasl_passwd.db` шире `0600` — пароли открытым текстом читает весь сервер.
- Разные аккаунты — разные SPF/DKIM-политики. Домен второго сайта надо отдельно прописать у провайдера, иначе DMARC начнёт ронять письма в спам.
- `smtp_sasl_auth_soft_bounce = yes` по умолчанию: при отказе `535` письмо не отбивается, а копится в очереди — можно неделю не замечать сломанный пароль.
- Порт 465 без `smtp_tls_wrappermode = yes` — соединение висит до таймаута; порт 587 с wrappermode — наоборот, TLS-хендшейк на plaintext-порту и ошибка.
Когда я это не делаю и что беру вместо
Sender-dependent схема хороша, когда назначение у отправителей одно или похожее, а различаются только учётки. Если же сайтам нужны разные транспорты, разные исходящие IP или разные политики TLS, я разворачиваю несколько инстансов Postfix через postmulti и развожу трафик транспортами. Это дороже в обслуживании, зато каждая рассылка изолирована полностью, включая очередь, лимиты и логи — при разборе инцидента экономит часы.
Второй вариант, который часто оказывается лучше всего перечисленного: не тащить письма сайта через SMTP-релей вообще. Транзакционные письма магазина отдать в API почтового сервиса прямо из приложения, а Postfix оставить системной почте и уведомлениям от cron. Тогда вопрос «каким логином» исчезает вместе с проблемой, а доставляемость и аналитика получаются лучше любого самосбора. Спорный момент, признаю: это перекладывание задачи с админа на разработчика, и не в каждом проекте на это готовы. Но если разработчик под рукой — предлагайте.
И честно про риски: сама по себе настройка обратима и безопасна. Худшее, что случается при ошибке, — письма остаются в очереди (smtp_sasl_auth_soft_bounce = yes по умолчанию их не отбивает) и уходят после исправления. Поэтому не бойтесь трогать; бойтесь трогать без postconf -n до и после и без тестового письма с явным -f. И держите main.cf под контролем версий — за пять лет через сервер проходит десяток правок, и без истории вы не вспомните, зачем там появилась третья строка в sender_relay.
- Разные исходящие IP или транспорты → `postmulti` и отдельные инстансы.
- Массовая рассылка тысячами писем → отдельный инстанс или API сервиса, не sender-dependent.
- Только разные учётки на одном релее → штатная схема из этой статьи, полчаса работы.
- Нужен разный `default_transport` по отправителю → `sender_dependent_default_transport_maps` (Postfix 2.7 и новее).
Частые вопросы
Почему Postfix игнорирует мою запись в sender_relay, хотя адрес написан правильно?
Три причины по частоте. Первая: в конверте письма стоит не тот адрес, который вы видите в поле From — проверьте поле `from=<>` в mail.log. Вторая: забыт `postmap`, и читается старый индексный файл. Третья: несовпадение формы записи — если в relayhost указаны квадратные скобки или нестандартный порт, ровно та же форма должна быть в таблицах. Проверяется командой `postmap -q «адрес» hash:/etc/postfix/sender_relay`: если она молчит, записи для Postfix не существует.
Нужно ли прописывать каждый ящик отдельно или хватит записи по домену?
Таблицы просматриваются и по полному адресу конверта, и по `@domain`. Я почти всегда обхожусь доменными записями вида `@shop.example.ru` — это одна строка вместо десяти и никаких сюрпризов при появлении нового ящика. Поимённые записи имеют смысл, только когда внутри одного домена разным адресам действительно нужны разные учётки провайдера.
Что будет с письмами, для которых подходящей записи нет?
Они уйдут через глобальный `relayhost` с той учёткой, которая записана в sasl_passwd по ключу назначения. Поэтому строку для общего relayhost надо держать всегда: без неё Postfix не найдёт пароль вообще и попытается отправить без аутентификации, получив `530 5.7.0 Authentication required`.
Не просядет ли производительность отправки?
Включение `smtp_sender_dependent_authentication = yes` отключает кэширование SMTP-соединений — иначе сессию, открытую под одним логином, могли бы переиспользовать письма другого отправителя. На потоке в сотни писем в сутки это незаметно. На массовых рассылках в тысячи писем каждое отправление получает свой TLS-хендшейк и AUTH, и очередь выгребается заметно медленнее — такой трафик лучше вынести в отдельный инстанс Postfix или в API почтового сервиса.
Пароль от ящика содержит двоеточие — как записать его в sasl_passwd?
До Postfix 3.9 — никак, разделитель логина и пароля жёстко зашит как двоеточие, придётся менять пароль. Начиная с 3.9 появился параметр `smtp_sasl_password_result_delimiter`, которым разделитель переназначается на любой символ без пробелов, не встречающийся в имени пользователя. Проверьте наличие параметра командой `postconf -d | grep result_delimiter` — например, в Postfix 3.7 из Debian 12 его ещё нет.
Нужно ли что-то менять в SPF и DKIM после разделения аккаунтов?
Да, и это половина задачи. Каждый домен нужно отдельно подтвердить и подписать у провайдера: раньше оба сайта уходили под одним аккаунтом с его SPF и DKIM, теперь потоков два. Если домен второго сайта у провайдера не настроен, вы поменяете проблему «письма от чужого имени» на проблему «письма в спаме». Через неделю после внедрения обязательно посмотрите DMARC-отчёты по обоим доменам.
Яндекс или Mail.ru на 465 — почему письма висят в очереди с lost connection?
Потому что 465 — это TLS с самого начала соединения (SUBMISSIONS), а по умолчанию SMTP-клиент Postfix ждёт открытый SMTP и STARTTLS. Для 465 нужны `smtp_tls_wrappermode = yes` и `smtp_tls_security_level = encrypt` (Postfix 3.0+). У Яндекса можно вместо этого перейти на официальный порт 587 со STARTTLS; у Mail.ru в справке указан только 465. И не забудьте, что обоим провайдерам нужен пароль приложения, а ключ в `sasl_passwd` должен совпадать с relayhost вместе с портом.
Источники
- Postfix SASL Howto — Раздел «Configuring Sender-Dependent SASL authentication» — порядок поиска (по отправителю раньше, чем по назначению), примеры main.cf, sasl_passwd и sender_relay, требование совпадения скобок и порта, необходимость postmap. https://www.postfix.org/SASL_README.html
- Postfix Configuration Parameters (postconf.5) — sender_dependent_relayhost_maps (default: empty; поиск по envelope sender и @domain; DUNNO прекращает поиск — Postfix 2.6+; параметр доступен с Postfix 2.3). https://www.postfix.org/postconf.5.html#sender_dependent_relayhost_maps
- Postfix Configuration Parameters (postconf.5) — smtp_sender_dependent_authentication (default: no; доступен с Postfix 2.3; отключает кэширование SMTP-соединений) и smtp_sasl_password_maps (default: empty; per-sender поиск выполняется только при включённой sender-dependent аутентификации). https://www.postfix.org/postconf.5.html#smtp_sender_dependent_authentication
- Postfix Announcements — Список стабильных релизов: текущая ветка 3.11 (3.11.0 — 5 марта 2026), последний релиз 3.11.7 от 7 сентября 2026 вместе с legacy 3.10.14, 3.9.15, 3.8.21, 3.7.23. https://www.postfix.org/announcements.html
- Postfix Configuration Parameters (postconf.5) — smtp_sasl_password_result_delimiter (default: :; доступен начиная с Postfix 3.9) и smtp_tls_security_level (при compatibility_level ≥ 3.11 значение по умолчанию — may). https://www.postfix.org/postconf.5.html#smtp_sasl_password_result_delimiter
- Яндекс 360 — справка Почты: «Другие программы» — Сервер исходящей почты smtp.yandex.ru, порт 465 (SSL) или 587, логин — полный адрес, пароль приложения. https://yandex.ru/support/yandex-360/customers/mail/ru/mail-clients/others
- Помощь Mail — «Как настроить почтовую программу» — SMTP-сервер smtp.mail.ru, порт 465, защита SSL/TLS, пароль для внешнего приложения. https://help.mail.ru/mail/mailer/popsmtp/
- Postfix Configuration Parameters (postconf.5) — smtp_tls_wrappermode (default: no; требует smtp_tls_security_level = encrypt; Postfix 3.0+), smtp_sasl_security_options (default: noplaintext, noanonymous), smtp_sasl_auth_soft_bounce (default: yes, Postfix 2.5+). https://www.postfix.org/postconf.5.html#smtp_tls_wrappermode
