如何配置Postfix使LDA投递失败时将邮件保留在队列中重试?
嘿,我正好碰到过类似的问题,咱们一步步来捋清楚解决思路。
首先得搞明白Postfix和Procmail配合时的返回码逻辑:Postfix负责本地投递的local代理,对mailbox_command的退出码有明确判断——默认情况下,大多数非0退出码会被当成永久失败直接 bounce 邮件,只有特定的几个码会被识别为临时失败,让邮件留在队列里重试。
你之前用/usr/bin/false返回的是1,这个属于Postfix认定的永久失败,所以直接退信了;而你需要让Procmail返回Postfix认可的临时失败码,比如75(标准的EX_TEMPFAIL,临时故障),这样Postfix才会把邮件留在队列里等待重试。
第一步:修改Procmail规则,返回正确的临时失败码
把你之前的测试规则改成下面这样,核心是在外部命令失败时,强制Procmail返回75而非程序本身的错误码:
:0 * ^Subject: failme$ { # 执行外部命令,-w等待命令完成,c保留原始邮件副本(可选,按需开启) :0wc |/usr/bin/false # 仅当前面的命令执行失败时触发,设置退出码为75(临时失败标识) :0e { EXITCODE=75 } }
这里的:0e是Procmail的错误捕获规则,专门用来处理前一个命令执行失败的场景,把EXITCODE设为75后,Postfix就会识别为临时故障,不会直接退信。
第二步:确认Postfix默认配置(一般无需额外修改)
Postfix的local代理默认已经把69(EX_UNAVAILABLE)、70(EX_SOFTWARE)、75(EX_TEMPFAIL)这几个退出码当成临时失败处理了,所以你不需要修改main.cf里的额外参数。不过可以按需检查下queue_run_delay(队列重试间隔,默认300秒)和maximal_queue_lifetime(邮件在队列中保留的最长时间,默认5天)这些参数是否符合你的需求。
第三步:测试验证
发送一封主题为failme的测试邮件,然后用postqueue -p查看队列,你应该能看到这封邮件处于deferred(延迟投递)状态,而非被直接退回。之后Postfix会按照配置的间隔自动重试投递,直到成功或者达到最大保留时间。
另外补充下你之前的疑问:Procmail的-t选项其实是针对Procmail自身无法写入用户邮箱的场景返回临时失败,而非你调用的外部程序出错的情况,所以单独用-t解决不了你的问题,必须手动在Procmail规则里捕获外部命令的错误并设置正确的退出码。
备注:内容来源于stack exchange,提问作者PoC

