在Debian上安装邮件系统
反垃圾邮件
本节内容抄自O'RELLY出版的《Postfix权威指南》,2006年第一版,基于postfix 2.0.18。
现在官网最新的版本是3.7;debian11上安装的是3.5.6;FreeBSD上安装的是3.7.2,1。
Postfix的挡信机制有四种:
- 客户端判别规则
- 语法检查规则
- 内容检查
- 自定义过滤规则
客户端判别规则
下列过滤规则可设定一系列对客户端信息的限制条件:
- smtpd_client_restrictions
客户端限制
- smtpd_helo_restrictions
helo命令限制
- smtpd_sender_restrictions
发件人限制
- smtpd_recipient_restrictions
收件人限制
- smtpd_relay_restrictions
转发限制
- smtpd_data_restrictions
data命令限制
- smtpd_end_of_data_restrictions
data命令限制
- smtpd_etrn_restrictions
etrn命令限制
Postfix内置了一些限制条件:
- reject_rbl_client
- reject_rhsbl_client
- reject_unknown_client
以上用于检视客户端的IP地址和主机名
- check_client_access type:mapname
- permit_naked_ip_address
- reject_invalid_hostname
- reject_non_fqdn_hostname
- reject_unknown_hostname
以上用于检视HELO提供的主机名
- check_helo_access type:mapname
- reject_non_fqdn_sender
- reject_rhsbl_sender
- reject_unknown_sender_domain
以上检视MAIL FROM提供的寄件人邮件地址
- check_sender_access type:mapname
- permit_auth_destination
- permit_mx_backup
- reject_non_fqdn_recipient
- reject_unauth_destiantion
- reject_unknown_recipient_domain
- check_recipient_access type:mapname
以上检视RCPT TO提供的收件人邮件地址
- reject_unauth_pipelining
以上检视DATA命令
- permit
无条件允许
- reject
无条件拒绝
- defer
无条件延迟
- warn_if_reject
将原本的REJECT动作改为WARN(需放在其他条件之前,不能单独使用)
- reject_unauth_pipelining
禁止非授权用户使用PIPELINING命令
一个简单的实例:
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处理。
定义限制条件
依据对象不同,限制条件可分为六类:
- 访问表
- 客户端参数
- 严格语法检查
- DNS检查
- 实时黑名单
- 通用限制条件
访问表
访问表(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等,不太常用。
客户端参数
- permit_auth_destrination
允许收件地址位于Postfix辖域的邮件。辖域包括mydestination、inet_interfaces、virtula_alias_maps、virtual_mailbox_maps以及relay_domain所列的所有网域及子网域。
收件地址不能包含任何“发信方指定的递送路径”(例如:user@example.com@example.net)。
如果收件地址不符合permit_auth_destrination条件,则返回DUNNO,而非REJECT,所以Postfix可以继续检查后续的限制条件。
- permit_mynetworks
如果客户端的IP位于mynetworks参数所列的任何地址范围内,则批准请求。
一般使用此条件让本地客户可使用Postfix server转发邮件。
- reject_unauth_destination
如果收件地址不位于Postfix辖域,则拒绝请求。
本条拒绝码为relay_domain_reject_code,其默认值为554。
严格语法条件
- reject_invalid_hostname
如果客户端在HELO命令提供的主机名不是有效的主机名,则返回invalid_hostname_reject_code参数指定的拒绝码(默认为501)。
- reject_non_fqdn_hostname
如果客户端在HELO命令提供的主机名不是RFC要求的完整形式(FQDN),则返回non_fqdn_reject_code参数指定的拒绝码(默认为504)。
Windows系统可能不会提供FQDN完整名称。
- reject_non_fqdn_recipient
如果客户端在RCPT TO命令提供的收件地址的网域部分不是RFC要求的完整形式(FQDN),则返回non_fqdn_reject_code参数指定的拒绝码(默认为504)。
- reject_non_fqdn_sender
如果客户端在MAIL FROM命令提供的发件地址的网域部分不是RFC要求的完整形式(FQDN),则返回non_fqdn_reject_code参数指定的拒绝码(默认为504)。
- reject_unauth_pipelining
pipelining(流水线操作)是一种加速处理大宗邮件的技术,其原理是容许客户端一次送出多个SMTP命令。这需要客户端必须先检查服务器是否支持pipelining,才可以开始流水线操作。
DNS限制条件
- reject_unknown_client
如果Postfix从DNS查不出客户端IP地址的PTR记录,则拒绝服务。
由于Internet上一大半IP地址查不出PTR记录,所以此条过于严苛,不建议使用。
- reject_unknown_hostname
如果HELO命令提供的主机名没有A记录,也没有MX记录,则拒绝服务并返回unknown_hostname_reject_code参数指定的拒绝码,其默认值为450。
实际上许多客户端的HELO命令提供的主机名不是完整形式(FQND),所以此条也不建议使用。
- reject_unknown_recipient_domain
如果RCPT TO命令提供的收件人地址的网域部分查不出有效的A或MX记录,则以unknown_address_reject_code参数定义的拒绝码拒绝服务,该参数的默认值为450。
- reject_unknown_sender_domain
如果MAIL FROM命令提供的发件人地址,其网域部分查不出有效的A或MX记录,则以unknown_address_reject_code参数定义的拒绝码拒绝服务,该参数的默认值为450。
由于捏造MAIL FROM地址使垃圾邮件发送者常用的技俩之一,所以强烈建议加上此条。
实时黑名单
- reject_rbl_client rblprovider.domain
将客户端IP颠倒顺序,搭配RBL管理员的网域名称,构成一个主机名称,然后以此主机名向DNS系统查询,如果能查出一条A记录,则表示该IP已经被列入黑名单,则Postfix立即拒收邮件。
- reject_rhsbl_client rblprovider.domain
如果客户端的主机名称在指定的rblprovider.domain网域上有一条A记录,则拒绝服务。
- reject_rhsbl_sender rblprovider.domain
如果寄件人的邮件地址的网域部分,在指定的rblprovider.domain网域下有一条A记录,则拒绝服务。
实时黑名单(RBL,Real-Time Blacklist)的抵制效果已经越来越差。
通用限制条件
- permit
批准收下邮件。Postfix不再继续当前的过滤规则,但会跳到下一组过滤规则。
- reject
无条件拒收,且不再继续处理任何过滤规则。
- defer
婉拒,客户端会被告知稍后再试。
SMTP语法规范
SMTP协议相当宽松,建议设置smtpd_helo_required = yes要求对方提供HELO/EHLO。
strict_rfc821_envelopes = yes则要求对方提供正确格式的地址,否则将拒收。
内容检查
- head_checks
检查标题
- mime_header_checks
检查标题的MIME相关字段
- nested_header_checks
检查夹带附件的标题
- body_checks
检查邮件正文
设定内容检查参数
默认情况下,如果没有设定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的垃圾邮件过滤条件。具体做法如下:
- 定义多组“规范等级”
每个等级都有自己专属的名称,不同的等级由松紧不等的限制条件组合而成。
- 制作一个访问表
索引键时用户的标识信息(通常时邮件地址),对应值是该用户适用的限制等级的名称。
- 在一般的smtp_*_restrictions过滤规则中,加上一条检查访问表的条件
任何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"
可以在双引号内设置三个选项:
- --inet=10023
端口号,共postfix内参数调用。此项需要改为--inet=127.0.0.1:10023
- --delay=N
等待时间N秒,默认为300秒。对于一些有时效性要求的邮件(比如有时间限制的验证码),此项可能会造成麻烦。
- --max-age=N
N天后删除旧记录,默认为35天
而在/etc/postgrey目录中有两个文件:
- whitelist_clients
发件者域名或IP白名单(也可以是正则表达式),每行一条记录。已经内置了一些“正规的”发件者。
- whitelist_recipients
收件人白名单,在此白名单内收件者不受灰名单机制影响,凡是发给它们的邮件都会直接进来。每行一条记录,示例记录只有收件者名称和@符号,没有域名。
设置好之后可以通过以下命令启动:
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可以查看当前白名单列表。
其他反垃圾邮件方法
- SPF记录
SPF(Sender Policy Framework),用于帮助邮件域的拥有者确认发送方是否来自他们的域,目的是其他邮件系统能够保证发送方在发送邮件时来自经过授权的来源。
这种方法可以减小遇到邮件地址欺骗、网络钓鱼和垃圾邮件的风险。
网易邮箱和gmail采用了此方法。网易的已经解决,gmail尚未解决。
- DKIM
域名密匙是一套电子邮件身份认证系统,用于验证邮件发送方的DNS域和邮件的完整性。
域名密匙规范采用互联网电子邮件认证技术,建立了一套加强版协议:域名密匙识别邮件(DKIM——Domain Keys Identified Mail)。
目前尚不清楚哪些邮件服务器强制要求此项。
- ASSP
ASSP(Anti-Spam SMTP Proxy,反垃圾代理) 是一个网关服务器,安装在你的 MTA 前面。
通过自建白名单、自动学习贝叶斯算法、灰名单、DNS 黑名单(DNSBL)、DNS 白名单(DNSWL)、URI黑名单(URIBL)、SPF、SRS、Backscatter、病毒扫描功能、附件阻拦功能、基于发送方等多种方法来反垃圾邮件。
大多数邮件系统的教程都会提到amavisd-new和ClamAV及SpamAssassin的集成来实现反病毒邮件、反垃圾邮件,后续再研究。
需要注意的是:反垃圾规则越严格,越容易漏掉正常的邮件。采用哪种方式、如何设置规则,需要根据实际情况进行取舍。