Discourse SMTP отправляет "EHLO localhost" вместо домена, что нарушает работу Google smtp-relay

Некоторый контекст: Emails have stopped sending - end of file reached

Примерно неделю назад (13 января 2021 года) отправка писем через сервер Google smtp-relay.gmail.com начала завершаться ошибкой (это разрешённый и intended способ использования для пользователей Google Apps).

Sidekiq сообщал об ошибках с EOFErrors:

Jobs::HandledExceptionWrapper: Wrapped EOFError: end of file reached

А в логах (/logs) также отображались неудачные задачи:

Job exception: end of file reached

Стек вызовов доступен в другом посте.

===================

Расследование показало, что актуальные установки Discourse подключаются к SMTP-ретрансляторам с использованием команды ‘EHLO localhost’, и Google начал отклонять такие запросы примерно неделю назад.

По данным tcpdump на продакшн-инстансе:

0x0030:  d10f f8e4 4548 4c4f 206c 6f63 616c 686f  ....EHLO.localho
	0x0040:  7374 0d0a                                st..
...
	0x0030:  de62 f0c3 3432 3120 342e 372e 3020 5472  .b..421.4.7.0.Tr
	0x0040:  7920 6167 6169 6e20 6c61 7465 722c 2063  y.again.later,.c
	0x0050:  6c6f 7369 6e67 2063 6f6e 6e65 6374 696f  losing.connectio
	0x0060:  6e2e 2028 4548 4c4f 2920 6a31 3673 6d34  n..(EHLO).j16sm4
	0x0070:  3831 3932 3976 736d 2e31 202d 2067 736d  81929vsm.1.-.gsm
	0x0080:  7470 0d0a                                tp..

Повторение теста через telnet дало тот же результат:

root@conversation:~# telnet smtp-relay.gmail.com 587
Trying 74.125.137.28...
Connected to smtp-relay.gmail.com.
Escape character is '^]'.
220 smtp-relay.gmail.com ESMTP ls8sm507258pjb.6 - gsmtp
ehlo localhost.localdomain
421 4.7.0 Try again later, closing connection. (EHLO) ls8sm507258pjb.6 - gsmtp
Connection closed by foreign host.

Однако использование домен-специфичного EHLO работает корректно:

root@conversation:~# telnet smtp-relay.gmail.com 587
Trying 74.125.137.28...
Connected to smtp-relay.gmail.com.
Escape character is '^]'.
220 smtp-relay.gmail.com ESMTP p10sm668563uaw.3 - gsmtp
ehlo conversation.sevarg.net
250-smtp-relay.gmail.com at your service, [64.227.96.27]
250-SIZE 157286400
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8

======

На основе логов я определил файл, который нужно изменить для проверки исправления (в образе Docker):

/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/mail-2.7.1/lib/mail/network/delivery_methods/smtp.rb

Изменение

DEFAULTS = {
      :address              => 'localhost',
      :port                 => 25,
      :domain               => 'localhost.localdomain',

на

    DEFAULTS = {
      :address              => 'conversation.sevarg.net',
      :port                 => 25,
      :domain               => 'conversation.sevarg.net',

решило проблему (после перезапуска инстанса). Теперь команда EHLO отправляется с доменным именем, и письма корректно отправляются с моего инстанса.

================

Желаемое поведение: при отправке писем стандартная установка Discourse должна использовать настроенное доменное имя для начального подключения к SMTP-серверу. Альтернативно, должна существовать конфигурационная опция для переопределения отправляемого домена. Если такая опция существует, я не смог найти её через поиск.

5 лайков

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

Более долгосрочное решение — добавить в ваш app.yml некое «магическое» правило, которое выполнит эту переадресацию за вас. Но, надеюсь, вскоре появится настоящее исправление.

Если есть способ исправить это через app.yml, я, конечно, заинтересован — прописывание моего домена в коде для работы электронной почты явно не является «правильным» решением, но это показывает, где проблему можно устранить более постоянно.

Есть ли причина, по которой он не использует просто настроенный домен для ehlo? Это будет «более правильно», чем localhost.

Отличная расследовательская работа, @Syonyk!

Не могли бы вы поделиться настройками SMTP из вашего файла app.yml?

2 лайка

Внутри ничего нет, кроме стандартных необходимых настроек.

  DISCOURSE_SMTP_ADDRESS: smtp-relay.gmail.com
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: [имя пользователя]
  DISCOURSE_SMTP_PASSWORD: [пароль]

Можете, пожалуйста, добавить новую строку с

DISCOURSE_SMTP_DOMAIN: conversation.sevarg.net

и попробовать снова?

3 лайка

Я добавил эту строку и снова собрал приложение (есть ли способ обойти этот шаг?).

Теперь в моих настройках почты отображается «domain», файл smtp.rb возвращён к значению по умолчанию localhost, и письма отправляются корректно — я могу отправлять тестовые сообщения, и они успешно доставляются.

Таким образом, проблема, насколько я понимаю, решена. Можно ли добавить эту информацию в документацию или в процесс настройки? Я долго искал такую настройку, но не мог найти этот параметр — даже зная о существовании этого параметра конфигурации, информации о нём крайне мало.

2 лайка

Это можно добавить в этот блок в образце файла app.yml по умолчанию:

Считаете ли вы это полезным?

1 лайк

Пока это где-то задокументировано, я доволен.

Однако, если это не установлено, может ли код использовать значение DISCOURSE_HOSTNAME (что было бы правильно почти во всех простых случаях)? Отправка ‘localhost’ в EHLO, как правило, неверна (по крайней мере, при общении с сервером, отличным от localhost).

Я думаю, что добавить это в standalone.yml и web_only.yml — хорошая идея.

Я согласен, что по умолчанию, вероятно, должно быть DISCOURSE_HOSTNAME. Это, безусловно, лучше, чем localhost. Не менялось ли это недавно?

Дело в том, что в настоящее время это работает для всех десятков тысяч экземпляров Discourse, которые не полагаются на Google для отправки электронной почты. Изменение такого значения по умолчанию может нарушить работу у всех, исправив ситуацию лишь для относительно небольшого числа пользователей Google Apps.

Я добавлю это в пример, и нам также следует создать тему howto под названием «Использование Google Apps для исходящей почты», где это будет задокументировано. Кто-нибудь может взяться за это?

4 лайка

Хорошо. Я согласен, что, вероятно, разумнее проявить осторожность, но я предполагаю, что любой почтовый сервер, который принимает localhost, примет и DISCOURSE_HOSTNAME, хотя у меня нет никаких данных, подтверждающих это. :wink: Включение этого в стандартные шаблоны, вероятно, достаточно.

1 лайк

Да, но отправка ‘localhost’ (на удалённый хост) также является ошибкой согласно RFC.

Выделение моё.

Более старые RFC указывают, что сервер не должен отклонять клиентов на основе строки EHLO, что, похоже, делает Google, но я не вижу такой формулировки в RFC 5321.

Я ожидаю, что любой удалённый почтовый сервер, который допускает localhost, также допустит (и предпочтёт) FQDN, как того требует RFC. Я понимаю стремление ничего не ломать, но, судя по моим чтениям соответствующих RFC, настройки по умолчанию в Discourse просто неверны, а то, что всё работает, является следствием чрезмерно либеральных удалённых SMTP-серверов.

1 лайк

Я с радостью приму pull request в ./discourse-setup, который по умолчанию устанавливает это значение в соответствии с DISCOURSE_HOSTNAME, при условии, что будет доказана его безопасность при использовании с наиболее распространёнными SMTP-сервисами, которые мы рекомендуем.

2 лайка

Я не могу протестировать полную доставку почты от начала до конца, так как у меня нет учётных записей, однако:

Mailgun

% nc smtp.mailgun.com 587
220 Mailgun Influx ready
ehlo conversation.sevarg.net
250-smtp-out-n04.prod.us-west-2.postgun.com
250-AUTH PLAIN LOGIN
...

Sendgrid

% nc smtp.sendgrid.net 587
220 SG ESMTP service ready at ismtpd0021p1las1.sendgrid.net
ehlo conversation.sevarg.net
250-smtp.sendgrid.net
250-8BITMIME
...

Mailjet

 % nc smtp.mailjet.com 587
220 in.mailjet.com ESMTP Mailjet
ehlo conversation.sevarg.net
250-smtpin.mailjet.com
250-PIPELINING
...

ElasticMail

… действительно не отвечает ни на helo, ни на ehlo, ни на что-либо ещё. o.O Ни localhost, ни какие-либо реальные домены.

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

6 лайков

Существует ещё одна связанная проблема: утилита discourse-doctor, похоже, некорректно устанавливает домен, и она всё равно будет давать сбой, даже когда реальная установка сможет отправлять письма.

При работающей конфигурации утилита discourse-doctor всё ещё сообщает об ошибке «конец файла».

======================================== ОШИБКА ========================================
                                    НЕОЖИДАННАЯ ОШИБКА

достигнут конец файла

====================================== РЕШЕНИЕ =======================================
Это не распространённая ошибка. Рекомендованного решения не существует!

Пожалуйста, сообщите точное сообщение об ошибке выше по адресу https://meta.discourse.org/
(И, если найдёте решение, укажите и его!)
=======================================================================================

В тестовом скрипте нет упоминания о SMTP_DOMAIN.

root@conversation:/var/discourse# grep SMTP_DOMAIN discourse-doctor
root@conversation:/var/discourse#

Кроме того, tcpdump показывает, что при запуске утилиты discourse-doctor в команде EHLO всё ещё передаётся «localhost». Это также нужно исправить.

	0x0030:  cccd b12c 4548 4c4f 206c 6f63 616c 686f  ...,EHLO.localho
	0x0040:  7374 0d0a                                st..
...
	0x0030:  e247 1aa5 3432 3120 342e 372e 3020 5472  .G..421.4.7.0.Tr
	0x0040:  7920 6167 6169 6e20 6c61 7465 722c 2063  y.again.later,.c
	0x0050:  6c6f 7369 6e67 2063 6f6e 6e65 6374 696f  losing.connectio
	0x0060:  6e2e 2028 4548 4c4f 2920 6e6d 3773 6d31  n..(EHLO).nm7sm1
	0x0070:  3032 3832 3139 706a 622e 3620 2d20 6773  028219pjb.6.-.gs
	0x0080:  6d74 700d 0a                             mtp..

Это не discourse-doctor, а emails.rake:

А, и похоже, что он использует localhost. Думаю, там должно быть #{ENV["DISCOURSE_SMTP_PORT"]} — или пока DISCOURSE_SMTP_DOMAIN.

@falco уверены ли мы, что это всегда было так, а не недавняя регрессия?

Я помню проблемы, связанные с отправкой

ehlo {недопустимый домен}

ещё с очень давних времён, поэтому меня очень удивит, если мы долгое время отправляли

ehlo localhost

неправильно?

1 лайк

Извините, просто сообщаю то, что вижу. Я почти уверен, что там ‘localhost’ тоже указан неверно. Моя веб-разработка в основном устарела, прошло уже более десяти лет с тех пор, как я профессионально работал с такими вещами. Дойти до того, чего я добился, заняло некоторое время; Docker, Ruby и тому подобное для меня всё ещё довольно новы. А вот tcpdump… Я знаю этот инструмент. :wink:

Тем не менее, я всё ещё считаю, что отправка ‘localhost’ на удалённый сервер в любой ситуации — это неправильное поведение.

1 лайк

На всякий случай, у нас с 13 января наблюдается точно такая же проблема. Последнее успешное письмо было отправлено 13 января, и мы тоже используем smtp-relay.gmail.com. Пока не придумали, как обойти это (без изменения исходного кода).