Общение с другими сайтами: проблема SMTP — достигнут конец файла

(для сотрудников: это не связано с размещенной подпиской, к которой также привязан мой аккаунт)

Я установил Discourse на VPS, следуя инструкциям с Run other websites on the same machine as Discourse, но с одним отличием: я адаптировал файл app.yml до первой установки:

sudo mkdir /var/discourse
sudo git clone https://github.com/discourse/discourse_docker.git /var/discourse
cd /var/discourse
sudo cp samples/standalone.yml containers/app.yml
sudo nano containers/app.yml

В YAML-файле я закомментировал секцию портов, добавил - "templates/web.socketed.template.yml" и указал имя хоста и настройки SMTP:

  ## TODO: Доменное имя, на которое будет отвечать этот экземпляр Discourse
  ## Обязательно. Discourse не будет работать с чистым IP-адресом.
  DISCOURSE_HOSTNAME: 'discuss.mydomain.community'

  ## TODO: Список email-адресов через запятую, которые станут администраторами и разработчиками
  ## при первой регистрации, например 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'someuser@protonmail.com,anotheruser@otherdomain.io'

  ## TODO: SMTP-сервер, используемый для проверки новых аккаунтов и отправки уведомлений
  # Адрес, имя пользователя и пароль SMTP обязательны
  # ВНИМАНИЕ: символ '#' в пароле SMTP может вызвать проблемы!
  DISCOURSE_SMTP_ADDRESS: smtp.myprovider.email
  DISCOURSE_SMTP_PORT: 465
  DISCOURSE_SMTP_USER_NAME: mydomain-no-reply@otherdomain.io
  DISCOURSE_SMTP_PASSWORD: pa$$word
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (опционально, по умолчанию true)

Затем я выполнил инициализацию и запуск приложения Discourse:

sudo ./launcher bootstrap app
sudo ./launcher start app

Это сработало. Я остановил Discourse и настроил внешний nginx (в /etc/nginx/conf.d/discourse.conf) с предложенной конфигурацией только для HTTP (только для тестирования). Я мог получить доступ к http://discuss.mydomain.community.

Я снова остановил Discourse и с помощью certbot обновил файл discourse.conf, теперь он выглядит так:

server {
	server_name discuss.mydomain.community;  # <-- измените это

	location / {
		proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
		proxy_set_header Host $http_host;
		proxy_http_version 1.1;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header X-Real-IP $remote_addr;
	}

    listen [::]:443 ssl ipv6only=on; # управляется Certbot
    listen 443 ssl; # управляется Certbot
    ssl_certificate /etc/letsencrypt/live/discuss.mydomain.community/fullchain.pem; # управляется Certbot
    ssl_certificate_key /etc/letsencrypt/live/discuss.mydomain.community/privkey.pem; # управляется Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # управляется Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # управляется Certbot

}
server {
    if ($host = discuss.mydomain.community) {
        return 301 https://$host$request_uri;
    } # управляется Certbot


	listen 80; listen [::]:80;
	server_name discuss.mydomain.community;
    return 404; # управляется Certbot
}

Пока всё хорошо. Я могу получить доступ к Discourse по HTTPS (позже, когда что-то не работало, я выполнил ./launcher rebuild app, но это не изменило ситуацию).

На экране «Завершение установки» после отправки формы регистрации я вижу сообщение: «Мы отправили письмо с активацией на someuser@protonmail.com».

Проблема: письмо не приходит.

Письмо не поступает во входящие администратора. Я проверил почтовый ящик mydomain-no-reply@otherdomain.io, но там тоже ничего нет.

Примечание: Возможно, проблема в том, что почтовый пользователь находится на домене otherdomain.io, а должен быть на mydomain.community. Но насколько мне известно, это не является обязательным требованием.

Я начал устранение неполадок и последовательно тестировал следующие изменения в app.yml (с пересборкой приложения между ними):

  DISCOURSE_SMTP_ENABLE_START_TLS: false

Без успеха. Продолжаю:

## Если вы хотите установить адрес «От кого» для вашей первой регистрации, раскомментируйте и измените:
  - exec: rails r "SiteSetting.notification_email='mydomain-no-reply@otherdomain.io"

Затем я следовал инструкции с Troubleshoot email on a new Discourse install :

  • У otherdomain.io в DNS правильно настроены DKIM и SPF.
  • Использование telnet с VPS к SMTP-провайдеру работает.
  • Использование telnet из контейнера Docker (через docker exec и установку telnet) также работает.

Я запустил ./discourse-doctor, и две части результатов сразу бросились в глаза (остальное было как ожидалось):

========================================
Версия Discourse на discuss.mydomain.community: Discourse 2.6.0.beta2 
Версия Discourse на localhost: НЕ НАЙДЕНО
==================== ПРОБЛЕМА С DNS ====================
Этот сервер сообщает «НЕ НАЙДЕНО», но discuss.mydomain.community показывает Discourse 2.6.0.beta2.
Это указывает на проблему с DNS или на то, что виноват промежуточный прокси.
Если вы используете Cloudflare или CDN, возможно, они настроены неправильно.
==================== ТЕСТ ОТПРАВКИ ПИСЕМ ====================
Для надежного теста получите адрес на http://www.mail-tester.com/
Отправка письма на REDACTED . . 
Тестирование отправки на test-k86jiyqb9@srv1.mail-tester.com через smtp.myprovider.email:465.
======================================== ОШИБКА ========================================
                                    НЕОЖИДАННАЯ ОШИБКА

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

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

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

Я пробовал дважды: один раз с настроенным email, второй раз с адресом, предоставленным mail-tester.com. Результаты одинаковые. Так что это выглядит не очень хорошо.

Примечание: Похоже, процедура установки Docker также предоставляет бета-версии. Хорошо это знать.

В производственном логе я вижу:

Started GET "/" for REDACTED-IP at 2020-09-03 06:21:57 +0000
Processing by FinishInstallationController#index as HTML
  Rendering finish_installation/index.html.erb within layouts/finish_installation
  Rendered finish_installation/index.html.erb within layouts/finish_installation (Duration: 3.2ms | Allocations: 356)
  Rendered layouts/_head.html.erb (Duration: 15.7ms | Allocations: 2969)
Completed 200 OK in 323ms (Views: 140.3ms | ActiveRecord: 0.0ms | Allocations: 32137)
Started GET "/finish-installation/register" for REDACTED-IP at 2020-09-03 06:22:01 +0000
Processing by FinishInstallationController#register as HTML
  Rendering finish_installation/register.html.erb within layouts/finish_installation
  Rendered finish_installation/register.html.erb within layouts/finish_installation (Duration: 7.1ms | Allocations: 1607)
  Rendered layouts/_head.html.erb (Duration: 22.3ms | Allocations: 3139)
Completed 200 OK in 107ms (Views: 41.3ms | ActiveRecord: 0.0ms | Allocations: 11760)
Started POST "/finish-installation/register" for REDACTED-IP at 2020-09-03 06:22:22 +0000
Processing by FinishInstallationController#register as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"l1hEEsK/gur5yplJdMIHttZAgYcuzLLkESaAI87IMb88nAFqNwi7l3yJ+3EJBw7leFypVGbH4C5hJl7VnJVYBQ==", "email"=>"someuser@protonmail.com", "username"=>"someusername", "password"=>"[FILTERED]", "commit"=>"Register"}
Redirected to https://discuss.mydomain.community/finish-installation/confirm-email
Completed 302 Found in 48ms (ActiveRecord: 0.0ms | Allocations: 4246)
  Rendering layouts/email_template.html.erb
  Rendered layouts/email_template.html.erb (Duration: 0.2ms | Allocations: 31)
Started GET "/finish-installation/confirm-email" for REDACTED-IP at 2020-09-03 06:22:22 +0000
Processing by FinishInstallationController#confirm_email as HTML
  Rendering finish_installation/confirm_email.html.erb within layouts/finish_installation
  Rendered finish_installation/confirm_email.html.erb within layouts/finish_installation (Duration: 2.3ms | Allocations: 418)
  Rendered layouts/_head.html.erb (Duration: 7.9ms | Allocations: 1612)
Completed 200 OK in 25ms (Views: 22.1ms | ActiveRecord: 0.0ms | Allocations: 4611)
Delivered mail ea8af868-4a2c-4312-85dd-f57061a3cd90@discuss.mydomain.community (60015.9ms)
Job exception: Net::ReadTimeout

Подошел немного и затем через браузер запросил «Повторно отправить уведомление». В производственный лог были добавлены следующие записи:

Delivered mail 47ca6f15-cd9e-4c96-a670-6646e2bda585@discuss.mydomain.community (60007.9ms)
Job exception: end of file reached

  Rendering layouts/email_template.html.erb
  Rendered layouts/email_template.html.erb (Duration: 0.2ms | Allocations: 31)
Delivered mail 02367872-af3a-4df4-9a68-4e5e7c5eda60@discuss.mydomain.community (60007.4ms)
Job exception: end of file reached

  Rendering layouts/email_template.html.erb
  Rendered layouts/email_template.html.erb (Duration: 0.2ms | Allocations: 31)
Delivered mail ee9ee1fc-6fd1-4970-89fa-260efe2dd04c@discuss.mydomain.community (60014.1ms)
Job exception: end of file reached

  Rendering layouts/email_template.html.erb
  Rendered layouts/email_template.html.erb (Duration: 0.1ms | Allocations: 31)
Delivered mail 9030f87b-99df-4de2-9a60-2c57f7c752de@discuss.mydomain.community (60007.0ms)
Job exception: end of file reached

  Rendering layouts/email_template.html.erb
  Rendered layouts/email_template.html.erb (Duration: 0.2ms | Allocations: 31)
Started PUT "/finish-installation/resend-email" for REDACTED-IP at 2020-09-03 06:31:22 +0000
Processing by FinishInstallationController#resend_email as HTML
  Parameters: {"authenticity_token"=>"NE21scxxyZz3/DxDkoF8kwi9GXoNKvnstNJdKZjQs7afigDKWcbw4XK/XnvvRHTApqExqUUghybE1oPfyo3aDA=="}
  Rendering finish_installation/resend_email.html.erb within layouts/finish_installation
  Rendered finish_installation/resend_email.html.erb within layouts/finish_installation (Duration: 0.9ms | Allocations: 163)
  Rendered layouts/_head.html.erb (Duration: 2.5ms | Allocations: 269)
Completed 200 OK in 63ms (Views: 6.5ms | ActiveRecord: 0.0ms | Allocations: 6408)
  Rendering layouts/email_template.html.erb
  Rendered layouts/email_template.html.erb (Duration: 0.2ms | Allocations: 31)
Delivered mail 127cc350-3fb0-4ef1-8759-391fb407b1cb@discuss.mydomain.community (60008.0ms)
Job exception: Net::ReadTimeout

Delivered mail d9c70dfd-5a8b-4f4c-bafa-5a540fc8ed4f@discuss.mydomain.community (60010.0ms)
Job exception: end of file reached

  Rendering layouts/email_template.html.erb
  Rendered layouts/email_template.html.erb (Duration: 0.1ms | Allocations: 31)
Delivered mail e9e67ed1-d7b1-4e8a-ba11-30501c6fae89@discuss.mydomain.community (60012.4ms)
Job exception: end of file reached

  Rendering layouts/email_template.html.erb
  Rendered layouts/email_template.html.erb (Duration: 0.2ms | Allocations: 31)
  Rendering layouts/email_template.html.erb
  Rendered layouts/email_template.html.erb (Duration: 0.1ms | Allocations: 31)
Delivered mail 82d0361e-0349-4a1a-928e-dcb16dcffdbc@discuss.mydomain.community (60008.6ms)
Job exception: end of file reached

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

Вот моя текущая ситуация.. публикую это на Meta.

Не исключено, что исходящий трафик на почтовый сервер блокируется? Можете ли вы подключиться к нему через telnet? Изнутри контейнера?

Да, я могу успешно подключиться к SMTP-серверу с помощью telnet изнутри контейнера Docker. Однако отправить тестовое письмо через telnet не получается, так как мой провайдер это запрещает (прекращает соединение при получении дальнейших команд).

Решение оказалось простым. Хотя мой провайдер указывает порт выше, я подключился по telnet к порту 587 и убедился, что он работает. Поэтому я изменил app.yml, выполнил пересборку… и всё заработало :smiley:

До того, как я увидел это, я хотел порекомендовать это изменение.

Порт 465 — это SMTP-over-SSL, который устарел.
Порт 587 — это порт MSA (агент отправки почты), на котором обычно поддерживается STARTTLS (переход от незашифрованного соединения к TLS).

Когда в конфигурации был указан порт 465, Discourse пытался общаться в незашифрованном режиме с портом, использующим SSL.

Спасибо, @supermathie, я принял ваш ответ как решение. Понятное объяснение.

Привет, @supermathie

Похоже, порт 465 был (или находится в процессе) возвращён в строй согласно RFC 8314. Мой текущий провайдер электронной почты (mailfence.com, похоже, следует этой рекомендации).

Есть ли способ принудительно использовать TLS при подключении через порт 465? Это, надеюсь, решит мою проблему, так как я проверил с помощью swaks, что использование STARTTLS (–tls) не работает, тогда как использование --tls-on-connect (–tlsc) работает корректно из контейнера:

swaks --to user@example.com --from myuser@mailfence.com --server smtp.mailfence.com:465 --auth LOGIN --tlsc --auth-user myuser@mailfence.com

С уважением,

А, он переделан для отправки через TLS, это разумно.

На данный момент нет. Похоже, что ActionMailer поддерживает это, когда вы устанавливаете :ssl = true в smtp_settings, но у нас пока нет кодового пути для включения этой функции.

Уверен, мы с радостью примем PR, который добавит эту поддержку.