SMTP-письма не отправляются и соединение не устанавливается, но openssl работает

Привет! Извините, если у этой проблемы уже есть чёткое решение; я действительно искал, но не нашёл ясного ответа на свой вопрос.

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

Сначала я подумал, что проблема в невозможности подключения к SMTP из-за каких-то DNS-шутей, так как при работе с утилитой ./discourse-doctor и последующем входе в оболочку контейнера я получил:

Testing sending to xxxx@gmail.com using smtp-relay.brevo.com:587, username:xxxxxxx@smtp-brevo.com with plain auth.
======================================== ERROR ========================================
Connection to port 587 failed.
====================================== SOLUTION =======================================
The most likely problem is that your server has outgoing SMTP traffic blocked.
If you are using a service like Mailgun or Sendgrid, try using port 2525.
=======================================================================================

Однако команда openssl, рекомендованная в руководстве по устранению неполадок SMTP, не только установила соединение, но и позволила мне отправить тестовое письмо самому себе изнутри контейнера, используя команды EHLO, AUTH LOGIN и другие (о которых я до сих пор не знал, хех ^^'). Поэтому я не думаю, что проблема в том, что контейнер не может подключиться к SMTP-серверу.

РЕДАКТИРОВАНИЕ ДЛЯ ПРОЯСНЕНИЯ
Я смог сделать это изнутри контейнера: я вошёл в контейнер с помощью команды ./launcher enter container. Из этого приглашения я выполнил вышеупомянутые команды.

Вот мои настройки SMTP, если это поможет. Я, конечно, удалил данные для входа.

  DISCOURSE_SMTP_ADDRESS: 'smtp-relay.brevo.com'
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: 'xxxxxxxx@smtp-brevo.com'
  DISCOURSE_SMTP_PASSWORD: 'xxxxxxxxxxxx'
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (optional, default true)
  #DISCOURSE_SMTP_DOMAIN: discourse.example.com    # (required by some providers)
  DISCOURSE_NOTIFICATION_EMAIL: 'noreply@mydomain.xyz'    # (address to send notifications from)
  #DISCOURSE_SMTP_OPENSSL_VERIFY_MODE: none

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

В любом случае, я совершенно запутался. Надеюсь, кто-нибудь сможет мне помочь, и ещё раз извините, если это уже решённая проблема, которую я просто не нашёл!

Можете ли вы попробовать то, что предлагает ошибка, чтобы проверить, сработает ли это случайно?

Привет! Я уже пробовал это, но не сработало :frowning:

Мне удалось отправить тестовое письмо через openssl, но через порт 587. Однако через интерфейс Discourse (как через кнопку «Проверить электронную почту» в GUI, так и через команду rake tests:email[mail]) отправить письмо не удалось — ни через порт 287, ни через 2525.

Кстати, я нашёл ошибку sidekiq для неотправленного письма:

Jobs::HandledExceptionWrapper: Wrapped Net::OpenTimeout: execution expired

Найдены соответствующие строки в shared/standalone/log/rails/production.log:

Started POST "/admin/email/test" for 192.168.0.206 at 2024-10-18 23:49:02 +0000
Processing by Admin::EmailController#test as */*
  Parameters: {"email_address"=>"jggalindez@gmail.com"}
Completed 422 Unprocessable Entity in 5201ms (Views: 0.4ms | ActiveRecord: 0.0ms | Allocations: 12487)

Можете ли вы подключиться к порту 587 изнутри контейнера?

Похоже, что подключение к порту 587 работает с хоста, но, возможно, не работает изнутри контейнера. Это может указывать на проблему с сетью на вашем сервере.

Привет! Извините, я, возможно, выразился не совсем ясно. Я смог подключиться к порту 587 изнутри контейнера, по крайней мере, насколько я понимаю. Вот что я сделал:

  • Зашел в контейнер, выполнив команду ./launcher enter containername.
  • Изнутри контейнера запустил команду openssl.
  • В псевдо-терминале telnet (не совсем уверен, что это именно он) я аутентифицировался на SMTP-сервере (сделав всё необходимое для преобразования имени пользователя и пароля в base64) и отправил письмо.

Однако я не пытался отправить письмо непосредственно с самого сервера. Только изнутри контейнера (насколько я понимаю). Но я предположил, что если это удалось сделать из контейнера, то должно сработать и с самого сервера.

Итак, у меня получилось. Похоже, проблема действительно была в DNS (хотя команды getent и openssl успешно разрешали URL). Поэтому я выполнил getent, чтобы получить IP-адрес SMTP-шлюза, и запустил контейнер, изменив адрес сервера на этот IP. Это привело к другой ошибке — той, что обсуждается в этой теме. Решение, указанное там (добавить строку DISCOURSE_SMTP_OPENSSL_VERIFY_MODE: none в YAML-файл), в итоге помогло.

Не уверен, вызовет ли проблема с DNS какие-либо сложности в будущем, надеюсь, что нет, lol. Но спасибо вам обоим за помощь!

Итак, у вас действительно проблема с сетью в контейнере, но она вызвана DNS, а не отсутствием IP-соединения.

Можете ли вы подключиться с помощью openssl s_client к имени хоста изнутри контейнера?

Вы должны иметь возможность выполнить:

openssl s_client -connect smtp-relay.brevo.com:587 -starttls smtp

и получить:

…
Verify return code: 0 (ok)
…

Это ожидаемо, так как вы подключаетесь к IP-адресу, и этот IP-адрес не будет указан в сертификате.

Вызовет.

Да, я так и подумал, поэтому в итоге добавил тег docker_args в файл конфигурации и указал напрямую несколько DNS-серверов.

Для тех, кто интересуется (мне пришлось поискать, куда именно помещается тег docker_args), вот часть файла app.yml:

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

docker_args: "--dns 1.1.1.1 --dns 8.8.4.4 --dns 8.8.8.8"

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  ## Раскомментируйте следующую строку, чтобы включить прослушивание IPv6
  #- "templates/web.ipv6.template.yml"
  - "templates/web.ratelimited.template.yml"
*******Пропущенная остальная часть файла ниже*********