Configure direct-delivery incoming email for self-hosted sites with Mail-Receiver

Привет! У меня возникла странная проблема: я настроил всё согласно руководству, и всё работает отлично! Однако что-то пошло не так с исходящей почтой, и я думал, что это не должно быть затронуто. Sidekiq выдаёт следующую ошибку для каждой попытки отправки письма (все они застряли в списке «Retries») после включения mail-receiver:

Jobs::HandledExceptionWrapper: Wrapped OpenSSL::SSL::SSLError: SSL_read: unexpected eof while reading

Мои поиски заставляют предположить, что это как-то связано с TLS. Я раскомментировал строки, связанные с TLS, в файле .yml, но повторное закомментирование не исправило проблему. Я попробовал инструкции из руководства по разрешению конфликтов Postfix, но, похоже, у меня нет Postfix? (Директория /etc/postfix из руководства отсутствует на моём экземпляре, и служба postfix не распознаётся.) И согласно результатам netstat, порт 25 использует только docker-proxy.

Мы используем Gmail в качестве исходящего SMTP-сервиса, и фактически мы использовали Gmail для входящей проверки POP3 до этого. Я удалил множество MX-записей, указывающих на Google, но в руководстве сказано было сделать именно это.

Вот мой файл mail-receiver.yml, разумеется, с определёнными данными, закрашенными:

## это шаблон контейнера для входящей почты
##
## После внесения изменений в этот файл вы ОБЯЗАТЕЛЬНО должны выполнить
## /var/discourse/launcher rebuild mail-receiver
##
## БУДЬТЕ *ОЧЕНЬ* ОСТОРОЖНЫ ПРИ РЕДАКТИРОВАНИИ!
## YAML-ФАЙЛЫ ЧРЕЗВЫЧАЙНО ЧУВСТВИТЕЛЬНЫ К ОШИБКАМ В ПРОБЕЛАХ И ВЫРАВНИВАНИИ!
## посетите http://www.yamllint.com/, чтобы при необходимости проверить этот файл

base_image: discourse/mail-receiver:release
update_pups: false

expose:
  - "25:25"   # SMTP

env:
  LC_ALL: en_US.UTF-8
  LANG: en_US.UTF-8
  LANGUAGE: en_US.UTF-8

  ## Куда должна отправляться почта на ваш форум. В целом, вполне допустимо
  ## использовать здесь тот же домен, что и сам форум.
  MAIL_DOMAIN: discourse.[mydomain].org
# раскомментируйте эти строки (и объём ниже!), чтобы включить поддержку TLS
  POSTCONF_smtpd_tls_key_file:  /letsencrypt/discourse.[mydomain].org/discourse.[mydomain].org.key
  POSTCONF_smtpd_tls_cert_file:  /letsencrypt/discourse.[mydomain].org/fullchain.cer
  POSTCONF_smtpd_tls_security_level: may


  ## Базовый URL для этого экземпляра Discourse.
  ## Это будет URL вашего сайта Discourse. Например,
  ## https://discourse.example.com. Если вы используете настройку с подпапкой,
  ## обязательно учтите это (например, https://example.com/forum).
DISCOURSE_BASE_URL: 'https://discourse.[mydomain].org'

  ## Главный API-ключ вашего форума Discourse. Его можно получить
  ## на вкладке «API» в панели администратора.
  DISCOURSE_API_KEY: [myapikey]

  ## Имя пользователя, используемое для обработки входящей почты. Если вы
  ## не переименовывали пользователя `system`, оставьте это значение без изменений.
  DISCOURSE_API_USERNAME: system

volumes:
  - volume:
      host: /var/discourse/shared/mail-receiver/postfix-spool
      guest: /var/spool/postfix
# раскомментируйте для поддержки TLS
  - volume:
      host: /var/discourse/shared/standalone/letsencrypt
      guest: /letsencrypt

Технологии работы с почтой немного выходят за рамки моих знаний, поэтому я буду благодарен за любой совет, даже если это просто указание на то, что я упустил что-то очевидное при настройке. Спасибо!

1 лайк

Как вы и думали, это не имеет отношения к шейному приёмнику. У хоста, через который вы отправляете почту, недействительный SSL-сертификат.

Ну, я наконец разобрался после долгих попыток устранить проблему. Вероятно, дело в том, что домен, на котором размещён наш экземпляр Discourse, не совпадает с доменом, для которого настроены MX-записи. Как только я преодолел это замешательство, всё встало на свои места.

Это, безусловно, моя собственная глупая ошибка, но руководство немного запутало меня вот этим:

Не совсем ясно, что две записи forum.example.com не обязаны быть идентичными, а в моём случае они должны были различаться. Возможно, люди, использующие это руководство, достаточно опытны, чтобы знать об этом, но я — нет. Поэтому я оставляю это здесь для тех, кто может столкнуться с похожей проблемой. Я узнал несколько вещей о DNS, о которых раньше не знал, так что это был хороший опыт обучения, и сейчас всё отлично работает. :slight_smile:

Что ж, я поспешил с выводами. Исходящая почта работает нормально, входящие ответы тоже, но отправка на email-адрес категории завершается неудачей без каких-либо уведомлений. Я скопировал адрес прямо из настроек и вставил его в новое письмо, так что опечаток точно нет.

В логах моего mail-receiver есть в основном три типа записей. Успешная запись, которая соответствует ответу на существующее сообщение, выглядит так:

Sep 20 16:59:44 discourse-mail-receiver postfix/smtpd[277]: connect from server168-1.web-hosting.com[68.65.122.144]
Sep 20 16:59:45 discourse-mail-receiver postfix/smtpd[277]: NOQUEUE: reject: RCPT from server168-1.web-hosting.com[68.65.122.144]: 454 4.7.1 <[category]@discourse.[domain].org>: Relay access denied; from=<ryan@[redacted].com> to=<[category]@discourse.[domain].org> proto=ESMTP helo=<server168-1.web-hosting.com>
<22>Sep 20 16:59:45 policyd-spf[288]: : prepend Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=[redacted]; helo=server168-1.web-hosting.com; envelope-from=ryan@[redacted].com; receiver=discourse.[domain].org Sep 20 16:59:45 discourse-mail-receiver postfix/cleanup[281]: 4CCED114200: message-id=<20240920165945.4CCED114200@discourse-mail-receiver.localdomain>
Sep 20 16:59:45 discourse-mail-receiver postfix/smtpd[277]: disconnect from server168-1.web-hosting.com[68.65.122.144] ehlo=1 starttls=0/1 mail=1 rcpt=0/1 data=0/1 quit=1 commands=3/6

Помимо этого, есть два типа ошибок (как я предполагаю), каждый из которых повторяется довольно часто. Первый выглядит так:

Sep 20 17:00:23 discourse-mail-receiver postfix/qmgr[124]: 5D162FC26D: from=<double-bounce@discourse-mail-receiver.localdomain>, size=960, nrcpt=1 (queue active)

А второй:

Sep 20 17:00:23 discourse-mail-receiver postfix/error[293]: 8DC3BFC141: to=<postmaster@discourse-mail-receiver.localdomain>, orig_to=<postmaster>, relay=none, delay=126622, delays=126622/0.05/0/0, dsn=4.4.3, status=deferred (delivery temporarily suspended: Host or domain name not found. Name service error for name=discourse-mail-receiver.localdomain type=MX: Host not found, try again)

А вот как выглядит мой mailq — одни и те же записи повторяются снова и снова:

3D07BFC23D      960 Fri Sep 20 06:42:23  double-bounce@discourse-mail-receiver.localdomain
(delivery temporarily suspended: Host or domain name not found. Name service error for name=discourse-mail-receiver.localdomain type=MX: Host not found, try again)
                                         postmaster@discourse-mail-receiver.localdomain

Кажется, часть этого связана с письмами, которые отправляет Discourse, а затем возвращается обратно по какой-то причине. Есть ли у mail-receiver какая-либо функциональность для обработки таких возвратов, или они будут висеть в очереди mailq вечно?

Во-вторых, почему ответы работают, а отправка писем напрямую в категорию — нет? Ещё раз спасибо за вашу помощь и терпение. :slight_smile:

Похоже, это записи журнала об ошибке при попытке отправить сообщение на адрес категории, а не об успешном ответе на пост.

Я не на 100% уверен, но, думаю, ошибка «relay access denied» указывает на то, что домен discourse.[domain].org, вероятно, не совпадает с доменом, указанным в параметре MAIL_DOMAIN в файле mail-receiver.yml. Возможно, адрес ответа разрешён другими способами.

Я знаю, что значение MAIL_DOMAIN используется как минимум в одном месте конфигурационного файла Postfix, поэтому его изменение, скорее всего, потребует пересборки контейнера. Вы меняли MAIL_DOMAIN? Если да, запускали ли вы после этого команду ./launcher rebuild mail-receiver?

2 лайка

[Извините, что случайно нажал Enter раньше времени, не закончив свой предыдущий пост]

Я всё ещё бьюсь головой об эту проблему. Но у меня появилась новая идея относительно того, в чём может быть дело. Я работаю с двумя доменами, назовём их [domain1] и [domain2]. Мой SMTP-ретранслятор Gmail размещён на [domain1]. Мой экземпляр Discourse, а также мой mail-receiver размещены на [domain2].

Как настроить параметр reply-by-email-address в Discourse так, чтобы адрес для ответа указывался в домене [domain2], когда письмо отправляется с [domain1]? При попытке сделать это я получаю ошибку SSL EOF, упомянутую выше. Предполагаю, что я упускаю какую-то хитрость с аутентификацией DNS или что-то подобное.

Кажется, я наконец-то разобрался. Чтобы адрес «Reply-To» находился в домене, отличном от SMTP-ретранслятора, мне пришлось ослабить некоторые настройки в Google Workspace. Всё работает как задумано в обоих направлениях.

1 лайк

Один последний вопрос. Несмотря на то, что сейчас всё работает корректно, в моём mailq всё ещё остаётся много старых записей. Скорее всего, это письма, сгенерированные с неправильными настройками, и они навсегда застрянут в подвешенном состоянии. Я бы предпочёл просто удалить их и двигаться дальше. Так как же очистить mailq?

Пост довольно старый, но, возможно, самая распространённая причина ошибки SSL EOF — конфликт версий OpenSSL: v1.1.1f против v3. Обновление устаревшей версии 1.1.1f решит проблему. И плохая новость в том, что, например, Ubuntu 20.x не может использовать более новую версию, поэтому необходимо обновить всю систему Ubuntu.

1 лайк

Я в отчаянии, уже потратил на это часы. Не могу справиться с этой ошибкой Launcher, хотя имена моих файлов конфигурации написаны только строчными буквами.

«ОШИБКА: Имя конфигурации не должно содержать заглавные буквы, пробелы или специальные символы. Исправьте имя конфигурации и запустите ./launcher заново.»

при выполнении

./launcher rebuild mail-receiver

или

./launcher bootstrap mail-receiver

или

./launcher start mail-receiver

Я вижу это в исходном коде Launcher здесь

Гррррррррр — Пожалуйста, помогите!

Попробовал всё, что указано в связанном выше посте, касающееся локали, и всё, что смог найти в других источниках.

./launcher rebuild app___ работают отлично!

У меня есть одна возможная подсказка: это началось сразу после того, как я случайно нажал CapsLock (но заглавными сделал только 2 буквы), когда называл файл конфигурации. Я сразу же отключил CapsLock и переписал эти 2 буквы, даже не сохраняя файл.

Трудно представить, как такая краткая опечатка/исправление могла вызвать это, но, возможно, заглавные буквы где-то застряли в буфере, или ???

Это далеко за пределами моих знаний, но меня удивляет, что сообщение об ошибке не выводит переменную $config :thinking:
Это точно помогло бы в отладке.

1 лайк

Спасибо @Canapin! — вот что я пытаюсь настроить:

https://www.perplexity.ai/search/provide-the-code-lJcI4BrFQ2auuD42ehYFwA

Можете скопировать и вставить весь вывод вашей командной строки?

От команды ./launcher start mail-receiver до сообщения об ошибке, а также точное имя файла .yml?

Если я переименую файл конфигурации в Mail-receiver.yml, то команда ./launcher start Mail-receiver выдаст:

ERROR: Config name 'Mail-receiver' must not contain upper case characters, spaces or special characters. Correct config name and rerun ./launcher.

В данном случае сообщение об ошибке содержит имя файла.

Кроме того, если вы запустите ./launcher start aaa, программа не найдет соответствующего файла и выведет список доступных. Она выбирает их только из папки, так что никакой магии здесь нет, но, возможно, вывод окажется интересным :person_shrugging:

ERROR: containers/aaa.yml does not exist or is not readable.

Available configs ( app, mail-receiver )

Привет, большое спасибо — я разобрался и всё заработало.

В чём в итоге была проблема? Это могло бы помочь другим :slight_smile:

1 лайк

Проблемы не было, это было просто время на освоение того, как взаимодействуют различные компоненты сервера для маршрутизации доменов и электронной почты. Раньше я никогда не углублялся в изучение Postfix. Это было увлекательно, и я многому научился.

В итоге я пришел к следующему решению: использовать mail-receiver.yml (контейнер Docker), привязанный к каждому экземпляру Discourse, все они используют порт 25, а маршрутизацию обрабатывает функция transport в Postfix.

2 лайка

На моём выделенном сервере (работающем под управлением Ubuntu 22.04 с установленным Postfix) для каждого экземпляра Discourse, в котором включена функция публикации через почту, используется отдельный файл mail-receiver.yml.

Такая конфигурация создаёт отдельный контейнер для каждого экземпляра Discourse на моём сервере (в дополнение к обычному контейнеру app), который принимает и обрабатывает электронные письма для соответствующего экземпляра Discourse.

Входящие письма для всех форумов Discourse на сервере принимаются Postfix через стандартный порт 25, где основной файл конфигурации Postfix использует «таблицу транспорта» (transport map) для «пересылки» каждого письма в нужный форум Discourse, извлекая доменное имя из адреса в поле «Кому:» письма.

Таким образом, помимо инструкций из этой темы, я…

  1. изменил существующий файл конфигурации Postfix по пути: /etc/postfix/main.cf

  2. затем добавил соответствующий файл таблицы транспорта Postfix по пути: /etc/postfix/transport

  1. наконец, добавил соответствующие файлы для создания почтового контейнера для каждого форума:
    /var/discourse/containers/mail-receiver-domain1.yml
    /var/discourse/containers/mail-receiver-domain2.yml
    /var/discourse/containers/mail-receiver-domain3.yml
    /var/discourse/containers/mail-receiver-domain4.yml
    /var/discourse/containers/mail-receiver-domain5.yml

4 лайка

В файле mail-receiver.yml нет переменной DISCOURSE_MAIL_ENDPOINT, а также нужно изменить DISCOURSE_BASE_URL.

2 лайка

Я использую сервис пересылки почты, который поддерживает пересылку писем в формате JSON на вебхук.

Является ли это вариантом для прямой доставки электронной почты?

1 лайк