DiscourseのSMTPがドメインの代わりに「EHLO localhost」を送信し、Google smtp-relayが機能しなくなります

いくつかのコンテキスト:Emails have stopped sending - end of file reached

約 1 週間前(2021 年 1 月 13 日)、Google Apps ユーザーにとって許可され、意図された使用法である Google の smtp-relay.gmail.com サーバーを介してのメール送信が失敗し始めました。

Sidekiq は EOFErrors を伴う失敗を報告しました:

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

また、/logs にも失敗したタスクが記録されていました:

Job exception: end of file reached

バックトレースは他の投稿で利用可能です。

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

調査の結果、最新バージョンの Discourse インストールは SMTP リレーに ‘EHLO localhost’ で接続していることが判明しました。Google は約 1 週間前からこれを拒否し始めました。

本番環境インスタンスでの 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 に自動書き換えを行う特別な処理を追加する方法がありますが、より本格的な修正が soon に提供されることを願っています。

app.yml で修正する方法があるなら、ぜひ知りたいですね。動作するメールのためにドメインをコードにハードコーディングするのは、明らかに「適切な」解決策ではありませんが、より永続的な解決策を見つけるための手がかりにはなります。

ehlo に設定されたドメインを使わない理由は何でしょうか?localhost よりも「より正しい」はずです。

素晴らしい調査作業ですね、@Syonyk さん!

アプリの app.yml ファイルにある SMTP 設定を共有していただけますか?

「いいね!」 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 の値を使用することはできないでしょうか(単純なケースのほとんどではこれが正しいはずです)。EHLO で ‘localhost’ を送信するのは一般的に誤りです(少なくとも localhost 以外のサーバーと通信する場合は)。

standalone.ymlweb_only.yml に追加するのは良いアイデアだと思います。

デフォルトを DISCOURSE_HOSTNAME にするのは、localhost よりも明らかに良さそうです。最近変更されましたか?

ここで重要なのは、現在 Google に依存していない数万の Discourse インスタンスすべてが正常に動作しているという点です。そのようなデフォルトを変更すると、比較的少数の Google Apps ユーザーの対応は改善される一方で、他のすべてのユーザーが動作しなくなる可能性があります。

これをサンプルに追加し、さらに「Google Apps を送信メールに使用する方法」と題した howto トピックを作成してこれを文書化すべきです。誰か引き受けていただけませんか?

「いいね!」 4

了解しました。慎重を期すのが妥当だとは思います。ただし、localhost を受け入れるメールサーバーであれば、DISCOURSE_HOSTNAME も受け入れるだろうと推測しています(ただし、データがあるわけではありません :wink:)。標準テンプレートに含まれているだけでも、おそらく十分でしょう。

「いいね!」 1

はい、しかし「localhost」をリモートホストに送信することも RFC に違反しています。

強調は私が追加しました。

古い RFC では、サーバーが EHLO 文字列に基づいてクライアントを拒否すべきではないと記載されていますが、Google はこれを拒否しているようです。ただし、RFC 5321 にはそのような表現は見当たりません。

localhost を許容するリモートメールサーバーであれば、RFC で要求されている FQDN も許容し、かつそれを優先すると予想されます。問題を壊したくないという気持ちは理解できますが、関連する RFC を読む限り、Discourse のデフォルト設定は誤っており、それが動作しているのは、リモート SMTP サーバーが過度に寛容であるためです。

「いいね!」 1

一般的に推奨されているSMTPサービスにおいて、この設定が問題を引き起こさないことが確認できれば、DISCOURSE_HOSTNAME と同じ値にデフォルトを設定するPRを ./discourse-setup にマージする用意があります。

「いいね!」 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 ローカルホストでも、実際のドメインでも同様です。

設定時にこれを設定するのが正しい解決策だと考えます。少なくとも、必要に応じてユーザーが確認し、変更できる状態にあるからです。

「いいね!」 6

別の関連する問題があります:discourse-doctor はドメインを正しく設定していないようで、実際のインストールがメールを送信できるようになっても、まだ失敗します。

正常に動作する設定でも、discourse-doctor は引き続き「ファイルの終端に到達した」というエラーを報告します。

======================================== ERROR ========================================
                                    UNEXPECTED ERROR

end of file reached

====================================== SOLUTION =======================================
This is not a common error. No recommended solution exists!

Please report the exact error message above to https://meta.discourse.org/
(And a solution, if you find one!)
=======================================================================================

テストスクリプトには 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 {invalid domain} を送信する際の問題を覚えています。そのため、長期間にわたって誤って ehlo localhost を送信していたとは非常に考えにくいのですが。

「いいね!」 1

申し訳ありませんが、私が目にしたことを報告しただけです。あの ‘localhost’ も間違いだと思われます。私のウェブ開発の経験は主に古く、プロとしてその手の仕事を手がけてから10年以上経っています。ここまで来るのに時間がかかりました。Docker や Ruby などは私にとって比較的新しいものです。一方、tcpdump は…あのツールはよく知っています。:wink:

それでも、どんな状況であれ ‘localhost’ をリモートサーバーに送信するのは誤った挙動だと考えています。

「いいね!」 1

参考までに、私たちは1月13日以降、全く同じ問題に直面しています。最後に正常に送信されたメールは1月13日のもので、smtp-relay.gmail.comも使用しています。ソースコードを変更せずにこの問題を回避する方法はまだ見つかっていません。