Top

在Debian上安装邮件系统

反垃圾邮件

本节内容抄自O'RELLY出版的《Postfix权威指南》,2006年第一版,基于postfix 2.0.18。
现在官网最新的版本是3.7;debian11上安装的是3.5.6;FreeBSD上安装的是3.7.2,1。

Postfix的挡信机制有四种:

客户端判别规则

下列过滤规则可设定一系列对客户端信息的限制条件: Postfix内置了一些限制条件: 一个简单的实例:
smtpd_recipient_restrictions =
  permit_mynetworks
  reject_unauth_destination
  reject_invalid_hostname
  reject_unknown_sender_domain
以上例子在执行时,若客户端来自mynetworks或mynetworks_style定义的网络,则permit_mynetworks返回OK,整个smtpd_recipient_restrictions过滤规则结束,不再检查其他条件。
如果客户端来自外网,则permit_mynetworks返回DUNNO(既不是OK,也不是REJECT),则继续检查reject_unauth_destination;
如果收件人不在mydestination定义的网域,则reject_unauth_destination返回REJECT;否则再次返回DUNNO,继续检查reject_invalid_hostname;
如果客户端使用HELO命令提供的主机名称是无效的,则reject_invalid_hostname返回REJECT;否则再次返回DUNNO,继续检查reject_unknown_sender_domain;
如果客户端在MAIL FROM提供的邮件地址的网域部分无效,则检查结果是REJECT;
如果上述四项限制条件都顺利通过而没有发生任何REJECT,则Postfix就会收下邮件,并交给适当的MDA处理。

定义限制条件

依据对象不同,限制条件可分为六类:
访问表
访问表(access map)是Postfix查询表的一种,由一系列key-value组成。key为客户端标识信息(IP地址、邮件地址、主机名等),value则表示处理动作(OK或REJECT)。
访问表较常用的是check_recipient_access type:mapname、check_sender_access type:mapname和check_sender_access type:mapname
访问表的动作最常用的是OK(通过当前规则检查,继续检查下一组过滤规则)、REJECT message-text(拒收邮件,可以跟一个简短信息说明拒收理由)。
另外还有一些动作,比如DUNNO、FILTER transport:nexthop、HOLD、DISCARD等,不太常用。
客户端参数
严格语法条件
DNS限制条件
实时黑名单
实时黑名单(RBL,Real-Time Blacklist)的抵制效果已经越来越差。
通用限制条件

SMTP语法规范

SMTP协议相当宽松,建议设置smtpd_helo_required = yes要求对方提供HELO/EHLO。
strict_rfc821_envelopes = yes则要求对方提供正确格式的地址,否则将拒收。

内容检查

设定内容检查参数

默认情况下,如果没有设定mime_header_checks和nested_header_checks,它们将与head_checks共享同一个查询表。
设定检查参数时,必须先注明正则表达式的语法格式(regexp代表Postfix的标准正则表达式语法,pcre代表Perl兼容的语法)以及模式表的完整路径。例如:
header_checks = regexp:/etc/postfix/header_checks
模式表的索引键时描述比较特征的正则表达式,此正则表达式本身必须被一对分隔符抱起来(通常是//),例如:
/驾照销分/ REJECT 我的驾照好着呢
如果邮件标题里出现“驾照销分”字样,则邮件会被拒收,对方会收到“我的驾照好着呢”的回复。

内容检查的响应动作

一般常用的是REJECT,后面可以跟一个字符串,将拒收原因反馈给发件人。
WARM message-text模拟拒收动作,可用于测试正则表达式的匹配效果。
IGNORE删除符合模式标题字段或整行文字。可用于删除某些敏感信息。不建议使用。

模式匹配

执行标题检查时,标题里的每一个字段都要依序与模式表里的正则表达式匹配。如果字段内容跨越多行,在对比之前会先被合并成一行(原本的CR、LF、Tab等字符也会被当作字段值的一部分)。
从第一个表达式开始对比,只要发现字段符合某正则表达式,整个对比过程立刻终止,并执行该正则表达式对应的动作。只有所有字段都不合法所有模式的情况下,整个标题才算通过检查。

执行正文检查时,body_checks指定的模式表里的每个正则表达式依序与正文里的每一行对比,每次只对比一行。
如果发现符合模式的字符串则整个对比过程结束,执行此模式对应的动作。

如果对比的文字行(无论时标题还是正文)超过长度上限,Postfix会将它们拆成符合长度限制的段落,然后分段进行检查。文字行的字符数上限由line_length_limit参数决定,默认值为2048。
标题的总长度由header_size_limit决定,默认为100K,超出的话也会被分段处理。
正文的总长度由body_checks_size_limit决定,默认为50K,超出部分不会被检查,这避免Postfix扫描整个文件。

可以用head_checks进行简单的病毒过滤:
/name ?="?.*\.(bat|scr|com|dell|exe|hta|pif|vbs)"?/ REJECT
这可以拦截夹带危险文件(Windows下的可执行文件)的邮件。

下面是一个典型的body_checks:
/increase you sales by/           REJECT 引诱性文字
/in compliance (with|of) strict/  REJECT 恐吓性文字
/lowest rates.*\|/                REJECT 任何含有引诱性文字,且其后跟任何文字(.*)以及一个感叹号的字符串,例如"We have our lowest rates in 40 years!"
/[:alpha:]<!--.*-->[:alpha:]/     REJECT 可疑的嵌入HTML注释 垃圾邮件发送者通常用这种技巧试图骗过过滤程序,因为正常的邮件一般不包含HTML注释。
使用postmap工具可以测试新写好的正则表达式。假设msg.txt时一个垃圾邮件样本文件,可以用下列方式将其导入postmap:
$ postmap -q - regexp:/etc/postfix/body_checks < msg.txt
opportunity, increase your sales by 500%. Consider REJECT
任何邮件都会被检查内容,即使邮件已通过所有smtpd_*_restrictions过滤规则的检验,甚至已被纳入白名单,最后仍有可能被header_checks和body_checks挡在门外。
制定过滤规则时要留意用户群的不同需求。有些人完全不能接受垃圾邮件,甚至愿意承担误判的风险;有些人则宁愿多收一些垃圾邮件也不愿错失任何真实的邮件。
如果必须分别为不用用户设定不同的过滤规则时,最好不要在MTA做这件事,应该考虑使用特殊的MDA,例如procmail、maildrop,或能够依用户类别来选择过滤规则的软件。

自定义过滤条件组合

Postfix的分级机制称为“规范等级”(restriction class),可以灵活运用Postfix的垃圾邮件过滤条件。具体做法如下:
  1. 定义多组“规范等级”
  2. 每个等级都有自己专属的名称,不同的等级由松紧不等的限制条件组合而成。
  3. 制作一个访问表
  4. 索引键时用户的标识信息(通常时邮件地址),对应值是该用户适用的限制等级的名称。
  5. 在一般的smtp_*_restrictions过滤规则中,加上一条检查访问表的条件
  6. 任何check_*_access条件都可以用来检查访问表,也就是说,可以依据客户端(check_client_access)、寄件人(check_sender_access)或收件人(check_recipient_access)来执行分级过滤。
分级过滤在结构复杂的机构可能有需求,但结构复杂的机构往往人数众多,访问表的维护将变得很困难。若要实施,恐怕需要设计一套很好的后台管理系统,甚至需要数据库的支持。

规范等级实例

假设有两组用户,一组可以接受垃圾邮件,命名为spamlover;一组不能接受垃圾邮件,命名为spamhater。
所有规范等级的名称必须列在smtpd_restriction_classes参数中:
smtpd_restriction_classes = spamlover, spamhater
然后像设置一般的smtpd_*_restrictions过滤规则一样定义规范等级,例如:
# 定义spamhater,限制条件相当严格
spamhater =
  reject_invalid_hostname
  reject_non_fqdn_hostname
  reject_unknown_sender_domain
  reject_rbl_client nospam.example.com

# 定义spamlover,只有一个简单的permit(无条件允许)
spamlover = 
  permit
下一步制作一个访问表,让Postfix知道哪些人适用哪种规范等级。
由于规范等级是针对不同用户(收件人)而设计的,所以访问表的索引键是收件人的邮件地址。假设访问表的名称是per_user_ube,它的内容看起里类似以下模样:
#
# per_user_ube
#
abc@test.com    spamhater
xyz@test.com    spamlover
最后,要求Postfix在审核收件人限制时,检查指定的访问表:
smtpd_recipient_restrictions =
  permit_mynetworks
  reject_unauth_destination
  check_recipient_access hash:/etc/postfix/per_user_ube
一切就绪之后,当外界有人写信给abc@test.com,Postfix先执行一遍正常的默认过滤规则。
在遇到check_recipient_access时,它会检查指定的收件人访问表,并查出abc@test.com适用于规范等级为spamhater,然后执行spamhter定义的限制条件。
如果spamhater下有任一条件返回REJECT,则拒收此邮件,否则交给适当的MDA处理。

反垃圾邮件实例

smtpd_restriction_classses =
  spamlover
  spamhater
  
spamhater =
  reject_invalid_hostname
  reject_non_fqdn_hostname
  reject_unknown_sender_domain
  reject_rbl_client nospam.example.com

spamlover = 
  permit

smtpd_helo_required = yes
smtpd_client_restrictions =
  check_client_access hash:/etc/postfix/client_access
smtpd_helo_restrictions =
  reject_invalid_hostname
  check_helo_access hash:/etc/postfix/helo_access
smtpd_sender_restrictions =
  reject_non_fqdn_sender
  reject_unknown_sender_domain
  check_sender_access hash:/etc/postfix/sender_access
smtpd_recipient_restrictions =
  permit_mynetworks
  reject_unauth_destination
  reject_non_fqdn_recipient
  reject_unknown_recipient_domain
smtpd_data_restrictions =
  reject_unauth_pipelining
header_checks = /etc/postfix/header_checks
body_checks = /etc/postfix/body_checks

灰名单

还有一种方案可以用于拦截垃圾邮件,就是灰名单。
灰名单的原理是MTA按时拒绝未被识别的发送者发来的邮件,返回标记defer提醒对方稍后再试。
如果邮件是正当合理的,发起者会在一段时间后重新发送,然后这份邮件就能被接收。因为大多数垃圾邮件服务器和僵尸网络的邮件只发送一次,而忽略再次发送的要求。
Postfix常用的灰名单软件有两个:postgrey和SQLGrey(sgwi是其web接口)。postgrey不依赖SQL,更小更便捷。
postgrey的安装很简单
apt-get install postgrey
安装完成后可以通过修改/etc/default/postgrey文件来改变它的配置,而配置文件中只有一句起效的:
POSTGREY_OPTS="--inet=10023"
可以在双引号内设置三个选项: 而在/etc/postgrey目录中有两个文件: 设置好之后可以通过以下命令启动:
service postgrey start
然后修改postfix的main.cf文件,在smtpd_recipient_restrictions规则中加入一句:
smtpd_recipient_restrictions =
  permit_sasl_authenticated,
  ......
  check_policy_service inet:127.0.0.1:10023
然后重启postfix。
使用postgrey --unix 127.0.0.1可以查看当前白名单列表。

其他反垃圾邮件方法

需要注意的是:反垃圾规则越严格,越容易漏掉正常的邮件。采用哪种方式、如何设置规则,需要根据实际情况进行取舍。