博客首页 注册 建议与交流 排行榜 加入友情链接
推荐 投诉 搜索: 帮助

高山流水的博客

gslsok.cublog.cn


用 qmail + vpopmail + maildrop + spamassassin 实现邮件过
用 qmail + vpopmail + maildrop + spamassassin 实现邮件过滤的方案


  qmail+vpopmail是很多用户采用的邮件系统方案. 论坛中也有很多朋友来询问和讨论垃圾邮件过滤的实现方法, 本文在总结不同方案的基础上, 结合自己在开发中的实际应用,将与大家讨论利用 maildrop + spamassassin 来实现用户级邮件垃圾过滤的可行方案.

相关的软件:
1. qmail 1.03
2. vpopmail >=5.4.6
3. maildrop >=1.7.0
4. spamassassin (以下简称 SA) >=3.0.3

目标:
1. 实现用户对垃圾邮件的自动识别及过滤
2. 实现可由用户设置是否使用 SA 过滤
3. 实现用户级对 SA 的简单自定义配置
4. 实现用户自定义黑白名单
5. 解决用户邮箱 maildirquota 的问题

1. 用户级垃圾邮件的自动识别及过滤

目前, qmail + vpopmail 调用 SA 的方式主要有以下几种:

A). 使用 qmail-scanner 软件包来实现.

qmail-scanner 是一个非常不错的软件包, 可支持丰富的外部第三方病毒扫描软件, 例如, 商业版的 H+BEDV's antivir scanner, 开源版本的clamAV 及 Spam Assassin Daemon 等等。优点是配置灵活, 适应性强, 邮件在进入 queue 队列前即被扫描. 缺点是该软件完全用 perl 语言开发, 存在效率和系统开销大的问题, 这个缺点在用户量和邮件量时更加明显.

  qmail-scanner 调用 SA 的方式是通过 sub-spamassassin.pl 来完成, 实际上也就是调用 spamc, 将其输出内容截获分析其中 X-Spam-Status: 等 header 标记, 然后重新写原邮件头并交给 qmail-queue 进入邮件队列. 邮件是重写邮件头, 因此, SA 的有些配置对于 qmail-scanner 来实际上是无效的, 例如 report_safe 等等. 因为 qmail-scanner 不理会这些改写. 下面是 qmail-scanner 的这部分代码:

代码:
========== cut from sub-spamassassin.pl ==========
open(SA,"<$scandir/$wmaildir/new/$file_id.spamc")||&error_condition("cannot open for read $scandir/$wmaildir/new/$file_id.spamc - $!");
  while (<SA>) {
    if ($sa_fast) {
      chomp;
      ($sa_score,$sa_max)=split(/\//,$_,2);
      $sa_tag++;
      last;
    } else {
      #X-Spam-Status: No, hits=2.8 required=5.0
      if (/^X-Spam-Status: (Yes|No), (hits|score)=(-?[\d\.]*) required=([\d\.]*)/) {
   $sa_tag++;
   $sa_status=1 if ($1 eq "Yes");
   $sa_score=$3;$sa_max=$4;
      }
    }
  }
  close SA ;
========== end of cut =============================


  另外, 邮件用户通过客户端发送的邮件也会通通经过这一层扫描(当然也可以通过改写 qmail-smtpd 来避免), 不仅带来不必要的系统开销, 如果邮件附件多或者大还会造成用户发送邮件超时的现象. 由于 qmail-scanner 对每一封邮件都要进行扫描, 因此其系统开销可想而知, 并且 qmail 或 vpopmail 的最终用户无权参与决定该用户的邮件是否扫描. 因此, 笔者在试用一段时间该方案后, 不得不放弃在 qmail-scanner 调用 SA 的方案, 只用其来调用病毒过滤. 前一段时间在 bbs.chinaunix.net 有人讨论过究竟是在 qmail-scanner 调用 SA 好? 还是在本地投递 local deliver 中调用 SA 好? 为此很多网友争论不休. 其实二者优缺点是明显的, 这正是本文的用意.


B). 使用 patch-vdelivermail.c 来调用

  由于 qmail+vpopmail 要经过 vdelivermail 来投递邮件到用户 Maildir 目录, 因此可以通过修改 vdelivermail 来实现对 SA 的调用. patch-vdelivermail.c 就是这样一个 patch. patch 代码如下:

代码:
    /* fork the SpamAssassin client - patch by Alex Dupre */
    if (!pipe(pim)) {
        pid = vfork();
        switch (pid) {
            case -1:
                close(pim[0]);
                close(pim[1]);
                break;
            case 0:
                close(pim[0]);
                dup2(pim[1], 1);
                close(pim[1]);
                if (execl(SPAMC, SPAMC, "-u", maildir_to_email(address), 0) == -1) {
                    while ((file_count = read(0, msgbuf, MSG_BUF_SIZE)) > 0)
                        write(1, msgbuf, file_count);
                    _exit(0);
                }
        }
        close(pim[1]);
        dup2(pim[0], 0);
        close(pim[0]);
    }


  该方法使用 unix 的管道方式调用 spamc, 执行效率应该比较高, 同时也不会影响原有 vdelivermail 的各项功能. 当然缺点仍然是用户无法参与决定是否 SA 过滤. 目前使用该方法的用户应该不多. 感兴趣的读者可以通过 FreeBSD ports 安装 vpopmail 来体会一下.

C). 通过 maildrop 来调用 SA

  maildrop 是大家广泛使用的一种方案, 也是目前网上讨论最多的, 有关的配置例子也多. 不过感觉整体比较混乱, 特别是有些配置明显过时, 有些没有发挥软件本来应有的功能, 更有张冠李戴者, 其配置中不仅在 qmail-scanner 中调用了 SA, 在本地投递又通过 maildrop 再次调用, 极大地浪费了系统, 简直误人子弟! 更多的用户朋友, 参考这类文章的结果是 "知其然, 而不知其所以然". 所以本文将结合自己的实践重点讨论 maildrop 的实现.

  其实通过 maildrop 调用 SA 的方案也是非常灵活的. 特别是要充分考虑到与 vpopmail 的配合, 首先要在熟悉各相关软件的原始文档, 充分领悟其开发者意图的基础上, 然后再多次试验网上流行的各种配置得出自己的可行性方案.

  目前网上有的方案是配置 domain 目录下域的 .qmail-default 来通过 maildrop 调用 SA, 其结果是与 A)中 qmail-scanner 是一样的道理, 用户无权参与后面的设置. 因此要让 maildrop 调用 SA 并且可让最终用户决定是否调用, 即用户可以配置 SA 相应的功能就必须在用户目录中下手. .qmail 文件是 qmail 系统的用户配置文件, 通过 .qmail 可以完成许多扩展功能, 例如转寄, 自动回复等等.

  以下是我的配置方案例子: (以用户 test@mydomain.com 为例)

  在用户目录( /home/vpopmail/domains/mydomain/test/ )下面建立 .qmail 文件, 内容如下:
代码:
#spamassassin
|/usr/local/bin/maildrop ./.mailfilter


该文件的作用是接管从 vdeliermail 投递过来的邮件, 并通过管道方式送给 maildrop 进行过滤处理. maildrop 使用的过滤脚本是 位于同一目录下面的 .mailfilter.

.mailfilter 文件内容如下:

代码:
import HOME
import EXT

#spamassassin
#
if ($SIZE < 262144)
{
    exception {
        xfilter "/usr/local/bin/spamc -f -u $EXT@$HOST"
    }
}
else
{
    to ./Maildir/
}

if (/^X-Spam-Status: Yes/:h)
{
    to ./Maildir/.Spam/
}
else
{
    to ./Maildir/
}


  为避免对大附件的邮件进行扫描, 减少系统开销, 脚本中仅处理小于 256K 的邮件. 读者可以看出这段脚本是相当简洁的. 对于判断为垃圾邮件的邮件, 直接投递到用户的 垃圾邮箱(Maildir/.Spam), 当然也可以采取直接删除或者弹回到发送者, 只需要简单改写为相应的脚本即可.

  用户需要开启SA功能, 只需要设置其目录下面的 .qmail 和 .mailfilter 文件即可, 取消则删除 .qmail 或者其中的相关部分.

2. spamassassin 的用户设置

  如果我们要为每一个 vpopmail 用户提供不同的 SA 配置, 这个该怎么解决呢? 因为 /usr/local/etc/mail/spamassassin 下面的 local.cf 全局的设置也许都具体的用户并不适用, 幸运地是 SA 中提供对 vpopmail 的支持. 解决的办法是在启动 spamd 时候采用下面的参数:

代码:
/usr/bin/spamd -d -v -u vpopmail -r /var/run/spamd/spamd.pid


其中, -v 参数表示支持 vpopmail 虚拟用户配置, -u 参数用于设置 spamd 采用 vpopmail 用户来运行, 以便可以读写用户目录下面的个性化配置 user_prefs 以及 bayes 用户数据库. 此时, 在系统启动后,spamd 会自动在 vpopmail 用户目录中建立一个 .spamassassin 的目录, 其可能的目录及文件结构如下:

代码:
/home/vpopmail/domains/
                 +---- /mydomain
                          +---- /test
                                  +---- /Maildir
                                  +        +---- cur
                                  +        +---- new
                                  +        +---- tmp
                                  +       
                                  +---- .qmail
                                  +---- .mailfilter
                                  +---- .blacklist
                                  +---- .whitelist
                                  +
                                  +---- ./spamassassin
                                            +---- user_prefs
                                            +---- auto-whitelist
                                            +---- bayes_journal
                                            +---- bayes_seen
                                            +---- bayes_toks

用户只需要设置 user_prefs , 就可以控制自己对 SA 的各项功能调用, 如是否启用 bayes 自动学习功能等等.

例如, test@mydomains.com 的 user_prefs 文件如下:

代码:
required_hits           5
use_bayes               1
bayes_auto_learn        1


这些配置可以根据 spamassassin 的配置手册来自行设置, 例如通过web 客户端由相应的程序来实现, 简化用户的设置.

3. 用户级黑白名单的处理

  由于 SA 存在一定的误判, 因此为其建立相应的黑白名单机制是很有必要的. 本黑白名单的设计意图是, 对于在用户黑名单中的来信地址, 通过 maildrop 直接投递到 指定的目录, 例如垃圾邮件目录, 或者直接删除, 或者弹回到发送者. 下面是实现用户黑名单相应的 maildrop 脚本:

代码:
#blacklist
#
`test -f .blacklist`
if ($RETURNCODE==0)
{
    if ($SENDER ne '' && lookup($SENDER, '.blacklist'))
    {
    to ./Maildir/.Spam/
    }
}

  对于在用户白名单的来信, 则直接投递到用户 Maildir/, 不用再经过 SA 过滤. 避免了误判, 也减少了系统的开销. 以下是实现用户白名单相应的 maildrop 脚本:

代码:
#whitelist
#
`test -f .whitelist`
if ($RETURNCODE==0)
{
    if ($SENDER ne '' && lookup($SENDER, '.whitelist'))
    {
        to ./Maildir/
    }
}

下面是将上述黑白名单和SA过滤结合在一起的 .mailfilter 脚本文件:

代码:
import HOME
import EXT

#blacklist
#
`test -f .blacklist`
if ($RETURNCODE==0)
{
    if ($SENDER ne '' && lookup($SENDER, '.blacklist'))
    {
    to ./Maildir/.Spam/
    }
}

#whitelist
#
`test -f .whitelist`
if ($RETURNCODE==0)
{
    if ($SENDER ne '' && lookup($SENDER, '.whitelist'))
    {
        to ./Maildir/
    }
}

#spamassassin
#
if ($SIZE < 262144)
{
    exception {
        xfilter "/usr/local/bin/spamc -f -u $EXT@$HOST"
    }
}
else
{
    to ./Maildir/
}

if (/^X-Spam-Status: Yes/:h)
{
    to ./Maildir/.Spam/
}
else
{
    to ./Maildir/
}


4. 用户 maildirquota 的处理

  由于上述方案中 maildrop 通过管道接管了来自 vdelivermail 的邮件投递, 因此影响了 vdelivermail 原有的功能, 例如用户 maildirquota 以及 domain-quotas 等等. 好在 maildrop 本来也有相应的用户 maildirquota 支持, 这需要在编译安装时加上相应的参数:

代码:
./configure --enable-maildrop-uid=vpopmail \
    --enable-maildrop-gid=vchkwp \
    --enable-maildirquota


这样, 当 maildrop 在执行本地投递的时候, 会先检查 ./Maildir 目录下面的 maildirsize 文件. maildirsize 文件记录了 Maildir 目录的 quota 设置以及邮件进出的操作, 其具体的含义用户可以参考 maildrop 或 vpopmail 的相关文档.

  maildirsize 机制实际上是对 qmail maildir 的扩展, 这个扩展被称之为 maildir++. 它采用了一种简单而有效的机制, 即利用 maildirsize 文件来简单记录邮件的进出, 从而统计 maildir 是否超过其限制. maildirsize 的格式很简单, 其第一行为当前 Maildir 目录的 quota 限制, 例如如下的 maildirsize文件:

代码:
104857600S,10000C   ----> 表示该目录的容量限制是 100M左右, 10000 封邮件
9230910 689         ----> 上一次统计后目录的容量情况 
4029    1           ----> 表示一封大小为 4029 的邮件投递到该目录
3122    -1          ----> 表示一封大小为 3122 的邮件被删除


这样, 相关的程序就能够通过 maildirsize 并通过简单的统计, 从而可以判断出该目录是否超过了限额. 此外, 当 maildirsize 文件本身的大小超过 5120 字节或者最近一次访问时间超过 15 分钟, "系统" 会重新统计 Maildir 目录并生成新的 maildirsize 文件.

5. 存在的问题和bugs

  实际使用中我们发现 spamd 用户目录 .spamassassin 出现权限问题. 尽管在前面我们使用 -u vpopmail 来运行 spamd, 按理说, spamd 的对用户目录文件的读写操作均应该使用 vpopmail 用户来操作, 这对于大多数情况下是正确的, 但是在部分情况下, 用户的 .spamassassin 目录属主是 root, 这样的话就会对 web 客户端设置 .spamassassin 目录下面的 user_prefs 文件造成因权限问题而无法更改. 好在我们可以在用户开启 SA 功能的时候, 由 web 程序自动建立好用户的 .spamassassin 目录已经 user_prefs 等文件, 但是在运行中发现, 仍然有部分用户目录下面的 bayes* 文件属主是 root, 尽管并没有采用 root 来运行 spamd. 虽然这一结果并不影响系统的运行, 但是会因为文件权限问题而会给目录备份等操作带来一定的困扰. 看来, 解决这个问题的办法还得花时间来研究 spamd 的源代码.


6. 结束语

  通过以上简单的方案讨论, 基本上实现了本文提出的目标, 充分减少系统开销, 具备用户功能设置. 是一套可行的 qmail+vpopmail 的邮件过滤实施方案. 该方案目前在 iGENUS 3.1.0 版本中得以实现. 并在 iGENUS 中实现了 web 客户端对上述功能的基本功能设置, 实现了用户对功能的开启和关闭, 以及简单的个性化配置. 用户实际使用达到比较理想的效果. 下一步将在上述方案的基础上, 进一步完善, 并逐步地推出垃圾邮件的标记, 即手工 bayes 学习等用户功能. 力求垃圾邮件的识别率更高.

  另外, 很多朋友问我为什么开源版本的 iGENUS 没有再继续开发下去了? 在此, 我要声明的一点是, 我非常愿意将 iGENUS 开源项目继续下去, 但是由于 iGENUS2 存在太多的问题, 我个人认为整个框架已经不太适合继续下去, 但求另辟蹊径. 此外, 正如上述方案一样, 我认为把一些实现的方案细节 "公开" 出来与大家讨论, 比单纯的开源程序更好一些, 抛砖引玉, 以求更多的朋友参与进来, 大家一起提高. 我想, 这正是我此文的目的.

发表于: 2008-05-03 ,修改于: 2008-05-03 13:49,已浏览265次,有评论0条 推荐 投诉


网友评论

发表评论