建置 POSTFIX 服务器
postfix 是除了 sendmail 以外 ,最被广泛采用的 Linux 邮件服务器,一般使用的观感不外乎两点:
一、安全:垃圾信过滤机制较聪明,就算什么都没设定,也能滤掉许多 sendmail 挡不到的信。
二、简单:不需设定,服务器就能正常运作。
虽然以上谈的这两点特色,到底是好是坏还有许多争议,但 Postfix 对于邮件服务需求量不高的校园来说,可以说是一个很好的选择!
本文是假设您已经读过 Linux 进阶班讲义,因此对于 MUA、MTA、MDA、MSA 已经有概念,并且了解电子邮件结构及其字段义涵,能自行设定启用 IMAP 及 POP3 服务,并能利用 telnet 工具检测服务器情形,但对于 postfix 运作原理不甚清楚,想知道如何依自己的需要作各种过滤设定!那么本文的内容应该对你很实用!
壹、从 sendmail 到 postfix
redhat 系统默认的邮件服务器是 sendmail ,而 postfix 是不会自动安装的,如果一开始安装系统时忘记把 postfix 装起来,请放入系统光盘第二片,依底下指令安装:
mount /mnt/cdrom
cd /mnt/cdrom/RedHat/RPMS
rpm -Uvh postfix*
cd /
eject
为了便利于 sendmail 和 postfix 之间进行切换,系统提供一个工具可以自动帮我们作设定迁移,请放入系统光盘第三片,依底下指令安装:
mount /mnt/cdrom
cd /mnt/cdrom/RedHat/RPMS
rpm -Uvh redhat-switch-mail*
cd /
eject
要进行邮件服务器切换,请输入 redhat-switch-mail 然后选择您想要用的服务器(sendmail 或是 postfix),虽然选好后系统会自动将服务器关闭和启动好,但我们依然可以使用底下指定来确定这件事(假设您选中的是 postfix):
service sendmail stop
service postfix start
这样 postfix 应该就会开始提供服务了!什么都不用设定就会跑了吗?是的!
postfix 本身也可以当成指令来执行,例如:
postfix check 检查配置文件是否正确,相关文件夹是否建立,档案拥有人和访问权限是否正确
postfix start 启动服务
postfix stop 停止服务
postfix abort 立刻中断服务
postfix reload 重载配置文件
postfix flush 将队列中的邮件全部寄出去
贰、postfix 工作流程
sendmail 与 postfix 最大的不同在于程序结构上,sendmail 以一支大程序来执行所有的工作,而 postfix 则改采模块化设计,这种设计的好处在于:程序代码容易维护、模块功能有弹性容易扩充。底下将针对组成 postfix 的每个模块,它们各是负责哪些工作,又如何与其它模块配合,作一个解说。
底下先说明收信流程:
图例说明:椭圆代表模块程序,黄色框代表邮件队列或文件夹,蓝色框代表配置文件。大框线内的模块是由 master 模块来控制行程。
- 当邮件由本机寄出时,系统执行 sendmail 程序,由于之前执行过 redhat-switch-mail 的关系,此时 sendmail 已经连结到 postfix 套件中的 postdrop 模块,postdrop 模块负责将邮件存放在 maildrop 文件夹中,等候 pickup 处理, pickup 模块会对邮件内容进行检查以保护 postfix 系统不会受到异常邮件的攻击(譬如利用字段溢位来破解主机)。
- 如果邮件是来自网络,postfix 套件中的 smtpd 将会接收邮件并进行邮件过滤,为了应付千变万化的伪装邮件, 我们可以透过修改 UCE 配置文件(例如:来自 sendmail 的 access 配置文件),设定黑名单(或透过 RBL 黑名单数据库取得黑名单)、 DNS 反查验证,甚至是使用正规表达式来作字符串匹配检查等方法,进行严格的过滤。
- 邮件有时候是来自 postfix 本身的 bounce 模块,例如:当邮件无法递送时,bounce 模块会自动产生讯息以便通知寄信人。
- 邮件有时候也会由 LDA 产生,在上图中用一道单独存在的箭头来表示(可以想象一下:该箭头是链接到送信流程中的 local 模块)。这种情形包括:根据 alias 别名数据库改写过收信人的邮件,或是根据每个使用者的 .forward 档案指定的转信信箱修改过收信人字段的邮件。
- 除了上述因无法递送邮件引发的问题会由 bounce or defer 模块产生通知邮件外,其它的错误产生时 postfix 也会自行产生通知邮件,例如:SMTP 通讯协议逾时,UCE 过滤规则冲突......等等,当然这种情形下只会通知 postmaster,在上图中一样用那一道单独存在的箭头来表示。
- 以上五种情况产生的新邮件,最后都会由 cleanup 模块进行最后的处理。首先它会补足邮件中所缺少的字段(例如来自 console 的信件将会缺省 From: 字段),并根据完整邮件地址格式改写寄信人字段(例如:shane@localhost 改成 shane@mail.spps.tp.edu.tw),接着读取 canonical(全名对应) 和 virtual(虚拟对应) 配置文件,根据该设定改写收信人字段,所有动作处理完毕后,邮件被放置在 incoming 文件夹中,接着就会通知 qmgr 模块有新邮件到了,由 qmgr 模块开始进行送信作业 。
- 当 cleanup 模块进行邮件字段改写时,实际上是呼叫 rewrite 模块来处理,和 sendmail 不同的是,postfix 并未发展出一套复杂的宏语言来进行 rewrite 作业,相反的是采用单纯的查表法来解决, 这也是 postfix 被批评功能不如 sendmail 强大的原因。postfix 开发者似乎认为 sendmail 之所以漏洞百出,与 rewrite 宏语言大而无当有直接相关(纯属猜测)。
底下说明寄信流程:
- qmgr 模块是整个邮件服务器的核心,它将视情形呼叫 local、smtp、pipe 等 agent 模块来进行寄信作业。local 模块就是 LDA ,它负责将信件分到使用者信箱。smtp agent 顾名思义是指 smtp client 程序,而非 smtpd ,它负责将信件透过 SMTP protocol 递送到网络上的远程邮件服务器。pipe 则用来处理传真或其它封闭式网络系统的特殊电子邮件通讯协议。
当邮件出现递送失败的情形,qmgr 就会将邮件放在 deferred 队列中,并呼叫 bounce 模块处理,以避免延迟其他邮件的递送作业 。
正在处理中的邮件会被放在 active 队列中,该队列仅存放少量邮件,因为若邮件过多的话将会耗用海量存储器,来自 incoming 文件夹的邮件,会根据邮件到达的顺序依序进入 active 队列, 而 deferred 队列的邮件,将会暂停四个小时后,重新排入 active 队列。
此外,qmgr 也会根据 relocated 配置文件来产生通知邮件,relocated 配置文件用来设定账号或网域迁移的通知讯息,当 qmgr 发现某封邮件的收信人已经迁移,则会呼叫 bounce 模块处理 。
- 在递送邮件的过程中,qmgr 会呼叫 rewrite 模块解析收信人地址,通常 rewrite 模块只会区分该信件是要到达本机,还是需要 relay,当然我们也可以透过 transport 配置文件,来要求 rewrite 模块针对特定收信人地址递送给特定主机(通常用来设定将 mail gateway 收到的邮件转给下属邮件服务器)。
- 当信件无法递送时(收信人或收信主机不存在或无响应), qmgr 呼叫 bounce 模块处理。bounce 模块会将障碍情形以邮件型式递交给 cleanup 模块进行收信作业,同时将原始邮件排入 deferred 队列,等待延迟递送。如果无法递送的原因是通讯协议或配置文件错误造成的(这种情形应通知管理员,而非寄信人),则信件将直接由 qmgr 处理,并不会去呼叫 bounce 模块。
- LDA 模块主要工作是将信件写入使用者的信箱中,或是根据 alias 设定或 .forward 设定改写收信人字段,并递交给 cleanup 模块进行收信作业,除了 postfix 默认的 local 模块外,其它在 sendmail 中经常使用的外挂 LDA,例如:procmail,也可以继续使用,这是因为 postfix 的 local 模块是与 sendmail 完全兼容的。
其它幕后常驻模块:
- master:这个模块用来监督 postfix 所有模块,是否有依照 master.cf 中的设定执行,并会将超过上限的行程砍掉,以符合效能调校相关设定值。
- flush:这个模块用来维护incoming 邮件队列的哈希表,它将邮件依收信目的地分类,建立多个哈希表,当客户端送出ETRN spps.tp.edu.tw 要求时,则可以将要给spps.tp.edu.tw 的邮件(有些可能还在排队)优先全部寄出。
- showq:当执行 mailq 指令查询邮件队列时,就是由此模块提供信息。
- proxymap:用来读取配置文件,并提供配置文件内容给其他模块,之所以这样做是为了减少硬盘 I/O 存取。
- spawn:用来呼叫执行插件,透过这个机制我们可以执行非 postfix 提供的过滤程序,例如:病毒过滤。
- pipe:这个模块用来将邮件队列中的邮件数据结构,传输给外部程序处理。当 master 模块启动插件时,透过 pipe 当白手套就可以和插件凭借参数传递来沟通。
常用工具:
- postalias:当我们执行newalias 时,由于redhat-switch-mail 的作用,其实是在执行 postalias,所以你也可以不用因此改变使用习惯喔!如果要透过这个指令建立alias 哈希表,指令格式比newalias 稍微复杂一点:postalias hash:/etc/postfix/aliases,这道命令也可以用 postmap 代替,例如:postmap hash:/etc/postfix/aliases,其余功能请参阅 postmap 的介绍。
- postcat:这是一个邮件RFC 格式的剖析器,可以将原始邮件显示成容易阅读的格式,有点像用outlook express 看到的邮件内容(假如在邮件上右键单击查看原始内容,你可能就看不懂了),请指定要查看的档名:postcat /var/spool/postfix/deferred/filename,由于档名都是使用邮件编号,如果不知道要看的邮件是什么档名时,可以用下面的指令查询!
- postqueue:用来取代 mailq 指令,使用-p 参数就可以将邮件依编号顺序列出邮件头(寄信人、收信人和信件主旨),方便检索查询!-s 参数则是列出前面介绍过的flush 哈希表!
- postsuper:这个命令用来操作队列中的邮件,只有root 身分才能执行。参数-d 用来删除邮件,-r 用来将邮件排到不同队列里(maildrop、incoming、active或deferred),-p 当服务器(或postfix 程序)当掉重开机后,用来救回工作到一半的邮件。
- postconf:这是个功能强大的工具,除了可以用来显示main.cf 的详细设定内容外(自动分类),也可以用来除错,甚至还提供修改设定的接口,例如:postconf -e mynetworks=168.100.189.0/25,这个功能如果结合script 程序来运作,就能做到透过网页远程管理设定挡。我们经常使用-n参数,来查看postfix 配置文件摆放的位置!
- postmap:用来取代makemap 指令,并且与makemap 完全兼容,支持的文件类型包括:btree、dbm、hash。当用来建立数据库档案时,语法如后:postmap hash:/etc/postfix/access,这个工具也可以用来查询数据库档案里的内容,例如:postmap -q 172.16.2 hash:/etc/postfix/access,这会列出该行的规则,如要删除可以使用:postmap -d 172.16.2 hash:/etc/postfix/access,透过这个方式能及时有效的管理,不用重新修改access 文本文件,然后哈希成数据库,然后重新启动postfix 加载配置文件等等,作一连串动作!
叁、main.cf 基本设定
postfix 最被人称道的地方在于配置文件的可读性很高(当然是跟 sendmail 比),而在主要配置文件 main.cf 中,需要自行定义的东西并不多,而且这些参数就算不去定义,依照默认值也可以运作:
由本机寄出的邮件要使用哪个域名
在 sendmail 中,这个功能称为网域伪装,也就是说可以和主机名不同,例如:本机名称为 mail.spps.tp.edu.tw,而寄出的邮件其寄信人字段则可以使用 spps.tp.edu.tw。(须配合 DNS MX 纪录使用)
myorigin=spps.tp.edu.tw
不使用简略名称时,设定如下:
myorigin=mail.spps.tp.edu.tw
由于主机名与域名会被反复引用在不同设定值,为了简化设定,通常是定义成变量:
myhostname = mail.spps.tp.edu.tw mydomain = spps.tp.edu.tw
#myorigin=$mydomain myorigin=$myhostname (省略时,视同此设定)
当然以上变量也可以省略不定义,这种情况下 postfix 将使用 gethostname( ) 函数取得系统设定。如果是将 myorigin 省略,则视同为设定 $myhostname。
另外,如果本机是某个网域的 mail gateway,也就是说网域内的所有邮件服务器,都必须把它们的邮件送到本机来处理,这种情形可以使用 masquerade_domains 让 postfix 自动缺省寄信人字段中的邮件主机名,例如:
masquerade_domains = $mydomain
假设 $mydomain 是 spps.tp.edu.tw,则无论信件是来自 stu.spps.tp.edu.tw 或 mail.spps.tp.edu.tw 都将会被改写成 spps.tp.edu.tw。
如果你有某些账号不想要让 mail gateway 改写寄信人字段,可以设定底下的参数(预设不启用):
masquerade_exceptions = root
启用 masquerade_domains 功能只会改写邮件头与信封里的寄信人字段,如果想要将收信人字段一并改写,可以设置底下的参数:
masquerade_classes = envelope_sender, envelope_recipient, header_sender, header_recipient
注意:这样做将使得 mail gateway 无法寄信给下属邮件服务器,因为收信人字段 @ 后面的邮件域名会被改写成 mail gateway 自己。
要接收来自哪些网络接口的邮件
底下参数是用来定义要处理来自哪些网络接口的邮件,当未定义时,预设会处理本机所有网卡(可以使用 ifconfig 观察),如果定义成 localhost ,则只有 loopback 接口会接受邮件,也就是说只接受控制台或 WEB 接口寄出的邮件,不接受 MUA(outlook express)递交的邮件:
inet_interfaces = localhost
设定成 all ,表示接受所有网络接口的邮件,这是默认值:
#inet_interfaces = all
设定成 $myhostname ,表示要透过 DNS 反查 IP,当本机 IP 是由 DHCP 指派,而 DNS 又未被设定成 DDNS ,将会出现不可预期的错误!
#inet_interfaces = $myhostname
底下范例将会在有DNS A 纪录的IP 以及loopback 接口上提供服务,在这里我们可以发现main.cf 中有许多设定可以设定超过一个值,将它们用逗号区隔开来就行了!(底下的设定范例不适用于postfix 本身兼Proxy/NAT 的场合)
#inet_interfaces = $myhostname, localhost
哪些网域的邮件是给本机的
定义在这里的网域将被视为是 local 网域,换句话说寄到这些网域的邮件,会被接收并分信给本机使用者,相当于 sendmail 中的 local-host-name,当不使用 DNS MX 纪录时,设定如下:
mydestination = $myhostname localhost.$mydomain
如果有使用 DNS MX 纪录,需修改为:
mydestination = $myhostname localhost.$mydomain $mydomian
假设该服务器在 DNS 上有多笔 A 或 CNAME 纪录,则须将这些纪录也写上去,例如:
mydestination = $myhostname localhost.$mydomain $mydomian www.$mydomain ftp.$mydomain
哪些邮件要进行 relay
在 postfix 中针对转信网域作处理的参数有两个,一是用来定义局域网络网段的 mynetworks,另一个是 relay_domains,未被定义在这些 参数里的网域,系统将拒绝转信!
首先说明局域网络网段设定,相关参数共有两个:mynetworks_style 和 mynetworks,前者用来宣告局域网络类型,subnet 代表是子网,这也是默认值,当未作设定时,postfix 将会自行根据 ifconfig 上登记的 IP 和 网络屏蔽作运算,自动求出子网的范围;如果设定成 class,则是不理会屏蔽,自动信任同一个 class 等级的计算机,如果该服务器使用拨接上网,这样设定将使得同一家 ISP 的拨接用户,都可以利用本服务器转信,这是非常危险的(除非你是 ISP 公司);设定成 host 则仅该单机可以寄信。
#mynetworks_style = class #mynetworks_style = subnet #mynetworks_style = host
mynetworks 用来设定局域网络的 IP 范围,刚才设定的mynetworks_style 将会被此参数取代,如果省略不设定,则由mynetworks_style 来决定要binding 的网卡:
mynetworks = 168.100.189.0/25
或
mynetworks = 168.100.189.10
除了上面范例,设定成网段或单机外,也可以指定多个网段(多重 NAT 网域时),或是使用配置文件(条列式),或使用哈希表(makemap hash或postmap hash:)。
#mynetworks = 168.100.189.0/28, 127.0.0.0/8 #mynetworks = $config_directory/mynetworks #mynetworks = hash:/etc/postfix/network_table
有关转信网域设定,以上两个参数就已经足以应付各种需求,但为了与 sendmail 兼容,仍然保留 relay_domains 设定,与 sendmail 不同的是,如果该 relay_domain 在 DNS 上有定义 MX 纪录,将会被 postfix 故意忽略,而当成 mynetworks 来处理,这是为了避免被利用作为广告信回信站台;relay domain 对 sendmail 而言是双向的,但对 postfix 而言则是单向的,只能寄信,而且不代收回信,因此如果要将本机设定成 mail gateway,请使用 mydestination,而不要使用 relay_domains(注意:很多介绍postfix 的文章都犯此错误!)建议不要设置此参数,使用默认值「不启用」。
#relay_domains = $mydestination
当本机为某台 mail gateway 的下属服务器时,必须要将所有寄出的信件交给 mail gateway 代转,这时可以设定 relayhost 为该 mail gateway 的 IP。预设不启用。
#relayhost = $mydomain #relayhost = mg.spps.tp.edu.tw #relayhost = [172.16.1.7]
哪些情况须通知管理员
所谓管理员是指 postmaster 信箱,请修改 alias 设定将 postmaster 对应成管理人员真正使用的信箱,以免警告邮件没人理会堆积在服务器上。
notify_classes 参数用来决定哪些情形下须通知管理员,默认值如下:
notify_classes = resource, software
可以使用的参数包含:
bounce |
当一般邮件无法交递时,产生警告信给管理员(会附上原始邮件的内文)。注意:邮件无法交递时,本来就会寄警告信给发信人,管理员收到的不过是复制版本。 |
2bounce |
当寄给发信人的警告信无法交递时, 产生警告信给管理员。 |
delay |
当对方站台忙碌要求延迟递送时,产生警告信给管理员(不会附上原始邮件)。 |
policy |
对方寄信的要求因为不符合安全规范已经被回绝(被规则过滤掉),此时会产生警告信给管理员。 |
protocol |
通讯协议错误时产生警告信通知管理员,我们比较感兴趣的是对方使用了哪些不合法的 SMTP 命令。 |
resource |
系统资源短缺导致信件无法寄出,例如:硬盘 I/O 错误,此时会产生警告信给管理员。 |
software |
软件安装不全或程序错误造成的问题,产生警告信给管理员。 |
Proxy/NAT 地址
当邮件服务器位于 proxy 或 NAT 防火墙的后方时,此参数用来设定真实 IP,以避免因为 MX 纪录与本机 IP 不同,将该信误判成需 relay 到其它 MX 的邮件;当本机所查询的 DNS 其回复 MX 纪录为虚拟 IP 时,此种现象将不会发生,因此在 NAT 虚拟网域中,架设内部专用 DNS 是非常重要的,请参考网络名称系统一文的解说。默认值为:
proxy_interfaces =
当本机被设定成其它网域的备份 MX 时,如果未设置此参数,邮件会成为服务器间互踢的皮球(去询问其它网域的 DNS 查询 MX 纪录时,查到的必然是真实 IP,即使有架设内部专用 DNS 也没用)。设定如下:
proxy_interfaces = 163.21.166.7
肆、main.cf UCE(unsolicited commercial email)过滤
过去这类的邮件被称为垃圾邮件,比较正式的称呼是 SPAM 邮件,postfix 则称此种邮件为 UCE,有那么一点缩小打击范围的含意,因此使用 UCE 过滤并无法解决其它问题邮件(例如:匿迹邮件、病毒邮件、邮件炸弹)所带来的困扰,请不要期望过高。
尽管如此,与 sendmail 使用 access 来进行存取控管相比较,postfix 的 UCE 过滤显然要精细得多,弹性也比较好,以外挂方式读取过滤规则使得管理员能随时修改设定,并将它模块化,可以说是 postfix 最大的优点。
如前所述,postfix 并不使用复杂的宏语言来进行规则运算,而是采用较为单纯的查表法来控制,但各位可不要小看它,它所支持的查表方式可谓琳琅满目,诸如:字段比对( 纯文本档,字段以逗号或空格或定位点区隔)、DBM 检索、HASH 哈希、NIS 查询、RBL 查询.....等,比对规则也可以选择采用正规表示法(regexp)或是 perl 改良过的正规表示法(pcre)。
邮件头过滤
标头过滤所过滤的对象,除了邮件头外,更扩大范围到附加档案的 MIME 标头,使得过滤可以更精确的进行,而不会因规则过于模糊,殃及无辜的邮件。用过 procmail 的使用者要特别注意:附加档案档名或文件类型是在此过滤,而非在邮件内文过滤。设定方式如下:
header_checks = regexp:/etc/postfix/header_checks header_checks = pcre:/etc/postfix/header_checks
在外挂配置文件 header_checks(可以改用其它档名)中,当字符串比对命中时,可以采取各种处理动作,包括:
REJECT |
拒收信件。 |
OK |
跳过符合条件的标头不作后续检查,在 sendmail 中一旦 OK 该信件就会被接受,但在 postfix 中,OK 仅用来跳过该标头的后续比对,万一有其它标头被拒绝,该封邮件一样会被拒绝。 |
IGNORE |
从邮件删除该标头。 |
WARN |
附加警告讯息。 |
HOLD |
放回队列,等候处理。 |
DISCARD |
直接将邮件丢弃,不回应拒收讯息。 |
FILTER transport.nexthop |
呼叫外挂过滤程序,进行邮件内文剖析过滤。外挂过滤程序可以是任何一种可执行的档案,例如:shell script。该程序必须先定义在 master.cf 中,模拟成一个 socket 来执行(由 master 模块负责伺服监听),当需要呼叫它执行时,postfix 中的 clearup 模块会将整封邮件丢到指定的 port 号,master 模块监听到讯息后会执行相对应的过滤程序。 |
header_checks 的范例如下:
/^Subject: Make Money Fast/ REJECT /^To: friend@public.com/ REJECT
如果未设置此参数,则邮件头过滤功能将会关闭不启用,这是系统默认值。
邮件内文过滤
这是用来过滤所有标头过滤没检查到的邮件内容,设定方式与前面相同:
body_checks = regexp:/etc/postfix/body_checks body_checks = pcre:/etc/postfix/body_checks
如果未设置此参数,则邮件头过滤功能将会关闭不启用,这是系统默认值。
客户端过滤
当用户使用 SMTP 通讯协议连上服务器提出寄信请求时,针对客户端输入的指令进行过滤。在 Linux 系管师进阶班讲义 中已经详细论及在 SMTP 联机阶段中出现的各种欺骗服务器的手法, postfix 提供非常详尽的设定可以针对这些问题加以预防。使用客户端过滤时,必须将 smtpd_delay_reject = yes 设定上去,这是系统默认值。当设定成 no 时,虽然效率较高,但是这样做将会使得 HELO 网域伪装、送信人信箱伪装、寄信人信箱伪装 以外的其它过滤功能失效。
客户端过滤能够使用的过滤功能,包括:
reject_unknown_client |
客户端之 IP 或 Domain name 无法从 DNS 查询验证时,拒绝联机。 |
permit_mynetworks |
符合 $mynetworks 定义的客户端允许联机。 |
reject_rbl_client |
从 SPAM 数据库站台验证客户端域名,符合时拒绝联机,当这种情况发生时,postfix 将会依照 default_rbl_reply 的设定回复相关讯息,也可以依照 rbl_reply_maps 的设定根据不同客户端给予不同讯息,事实上我们根本不需要设置此两个参数(除非想将讯息改成中文)。这个参数必须放在最后面,当成过滤政策。 |
reject_rhsbl_client |
同上,使用另一种 SPAM 数据库站台。这个参数必须放在最后面,当成过滤政策。 |
check_client_access |
根据 access 设定过滤访问权限,与 sendmail 中的 access 数据库兼容。 可以省略参数名称,直接写档名,例如:hash:/etc/postfix/access。 |
permit |
允许联机,设定在过滤规则的最后面,表示未被之前的规则拒绝的客户端一律允许联机,也就是采用黑名单政策。 |
defer |
延迟联机,设定在过滤规则的最后面,表示未被之前的规则拒绝或接受的客户端,必须在稍后重新接受检验,也就是采用拖延政策。 |
reject |
拒绝联机,设定在过滤规则的最后面,表示未被之前的规则接受的客户端一律拒绝联机,也就是采用白名单政策。 |
warn_if_reject |
被拒绝时产生警告讯息,这是用来测试过滤规则用的。 |
reject_unauth_pipelining |
当客户端持续一直传送 SMTP 命令时,拒绝其联机,这可以防止某些软件一次寄送大量邮件。 |
使用客户端过滤跟稍后会介绍的各种 SMTP 过滤,可以把规则依照前后顺序编排成一组规则炼(写成一行,中间用逗号隔开或从逗号后面分行),由于组合出来的过滤功能并非单独运作的,因此顺序非常重要!
smtpd_client_restrictions = reject_rbl_client dialup.ecenter.idv.tw(*免费的 SPAM 数据库:挡拨接发广告信)
smtpd_client_restrictions = reject_rbl_client relays.ordb.org(国外免费的 SPAM 数据库 :挡开放转信的服务器)
smtpd_client_restrictions = reject_rbl_client spam.ecenter.idv.tw(*免费的 SPAM 数据库:挡寄广告信的信箱)
smtpd_client_restrictions = hash:/etc/postfix/access, reject(采用白名单政策)
smtpd_client_restrictions = permit_mynetworks,reject_unknown_client
smtpd_client_restrictions = permit_mynetworks,hash:/etc/postfix/client_checks,reject_unknown_client,reject_unauth_pipelining
是否要求使用 HELO 命令
当启用此功能时,将要求客户端进行联机时须先传送 HELO 字符串,稍后我们可以根据 HELO 字符串传回来的域名进行过滤,由于某些寄信程序不会传送 HELO 命令,这样做有可能会使得这些客户端程序无法正常寄信。默认值是:
smtpd_helo_required = no
HELO 命令过滤
用来过滤 HELO 命令后面的域名是否允许其联机,能够使用的过滤功能,包括:
reject_invalid_hostname |
域名字符串不符合文法时,拒绝其联机。 |
reject_unknown_hostname |
域名无法从 DNS 查到 A 或 MX 纪录时,拒绝其联机。 |
reject_non_fqdn_hostname |
域名不是完整 FQDN 格式时,拒绝其联机。 |
check_helo_access |
根据 access 设定过滤访问权限。 |
其它 permit、defer、reject、warn_if_reject、reject_unauth_pipelining 请参考前面的说明。 |
设定范例:
smtpd_helo_restrictions = permit_mynetworks, reject_invalid_hostname
信封标头字段过滤
此功能用来过滤邮件的信封标头是否符合 RFC 821 之规定,预设是不启用此过滤。因为目前最多人使用的 MUA 是 outlook express,它会使用许多额外的标头来进行邮件控制,例如:大家熟知的要求回复功能,如果启用此参数将使得这些信件被拒绝无法寄出。
strict_rfc821_envelopes = yes
寄信人过滤
此功能并非过滤邮件头里的寄信人字段,而是过滤 mail from: 命令后面的字符串,默认值是不过滤,但由于广告信寄信程序为了能顺利寄信,经常会伪造此字符串,建议应该启用。
可以使用的选项包括:
reject_unknown_sender_domain |
寄信人的域名无法从 DNS 查询验证时,拒绝联机。 |
reject_rhsbl_sender |
寄信人信箱如果被纪录在 SPAM 数据库站台,就拒绝他联机。 |
check_sender_access |
根据 access 设定过滤访问权限。 |
reject_non_fqdn_sender |
寄信人的域名不是完整 FQDN 格式时,拒绝其联机。 |
reject_sender_login_mismatch |
寄信人信箱与登入的账号不吻合时,拒绝其联机。须配合 SASL 使用者认证机制使用(SMTP AUTH)。 配合 smtpd_sender_login_maps 指定的对应表,可以让登入账号与使用的信箱作对应,例如:shane 账号可以用 webmaster 信箱寄信。 |
其它 permit、defer、reject、warn_if_reject、reject_unauth_pipelining 请参考前面的说明。 |
设定范例如下:
smtpd_sender_restrictions = reject_rhsbl_sender dsn.rfc-ignorant.org(国外免费的 SPAM 数据库:挡寄广告信的信箱)
smtpd_sender_restrictions = hash:/etc/postfix/access, reject_unknown_sender_domain
smtpd_sender_restrictions = permit_sasl_authenticated,reject_unknown_sender_domain,reject_non_fqdn_sender
收信人过滤
此功能并非过滤邮件头里的收信人字段,而是过滤 rcpt to: 命令后面的字符串,默认值是不过滤,但由于广告信寄信程序为了能顺利寄信,经常会伪造此字符串,建议应该启用。
可以使用的选项包括:
permit_auth_destination |
收信人网域符合 $relay_domains、$mydestination、$inet_interfaces、$vitual_alias_domains、$virtual_mailbox_domains 的定义时,接受联机。 |
reject_unauth_destination |
收信人网域不符合上述设定时,拒绝联机。 |
permit_mx_backup |
当从 DNS 上查到本机为收信人网域的备份 MX 时,接受联机。使用此功能有安全漏洞,可以配合 permit_mx_backup_networks = 172.16.0.0/16 来检查主要 MX 是否在该网段内,来加强过滤功能(避免被不信任的网域设定为转信 MX)。 |
check_relay_domains |
允许代收要给 relay_domians 的信件。 |
check_recipient_access |
根据 access 设定过滤访问权限。 |
check_recipient_maps |
当收信人网域不符合 permit_auth_destination 之要求,或是收信人信箱不符合 $local_recipient_maps、$virtual_alias_maps、$virtual_mailbox_maps、$relay_recipient_maps 的定义时,拒绝联机。此参数可以放在收信人过滤规则的最后面,当作过滤政策。 |
reject_unknown_recipient_domain |
收信人的域名无法从 DNS 查询验证时,拒绝联机。 |
reject_rhsbl_recipient |
收信人信箱如果被纪录在 SPAM 数据库站台,就拒绝他联机。 |
reject_non_fqdn_recipient |
收信人的域名不是完整 FQDN 格式时,拒绝其联机。 |
其它 permit、defer、reject、warn_if_reject、reject_unauth_pipelining 请参考前面的说明。 |
设定范例如下:
smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination,reject_non_fqdn_recipient
ETRN 命令过滤
用来过滤哪些网域或哪些客户端,可以使用 ETRN 命令。ETRN 命令用来一次处理大量邮件,当某个客户端使用 ETRN 时,有时候会影响到其它用户寄信的效能,通常只有拨接用户、帮拨接用户转信的 mail gateway、邮件讨论组(mailing list)或电子报发行站台,需要使用此功能。postfix 的默认值是所有客户端都可以使用 ETRN 命令。
能使用的特殊参数只有 check_etrn_access,其余与客户端过滤参数相同,请自行查阅前文。设定范例如下:
smtpd_etrn_restrictions = permit_mynetworks, hash:/etc/postfix/etrn_access, reject
伍、效能调校
在这一章中,所有未特别说明的参数,都是设定在 main.cf 中!
行程限制
系统默认行程限制(default_process_limit)为 100,也就是说同时可以收发总共 100 封邮件,如果发现服务器效能很差,可以尝试降低此数值,请修改 master.cf:
# ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) # ========================================================================== . . . smtp inet n - - - 10 smtpd . . .
以上各字段意义说明如下:
service |
识别名称 |
type |
服务类型总共有三种:inet、unix、fifo。inet 是指透过网络接口 sockets 提供服务(例如:127.0.0.1:25),unix 指使用 unix sockets 提供服务(直接呼叫执行),fifo 是指使用 pipe name 提供服务(例如:网络传真) 。 |
private |
切断对外服务,默认值是 yes。注意:inet 类型无法设定成 yes。 |
unpriv |
不要以 root 身分执行,而是以 $mail_owner 身分执行。默认值是 yes。 |
chroot |
开启邮件暂存文件夹时,要不要将该文件夹变成根目录,这是为了防止与 postfix 无关的文件夹遭到入侵者以 $mail_owner 身分闯入。默认值是 yes。注意:pipe、virtual 和 local 模块无法设定成 yes。 |
wakeup |
服务每隔多久唤醒一次,默认值是 0(不唤醒)。只有 pickup、qmgr 和 flush 模块需要设定唤醒周期。 |
maxproc |
最大线程。 |
command + args |
该服务执行的命令及参数。 |
master.cf 除了控制 postfix 各个模块的运作方式外,也可以加入外挂过滤引擎,postfix 希望透过这个方式与其它程序设计专家合作,后文将介绍两个经常使用的过滤程序 SapmAssassin 以及病毒过滤软件 amavisd + clamav。更详细的内容可以自行到 http://www.postfix.org/ 查看或阅读 /usr/share/doc/postfix-1.xx/README_FILES/FILTER_README!
同步处理限制
postfix 采用同步处理限制来进行流量调整和控制,当 postfix 寄信到某个邮件主机时,首先传两封信过去(initial_destination_concurrency = 2),如果一切正常则逐步增加每次传送的量,一直到传输失败或者是到达同步上限每次 20 封信(default_destination_concurrency_limit = 20)。
如果想要针对不同 agent 来设定同步上限,也可以使用底下的参数(未设定的参数将会沿用 default_destination_concurrency_limit 限制):
local_destination_concurrency_limit = 2 uucp_destination_concurrency_limit = 2 smtp_destination_concurrency_limit = 10
收信人限制
这是指一封信可以寄给多少人,postfix 预设可以处理 50 个收信人(default_destination_recipient_limit = 50),如果一封信的收信人超过 50 人,postfix 会自动将此信复制成很多份,以 50 人为单位分批寄送。
和同步处理限制一样,可以针对不同 agent 来设定不同上限:
uucp_destination_recipient_limit = 2 smtp_destination_recipient_limit = 10
延迟传送
当邮件服务器使用拨接线路联机时,由于部分时段处于断线状态,当 postfix 处理信件时会因为无法收发信件,持续产生错误讯息,为了避免发生这个现象,我们可以设定 defer_transports = smtp 来告知 postfix,要从 smtp agent 传送出去的邮件暂时不要传送。这些邮件可以等到上线后,再以 ETRN 指令全部寄出。
如果本机是前述邮件服务器的 mail gateway,由于该服务器只有部分时段上线,因此有可能 mail gateway 已经累积许多信件等待传送给它,为了避免 mail gateway 持续尝试传送,可以设定:
defer_transport = hold
接着在 /etc/postfix/transport 设定:
customer.com hold:[gateway.customer.com]
这个设定的意思是,要给 customer.com 的邮件先暂存在 gateway.customer.com,等待前者上线后再全部传送给它(使用 ETRN 命令)。
设定好后,还需修改 master.cf,找到 smtp 行程设定(可参考前面小节),将 smtp 改为 hold 即可:
hold unix - - n - - smtp
传送失败处理
当邮件传送失败的时候,负责传送邮件的 Agent 会将邮件退回给 qmgr 模块,qmgr 模块则会计算从邮件到达到现在的时间间隔,依此时间间隔将邮件排入延迟传送队列中,以等待下次传送。
如果该封邮件传送到一半的时候失败了,也就是说有些收信人有收到,有些没有。这种情况下,除了将该邮件排入延迟传送队列外,也会将传送失败的对象排入 dead 列表一段时间,在这段时间内如果有其它邮件要传送给这些对象时,就会直接排入延迟传送队列,而不用徒劳无功地去尝试传送!
底下是有关于邮件传送失败处理的相关效能设定:
queue_run_delay = 1000s |
qmgr 模块每 1000 秒(约 16 分钟)检查一次 defer 队列,查看是否有邮件须排入 active 队列 |
maximal_queue_lifetime = 5d |
无法传送的信件在 defer 队列里最多保存 5 天,超过时间则退给寄信人 |
minimal_backoff_time = 1000s |
传送失败的邮件至少在 defer 队列中暂停 1000 秒,而且被排入 dead 清单的收信人至少也要待 1000 秒,也就是说在这段时间内不再尝试寄信给他 |
maximal_backoff_time = 4000s |
传送失败的邮件最多在 defer 队列中等待 4000 秒(约 1 小时) |
qmgr_message_recipient_limit = 1000 |
dead 清单的大小,也就是说第 1001 个传送失败的对象,不会被排入 dead 清单 |
拖延战术
当怀有恶意的客户端连续传送大量邮件时,postfix 为了处理这些邮件耗掉太多资源,导致无法正常工作,这也就是经常被讨论的「阻断服务攻击」。
postfix 的设计者认为阻断服务攻击是不可能被解决的,因为我们无法单从邮件区分出它是恶意或善意,但是我们可以透过一些手段来降低损害。postfix 采用的方法是针对每条联机,设定一个联机错误计数器( session error count),当客户端联机时,开启计数器,如果客户端传送不存在的 SMTP 命令(这绝对是恶意想阻断服务),或是超过字数限制的长字符串(内存溢位攻击)、超过一行的标头(引发邮件剖析错误),计数器就会不断累加。当邮件交寄成功时,计数器才会归零重新计算。
现在我们只要根据计数器采取适当的处理动作就行了:
smtpd_soft_error_limit = 10 |
当计数器到达 10 时,就暂停该联机一段时间 |
smtpd_hard_error_limit = 100 |
当计数器到达 100 时,直接断线 |
smtpd_error_sleep_time = 5s |
每次暂停 5 秒钟 |
陆、资源管制
postfix 可以在内存有限的系统上执行,而不会影响其它服务的效能,这是因为 postfix 提供的内存管理功能非常有弹性,可以依据各种需求加以调整。
每封邮件用量限制
当 postfix 处理邮件时,必须将邮件暂存于邮件队列中,其中 maildrop 和 incoming 队列使用硬盘,而 active 和 deferred 则使用内存,每封暂存在队列中的邮件耗用多少内存是由邮件数据结构来决定,幸运的是这个数据结构的字段大小是可以微调的,透过这些微调就能决定 postfix 的最大内存用量了!
line_length_limit = 2048 |
从客户端接收待寄邮件时,每行最多 2 KB |
header_size_limit = 102400 |
每封邮件的标头大小不得超过 100 KB |
extract_recipient_limit = 10240 |
每封邮件的收信人字段不得超过 10 KB |
message_size_limit = 10240000 |
每封邮件(包含信封)的大小,不得超过 10 MB |
queue_minfree = |
当内存剩下多少 Bytes 时,才可以处理下一封邮件,预设是没有限制。 |
bounce_size_limit = 50000 |
警告信的大小限制为 50 KB。 |
假设通通使用默认值,也就是所有参数都不设置,那么处理一封邮件须耗用 10.05 MB,再加上 postfix 模块程序的大小,总共约 20 MB,这也就是 postfix 运行的最小需求了!
邮件数量限制
当前述用量限制设置完毕后,接着我们还可以针对邮件队列一次要处理多少邮件作出限制,把每封邮件内存用量乘上邮件数量,就可以算出所需的内存总量,当内存足够时,我们当然希望尽可能多处理几封邮件来增进 postfix 的效能。
qmgr_message_recipient_limit = 1000 |
这个参数之前介绍过了,除了用来控制 dead 清单的大小外,也控制着处理中的邮件收信人总量,两者的预设限制都是 1000。 |
qmgr_message_active_limit = 1000 |
最多同时处理 1000 封邮件。 |
duplicate_filter_limit = 1000 |
在进行收信人过滤时,要快取多少已通过过滤的列表,这个功能是用来提高过滤效能,预设要快取 1000 个不同收信人 |
时间限制
postfix 某些模块在运作时,会依照配置文件的要求读取插件或是执行 shell 命令,有些则是会读取外部档案。如果无限制的让 postfix 等待外部命令执行完毕或等待外部档案读取完毕,将会因为这些外部程序的设计不当或 I/O 冲突,而导致 postfix 无法运作,因此就需要设定等候时间限制,超过此时间限制 postfix 将径行处理下一个程序。
举例来说:当 local agent 将邮件分到使用者信箱时,会透过 proxymap 模块读取 alias 数据库,接着根据 alias 设定读取 :include: 档案,最后读取 .forward 档案,前述动作中如果其中一个因为系统 I/O 忙碌无法于时间限制内读取档案,local agent 就会直接跳过进行下一个处理动作。
command_time_limit = 1000s |
等候外部命令或 I/O 的时间不可超过 16 分钟。 |
service_time_limit = |
这个参数的目的是允许不同 service 采用不同时间限制,因此它会取代前述参数的设定,其中 service 就是 master.cf 中的第一个字段。 |
档案锁定
当 local agent 要将信件分到使用者信箱时,有时候使用者正透过 POP3 读取信箱,因此信箱被锁定无法开启,这种情况发生时,local agent 必须等候一段时间重新尝试读取档案,但也不能一直等下去,所以必须要有一些限制。
postfix 支持两种的档案锁定机制:一、使用系统函式 fcntl( ) 或 flock( ),二、使用 local file,postfix 将根据操作系统的不同,选择其中一种或两种并用。有关档案锁定机制在这里不予讨论,有兴趣的读者可以从「专业 Linux 程序设计」(Wrox 出版,碁峯翻译经销)一书一窥究竟。
deliver_lock_attempts = 5 |
档案被锁定时,尝试读取 5 次。 |
deliver_lock_delay = 1s |
每次尝试读取前,先等候一秒钟。 |
stale_lock_time = 500 |
当 lock file 存在超过 500s 时,强制删除 lock file 解除其锁定状态。使用 lock file 其实是透过程序设计技巧来仿真档案锁定功能,它必须由程序设计师自行维护锁定状态,万一有粗心的设计者锁定档案后忘记解除,或是程序当掉无法解除锁定,都会造成档案长期被锁定的假象,所以需要此设定来排除问题。 |
行程自动回复
当行程或子线程因为某些原因当掉,例如:内存不足......等,这个时候 master 将会延迟一段时间后尝试重新启动该行程。当然,如果程序当掉的原因是 main.cf 档案损毁所造成的,就算是不断重复启动也不能恢复正常,因此 postfix 也会将当掉的情形纪录在系统日志里,以便管理员侦错并人工修复。
fork_attempts = 5 |
行程当掉以后,会尝试重新启动它 5 次! |
fork_delay = 1s |
每次重新启动前,先等候一秒钟。 |
transport_retry_time = 60s |
qmgr 每隔 60 秒尝试驱动 agent 进行分信。 |
柒、邮件地址改写机制 vs 相关配置文件
当 cleanup 模块进行邮件过滤之前,会先依据各种配置文件呼叫 rewrite 模块,进行邮件地址改写,这样做的好处是可以减少标头字段的变化,使得过滤快取(请参考 duplicate_filter_limit 的说明)能更有效率的工作。
邮件地址改写的顺序如下:
将 @hosta,@hostb:user@site 改写成 user@site |
postfix 不支持前面那种特殊写法 |
将 site!user 改写成 user@site |
将 UUCP 格式改写为现在使用的地址格式 |
将 user%domain 改写成 user@domain |
将 % 符号去掉 |
将 user 改写成 user@$myorigin |
从 local 寄出的信会缺少域名,在此补上 |
将 user@host 改写成 user@host.$mydomain |
将 hostname 改成 FQDN 型式 |
将 user@site. 改写成 user@site |
去掉绝对域名后面的小数点 |
根据各种配置文件改写邮件地址:alias、canonical、virtual。 |
alias、canonical、virtual 等配置文件是为了达成某些特殊功能而设计的,底下说明这些配置文件的功能以及如何启用它们:
别名对应
alias 是 sendmail 最被广泛使用的一项功能,除了用来对应虚拟信箱外,也可以运用在邮递清单或邮件讨论组上面,由于 postfix 在这部分完全与 sendmail 兼容,因此我们就不再讨论它,详细应用方法请参考 Linux 系管师进阶班讲义 一文。
请在 main.cf 中加入这一行,以便启用 alias:
alias_maps = hash:/etc/aliases
如果您的系统仅支持 dbm 格式的别名,或是透过 NIS 服务器查询别名,请使用底下的参数:
alias_maps = dbm:/etc/aliases, nis:mail.aliases
全名对应
这个配置文件用来建立使用者全名与账号的关系,例如:
shane@mail.spps.tp.edu.tw shane.lee@mail.spps.tp.edu.tw
如果域名,已经设定在 $myorigin、$mydestination 或 $inet_interfaces 中就可以省略不写,直接使用底下的语法:
shane shane.lee
此外这个配置文件还有一个用途,就是当网域迁移时,可以将信件对应到新的域名,例如:
@mail.spps.tp.edu.tw @tp.edu.tw
当本机是 mail gateway 时,透过底下的设定可以得知哪些使用者是位于哪一台邮件主机上,分信时会分给该主机,而非分到本机的 /var/spool/mail 中:
shane @stu.spps.tp.edu.tw(账号相同时) shane s60101@stu.spps.tp.edu.tw(账号不同时)
当配置文件输入完成后,必须先将它哈希成数据库文件,以 btree 为例(与 sendmail 兼容,但效能较差,建议改用 hash):
postmap btree:/etc/postfix/canonical (档名可随个人喜好更改)
接着在 main.cf 中加入底下这一行:
canonical_maps = btree:/etc/postfix/canonical
如果只想对应收信人字段则将该行,取代成:
recipient_canonical_maps = hash:/etc/postfix/recipient_canonical
如果只想对应寄信人字段则将该行,取代成:
sender_canonical_maps = hash:/etc/postfix/sender_canonical
虚拟对应
虚拟对应仅能作用在信封里的收信人字段,而不会去改写邮件头,与全名对应相比较,虚拟对应的功能只能算是半套。虚拟对应一般应用在两种场合:一、将收信人字段中的全名改成账号名称,二、虚拟网域对应。想启用虚拟对应,请在 main.cf 中加入底下这一行:
virtual_maps = btree:/etc/postfix/virtual
至于虚拟对应的设定方式与全名对应相同,请自行参考前面小节的介绍。
使用者迁移
当使用者已经不再使用此信箱,而改用其它服务器提供的信箱时,并无法通知所有的亲朋好友,因此仍然会有不知情的寄信人寄信过来,这些邮件到底该如何处理呢?
首先用户可以在自己的家目录里,建立 .forward 档透过这个档案将邮件转寄到新的信箱,管理员也可以透过 alias 别名对应帮该名使用者转信。虽然这样做很人性化,但也有两个缺点:一、转信会增加 postfix 处理邮件的负担,导致效能降低,二、亲朋好友并不知道要把信寄去哪里才对,所以不断地麻烦我们!
另一种处理的方法是不要替已经搬走的使用者转信,而直接告诉对方该寄去哪里才对,如果管理员也不清楚新的邮件地址,直接告诉对方不要再寄信过来也可以,使用这个功能请在 main.cf 加入:
relocated_maps = hash:/etc/postfix/relocated
/etc/postfix/relocated 的设定范例如下:
shane 使用者 shane 已经不再使用此信箱了,请将信寄到 tp.edu.tw 主机,新账号是 sean!
在提示讯息中,我们不使用 @ 符号(sean@tp.edu.tw)是为了避免被信箱收集者卖给广告商。
邮件绕送路由
transport_maps 允许我们直接将符合条件的邮件,透过特定的方式,传送到特定的主机,而不需要依靠 DNS MX 纪录,譬如前面已经介绍过的延迟传送机制外(给拨接用户使用的),要启用 transport 功能,请在 main.cf 加入:
transport_maps = hash:/etc/postfix/transport
底下是 transport 配置文件的应用范例,意思是要将寄给 @spps.tp.edu.tw 网域的信件,传送到 172.16.1.6 这台主机的 8025 port:
spps.tp.edu.tw smtp:[172.16.1.6]:8025
应用场合
优秀的管理员明白各种配置文件各有职司,就算功能可以互相取代,也不应该混合着用,例如:用全名对应机制作别名对应,用别名对应机制作虚拟对应......这样作恐怕没有人可以厘清其逻辑关系,将来要如何维护呢?
到底什么情况下要用哪一种机制来实作呢?笔者给大家的建议是:
将寄信人账号对应成全名 |
sender_canonical_maps = hash:/etc/postfix/sender_fullname |
将收信人全名对应成账号 |
virtual_maps = hash:/etc/postfix/recipient_loginname |
域名迁移 |
canonical_maps = hash:/etc/postfix/domain_canonical |
使用者迁移 |
relocated_maps = hash:/etc/postfix/relocated |
将寄给本机账号的信件分到其它服务器 |
canonical_maps = hash:/etc/postfix/individual_user |
将寄给虚拟网域的邮件转给真实存在的网域 |
virtual_maps = hash:/etc/postfix/virtual_domain |
让使用者登入后能改用虚拟信箱寄信 |
smtpd_sender_login_maps = hash:/etc/postfix/unaliases |
将寄给虚拟信箱的邮件转给真实存在的使用者 |
alias_maps = hash:/etc/aliases |
将邮件导向某台邮件过滤或扫毒主机 |
transport_maps = hash:/etc/postfix/transport |
捌、SpamAssassin
SpamAssassin 是一套用来协助过滤垃圾邮件的程序,它会针对信件中特定的比对样式给予不同的分数,当分数超过指定的值后,该封邮件就会被当作垃圾邮件处理,处理方式依据设定的不同,会在信件标头或邮件主旨字段加入警告讯息。
SpamAssassin 必须结合 MTA 来使用,单独执行并不能发挥其功效,由于该程序是使用 perl 语言开发,因此如果刚开始安装 Linux 操作系统时没有一并安装,事后要补装有一点小麻烦,安装所需的套件如下(省略版次号码,请自行按 TAB 补字,依顺序安装),这些 RPM 档案可以从 RedHat 光盘取得:
perl perl-Time-HiRes perl-Digest-HMAC perl-Net-DNS spamassassin
安装好之后,请以下列指令启动 spamd:
service spamassassin start
并将该服务设定为开机自动执行:
chkconfig --level 3,5 spamassassin on
请用底下指令,测试程序能否成功执行:
spamc -c < /var/mail/username
假设该使用者信箱已经有至少一封邮件,这时 spamc 程序会将该邮件传送给 spamd(127.0.0.1:783)进行评分,如果执行成功应该会看到 SpamAssassin 的评分,例如:
3.5/5.0
让 postfix 使用 SpamAssassin 过滤邮件
确定 SpamAssassin 可以正常执行之后,接着设定 /etc/postfix/master.cf,请修改 smtp deamon 服务的设定:
smtp inet ............中间五个字段不用修改............. smtpd -o content_filter=spamfilter:
这一行的意思是要求 smtpd 在进行完邮件过滤后,透过 spamfilter 服务进行邮件全文过滤,因此我们还得定义 spamfilter 这个服务(服务名称可以自行修改):
spamfilter unix - n n - 100 pipe flags=Rq user=nobody argv=/usr/sbin/spamc.sh -f ${sender} -- ${recipient}
这里定义 spamfilter 服务使用 unix socket 执行,最多同时进行 100 封信的过滤(如果内存不足请自行修改数量),过滤方式是透过 pipe 模块以 nobody 身分启动 /usr/sbin/spamc.sh ,并且将目前队列中的邮件其数据结构中的 sender 和 recipient 参数读出来,连同 -f 参数传递给 spamc.sh 当作参数。旗号 R 表示要把信封中的寄信人地址,以 Return-Path: 标头插入邮件中。旗号 q 表示参数中的特殊字符要用单引号括住,以避免被当成控制字符处理,在这个例子中用来避免邮件地址中的 @ 符号被当成控制字符。
接着我们还得自己写一支小小的 shell script,底下就是 /usr/sbin/spamc.sh 的内容(这个档案须自己建立,之所以放在 /usr/sbin/ 是因为这个文件夹的权限比较严格,也比较安全):
#!/bin/sh /usr/bin/spamc -t 30 -e /usr/sbin/sendmail -i "$@"
不要忘记将该程序的档案权限改成 755。pipe 模块传递参数给上面这支程序时,会将整封邮件的内容放在键盘的 input 队列中,所以我们使用 sendmail -i 这道指令将邮件从 input 队列读进来,"$@" 会被 spamc.sh 的参数宏取代,也就是说 "$@" 会变成
-f sender@sender.domain -- recipient@recipient.domain
这个字符串将当成参数传递给 sendmail 程序,意思是要 sendmail 把参数传递进来的「寄信人地址 -- 收信人地址」写在信封的寄信人字段里。 mail from: 与 rcpt to: 原始参数 的取得,是由 smtpd 于建立 SMTP 联机时,将该字符串撷取出来放在邮件队列的数据结构中,当 master 唤醒 spamfilter 服务时,透过 pipe 当白手套,把邮件数据结构中的字符串传递给 spamc.sh 再传递给 sendmail。透过这些程序的接力表演,现在终于可以把这两个参数当作过滤的键值。
附带一提:如果本机没有安装 SpamAssassin,想要透过别台主机安装好的 SpamAssassin,这时候可以修改 spamc.sh:
/usr/bin/spamc -d 172.16.1.6 -p 783 -t 30 -e /usr/sbin/sendmail -i "$@"
使用这个方法前,请先确认从本机到过滤主机之间的防火墙已经放行 783 port。
删除可疑的垃圾邮件
SapmAssassin 发现垃圾邮件的可疑对象时,会加入一个邮件头,至于要不要把信删除,则不关它的事。我们可以透过 header_checks 机制(前参照第肆章 UCE 过滤的说明)把具有 SPAM 标头的邮件删除,然而这样做是有风险的,万一有些普通信件因为长得太像垃圾信而被标示为 SPAM,那就会一起被删除!
如果你真的想删除可疑邮件,请修改 main.cf 设置下列参数:
header_checks = pcre:/etc/postfix/header-checks
找到 /etc/postfix/header-checks 档案,如果档案不存在请自己建立,加入底下这一行:
/^X-Spam-Flag: Yes/ DISCARD
垃圾邮件经验学习法
SpamAssassin 判断一个档案是否为垃圾邮件,是透过邮件特征比对 hits 比率(原理有点像 proxy server),也就是说必须使用一段时间后,才能从经验中学习到哪些邮件内容反复不断出现, 而要学会哪些邮件是垃圾邮件,最有效的方法是透过一个垃圾信箱来学习,首先我们建立一个没人使用的信箱,接着以此信箱为名义在 news server 上发表测试文章。
等几天后,很快的就有一大堆垃圾信涌入该信箱,现在我们只要把信喂给 SpamAssassin 就行了,底下是透过 alias 来喂信:
fakeuser: "| /usr/bin/spamassassin -r -w fakeuser"
上面这个范例中,指令参数 -r 的意思是要 SpamAssassin 把所有邮件的寄信人信箱当成垃圾信的样板,从此以后符合此样板的邮件都会被认为是垃圾信。-w fakeuser 是一个非必要参数,SpamAssassin 将会以 fakeuser 信箱的名义寄警告信给对方,但是因为对方使用伪装过的信箱,因此也没有人会收到警告信,寄了也是白寄,建议不要使用此功能。
除了透过 alias 可以喂信给 SpamAssassin 外,我们也可以利用 .forward 来喂信,请在 fakeuser 的家目录建立此档案,档案内容如下:
| /usr/bin/spamassassin -r -w fakeuser
决定 SPAM 过滤门坎
SpamAssassin 根据邮件比对的得分高低,来决定该邮件是否为垃圾信,默认值是 5 分,建议修改为 9 分。请修改 /etc/mail/spamassassin/local.cf:
required_hits 9.0
白名单与黑名单
SapmAssassin 维护一个邮件数据库,该数据库将透过自动学习机制来增减邮件的分数,分数高于门坎的称之为黑名单,分数低于门坎的称之为白名单,一旦某种特征的邮件被评为黑名单,以后该信箱寄出的邮件都会被认为是垃圾邮件,虽然自动学习机制对于主动发现垃圾邮件很有用,但难免也有误判的时候,这个时候我们可以透过以下命令来调整:
/usr/bin/spamassassin -W < message |
将该邮件加入白名单 |
/usr/bin/spamassassin --add-to-blacklist < message |
将该邮件加入黑名单 |
/usr/bin/spamassassin -R < message |
将该邮件移出白名单 |
/usr/bin/spamassassin --add-addr-to-whitelist=mailbox |
将该信箱加入白名单 |
/usr/bin/spamassassin --add-addr-to-blacklist=mailbox |
将该信箱加入黑名单 |
/usr/bin/spamassassin --remove-addr-from-whitelist=mailbox |
将该信箱移出白名单 |
当某个信箱被误判为垃圾邮件,这时我们可以直接将该信箱加入白名单,例如:
/usr/bin/spamassassin --add-addr-to-whitelist=shane@mail.spps.tp.edu.tw
该信箱一旦加入白名单后,只能以手动方式移出或重新加入黑名单。如果要把具有某种特征的邮件加入白名单 (例如:校内邮件讨论组、校园电子报、网站更新通知......等,这类邮件经常被视为垃圾邮件),请先将该封邮件另存成纯文本档,接着将该信喂给 SapmAssassin 即可,请使用底下指令:
/usr/bin/spamassassin -W < myletter.eml
玖、amavisd-new + clamav
amavisd 用来将病毒过滤功能提供给邮件服务器,和 SpamAssassin 一样也是用 perl 开发的,而新版的 amavisd-new 更结合了 SpamAssassin 和许多额外设定,透过 perl NEt::Server 模块提供类似 Apache prefork 模式的效能,可以应付 ISP 庞大的邮件处理量。也就是说,使用了 amavisd-new 就不需要再去作前一章提到的 SpamAssassin 相关设定,同时也可以根据自己的需求来制定线程的数量。
clamav 是一套免费的病毒过滤引擎,用户不须注册就可以透过病毒更新程序自动更新病毒特征,而且病毒特征的维护也很迅速,每天至少会更新一次。clamav 主要用来应付硬盘档案的扫毒工作,它可以因应硬盘 IO 动作实时监控扫毒,也可以排程进行文件夹扫描,但它却无法与各种服务器结合一起运作,这也就是为什么我们需要 amavisd 的原因。更精确的说,amavisd 只是将现成的扫毒功能提供给邮件服务器而已,它自己并不会扫毒。
设定 clamav
在这里我们将只介绍 clamav 的安装程序,并不打算详细介绍 clamav 的各种功能和使用方法,有兴趣的学员可以自行到 clamav 的官方网站 http://www.clamav.net/ 查看详细的内容。首先请按这里下载 clamav 的 RPM,使用底下指令进行安装:
rpm -Uvh clamav-*
安装完成后,可以使用 man clamd 来查看详细介绍,请使用 ntsysv 或底下指令将 clamav 设定为开机时自动启动:
chkconfig --level 3,5 clamd on
手动启动 clamav 的方式和其它的服务一样:
service clamd start
要更新病毒特征可以使用 freshclam 指令,如果要让它自动更新,则可以加以下参数(意思是每天检查更新两次):
freshclam -d -c 2
freshclam 也是一个 deamon ,因此我们可以使用 ntsysv 或底下指令将它设定为开机时自动启动:
chkconfig --level 3,5 freshclam on
在网络上可以找到 OpenAntiVirus 发展计划,该计划以 GPL 精神来整合 Linux 平台上的各种扫毒引擎,clamav 只是其中之一,其它发展中的计划还包括:mod_vscan(在 Apache 上扫毒)、squid_vscan(在 Proxy 上扫毒)、amavisd(在 Mail 上扫毒)、pop3_vscan(在 iptables 防火墙上直接扫掉邮件病毒),有兴趣的学员不妨去逛一逛。
安装 amavisd-new
amavisd-new 的原始套件可以在 http://www.ijs.si/software/amavisd/ 取得,或按这里下载。这个套件与 perl 模块的相关性很高,安装异常复杂,开发者也未提供自动安装程序,仅有一份简要说明,使用开发者建议的方式装,会因为 CPAN.pm 模块与特定模块有相依性的问题,导致某些特定模块安装失败,即使能成功安装完成,也须自行修改 amavisd 源代码,同时也无法将执行程序自动化,为了避免麻烦,请按照底下笔者的建议来安装,安装方式如下:
- 安装 amavisd 之前,请用底下指令先安装 CPAN: perl -MCPAN -e 'install CPAN'; 安装 cpan 时,会问一大堆问题,通通按 Enter 就可以了,一直到出现底下问题: (1) Africa (2) Asia (3) Central America (4) Europe (5) North America (6) Oceania (7) South America Select your continent (or several nearby continents) [] 问所在地区,请选 2 (1) China (2) Indonesia (3) Israel (4) Japan (5) Malaysia (6) Philippines (7) Republic of Korea (8) Russian Federation (9) Saudi Arabia (10) Singapore (11) * (12) Thailand Select your country (or several nearby countries) [] 问所在国家,请选 11 (1) ftp://cpan.cdpa.nsysu.edu.tw/pub/CPAN (2) ftp://ftp.isu.edu.tw/pub/CPAN (3) ftp://ftp.nctu.edu.tw/UNIX/perl/CPAN (4) ftp://ftp.tku.edu.tw/pub/CPAN/ (5) ftp://ftp1.sinica.edu.tw/pub1/perl/CPAN/ Select as many URLs as you like, put them on one line, separated by blanks [] 问最近的映射站台,请选 5(中研院) 剩下的其余问题全部按 Enter 即可
- CPAN 装好后,请先将刚才下载回来的 amavisd tarball 解压缩:tar xzvf amavisd-new-<TAB补字>
- 切换目录到解开的文件夹:cd amavisd-new*
- 测试所需套件是否已经安装:./amavisd debug
- 响应消息如下(由于每台机器已安装的 perl 模块不一样多,因此底下讯息仅供参考): ERROR: MISSING REQUIRED BASIC MODULES: Time::HiRes IO::Wrap IO::Stringy Unix::Syslog Mail::Field Mail::Address Mail::Header Mail::Internet MIME::Words MIME::Head MIME::Body MIME::Entity MIME::Parser Net::Server Net::Server::PreForkSimple MIME::Decoder::Base64 MIME::Decoder::Binary MIME::Decoder::Gzip64 MIME::Decoder::NBit MIME::Decoder::QuotedPrint MIME::Decoder::UU BEGIN failed--compilation aborted at ./amavisd line 123.
- 使用 cpan 指令安装缺少的模块,例如: cpan Time::HiRes
- 由于某些模块可能被包在一起,安装时会一并装好,所以每安装好一个就应该回头来检视一下,还剩哪些模块需要安装,请再一次执行:./amavisd debug
- 反复步骤 6~7 一直到出现其它错误讯息为止(假如很不幸刚好有的话!)例如: Can't locate Convert/TNEF.pm in @INC (@INC contains: /usr/lib/perl5/5.8.0/i386.........(略) BEGIN failed--compilation aborted at ./amavisd line 3525.
- 这也是相依套件没装好造成的(amavisd debug 模式没发现此问题,而是由 perl 编译程序发现的),处理方式与前面相仿,只是要特别注意错误讯息中的 Convert/TNEF.pm 档案,在进行安装时要写成: cpan Convert::TNEF
- 反复执行 ./amavisd debug 一直到出现底下错误讯息为止 find_program_path: relative paths not implemented: ./amavisd
- perl 模块的安装到此已经完成,接下来就是建立服务账号及相关目录,这部分的工作可以透过笔者自行开发的安装程序来作,请按这里下载 install.sh,将该程序放在解开的 amavisd-new* 文件夹中并执行: chmod 755 install.sh ./install.sh
- 安装完成后,amavisd 会自动被设定为开机启动,你可以使用 ntsysv 或 chconfig 指令来修改启动设定,也可以手动控制:service amavisd start
安装外挂延伸套件
虽然在前面已经将 amavisd 装好,也可以运作了,但考虑到扫毒成效,某些延伸套件也不得不安装,举例来说:有人把中毒的文件压缩后再寄出,假如没有安装解压缩引擎,那岂不是扫不到病毒。要知道扫毒是透过病毒特征的比对,一个被压缩过的档案,病毒特征都消失了,当然就比对不出来!因此要让 amavisd 发挥最大的扫毒效益,就必须安装各种解压缩软件,底下是各种 Linux 版的解压缩程序(非 RH 9.0 的使用者请下载 Tarball 安装),你可以连到 ftp://ftp.spps.tp.edu.tw/linux/compress/ 下载,或点选下列的超链接:
压缩格式 |
官方网站 |
下载 |
file |
||
compress |
无 |
|
gzip |
||
bzip2 |
||
nomarch |
||
arc |
无 |
|
lha |
||
unarj |
无 |
|
arj |
||
rar, unrar |
||
zoo |
||
cpio |
||
lzop |
||
freeze |
无 |
除了文件压缩部分的外挂套件外,如果希望让 amavisd 帮我们管理 SpamAssassin,请使用 cpan 指令安装该套件的 perl 模块,指令如下:
cpan Mail::SpamAssassin
如果想要利用 SQL 数据库来存取中毒或垃圾邮件的黑、白名单,还必须安装 DBD::DBI,这并不是一个模块,而是一群 perl 模块,你必须根据你使用的数据库系统来挑选安装,例如:mysql:
cpan DBD::mysql
如果要针对不同的使用者账号进行不同的过滤规则设定,而用户账号是透过 LDAP 提供,你还必须安装底下的模块:
cpan Net::LDAP
当然以上谈到的三个外挂套件: SpamAssassin、mysql、openldap,也必须安装才行,并不是只装 perl 模块就可以运作,由于以上三个功能是针对 ISP 的需求而开发,校园内应该还用不上,所以不去理会也就算了!
外挂扫毒引擎
amavisd 支持 21 种扫毒软件(包含前面介绍过的 clamav),其中大多数都是商业软件,包括国内知名的趋势科技和美国的赛门铁克公司(当然得买 Linux 版,Windows 版的授权并不通用),因此在这里我们就不介绍了!
修改 amavisd.conf
amavisd 的设定分为底下八个部分,说明如下:
Essential daemon and MTA settings |
amavisd deamon 的基本设定以及指定使用何种 Mail server,需要手动修改的就是这个部分。 |
MTA specific |
SMTP 通讯协议相关设定,使用默认值即可。 |
Logging |
系统日志设定,使用默认值即可。 |
Notifications/DSN, BOUNCE/REJECT/DROP/PASS destiny, quarantine |
订定邮件过滤政策,包括:邮件格式设定、警告讯息、中毒和垃圾邮件之处理......等等,请依需要修改,使用默认值也无妨(中毒时只警告不删除)。 |
Per-recipient and per-sender handling, whitelisting, etc. |
订定中毒及垃圾邮件黑、白名单管理政策,是否要透过数据库存取、特定网域或信箱排外处理......等等,不是 ISP 应该用不到,所以我们不去理会它! |
Resource limits |
系统资源限制,请依据自己的硬设备等级来调整,原则上尽量调高效能才会好!(但会影响其它服务的效能)。 |
External programs, virus scanners, SpamAssassin |
外挂套件的设定,由于程序会自动侦测,所以使用默认值就可以了! |
Debugging |
设定 debug 模式启用时机,默认是不启用。 |
在第一个部分一定得设定的有底下这些参数:
$MYHOME = '/var/amavis'; |
指定 amavis 服务账号的家目录,如果是使用笔者写的 script 安装的,这个部分不用修改。 |
$mydomain = 'spps.tp.edu.tw'; |
指定邮件服务器网域。 |
$deamon_user = 'vscan'; |
指定 amavisd 服务账号,如果是使用笔者写的 script 安装的,这个部分不用修改。 |
$deamon_group = 'sweep'; |
指定 amavisd 服务群组,如果是使用笔者写的 script 安装的,这个部分不用修改。 |
$forward_method = 'smtp:127.0.0.1:10025'; |
根据邮件服务器的不同,须设定呼叫 amavisd 的方式,默认值是给 postfix 用的,如果您的邮件服务器不是 postfix 请依文件说明修改。 |
$max_servers = 2; |
预设启动几个行程。 |
$max_requests = 10; |
预设每个子行程最多服务几次要求,超过次数该子行程会重新启动。 |
$child_timeout = 5*60; |
每次过滤邮件不可超过 5 分钟,如果觉得病毒滤不干净,可以延长时间,预设是 8 分钟。 |
在第四个部分底下这些参数,可依需要考虑设定:
$hdr_encoding = 'iso-8859-1'; |
指定标头使用的字符集,使用默认值即可。因为中文字和其它双位字都会编成 quoted-printable code 再寄出,因此没有兼容上的问题,除非是想要启用 utf8 才须修改。 |
$bdy_encoding = 'iso-8859-1'; |
指定邮件内文使用的字符集,使用默认值即可。 |
$final_virus_destiny = D_BOUNCE; |
设定中毒邮件处理方式,默认值是发出警告信。 |
$final_banned_destiny = D_BOUNCE; |
当邮件夹带禁止通行的档案时(例如:exe、com、vbs、scr、pif....等等,稍后可自行修改)要如何处理,默认值是发出警告信。 |
$final_spam_destiny = D_REJECT; |
设定垃圾邮件的处理方式,默认值是拒收(邮件内容将遭到删除)。 |
$final_bad_header_destiny = D_PASS; |
邮件头中含有非 ASCII 字符时,要如何处理?默认值是接受。这是垃圾信常用的招数,例如:收信人为「亲爱的客户@mail.spps.tp.edu.tw」之类的,虽然这些邮件仍然会由 postfix 再次过滤,但最好还是拒收。 |
当过滤政策设定为 D_PASS 时,可以利用底下参数来订定要不要寄警告信,默认值是不要。 |
|
$warnvirussender = 1; |
中毒邮件要不要警告寄信者,其实这个功能还蛮实用,但是会增加服务器负担。 |
$warnspamsender = 1; |
垃圾邮件要不要警告寄信者,这个功能没有用,不须设定。 |
$warnbannedsender = 1; |
邮件夹带禁止通行的档案时要不要通知寄信者,其实这个功能还蛮实用,但是会增加服务器负担。 |
$warnbadhsender = 1; |
邮件头含有非 ASCII 字符时要不要通知寄信者,这个功能没有用,不须设定。 |
$warnvirusrecip = 1; |
要不要警告收信者他收到的信有毒,这个功能其实没什么用,因为不管有没有收到警告信,outlook express 都会自动打开中毒邮件。 |
$warnbannedrecip = 1; |
要不要警告收信者他收到的附件档案可能有危险,这个功能其实没什么用,理由同上。 |
$virus_admin = "virusalert\@$mydomain"; |
请将管理员信箱改成自己常用的信箱或是透过 alias 对应到常用信箱。当管理员信箱有设定时,管理员会同步收到所有的警告信。 |
$banned_filename_re = new_RE( qr'\.[a-zA-Z][a-zA-Z0-9]{0,3}\.(vbs|pif|scr|bat|com|exe|dll)$'i, # double extension # qr'.\.(exe|vbs|pif|scr|bat|com)$'i, # banned extension - basic # qr'.\.(ade|adp|bas|bat|chm|cmd|com|cpl|crt|exe|hlp|hta|inf|ins|isp|js| # jse|lnk|mdb|mde|msc|msi|msp|mst|pcd|pif|reg|scr|sct|shs|shb|vb| # vbe|vbs|wsc|wsf|wsh)$'ix, # banned extension - long # qr'^\.(exe|zip|lha|tnef)$'i, # banned file(1) types # qr'^application/x-msdownload$'i, # banned MIME types # qr'^message/partial$'i, qr'^message/external-body$'i, # rfc2046 ); |
|
上面是用来设定哪些档案要禁止通行,采用 pcre 的格式来设定,如果你对 perl 程序设计有概念,可以自行修改内容! |
设定 postfix
首先在 master.cf 中先定义一个病毒过滤服务,服务名称可以自己取:
amavis unix - - - - 2 smtp -o smtp_data_done_timeout=1200
这个服务将会执行 smtp client,配合稍后会作的 main.cf 的修改,该 smtp client 将会连往本机(127.0.0.1)的 10024 埠,该埠号已经 binding 给 amavisd。限制行程数为 2 个,这是为了配合 amavisd.conf 中的设定,如果觉得不够用想修改,不要忘记一并修改 amavisd.conf。
在启动外挂过滤程序的做法上与前面介绍过的 SpamAssassin 略有不同,后者是透过 pipe 模块呼叫一个外部 client 端程序(spamc)来启动。而 amavisd 则由于本身并未提供 client 端程序,所以需要由 postfix 的 smtp 模块来代劳。
接着请在 master.cf 加入底下几行设定:
127.0.0.1:10025 inet n - - - - smtpd -o content_filter= -o local_recipient_maps= -o relay_recipient_maps= -o smtpd_restriction_classes= -o smtpd_client_restrictions= -o smtpd_helo_restrictions= -o smtpd_sender_restrictions= -o smtpd_recipient_restrictions=permit_mynetworks,reject -o mynetworks=127.0.0.0/8 -o strict_rfc821_envelopes=yes -o smtpd_error_sleep_time=0 -o smtpd_soft_error_limit=1001 -o smtpd_hard_error_limit=1000
这个设定用来启动一个 smtp deamon,以便配合 amavisd 的过滤动作,邮件从 postfix 透过 smtp client 模块传送给 amavisd 进行病毒特征比对,比对有结果后 amavisd 会将邮件送往 10025 埠,而这个端口已经定义给 postfix smtp deamon 模块,虽然该模块会读取 main.cf 的设定,但上面所指定的参数将会取代 main.cf 的设定,仔细看其中 content_filter= 这个参数,就会发现 postfix 将不会再启动其它的外挂过滤机制。
细心的学员应该会想到,一旦使用了 amavisd 就不会再进行 SapmAssassin 过滤!这也就是为何 amavisd-new 必须支持 SpamAssassin 的原因,当两个过滤引擎结合为一体时,也就不必去烦恼如何整合的问题。
配合上述的修改,请将 main.cf 中的 content_filter 参数设定为:
content_filter = amavis:[127.0.0.1]:10024
上面这一行的服务名称须与 master.cf 中的定义匹配,如果有改过服务名称,不要忘记一并修改。另外埠号也是可以配合 amavisd.conf 来修改,在这里就不讨论了!
还记得在前一章设定 SpamAssassin 时,曾经修改过 master.cf 中的 smtp deamon 服务吗?这个服务是 binding 在 25 埠的主服务,不同于刚才加入的 10025 埠的 smtp deamon,当时在修改时有指定 content_filter 参数,这将会取代 main.cf 中的参数设定,导致 amavisd 过滤引擎不会被启动,请将该参数移除:
smtp inet ............中间五个字段不用修改............. smtpd -o content_filter=spamfilter:
改为:
smtp inet ............中间五个字段不用修改............. smtpd
当然 spamfilter 服务也不再起作用了,可以一并删除,不删除也无所谓!
过滤测试
底下来测试 amavisd 的过滤功能是否有正常运作,测试时我们需要一个带有病毒的测试档案,这个档案可以到 http://www.eicar.org/anti_virus_test_file.htm 取得,檔名就叫 eicar.com(这只病毒不具有破坏能力),由于笔者的计算机有装防病毒软件,所以无法下载这个档案,但我们也可以直接使用病毒特征来测试,eicar.com 的病毒特征如下:
X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
我们先来测试进行邮件滤后处理的 postfix smtp deamon 是否正常:
> $ telnet 127.0.0.1 10025 220 yourhost.example.com ESMTP Postfix quit//已正常启动并能接受指令 221 Bye Connection closed by foreign host.
接着测试 amavisd 的过滤功能:
> $ telnet 127.0.0.1 10024 220 [127.0.0.1] ESMTP amavisd-new service ready MAIL FROM:<test@example.com> //服务已经启动,来寄信试试看 250 2.1.0 Sender test@example.com OK RCPT TO:<postmaster> 250 2.1.5 Recipient postmaster OK DATA 354 End data with <CR><LF>.<CR><LF> Subject: test1
test1 .
*** 250 2.6.0 Ok, id=31859-01, from MTA: 250 Ok: queued as 90B7F16F //邮件已被接受,接着寄病毒信试试看
MAIL FROM:<test@example.com> 250 2.1.0 Sender test@example.com OK RCPT TO:<postmaster> 250 2.1.5 Recipient postmaster OK DATA 354 End data with <CR><LF>.<CR><LF> Subject: test2 - virus test pattern //在标头后面输入病毒特征 X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H* .
*** 550 5.7.1 Message content rejected, id=16968-01 - VIRUS: EICAR-AV-Test //发现病毒,邮件已被拦截 *** 250 2.5.0 Ok, but 1 BOUNCE //发送警告信通知寄信人 *** 250 2.7.1 Ok, discarded, id=16984-01 - VIRUS: EICAR-AV-Test //中断收信动作 *** 250 2.6.0 Ok, id=17041-01, from MTA: 250 Ok: queued as 3F1841A5F5 //警告信已经排入队列
QUIT 221 2.0.0 [127.0.0.1] (amavisd) closing transmission channel Connection closed by foreign host.
拾、寄信认证与安全联机
这里所谈的寄信认证就是指 SMTP AUTH,RedHat 所支持的 Cyrus SASL 机制可以透过三种方式进行 SMTP AUTH:
- pwcheck:直接使用 /etc/shadow 进行账号认证
- pam:透过 PAM 模块可以使用 kerbros、LDAP、NIS、Samba、Radius......等认证机制
- sasldb:使用 SASL 用户数据库进行认证
这三种方法中,以第一种方法最方便,因为我们不需要额外维护一个用户配置文件(这意味着必须重设所有用户的密码),也不需要去设定复杂的 PAM 组态。
使用前两种方法认证必须具备 root 身分才行,但是 postfix 预设是以 $mail_owner 来执行,所以只剩下第三种方法能利用。在这里我们必须思考一个问题,postfix 之所以不用 root 身分执行是为了避免漏洞被黑客利用,但 postfix 本身已经提供 chroot 牢笼了,也就是说即使被黑客骇掉,也仅仅只能改变 /var/spool/postfix 内的档案,顶多就是被利用来转信而已,这样还需要回避使用 root 吗?有关这个问题的答案,由于笔者才疏学浅,不敢给什么建议!
先来谈谈第三种认证方式如何使用?
- 首先替所有用户建立 SASL 密码:saslpasswd -u realm -c user
- 所有账号建好后,将 sasldb 拷贝到 chroot 牢笼中,以免 postfix 读不到: cp /etc/sasldb /var/spool/postfix/etc/sasldb
- 接着修改该用户数据库的拥有人和权限: chgrp postfix /var/spool/postfix/etc/sasldb chmod g+r /var/spool/postfix/etc/sasldb
这种做法必须付出庞大的管理成本(例如:加入或移除使用者时),如果把 postfix 改成以 root 身分执行,这些问题就迎刃而解了。要让 postfix 以 root 身分执行,请修改 master.cf:
smtp inet n n n ...........后面字段不用修改
接着修改 /usr/lib/sasl/smtpd.conf 的内容,将 sasldb 改成 pam,如下:
pwcheck_method: pam
有关 pam 的设定方法,在这里不讨论请自行参考系统文件,跟 SMTP AUTH 有关的设定放在 /etc/pam.d/smtp ,默认值是使用 Linux 系统认证,事实上 sendmail 就是使用这个方法。
启用 SMTP AUTH
postfix 预设不启用寄信认证机制,要让 postfix 启用 SMTP AUTH,请在 main.cf 中加入:
smtpd_sasl_auth_enable = yes
注意:如果你是使用 sasldb 账号而非 Linux 账号,请加入底下参数,此参数用来定义账号的领域( realm),请与你设定的 sasl 使用者匹配(saslpasswd -u realm -c user): smtpd_sasl_local_domain = $myhostname |
启用认证功能后,加入底下的过滤规则,将使得通过认证的使用者可以随意寄信,而不会被过滤规则阻挡,同时其它未使用认证的使用者也能够继续寄信(但必须通过过滤):
smtpd_sender_restrictions = permit_sasl_authenticated ......原来设定的参数加在后面......
如果只想让通过认证的人才能寄信,未通过认证者无法寄信,请设置底下的参数:
smtpd_client_restrictions = permit_sasl_authenticated
在 postfix 中设置此选项会有一些后遗症,假设你的服务器是对外服务的,也就是说前端并没有一台 mail gateway 作为白手套,这样你的邮件服务器将无法与其他服务器交换邮件,这是因为这些服务器并不知道要用什么账号密码来登入你的邮件主机。
如果前端有一台 mail gateway 帮我们收信,然后再转信给我们的主机,这个时候我们的服务器一样会要求 mail gateway 登入,我们可以在 mail gateway 上面设置底下参数,让它能登入转信:
smtp_sasl_auth_enable = yes smtp_sasl_password_maps = hash:/etc/postfix/saslpass smtp_sasl_security_options = noanonymous,noplaintext
第一个参数很容易与 smtpd_sasl_auth_enable 混淆,这个参数是用来告诉 postfix 连上别人的邮件主机时,要不要进行登入。第三个参数则与 smtpd_sasl_security_options 相仿,用来定义当入时所使用的密码机制,请详见后文的介绍!
这三个参数设置好后,还必须在 mail gateway 上建立 /etc/postfix/saslpass 账号密码对照表(底下范例所用的账号必须先在自己的邮件主机上建立好):
mail.spps.tp.edu.tw mailhub:password
密码机制
Cyrus SASL 可以使用多种密码机制,从最简单的 PLAIN(明文密码)、LOGIN(POP3 密码,编成 base64)...... 到安全的 DIGEST-MD5、CRAM-MD5,后两者必须结合 SSL/TLS 安全联机才能使用。postfix 使用何种密码认证是由 smtpd_sasl_security_options 参数来决定:
noplaintext |
关闭明文密码认证功能(包含:PLAIN 和 LOGIN) |
noactive |
防止以暴力法破解密码 |
nodictionary |
防止以字典法破解密码 |
noanonymous |
禁止匿名登录 |
mutual_auth |
只允许使用 SASL 2.0 认证方式 |
postfix 预设禁止匿名登录,但是允许使用明文密码,当然我们知道使用 PLAIN 和 LOGIN 一样都不安全,因为密码很容易被监听封包的程序盗取。因此我们建议使用 SSL/TLS 安全联机来进行登入,底下参数将同时允许安全联机及一般联机:
smtp_sasl_security_options = noanonymous smtpd_use_tls = yes
如果想禁止使用纯文本方式登入,请将上面的参数改成像这样,如果 openSSL 还没安装设定好,请不要启用这个功能:
smtpd_tls_auth_only = yes
启用安全联机
在 postfix 中启用安全联机机制,你必须先安装好 openSSL 套件,postfix 的 smtpd 模块在进行安全联机时会呼叫 Postfix/TLS 这个模块来管理安全信道,这就相当于 sendmail 中的 MSA。要启用 MSA,请修改 master.cf:
smtps inet n - y - - smtpd -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes submission inet n - y - - smtpd -o smtpd_enforce_tls=yes -o smtpd_sasl_auth_enable=yes
另外在 MSA 进行联机时,需要使用随机数生成一次性密码,该随机数可藉由系统随机数装置 /dev/urandom 产生,如果你的系统找不到此装置,请设定底下的模块程序来代替:
tlsmgr fifo - - y 300 1 tlsmgr
安装凭证
postfix 安全联机必须要有密钥才能运作,因此利用 openSSL 产生认证所需的密钥和凭证(放在同一个档案里):
cd /usr/share/ssl/certs make /etc/postfix/server.pem
现在必备的凭证档案都有了,我们可以设定 mian.cf 让 postfix 读取凭证:
smtpd_tls_cert_file = /etc/postfix/server.pem smtpd_tls_key_file = $smtpd_tls_cert_file
联机测试
我们设定好相关参数后,可以将 postfix 重新启动并使用 telnet 进行底下联机测试:
[root@linux postfix]# telnet 172.16.11.1 25 Trying 172.16.11.1... Connected to 172.16.11.1. Escape character is '^]'. 220 linux.spps.tp.edu.tw ESMTP Postfix ehlo test.com //由于是从控制台联机,并不会过滤 HELO 命令字符串 250-linux.spps.tp.edu.tw 250-PIPELINING 250-SIZE 10240000 250-VRFY 250-ETRN 250-STARTTLS //系统支持安全联机 250-AUTH PLAIN LOGIN //系统支持寄信认证功能 250-XVERP 250 8BITMIME
当 main.cf 启用 smtpd_tls_auth_only = yes 参数时,进行底下测试:
auth plain //测试能否以纯文本方式登入 538 Encryption required for requested authentication mechanism starttls //测试能否开启 SSL/TLS 联机 220 Ready to start TLS quit