在 localhost:25 上连接 SMTP 服务器而无需认证?

./discourse-setup 连接到本地 localhost:25 且无需认证的 SMTP 服务器时,正确的设置是什么?

我非常惊讶这竟然不是开箱即用的功能;这在大多数 Linux 安装中是默认配置。

我的服务器在本地运行 postfix;它无法从互联网访问。例如,运行 mail 命令时一切正常。我在网上找到了一些非官方指南,建议修改 /var/discourse/containers/app.yml,最终我使用以下设置成功安装并启动了:

  DISCOURSE_SMTP_ADDRESS: localhost
  DISCOURSE_SMTP_PORT: 25
  DISCOURSE_SMTP_USER_NAME: discourse@opensouceecology.org
  DISCOURSE_SMTP_PASSWORD: "none"
  DISCOURSE_SMTP_AUTHENTICATION: none
  DISCOURSE_SMTP_OPENSSL_VERIFY_MODE: none
  DISCOURSE_SMTP_ENABLE_START_TLS: false

请注意,如果我省略 DISCOURSE_SMTP_USER_NAMEDISCOURSE_SMTP_PASSWORD 变量,安装脚本会报错提示它们是必需的(这是一个 bug 吗?)。

现在,当我在 Discourse 的 Web 界面中点击“重新发送激活邮件”按钮时,日志文件(/var/discourse/shared/standalone/log/rails/production.log)中会出现以下条目:

Started PUT "/finish-installation/resend-email" for 127.0.0.1 at 2019-11-07 13:15:31 +0000
Processing by FinishInstallationController#resend_email as HTML
  Parameters: {"authenticity_token"=>"SzQCvRWiqdXsBKzOjIB0X7KkvXro7Od6SdP8Qa8vvrskPeNYZNos5ORHJfyDUrHiKShZR/txM6NHuqHHCQCR1w=="}
  Rendering finish_installation/resend_email.html.erb within layouts/finish_installation
  Rendered finish_installation/resend_email.html.erb within layouts/finish_installation (Duration: 0.7ms | Allocations: 103)
  Rendered layouts/_head.html.erb (Duration: 0.5ms | Allocations: 103)
Completed 200 OK in 98ms (Views: 3.0ms | ActiveRecord: 0.0ms | Allocations: 4763)
  Rendering layouts/email_template.html.erb
  Rendered layouts/email_template.html.erb (Duration: 0.5ms | Allocations: 141)
Delivered mail c4ca58ca-345e-46c4-81bc-6d0eac7afa04@discourse.opensourceecology.org (11.3ms)
Job exception: wrong authentication type none

……但我的认证类型确实是 ‘none’。无需认证的正确设置应该是什么?

编辑:另外,有人能链接到定义所有可能的 DISCOURSE_SMTP_* 变量及其所有有效值的文档吗?

编辑 2:事实证明这比应有的困难得多。我认为 ‘localhost’ 在 Docker 容器内解析为 Discourse Docker 容器本身(app),而不是运行我的 postfix SMTP 服务器的 Docker 主机。这又因 postfix 的 mynetworksiptables(由 discourse-setup 脚本或其子脚本配置)而变得更加复杂。正确的配置应该是什么,才能让 Discourse 直接使用我想运行 Discourse 的那台服务器上的 SMTP 服务器,且无需 SMTP 认证?

我认为这种情况大约 20 年来已经不太符合事实了。

对于像您这样的场景,您可能无法使用 discourse-setup,因为即使是在防火墙后面,很少有人会使用未设置密码保护的 SMTP 服务器。

我会建议为我的邮件服务器配置 SMTP 密码。实际上这几乎没有 downside。

如果您不想这样做,我认为与其将“认证方式”设为“none”,不如尝试留空(即使用""),用户名和密码也同理。

我也这么认为。您可以尝试使用容器名称。我认为您需要确认它们是否位于同一个 Docker 网络中。

在 2019 年,这是 RHEL/CentOS 上 Postfix 的默认配置。Postfix 仅绑定到回环接口,并丢弃所有非源自 127.0.0.0/8 的 SMTP 请求。无需认证。我不确定 Debian 的情况,但我推测 exim 也有类似的默认配置。

论坛上有其他用户遇到此问题,以下是几个相关主题:

似乎还没有关于如何在 RHEL/CentOS 上设置此配置(包括对 Discourse 和 Postfix 进行必要更改)的主题,因此我在这里记录一下。

这似乎无法通过 discourse-setup 脚本实现,但我确实成功实现了。

首先,我需要找出 Docker 主机在 Docker 容器中看到的 IP 地址。使用 127.0.0.1 行不通,因为 Docker 容器会将 127.0.0.1 视为自身。相反,我们需要指定运行 Postfix SMTP 服务器的 Docker 主机的 IP 地址或主机名,并且该地址必须对 Docker 容器可访问(例如,如果您希望 SMTP 服务器不对外网开放,则不要使用 Docker 主机的公网 IP 地址)。

我在 Docker 主机上执行以下命令,从 docker0 接口中提取了相关的 Docker 主机 IP 地址(172.17.0.1):

[maltfield@osestaging1 ~]$ ip address show docker0
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether 02:42:80:35:65:a1 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:80ff:fe35:65a1/64 scope link 
       valid_lft forever preferred_lft forever
[maltfield@osestaging1 ~]$

然后,我编辑了 Discourse 应用的 yaml 文件,将 “DISCOURSE_SMTP_ADDRESS” 设置为上面的 172.17.0.1

[maltfield@osestaging1 ~]$ cd /var/discourse/
[maltfield@osestaging1 discourse]$ grep SMTP containers/app.yml
  DISCOURSE_SMTP_ADDRESS: 172.17.0.1
  DISCOURSE_SMTP_PORT: 25
  DISCOURSE_SMTP_AUTHENTICATION: none
  DISCOURSE_SMTP_OPENSSL_VERIFY_MODE: none
  DISCOURSE_SMTP_ENABLE_START_TLS: false
[maltfield@osestaging1 discourse]$ 

请注意,我最初尝试使用内部 Docker 主机名 host.docker.internal,但据称该主机名对 Linux Docker 用户不可用。

由于 RHEL/CentOS 中的默认 Postfix 配置仅绑定到 127.0.0.1(这很好),因此您需要修改 /etc/postfix/main.cf,使其也绑定到 docker0 接口,并将该子网添加到 mynetworks 组,以便接受来自 Docker 容器的 SMTP 流量。

[maltfield@osestaging1 postfix]$ grep -ir '172.17' /etc/postfix/*
/etc/postfix/main.cf:inet_interfaces = 127.0.0.1, 172.17.0.1
/etc/postfix/main.cf:mynetworks = 127.0.0.0/8, 172.17.0.0/16
[maltfield@osestaging1 postfix]$ 

进行这些更改后,重新构建 Discourse,它现在应该能够通过 Docker 主机的 Postfix 发送邮件了。

/var/discourse/launcher rebuild app

虽然这可以工作,但我还有几个问题:

  1. 是否已有其他环境变量或主机名指向 Docker 主机(本例中为 172.17.0.1)?

我注意到 launcher “注入”了一个名为 DISCOURSE_HOST_IP 的环境变量。是否可以将此 DISCOURSE_SMTP_ADDRESS yaml 键设置为与其他 yaml 键相同的值,例如:

DISCOURSE_SMTP_ADDRESS: $DISCOURSE_HOST_IP
  1. 一般来说,Docker 主机的 172.17.0.1 IP 地址有多稳定?在 RHEL/CentOS 系统上它总是这个 IP 吗?它会发生变化吗?

@maltfield 只是想感谢您提供的说明。

我在 Debian 上也遇到了同样的问题……我想可以创建一个单独的邮件用户用于 Discourse,并让它连接到 site:465 端口进行登录。不过,从内部连接到 25 端口在我看来更为合理。

顺便一提,在 Debian 10 上使用来自软件源的 docker.io 时,docker0 网段仍然是 172.17.0.1/16。

也许你误解了 @maltfield 的意思?

据我所知(这比 20 年前还要早),Linux(/ Unix / BSD / Solaris)系统一直运行着一个 SMTP 服务器,其默认配置为乐意中继任何来自 localhost 的邮件,无需任何询问。这减轻了运行在该机器上的任何其他应用程序自行担心和配置 SMTP 设置的负担。

是的,大多数 Linux 服务器在默认情况下或安装并配置后,发送邮件无需身份验证。如果它们不从本地网络以外的任何地方中继邮件,也不需要身份验证。默认的 Discourse 安装脚本不适用于大多数服务器,它是为特定的一组基于 Docker 的云服务设计的。

您可以在开源生态(Open Source Ecology)Wiki 上阅读我为在运行 RHEL/CentOS 7 的专用裸机服务器上安装 Discourse 编写的完整详细指南。请注意其中关于 SMTP 的部分:

好吧,在这方面我几乎总是听从你的意见!我想我已经很多年没有在本地主机上运行过 SMTP 服务器了,所以我不太清楚自己在说什么也是情有可原的。

这更证明我错了!:man_shrugging:

不错!

不过,discourse-setup 基本上只适合那些对系统管理几乎一无所知的人。如果你知道如何安装 SMTP 服务器,直接编辑 yml 文件就行了。

如果容器运行的主机可以通过 DNS 获取其名称,只需指定该名称即可:

DISCOURSE_SMTP_ADDRESS: real.machine.example.com

Postfix 仍会识别邮件来源为本地,并按此方式处理。

如果 DISCOURSE_HOSTNAMEDISCOURSE_SMTP_ADDRESS 相同,是否会导致问题?

我已经设置了 Docker 主机上的 Postfix 和 Dovecot,以接受 TLS 上的身份验证请求。我的服务器有有效的证书,但无论我尝试哪种配置,都无法让 Discourse 正确地将邮件发送到 Docker 主机上运行的 Postfix。

如果我使用主机名(discourse.[myhost].com),它不起作用(甚至无法连接)。如果我使用 Docker 主机的 IP 地址(172.17.0.1),我会收到

主机名“172.17.0.1”与服务器证书不匹配

显然,我需要使用创建证书时使用的 FQDN。如果我尝试将 DISCOURSE_SMTP_ENABLE_START_TLS 设置为 false,它会提示我需要发出 start TLS 命令。我不想在没有 TLS 的情况下开放 Postfix,所以这对我来说并不是一个可行的选择。

使用外部 SMTP 中继也不是一个选项,因为我不想支付更多费用才能确保电子邮件不会进入人们的垃圾邮件/垃圾邮件文件夹。

我必须说,我对 Docker 网络只有初步的了解,对设置带有 Dovecot 进行身份验证的 Postfix SMTP 服务器的了解更少,但我不是完全的新手,但以安全/可靠的方式进行设置被证明极其困难。

@maltfield 我读了你关于开源生态的文章,但我不希望使用端口 25 上的未身份验证连接。我工作的大学对于使用未加密、未身份验证的通信(即使在本地网络设置中)有非常严格的协议。他们非常担心它会被心怀不满的学生滥用。

所以,我发现使用相同的hostname来运行Discourse服务器和SMTP服务器有些可疑。

进一步调查发现,docker容器在hosts文件中添加了以下条目:

172.17.0.2	discourse.[mydomain].com discourse

因此,任何试图为运行中的容器和docker主机使用相同hostname的尝试都会导致容器发出的所有请求都指向容器本身,而不是docker主机。

真是个惨败!为什么Discourse不在容器中提供默认配置的SMTP服务器!?
或者让不使用Docker也能更容易地使用Discourse!

我在这两个问题上浪费了两天生命。

最后,我接受了无法使用 TLS 与 Docker 主机上运行的 SMTP 进行通信(除非使用不同的主机名获取证书)这一事实。为了最终让它工作起来,我必须确保在 Postfix main.cf 文件中将 172.17.0.0/16 添加到 mynetworks 变量中:

mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 172.17.0.0/16

这允许 Postfix 将此服务器上运行的任何 Docker 容器视为网络的一部分。

我必须做的另一个更改是确保 smtpd_sasl_security_options 设置为 noanonymous,以防止“陌生人”将 Postfix 用作 SMTP 中继。请确保此处未设置 noplaintext 选项,否则 Postfix 将只允许使用 TLS 进行身份验证。

smtpd_sasl_security_options = noanonymous

有了这些配置设置,我就可以在 app.yml 中指定以下设置:

  DISCOURSE_SMTP_ADDRESS: 172.17.0.1                            # Docker 主机的 IP 地址(从该容器内部可见)
  DISCOURSE_SMTP_PORT: 25
  DISCOURSE_SMTP_USER_NAME: discourse
  DISCOURSE_SMTP_PASSWORD: pa$$word
  DISCOURSE_SMTP_ENABLE_START_TLS: false                         # (可选,默认为 true)
  DISCOURSE_SMTP_DOMAIN: discourse.[mydomian].com                # (某些提供商需要)
  DISCOURSE_NOTIFICATION_EMAIL: noreply@discourse.[mydomain].com # (用于发送通知的地址)

我还为 Docker 主机创建了一个本地用户帐户,仅用于 SASL 身份验证。
通过这些更改,我终于可以从容器发送邮件了,但我总觉得这是一个不理想的配置。理想情况下,应该可以使用所需的 Docker 主机名,并将对 discourse.[mydomain].com 的请求正确路由。

现在我需要设置 SPF 和 DKIM,以便邮件能够进入人们的收件箱(而不是垃圾邮件文件夹)。我非常希望有一天这个痛苦的过程能更加自动化。也许提供一些关于如何在不使用 Docker 的情况下配置 discourse 的说明(例如,可以与 cPanel 一起使用),或者通过在 Docker 容器中提供某种默认的 SMTP 配置来简化邮件设置过程!

如果你回到上世纪90年代初,情况就像你希望的那样。你所谈论的一切都运行正常。但垃圾邮件发送者导致了情况变得更加复杂。这个过程只会变得更加困难,这就是为什么建议使用一些能为你处理许多复杂问题的服务。

很高兴您的邮件设置已成功运行!

昨天以来,邮件故障排除指南有一些更新。如果您仍然对您的邮件设置不满意,不妨再试一次!


可以通过使用以下方法解决:


……只是默认的域名到 IP 的 DNS 解析。

别误会我的意思。我明白 SPF 和 DKIM 的重要性。只是有点烦人。我只希望这些东西能安装在我们现有的 cPanel 服务器上,因为这些东西已经在那里配置好了。

这正是我缺少的。现在使用 TLS 可以正常工作了。

我还启用了 DKIM,所以邮件应该不会再被标记为垃圾邮件了。