Zimbra Antispam &Antivirus机制分析

Zimbra的投递逻辑是:

Postfix smtpd ->Postfix queue -> amavisd(内容过滤) -> Postfixsmtpd -> Postfix queue -> deliver

其中,amavisd是一个perl写的内容过滤系统,以smtp协议接收postfix的请求,然后调用SpamAssassin引擎以及clamav杀毒引擎进行处理。

amavisd处理之后如果发现是垃圾邮件或者病毒,有几个可能的处理方式:

a) 直接丢弃或者放到隔离区,这样就不用再经过一次Postfix了

b) 告诉前端的Postfix queue邮件需要拒收,由前端的Postfix负责退信

c) 通过SMTP协议重新把邮件放回Postfix队列

这个存在的问题是:

1. 前端没有控制机制,假如压力测试的时候以垃圾邮件为样本进行测试,然后把信头的From设置成一个连接不上的域名,就可以使得Postfix堆积大量的退信;

2. amavisd是调用SpamAssassin作为反垃圾引擎的,其处理能力比较慢,在测试机尝试用一个线程通过smtp协议直接绕开Postfix前端发信到amavisd,处理速度是每分钟6封左右;

 (处理的时候整个系统基本都在swap, 4G的物理内存都用光了, 4个Java进程,每个进程占用500M内存。另外有5个amavisd进程,每个占用150M内存)。

3. 分析过amavisd进程,其大概处理过程如下:

5288 1261636569.403345accept() # 接收一个连接

5288 1261636570.712165read(9, "MAIL FROM:<[email protected]>\r\n", 4096) = 27

1.   从socket读取邮件内容,并保存到/opt/zimbra/data/amavisd/tmp/amavis-20091221T172325-05288/目录下,这个目录包含一个parts目录,parts保存的是分解后的bodypart

2.   现在开始连接clamav杀毒, 杀毒还是很快的,半秒就返回结果了

5288 1261636572.371280 connect(12,

sa_family=AF_INET, sin_port=htons(3310),sin_addr=inet_addr("127.0.0.1")

, 16) = 0

5288 1261636572.371380 sendto(12, "CONTSCAN/opt/zimbra/data/amavis"..., 73, 0, NULL, 0) = 73

5288 1261636572.798982 recvfrom(12,"", 8192, 0,

sa_family=AF_INET, sin_port=htons(38000),sin_addr=inet_addr("127.0.0.1")
, 0)

= 0

3.   现在是SpamAssassin的RBL, URLBL检查

5288 1261636573.504577 bind(12,

sa_family=AF_INET, sin_port=htons(44663),sin_addr=inet_addr("0.0.0.0")
, 16) = 0
5288 1261636573.504625 connect(12,
sa_family=AF_INET, sin_port=htons(53),sin_addr=inet_addr("192.168.170.13"), 16) = 0
5288 1261636573.504699 getsockopt(12, SOL_SOCKET, SO_RCVBUF, 223338434560,4)= 0
5288 1261636573.557825 getpeername(12, sa_family=AF_INET, sin_port=htons(53),sin_addr=inet_addr("192.168.170.13")
, [226060277865
51312]) = 0

4.   现在开始做Bayes检查

5288 1261636573.977665open("/opt/zimbra/data/amavisd/.spamassassin/bayes_toks", O_RDONLY) =13

5.   做一点优化,Bayes检查过的记录下来,下次遇到同样内容的无需再次Bayes

5288 1261636573.990256open("/opt/zimbra/data/amavisd/.spamassassin/bayes_seen", O_RDONLY) =14

6.   现在开始连接Postfix后端的smtpd(10025端口)

5288 1261636576.386879 connect(12,
sa_family=AF_INET, sin_port=htons(10025),sin_addr=inet_addr("127.0.0.1")
, 16) = 0
5288 1261636576.414884 select(16, 12,[], 12,
35, 0) = 1 (in 12,left 35, 0
)
5288 1261636576.415113 read(12, "220 zmbtest.mailtech.cn ESMTPPo"..., 16384) = 39

7.   一次处理结束
5288 1261636577.432211 flock(8, LOCK_EX <unfinished ...>
5288 1261636577.673285 <... flock resumed> ) = 0
5288 1261636577.673408 select(8, 57, NULL, NULL, NULL <unfinished ...>

可以看出, amavisd处理邮件一共花费了8秒(邮件20k左右,是一封html邮件),主要花时间在接收邮件之后分拆并保存到临时文件,以及Bayes检查这两部分。当然,这个测试有点极端的是测试的时候内存严重不足,swap的情况非常严重(但是系统已经有4G物理内存了,java进程是吃内存的大头)

Coremail的处理机制明显比Zimbra的优越

首先,Coremail在前端已经在客户段尝试投递邮件的时候,已经实时的检查了邮件,如果认为邮件是垃圾邮件的,立即就reject,而不是像Zimbra那样先进入队列。

其次,对于垃圾邮件,如果size小于100K的,Coremail甚至不会涉及IO操作就可以直接reject邮件(>100K的邮件将会使用队列文件临时保存,如果拒收的最后会删除邮件)。Zimbra是任何邮件都需要先进入队列的, 需要起码四五次IO(一次进入队列,几个分解邮件保存每个part一个文件的操作, 以及若干SpamAssassin做Bayes需要的IO读写操作, 然后还有一次退信涉及的几个IO)

就算是正常邮件,Coremail需要把邮件接收下来了,IO操作也远小于Zimbra. Coremail只需要写一次队列文件,然后由da负责投递,da投递的时候,如果需要杀毒的,会由da负责分拆邮件,每个附件保存成一个小文件,查毒后删除。而Zimbra, 则首先需要由Postfix的smtpd进程接收邮件并写一次队列文件,然后由qmgr进程把队列文件mv到qmgr目录,再把文件通过网络传给amavisd,amavisd在处理的时候会把邮件分成几个part, 每个part创建一个小文件,然后查毒。查毒完毕之后Zimbra会调用SpamAssassin进行Antispam检查,检查通过的,会再连接Postfix,把邮件放回队列,而这会导致一次额外的创建队列文件操作。也就是,正常情况下,Zimbra会比Coremail多了一次创建文件操作以及起码两次的mv文件操作。

除了IO之外,Zimbra的瓶颈还在于cpu的使用以及内存使用,因为使用了SpamAssassin, 这个perl开发的软件效率特别地下,另外amavisd也是用perl来分拆邮件body然后做antivirus检查的,strace分析发现这两个部分的耗时特别高。而内存使用高主要是因为有几个java进程,不过具体为什么java要这么多内存,就要William协助分析一下了

后来Tanyi在调研Zimbra的时候找到网上有人说过mailscanner + postfix的效率比amavisd + postfix的效率要高,到mailscanner的网站(http://www.mailscanner.info/cobalt.html)查看了一下,发现原理上确实如此。

mailscanner的原理是利用postfix的header_check机制,配置postfix, 把所有进入队列的邮件,先mv到hold目录, 然后由mailscanner直接扫描hold目录,通过的邮件再放回incoming, 不通过的邮件则reject。
这个机制确实省了一点IO(使用amavisd的话,通过检查的需要再发回队列,多了一次创建队列文件),但是按照之前的分析,整个过滤系统的瓶颈不在postfix本身,也不在amavisd,而是在amavisd所调用的SpamAssassin。而mailscanner本身也只是框架,没有Antispam能力,最终还是要依赖SpamAssassin进行antispam过滤
所以就算Zimbra使用了mailscanner, 效率也不会提高多少。另外,为了节省这些不是关键的IO,需要使用非标准的postfix接口(mailscanner会以postfix协议直接处理postfix的队列文件,postfix修改了文件结构的话,mailscanner需要跟着修改),导致维护成本的增高,也是得不偿失


猜你喜欢

转载自blog.csdn.net/yatum_2014/article/details/79961648