A.10. 添加 SpamAssassin

在 Exim 中,通常有两种方法可以在 SMTP 时调用 SpamAssassin

A.10.1. 通过 Exiscan 调用 SpamAssassin

Exiscan-ACL"spam" 条件通过 SpamAssassin 或 Brightmail 传递消息,如果这些指示消息是垃圾邮件,则触发。 默认情况下,它连接到在spamd上运行的 SpamAssassin 守护进程 (localhost)。 可以通过在 Exim 配置文件 *main* 部分中添加spamd_address设置来更改主机地址和端口。 有关更多信息,请参阅exiscan-acl-spect.txt随补丁包含的文件。

在我们的实现中,我们将拒绝被归类为垃圾邮件的消息。 但是,我们希望将此类消息的副本保存在单独的邮件文件夹中,至少暂时如此。 这样,用户可以定期扫描 误报

Exim 提供可以应用于接受消息的控制,例如freeze。 Exiscan-ACL 补丁添加了另一个这样的控制,即fakereject。 这会导致以下 SMTP 响应

550-FAKEREJECT id=message-id
550-Your message has been rejected but is being kept for evaluation.
550 If it was a legit message, it may still be delivered to the target recipient(s).

通过将以下代码段插入到 acl_data 中,可以在我们的实现中包含此功能,accept语句之前

  # Invoke SpamAssassin to obtain $spam_score and $spam_report.
  # Depending on the classification, $acl_m9 is set to "ham" or "spam".
  #
  # If the message is classified as spam, pretend to reject it. 
  #
  warn
    set acl_m9  = ham
    spam        = mail
    set acl_m9  = spam
    control     = fakereject
    logwrite    = :reject: Rejected spam (score $spam_score): $spam_report

  # Add an appropriate X-Spam-Status: header to the message.
  #
  warn
    message     = X-Spam-Status: \
                  ${if eq {$acl_m9}{spam}{Yes}{No}} (score $spam_score)\
                  ${if def:spam_report {: $spam_report}}
    logwrite    = :main: Classified as $acl_m9 (score $spam_score)

在这个例子中,$acl_m9最初设置为 "ham"。 然后以用户mail的身份调用 SpamAssassin。 如果消息被归类为垃圾邮件,则$acl_m9设置为 "spam",并且发出上面的FAKEREJECT响应。 最后,将X-Spam-Status标头添加到消息中。 我们的想法是,邮件传递代理或收件人的 邮件用户代理可以使用此标头将垃圾邮件过滤到单独的文件夹中。

A.10.2. 配置 SpamAssassin

默认情况下,SpamAssassin 以冗长、表格状的格式显示其报告,主要适合包含在消息正文中或作为附件。 在我们的例子中,我们需要一个简洁的报告,适用于上面示例中的X-Spam-Statusheader。 为此,我们在其站点特定配置文件中添加以下代码段(/etc/spamassassin/local.cf, /etc/mail/spamassassin/local.cf,或类似的文件)

### Report template
clear_report_template
report "_TESTSSCORES(, )_"

此外,内置了 贝叶斯评分功能,默认情况下已启用。 我们通常希望关闭此功能,因为它需要针对每个用户进行训练,因此不适用于系统范围的 SMTP 时间过滤

### Disable Bayesian scoring
use_bayes 0

要使这些更改生效,您必须重新启动 SpamAssassin 守护进程(spamd)。

A.10.3. 用户设置和数据

假设您有许多用户想要指定他们自己的 SpamAssassin 首选项,例如垃圾邮件阈值、可接受的语言和字符集、白名单/黑名单发件人等等。 或者也许他们真的希望能够利用 SpamAssassin 的原生贝叶斯评分(尽管我不明白为什么[1])。

正如文档前面 用户设置和数据 部分中所讨论的,有一种方法可以实现这一点。 我们需要限制每个传入邮件传递接受的收件人数量为一个。 我们接受调用者发出的第一个 RCPT TO: 命令,然后使用 451 SMTP 响应延迟后续命令。 与 灰名单一样,如果调用者是行为良好的 MTA,它将知道如何解释此响应,并在以后重试。

A.10.3.1. 告诉 Exim 每个传递只接受一个收件人

acl_rcpt_to 中,我们在验证收件人地址后,但在任何与从远程主机到本地用户的未经身份验证的传递相关的accept语句之前插入以下语句(即,在任何灰名单检查、信封签名检查等之前)

  # Limit the number of recipients in each incoming message to one
  # to support per-user settings and data (e.g. for SpamAssassin).
  #
  # NOTE: Every mail sent to several users at your site will be
  #       delayed for 30 minutes or more per recipient.  This
  #       significantly slow down the pace of discussion threads
  #       involving several internal and external parties.
  #
  defer
    message      = We only accept one recipient at a time - please try later.
    condition    = $recipients_count

A.10.3.2. 将收件人用户名传递给 SpamAssassin

acl_data 中,我们修改上一节中给出的spam条件,以便它将收件人地址的本地部分中指定的用户名传递给 SpamAssassin。

  # Invoke SpamAssassin to obtain $spam_score and $spam_report.
  # Depending on the classification, $acl_m9 is set to "ham" or "spam".
  #
  # We pass on the username specified in the recipient address,
  # i.e. the portion before any '=' or '@' character, converted
  # to lowercase.  Multiple recipients should not occur, since
  # we previously limited delivery to one recipient at a time.
  #
  # If the message is classified as spam, pretend to reject it. 
  #
  warn
    set acl_m9  = ham
    spam        = ${lc:${extract{1}{=@}{$recipients}{$value}{mail}}}
    set acl_m9  = spam
    control     = fakereject
    logwrite    = :reject: Rejected spam (score $spam_score): $spam_report

请注意,我们没有使用 Exim 的${local_part:...}函数来获取用户名,而是手动提取了任何 "@""=" 字符之前的部分。 这是因为我们将在我们的 信封签名方案中使用后一个字符。

A.10.3.3. 在 SpamAssassin 中启用按用户设置

现在让我们再次看看 SpamAssassin。 首先,您可以选择删除我们之前在其站点范围配置文件中添加的use_bayes 0设置。 无论如何,每个用户现在都能够决定是否覆盖他们自己的此设置。

如果系统上的邮箱直接映射到具有主目录的本地 UNIX 帐户,则您就完成了。 默认情况下,SpamAssassin 守护进程 (spamd) 执行setuid()到我们传递给它的用户名,并将用户数据和设置存储在该用户的主目录中。

如果不是这种情况(例如,如果您的邮件帐户由 Cyrus SASL 或其他服务器管理),您需要告诉 SpamAssassin 在哪里找到每个用户的首选项和数据文件。 此外,spamd 需要继续作为特定的本地用户运行,而不是尝试setuid()到一个不存在的用户。

我们通过指定启动时传递给 spamd 的选项来完成这些操作

  • 在 Debian 系统上,编辑OPTIONS=中的设置/etc/default/spamassassin.

  • 在 RedHat 系统上,编辑SPAMDOPTIONS=中的设置/etc/sysconfig/spamassassin.

  • 其他人,自己解决。

您需要的选项是

  • -u username- 指定 spamd 将在其下运行的用户(例如mail)

  • -x- 禁用用户主目录中的配置文件。

  • --virtual-config-dir=/var/lib/spamassassin/%u- 指定按用户设置和数据的存储位置。 "%u" 被调用用户名替换。 spamd 必须能够创建或修改此目录

    # mkdir /var/lib/spamassassin
    # chown -R mail:mail /var/lib/spamassassin
    

不用说,进行这些更改后,您需要重新启动 spamd

笔记

[1]

虽然贝叶斯训练确实特定于每个用户,但应该注意的是,IMHO,SpamAssassin 的贝叶斯分类器无论如何都不是那么出色。 特别是,我发现自垃圾邮件发送者已经学会通过在他们的邮件中(例如,在 HTML 消息的元数据中)播种随机字典单词或故事来击败此类系统的情况以来,情况就是如此。