添加 SMTP 事务延迟的最简单方法是附加一个delay控制到最终的accept语句,在我们声明的每个 ACL 中,如下所示
accept delay = 20s |
此外,您可能希望在deny语句中添加渐进延迟,该语句与 acl_rcpt_to 内的无效收件人("unknown user")有关。 这是为了减慢字典攻击的速度。 例如
deny message = unknown user !verify = recipient/callout=20s,defer_ok,use_sender delay = ${eval:$rcpt_fail_count*10 + 20}s |
应该注意的是,在接收到消息数据后,在 acl_data 中施加延迟是没有意义的。 恶意软件通常在此处断开连接,甚至在收到来自您服务器的响应之前。 在任何情况下,客户端是否在此处断开连接与 Exim 是否将继续传递消息无关。
如果您像我一样,您希望对哪些主机施加 SMTP 事务延迟更具选择性。 例如,如本文档前面所述,您可能会认为来自 DNS 黑名单的匹配或不可验证的 EHLO/HELO 问候本身不足以保证拒绝 - 但它们很可能足以触发事务延迟。
为了执行选择性延迟,我们希望将我们之前在 acl_rcpt_to 中执行的一些检查移动到 SMTP 事务的更早的点。 这样做是为了我们可以在看到任何麻烦迹象时立即开始施加延迟,从而增加导致恶意软件同步错误和其他麻烦的机会。
具体来说,我们想要
将 DNS 检查移动到 acl_connect。
将 Hello 检查移动到 acl_helo。 一个例外:我们此时还不能检查是否缺少 Hello 问候,因为此 ACL 是响应 EHLO 或 HELO 命令而处理的。 我们将在 acl_mail_from ACL 中进行此检查。
将发件人地址检查移动到 acl_mail_from。
但是,由于上述原因,我们不希望在 RCPT TO: 命令之后才实际拒绝邮件。 相反,在较早的 ACL 中,我们将转换各种deny语句为warn语句,并使用 Exim 的通用 ACL 变量来存储任何错误消息或警告,直到 RCPT TO: 命令之后。 我们这样做如下
如果我们决定拒绝传递,我们将错误消息存储在即将到来的 550 响应中,存储在$acl_c0或$acl_m0:
如果我们在邮件传递开始之前(即在 acl_connect 或 acl_helo 中)识别出条件,我们使用连接持久变量$acl_c0
一旦邮件事务开始(即在 MAIL FROM: 命令之后),我们将从$acl_c0复制任何内容到消息特定变量$acl_m0,并从此时开始使用后者。 这样,在此特定消息中识别出的任何条件都不会影响在同一连接中接收到的任何后续消息。
此外,我们将相应的日志消息存储在$acl_c1或$acl_m1,以类似的方式。
如果我们遇到不需要完全拒绝的条件,我们只将警告消息存储在$acl_c1或$acl_m1。 一旦邮件事务开始(即在 acl_mail_from 中),我们也会将此变量中的任何内容添加到消息标头中。
如果我们决定接受消息,而不考虑任何后续检查(例如 SpamAssassin 扫描)的结果,我们会在$acl_c0或$acl_m0中设置一个标志,但是$acl_c1和$acl_m1为空。
在每个 ACL 的开头,包括 acl_mail_from,我们记录当前时间戳在$acl_m2。 在 ACL 的末尾,我们使用$acl_c1或$acl_m1的存在来触发 SMTP 事务延迟,直到总共过去 20 秒。
下表总结了我们对这些变量的使用
作为这种方法的一个例子,让我们考虑我们在响应 Hello 问候时执行的两个检查; 一个是如果对等方使用 IP 地址问候,则拒绝邮件,另一个是警告问候中不可验证的名称。 以前,我们在 acl_rcpt_to 中进行了这两项检查 - 现在我们将它们移动到 acl_helo ACL。
acl_helo: # Record the current timestamp, in order to calculate elapsed time # for subsequent delays warn set acl_m2 = $tod_epoch # Accept mail received over local SMTP (i.e. not over TCP/IP). # We do this by testing for an empty sending host field. # Also accept mails received from hosts for which we relay mail. # accept hosts = : +relay_from_hosts # If the remote host greets with an IP address, then prepare a reject # message in $acl_c0, and a log message in $acl_c1. We will later use # these in a "deny" statement. In the mean time, their presence indicate # that we should keep stalling the sender. # warn condition = ${if isip {$sender_helo_name}{true}{false}} set acl_c0 = Message was delivered by ratware set acl_c1 = remote host used IP address in HELO/EHLO greeting # If HELO verification fails, we prepare a warning message in acl_c1. # We will later add this message to the mail header. In the mean time, # its presence indicates that we should keep stalling the sender. # warn condition = ${if !def:acl_c1 {true}{false}} !verify = helo set acl_c1 = X-HELO-Warning: Remote host $sender_host_address \ ${if def:sender_host_name {($sender_host_name) }}\ incorrectly presented itself as $sender_helo_name log_message = remote host presented unverifiable HELO/EHLO greeting. # # ... additional checks omitted for this example ... # # Accept the connection, but if we previously generated a message in # $acl_c1, stall the sender until 20 seconds has elapsed. accept set acl_m2 = ${if def:acl_c1 {${eval:20 + $acl_m2 - $tod_epoch}}{0}} delay = ${if >{$acl_m2}{0}{$acl_m2}{0}}s |
然后,在 acl_mail_from 中,我们将消息从$acl_c{0,1}转移到$acl_m{0,1}。 我们还将$acl_c1的内容添加到消息标头。
acl_mail_from: # Record the current timestamp, in order to calculate elapsed time # for subsequent delays warn set acl_m2 = $tod_epoch # Accept mail received over local SMTP (i.e. not over TCP/IP). # We do this by testing for an empty sending host field. # Also accept mails received from hosts for which we relay mail. # accept hosts = : +relay_from_hosts # If present, the ACL variables $acl_c0 and $acl_c1 contain rejection # and/or warning messages to be applied to every delivery attempt in # in this SMTP transaction. Assign these to the corresponding # $acl_m{0,1} message-specific variables, and add any warning message # from $acl_m1 to the message header. (In the case of a rejection, # $acl_m1 actually contains a log message instead, but this does not # matter, as we will discard the header along with the message). # warn set acl_m0 = $acl_c0 set acl_m1 = $acl_c1 message = $acl_c1 # # ... additional checks omitted for this example ... # # Accept the sender, but if we previously generated a message in # $acl_c1, stall the sender until 20 seconds has elapsed. accept set acl_m2 = ${if def:acl_c1 {${eval:20 + $acl_m2 - $tod_epoch}}{0}} delay = ${if >{$acl_m2}{0}{$acl_m2}{0}}s |
所有相关的更改都包含在接下来的最终 ACL 中。