设置

设置

设置

我们在 Debian Squeeze 6 (Squeeze) (LTS) 上运行 exim 版本 4.72。

Exim 用于仅限外发电子邮件- 它不处理来自其他服务器/域的电子邮件 - 它仅有的处理来自本地运行的 java(web)应用程序的(传出)消息。

为了匿名化,我们假设我们用于公司电子邮件地址的域名以及运行 Java Web 应用程序的域名称为示例.com

的背景

我们有一个用 Java 框架编写的 Web 应用程序,它允许用户发送电子邮件。envelope-from这些电子邮件的地址属于我们的域:[电子邮件保护]
从技术上讲,这些电子邮件是由 Java Web 应用程序创建和发送的(我们使用Java邮件API) 本地发送到 exim,然后最终将它们发送到目标地址。
发送到我们域的电子邮件由 Google Apps/Gmail 处理(我们还设置了 SPF 记录等)。
到目前为止,此设置运行良好,没有任何问题。

为了完整起见,以下是我们的(匿名)update-exim4.conf.conf

root@server:~# cat /etc/exim4/update-exim4.conf.conf 
# ... comments are skipped ...

dc_eximconfig_configtype='internet'
dc_other_hostnames='node1.example.com'
dc_local_interfaces='127.0.0.1 ; ::1'
dc_readhost=''
dc_relay_domains=''
dc_minimaldns='false'
dc_relay_nets=''
dc_smarthost=''
CFILEMODE='644'
dc_use_split_config='true'
dc_hide_mailname=''
dc_mailname_in_oh='true'
dc_localdelivery='maildir_home'

问题

最近,我们收到了退回的电子邮件,因为我们的 java web 应用程序的用户试图向不存在的电子邮件地址发送电子邮件。

让我们看一下(匿名)日志文件中的这种情况:

root@server:~# cat /var/log/exim4/mainlog
# try to send an email to an unrouteable address
2014-06-01 17:34:07 1Wr7lv-0000CR-G0 <= [email protected] H=localhost (node1.example.com) [127.0.0.1] P=esmtp S=620363 id=484648301.663.1401636847496.JavaMail.webapp@node1
2014-06-01 17:34:07 1Wr7lv-0000CR-G0 ** [email protected]: Unrouteable address
2014-06-01 17:34:07 1Wr7lv-0000CR-G0 Completed

# Immediately a bounce message is generated and gets send to the the origin sender
2014-06-01 17:34:07 1Wr7lv-0000CU-OZ <= <> R=1Wr7lv-0000CR-G0 U=Debian-exim P=local S=108065
2014-06-01 17:34:09 1Wr7lv-0000CU-OZ => [email protected] R=dnslookup T=remote_smtp H=aspmx.l.google.com [74.125.136.26] X=TLS1.0:RSA_ARCFOUR_SHA1:16 DN="C=US,ST=California,L=Mountain View,O=Google Inc,CN=mx.google.com"
2014-06-01 17:34:09 1Wr7lv-0000CU-OZ Completed

我们想要实现的目标

我们现在想要显示(或通知 - 无论如何)每个 Java Web 应用程序用户在 Java Web 应用程序中他/她想要发送的电子邮件实际上发送失败了。
(请记住:退回的电子邮件目前被发送回[电子邮件保护]-不是发送到 Web 应用程序用户的电子邮件地址 - 因此这些用户不知道他们的电子邮件没有被发送)

我们现在的基本(简单)想法是:
让 exim 记录id每条失败消息(在上面的例子中是484648301.663.1401636847496.JavaMail.webapp@node1) 尽快写入本地运行的 PostgreSQL 数据库,我们就知道邮件传递失败了(或者:尽快生成和/或发送相应的退回邮件)。
如果这是可能的,我们可以轻松地从 Java Web 应用程序中的数据库中读取这些“失败”id消息,然后 - 瞧! - 我们可以向用户显示他们的邮件已被退回。
很简单的想法,不是吗?

只是为了更好地理解:idjava web 应用程序中的每条消息都已经生成了所提到的(唯一)JavaMail API 将消息发送到 exim - 因此我们可以确保我们的 java web 应用程序知道每条id消息(当然也知道哪个用户发送了特定的消息)。

可能的解决方案?

我做了一些研究可以得到解决,这就是我现在想到的(如果我错了,请纠正我 - 这些只是我现在的想法):

方法 1:将失败的消息通过管道传送id到 bash 脚本。然后,这样的 bash 脚本可以轻松地将其存储id在本地 PostgreSQL 数据库中。这可能吗?如何实现?我看到的问题是,一旦消息失败,就不会执行其他 exim 路由器 - 这意味着我无法编写处理失败消息的路由器 - 失败的消息永远不会到达路由器。还是我错了?

方法 2:我想到的另一种可能性是不是查看失败的原始消息,但新生成的反弹消息。据我所知,这个新反弹的消息也会经过所有路由器,直到其中一个路由器接受它。所以也许我们可以编写一个路由器来检查消息是否是反弹消息,然后将其传送到 bash 脚本?但我怎么知道消息是否是反弹消息?它是否有特殊的标题或其他重要的东西?(记住:因为我们只使用 exim 来传出电子邮件我们可以确定这将始终是一个由我们的 Java Web 应用程序用户“生成”的退回消息)。

方法 3:与方法 2 类似,但我们不需要编写路由器,而是可以使用系统过滤器,它会处理反弹消息,然后执行命令/管道...

方法 4:是否可以配置error_copy甚至error_to不配置电子邮件地址而是配置到 shell 脚本的管道(当然,它会接收到 id 和/或整个失败消息)?

还有两件事:似乎 exim 也可以直接与数据库对话(我在某处读到过)——但我不知道如何利用它解决我所描述的问题。另外:不管这个问题如何解决,我都不想修改任何配置文件,因为当 exim 通过以下方式更新时,这些配置文件会被覆盖apt-get(我不想在每次更新后重新配置 exim)。

需要你的帮助

您将如何解决这个问题(将失败的消息保存id到数据库中)?如果我错了,请纠正我 - 我对 exim 配置了解不多。此外,正如您所见,我考虑的解决方案更具理论性 - 如果有人能向我展示真实世界的配置以及将这些配置放在哪里以使其工作,那就太好了。

非常感谢您的帮助!

答案1

Exim 版本 4.82.1 具有一项名为 TPDA(传输邮政投递操作)的功能,该功能可让您执行所需的操作。您应该能够切换存储库源并安装较新版本的 exim,这将为您提供具有此功能的版本。

如果您坚持使用当前发行版提供的版本,我认为这些都不是好的选择。

  1. 最好的解决方案是为每封邮件创建一个唯一的发件人。例如,获取收件人,附加时间,然后创建 MD5 哈希,并将其设为发件人 @support.example.com。将该信息放在一个表中,其中包含有关收件人以及发送电子邮件的人的信息。当您处理发往域 support.example.com 的电子邮件时,如果收件人与其中一个哈希完全匹配,您可以进行查找以查看谁发送了该邮件,通知发件人,禁止收件人将来发送邮件等。
  2. 下一种方法是将 support@ 电子邮件地址传送到两个地方:一个是您的支持人员可以阅读的邮箱,另一个是您编写的脚本,该脚本可以找出谁发送了该消息,通知发件人并禁止收件人将来发送等。
  3. 您没有建议的一个选项是编写一个应用程序来跟踪您的日志文件,解析行并提取传递信息并将其放入数据库,然后通知发件人该问题,并禁止收件人将来发送等。

相关内容