Linux-电子邮件教程-全-

Linux 电子邮件教程(全)

原文:zh.annas-archive.org/md5/7BD6129F97DE898479F1548456826B76

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

许多企业希望在 Linux 上运行他们的电子邮件服务器,以获得对企业通信的更大控制和灵活性,但入门可能会很复杂。在 Linux 上运行免费且稳健的电子邮件服务的吸引力可能会因明显的技术挑战而受到削弱。一些复杂性来自于电子邮件服务器由几个必须单独安装和配置,然后集成在一起的组件组成。

本书为您提供了设置和维护电子邮件服务器所需的一切。与其他一次处理一个组件的方法不同,本书提供了一种逐步跨越所有服务器组件的方法,为您的小型企业网络留下了一个完整的工作电子邮件服务器。

本书涵盖的内容

第一章:《Linux 和电子邮件基础知识》带您了解 Linux 电子邮件服务器的基本要素,以及使电子邮件成为可能的网络和邮件协议。不管你喜不喜欢,运行 Linux 电子邮件服务器确实需要对底层网络的一些了解,而这一章将是您开始获得这种理解的地方。本章解释了运行自己的电子邮件服务器的好处和缺点,并为典型组织的硬件规模提供了一些指导。

第二章:《设置 Postfix》讲述了基本的 Postfix 设置。Postfix 是我们选择的邮件传输代理(MTA),它是任何电子邮件服务器的核心。 MTA 负责在互联网上的各种邮件服务器之间传递消息,等等。

第三章:《使用 POP 和 IMAP 接收邮件》涵盖了如何处理收件箱中的电子邮件。它将向您展示如何设置 IMAP 和 POP 访问邮箱。这意味着用户将能够使用他们熟悉的电子邮件客户端发送和接收消息。

第四章:《提供 Webmail 访问》展示了如何使用 SquirrelMail 设置 Webmail 访问。这将使用户轻松地在外出时访问他们的电子邮件。

第五章:《保护您的安装》探讨了如何保护您的安装,以防止对用户数据和电子邮件设施本身的滥用。

第六章:《开始使用 Procmail》讨论了 Procmail 的基础知识,并使您熟悉了 Procmail 用于加载配方的各种文件,过滤的核心原则以及可用的选项。

第七章:《高级 Procmail》探讨了 Procmail,并解释了它在控制邮件方面可以提供的大量服务和大量功能。它还讨论了 Procmail 的高级功能及其好处。

第八章:《使用 SpamAssassin 打击垃圾邮件》展示了如何使用 SpamAssassin 与 Procmail 一起过滤困扰现代电子邮件用户的各种垃圾邮件。

第九章:《防病毒保护》展示了另一种保护用户免受恶意电子邮件侵害的方法——这次是电子邮件病毒的传播。使用 ClamAV,您可以扫描邮件以查找病毒,并安排任务以维护最新的防病毒数据库。

第十章:《备份您的系统》将向您展示如何通过备份来保护您的所有辛勤工作,不仅包括电子邮件本身,还包括组成您的电子邮件服务器的所有配置选项。提供了示例以创建自动化备份计划,以最小化数据丢失。当然,您还将学习如何从这些备份中恢复数据。

谁适合阅读本书

本书旨在面向初学者或中级水平的小型企业系统管理员,他们希望在不花费大量时间成为各个应用程序专家的情况下设置基于 Linux 的电子邮件服务器。

还需要基本的 Linux 知识。

约定

在本书中,您将找到一些文本样式,用于区分不同类型的信息。以下是一些这些样式的示例,以及它们含义的解释。

文本中的代码单词显示如下:“您需要修改的配置文件条目是DatabaseMirror

代码块设置如下:

##
## Example config file for freshclam
## Please read the freshclam.conf(5) manual before editing this file.
## This file may be optionally merged with clamd.conf.
##

当我们希望引起您对代码块的特定部分的注意时,相关行或项目将以粗体显示:

$ grep score.*BAYES /usr/share/spamassassin/* /etc/mail/spamassassin/* ~/.spamassassin/local.cf

任何命令行输入或输出都以以下方式编写:

# ls -al /etc/init.d/clamsmtpd

新术语重要单词以粗体显示。例如,屏幕上看到的单词,比如菜单或对话框中的单词,会以这种方式出现在文本中:“使用浏览器保存文件(通常,文件菜单有另存为选项)。”

注意

警告或重要说明会以这种方式出现在一个框中。

注意

提示和技巧会以这种方式出现。

读者反馈

我们始终欢迎读者的反馈。请告诉我们您对本书的看法——您喜欢或不喜欢的地方。读者的反馈对我们开发您真正受益的标题非常重要。

要向我们发送一般反馈,只需发送电子邮件至<feedback@packtpub.com>,并在消息主题中提及书名。

如果您需要一本书,并希望看到我们出版,请在www.packtpub.com建议标题表单上给我们发送一封信,或发送电子邮件至<suggest@packtpub.com>

如果您在某个专题上有专业知识,并且有兴趣撰写或为一本书做出贡献,请参阅我们的作者指南www.packtpub.com/authors

客户支持

现在您是 Packt 书籍的自豪所有者,我们有一些事情可以帮助您充分利用您的购买。

勘误

尽管我们已经尽一切努力确保内容的准确性,但错误确实会发生。如果您在我们的书中发现错误——也许是文本或代码中的错误——我们将不胜感激地希望您向我们报告。通过这样做,您可以帮助其他读者避免挫折,并帮助我们改进本书的后续版本。如果您发现任何勘误,请访问www.packtpub.com/support报告,选择您的书,点击让我们知道链接,并输入您的勘误详情。一旦您的勘误得到验证,您的提交将被接受,并将勘误添加到任何现有勘误列表中。可以通过选择www.packtpub.com/support上的您的标题来查看任何现有的勘误。

盗版

互联网上侵犯版权材料的盗版是所有媒体的持续问题。在 Packt,我们非常重视保护我们的版权和许可。如果您在互联网上以任何形式发现我们作品的非法副本,请立即向我们提供位置地址或网站名称,以便我们采取补救措施。

请通过<copyright@packtpub.com>与我们联系,并提供涉嫌盗版材料的链接。

我们感谢您在保护我们的作者和我们为您提供有价值的内容的能力方面的帮助。

问题

如果您在阅读本书的任何方面遇到问题,请联系我们<questions@packtpub.com>,我们将尽力解决。

第一章:Linux 和电子邮件基础知识

如果你是成千上万的系统管理员之一,负责管理中小型公司的网络和计算机,并且正在考虑托管自己的电子邮件服务,那么这本书就是为你准备的。

我们将从电子邮件系统的最基本组件开始。这些组件将允许你的用户向互联网上的任何人发送或接收邮件。这可能是你所需要的全部,但许多公司还希望为他们的用户提供一个可访问的网络邮件服务,人们可以在家里或在外出时使用。今天许多人不幸无法没有的另一个功能是有效防范通过电子邮件传播的病毒以及垃圾邮件的过滤。

我们还将涵盖安全性的最重要方面,以防止未经授权或恶意使用服务器。然后,我们将讨论如何保留服务器接收或发送的所有电子邮件的存档。最后,我们将描述一种备份和恢复服务器的过程,以保护所有消息免受数据丢失。

本书将介绍所讨论软件的主要特性,这将为你提供一个坚实的基础。

在本书结束时,你将拥有一个适合大多数小公司的运行良好的电子邮件服务器。

作为我们努力的技术平台,我们选择了 GNU/Linux 操作系统和一组经过验证的免费软件工具,这些工具将帮助我们实现为中小企业提供安全可靠的电子邮件服务器的目标。我们选择的工具是广为人知和使用的,由软件专业人员编写,并得到了大量用户的支持。

在本书的这一章中,我们将从你在开始处理服务器之前需要了解的内容开始。

  • 我们讨论了运行自己的电子邮件服务器的优缺点。

  • 我们提供了选择服务器所需的适当硬件和网络连接的指导。

  • 我们对用于在互联网上交换邮件的协议进行了简要介绍,以及允许用户访问他们的电子邮件的主要协议。

  • 为了正确路由电子邮件,我们讨论了连接到互联网的服务器上所需的配置选项。

  • 最后,我们简要介绍备用电子邮件服务器。

在本章结束时,你将对运行电子邮件服务器所需的主要组件有基本的了解。

为什么要管理自己的电子邮件服务器

大多数互联网服务提供商(ISP)已经为客户提供了在他们的服务器上发送和接收电子邮件的能力,那么为什么我们要自己拥有和管理它呢?毕竟,你正在阅读这本书,你可能已经有了自己的理由,但让我们来探讨这个问题以及一些可能的答案。

托管和管理自己的电子邮件服务器最重要的原因是控制。对于许多组织来说,电子邮件是信息技术基础设施的重要组成部分。保持对你的电子邮件的控制有许多优点。

  • 如果一家公司在多个地方设有办事处,你在选择如何连接它们时有完全的自由。办事处之间的虚拟专用网络,办事处之间的传输层安全(TLS)连接,所有办事处的单一服务器,每个办事处一个服务器,等等。

  • 通过在内部保留自己的消息,你可以相互发送消息,而无需让它们在未加密的线路上往返于 ISP 之间。如果你的互联网连接失败,这也会为你提供更可靠的服务,并避免不必要的延迟。

  • 你不依赖于提供商员工的能力。如果你管理自己的服务器并需要解决一个困难的问题或为某事实施自定义解决方案,你可以。或者在必要时,你可以雇佣顾问来帮助你。

  • 如果提供商破产,你所有的数据都安全地存放在你的服务器房间和备份介质上。

  • 您不受我们的提供商可能设置的限制,比如磁盘空间的使用或消息的最大大小。

  • 您可以实施任何您选择的消息归档、反垃圾邮件或反病毒策略。

更多的控制需要更多的责任和知识,这就是本书的用武之地。

除了这些令人信服的论点之外,自己托管电子邮件服务器也有缺点。这是一项需要一定水平的知识和承诺的任务,因此不应该由每个人来承担。拥有自己的服务器,您不仅对提供给用户的服务负责,还对整个互联网社区负有责任。配置不良的电子邮件服务器可以帮助蠕虫和垃圾邮件传播,这不仅对社区是一种伤害,还可能使您的服务器被列入黑名单。即使一个正确设置的服务器可以长时间运行而不需要太多维护,您也必须保持自己合理更新,并准备应对可能出现的新威胁。这并不是要吓唬您,而是要在着手这个项目之前仔细考虑。

您需要托管电子邮件服务器的内容

您的服务器需要通过固定 IP 地址的永久互联网连接可用。理论上,可以使用非固定(动态)IP 地址运行电子邮件服务器,但当 IP 地址更改时,它将不可靠,并且您将面临丢失消息的风险。使用动态 IP 地址,您还将面临更大的风险,可能会被列入动态 IP 地址范围的黑名单之一。

如果您认真考虑运行电子邮件服务器,请获取一个体面的商业级互联网连接。如今这些相对便宜,投资其中将会在以后节省很多麻烦。电子邮件流量不依赖于高带宽,因此简单的 DSL 线路的容量应该是足够的。

即使您需要固定 IP 地址,也不一定需要专用于邮件服务器的公共 IP 地址。如果您的公司只有几个外部 IP 地址,并且在内部使用私有 RFC 1918 地址(192.168.x.y)并且使用网络地址转换NAT)路由器,这不是问题。NAT 路由器将私有网络连接到世界其他地方,并且可以设置路由器以将电子邮件服务所需的端口转发到内部电子邮件服务器。

下表显示了最有可能用于此目的的 TCP 端口。

端口 服务
25 简单邮件传输协议(SMTP)
110 邮局协议(POP)
143 互联网消息访问协议(IMAP)
993 TLS 上的 IMAP

如果员工想要从家里或外出时访问他们的消息,只需确保没有防火墙阻止访问所需的端口,并且 NAT 路由器(如果有)正确地转发这些端口即可。如果用户想通过电子邮件服务器发送消息,则需要一些额外的配置,以允许主机执行身份验证,以防止未注册用户发送电子邮件。

调整电子邮件服务器的硬件规模

选择一台计算机作为电子邮件服务器时,很多人对执行这项任务所需的硬件存在误解。计算机性能不断增加似乎让人们认为他们真的需要最新和最符合时髦词汇的东西,即使他们只想处理几千封邮件。

尽管评估组织的硬件需求需要一定的专业知识,但常识也很重要。对于拥有 100 个用户的公司,每天的消息数量的合理上限可能是 5,000 条。这将允许每个用户每天发送或接收 50 条消息。即使我们假设每条消息都是在工作日的八个小时内发送的,系统平均每分钟处理的消息数量也不会超过 10 条。现代计算机可以在不到六秒的时间内接收并处理一封电子邮件,通常每封邮件的大小只有几千字节。

这个简单的估算显然很粗糙,例如没有考虑到消息通常不是均匀分布在时间上,但这仍然是一个相当不错的估算方法。

现在让我们更深入地看一下在选择服务器时要考虑什么。对于一个不执行任何内容扫描(病毒、垃圾邮件等)的电子邮件服务器,性能通常不受 CPU 的限制,而是受 I/O 性能的限制,特别是硬盘的寻道时间和 I/O 控制器的质量和配置。增加 CPU 的性能对问题没有帮助。现代计算机在 CPU 方面的装备相对较好,因此投资于多个千兆赫多核 CPU 配置可能是没有用的。对于任何合理现代的 1 GHz 级 PC,每秒处理几条消息都不是问题。这个负载相当于每小时接近 20,000 条消息。

添加内容扫描可能会显著增加 CPU 负载,I/O 系统也需要更多的能力来跟上。但是,每秒一两条消息不应该对系统造成明显的负载。

到目前为止,我们讨论的只是电子邮件服务器。它所做的只是接收消息并将其传递给其他主机或本地邮箱。在选择服务器时,你不应忘记人们也会想要阅读他们的电子邮件。这项服务是由额外的服务器软件提供的。就像消息处理软件一样,关键要求是 I/O 而不是 CPU。系统用户的数量本身是一个无关紧要的数字;重要的是使用模式。用户多久会轮询他们的邮箱?如果 100 个用户平均每五分钟轮询一次邮箱,那么平均每三秒就会有一个用户。检查邮箱是否有新消息只需要一小部分时间,所以负担不会很大。

最后,也可以说是最难考虑的是磁盘存储。使用预期的流量数字,我们可以做一些粗略的估算。假设我们的消息中 80%小于 1 KB,15%带有 200 KB 的文档附件,其余是视频和其他 1 MB 的大文件。因此,使用一个 200 天的工作年,大约需要 80 GB 的存储空间。一个典型的 1 TB 硬盘驱动器可以容纳超过 12 年的消息,假设没有消息被删除。

这些指导可能看起来模糊而不具体,但是给出确切的数字是不可能的。从给定的硬件中期望的性能取决于很多因素,试图给出除了一般性指导之外的任何东西都会产生误导。使用常识和简单的估算;除非你确定真的需要,不要购买你能找到的最花哨的服务器,但也不要使用你能找到的任何旧的废弃台式机。即使旧台式机的性能可能足够,但组件可能已经过时,服务协议或保修可能已经过期。

主要的电子邮件协议:SMTP、POP 和 IMAP

为什么我们在这本书中讨论基本的网络通信协议?难道我们不是在运行高级软件吗?确实是,但了解协议的使用方式不仅可以帮助调试可能不工作的系统,还可以增加对邮件系统行为的理解。我们将从对协议的非技术概述开始,然后专注于协议的细节。

概述

在 UNIX 环境中,传统的邮件应用程序根本不使用任何网络协议。它们通过文件系统直接访问本地存储的邮箱文件。通常,每个用户的收件箱存储在/var/mail/var/spool/mail目录中的单个文件中,文件名与用户的名称相同(例如,/var/spool/mail/joe)。本书的重点是讨论 Linux 基于的小型办公室的电子邮件解决方案,用户不希望使用终端应用程序登录到中央服务器以访问他们的邮件,因此本地邮件存储将只简要涵盖。

互联网邮件中最重要的协议是简单邮件传输协议SMTP)。它的目的是在两个系统之间传输电子邮件消息。这两台计算机可以是服务器,也可以是用户运行邮件应用程序的客户端机器,如 Outlook、Thunderbird、Eudora 等。用户不使用 SMTP 来收集新消息。这就是邮局协议POP)和互联网消息访问协议IMAP)的用武之地。

一些专有系统,如 Microsoft Exchange 和 Lotus Notes,使用自己的协议来访问消息,我们在这里不讨论它们。

POP 协议

POP 是这两种协议中更古老、更广泛使用的协议。它专注于让用户访问他们的收件箱,用户可以将新消息下载到本地计算机,然后从服务器上删除它们。POP 服务器不适用于永久存储消息。一些互联网服务提供商的 POP 服务甚至禁止用户在下载一次后将消息留在服务器上。POP 的主要缺点是它只提供一个中间存储介质,用户必须将他们的消息永久存储在其他地方(例如,他们的本地硬盘上)。这不仅对于想要从多个位置访问他们的电子邮件消息的用户来说是不切实际的,而且对于系统管理员来说也是一种麻烦,因为他们可能需要为用户在本地硬盘上的消息实施备份解决方案。POP 也没有提供为每个用户提供多个文件夹的概念;使用 POP,用户只能访问他/她的收件箱。

IMAP 协议

IMAP 被设计为访问一流邮件存储的方法,即它旨在允许用户永久地将消息存储在服务器上。这解决了系统管理员的备份问题,并允许用户从世界上的任何地方访问所有消息(除了防火墙限制)。IMAP 还有更广泛的 TLS 安全连接实现,使得在敌对环境中使用 IMAP 更加安全。为了提高性能并允许用户在未连接到邮件服务器时使用他们的邮箱,大多数支持 IMAP 的邮件应用程序会在本地硬盘上缓存下载的邮箱和消息。

与 POP 不同,IMAP 支持多个文件夹,并在服务器上存储消息状态信息(消息是否已读、已回复或已删除)。这意味着用户可以从不同位置访问他们的消息存储,可能使用不同的电子邮件客户端,但仍会看到一致、最新的消息视图。IMAP 还支持服务器端搜索,因此客户端应用程序不需要下载所有消息来搜索电子邮件。

SMTP 协议

SMTP 是一种基于行的文本协议,运行在 TCP 上,这使得解码 SMTP 传输并使用几乎任何计算机上找到的常规 telnet 客户端启动 SMTP 会话变得微不足道。 SMTP 客户端通过连接到 SMTP 服务器上的端口 25 开始会话。 服务器问候客户端后,客户端必须通过说 hello 或实际上是HELOEHLO,然后是客户端的主机名来做出回应。 如果服务器接受了友好的问候,客户端可以开始第一次邮件事务。

SMTP 邮件事务由三部分组成-发件人、一个或多个收件人和实际消息内容。 发件人由MAIL FROM命令指定,每个收件人由RCPT TO命令指定,消息内容的开始由DATA命令指定。 如果服务器接受消息,客户端可以继续进行其他事务或发出QUIT命令来终止 SMTP 会话。

让我们少些抽象,看一个实际的 SMTP 会话来说明协议。 粗体打印表示客户端发送给服务器的内容。

220 mail.example.com ESMTP Postfix (2.12.6.2)
EHLO gw.example.net
250-mail.example.com
250-PIPELINING
250-SIZE
250-VRFY
250-ETRN
250 8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
MAIL FROM:<jack@example.net> SIZE=112
250 Ok
RCPT TO:<jill@example.com>
250 Ok
RCPT TO:<jack@example.com>
250 Ok
RCPT TO:<joe@example.com>
550 <joe@example.com>: Recipient address rejected: User unknown in local recipient table
DATA
354 End data with <CR><LF>.<CR><LF>
Subject: Test mail
To: <root@example.com>
Date: Sun, 15 May 2009 20:23:22 +0200 (CEST)
This is a test message.
.
250 Ok: queued as B059D3C2B
QUIT
221 Bye

这个例子显示了一个自称为gw.example.net的主机连接到一个自称为mail.example.com的 SMTP 服务器。 因为服务器的第一个响应包含 ESMTP,客户端决定尝试增强 SMTPESMTP),并用EHLO而不是HELO向服务器打招呼。 服务器接受了这个问候,并用支持的 ESMTP 扩展列表做出了回应。

除了发件人地址,客户端还向服务器发送SIZE属性,以指示消息的大小。 这是允许的,因为服务器已经声明支持SIZE扩展。 如果客户端指定的大小超过服务器设置的消息大小限制,消息可以立即被拒绝,而不是在接收整个消息后再进行大小评估。

SMTP 消息显然可以有多个收件人。 这带来了一些必须在实施邮件系统和制定政策时记住的后果。 在前面的示例中,邮件服务器接受了前两个收件人,但拒绝了第三个收件人。 由于服务器已接受了两个收件人,客户端将尝试发送消息内容。 在这里,消息被服务器接受并排队等待传递(250 Ok: queued as B059D3C2B),这意味着 SMTP 服务器已经承担了将消息传递给接受的收件人的责任。 如果消息无法传递,服务器将向发件人发送一个非传递消息(弹回)。 服务器也可以选择拒绝整个消息。 如果是这样,它将拒绝所有收件人的消息,根本不传递。 换句话说,在消息内容的响应中,服务器必须要么拒绝所有收件人的消息,要么接受所有收件人的消息。

重要的是要理解信封和标题之间的区别。 一封邮件的信封由MAIL FROMRCPT TO命令中给出的信息组成,即用于传递消息的发件人和收件人信息。 SMTP 服务器根本不关心From, ToCc消息头。 在我们的示例中,To标题只包含一个地址,与实际收件人地址之间没有其他关系,但这只是一个巧合。 弹回消息始终发送到信封发件人地址,本例中为jack@example.net。 弹回消息的发件人地址是空发件人地址,通常称为空发件人。 无论有多大诱惑,都不应该阻止空发件人地址。

到目前为止,我们还没有评论服务器在每行开头给出的数字代码。 每个数字都有特定的含义,学习正确解释第一个数字是很重要的。

数字 含义
2 服务器已接受上一个命令,并正在等待您的下一个命令。
3 仅在响应DATA命令时使用,并表示服务器已准备好接受消息内容。
4 临时错误:请求目前无法执行,但可能稍后可以成功服务。
永久错误:请求将永远不会被接受。

在 SMTP 中,错误条件可以是临时的或永久的。45都用于表示错误。接收到由4指定的临时错误的客户端应断开连接,将消息保留在队列中,并在稍后重试。典型的临时错误条件包括完整的邮件队列磁盘,必须在消息可以被接受之前解决的服务器配置错误,或者临时的 DNS 查找错误。永久错误由第一个数字为5表示,意味着请求将永远不会被接受,因此客户端必须从队列中删除消息,并向发件人发送一个告诉他或她消息无法被送达的反弹。

SMTP 还有很多内容没有在这个简短的介绍中涵盖。有许多涵盖互联网相关主题的文档,称为请求评论RFC)。RFC 是由互联网工程任务组IETF)发布的备忘录,通常被采纳为标准。对于 SMTP,最重要的是RFC 821(简单邮件传输协议)和RFC 822(ARPA 互联网文本消息格式标准)。

电子邮件和 DNS

域名系统(DNS)在电子邮件中扮演着重要角色。DNS 被电子邮件客户端和电子邮件服务器使用。即使您不打算维护自己的 DNS 服务器,对 DNS 在电子邮件中的作用有一个深入的了解对于邮件服务器操作员来说是必要的。本节假定读者对 DNS 的一般工作原理有基本的了解。

电子邮件应用程序使用的 DNS 记录类型

在许多网络场景中,只使用两种 DNS 记录类型 - A 记录PTR 记录。这些分别将主机名映射到 IP 地址和 IP 地址映射到主机名。这些记录类型也用于电子邮件,但还有第三种 DNS 记录类型专门用于电子邮件。

SMTP 服务器如何发现某个域的消息应该发送到哪个主机?收件人域通常作为一个或多个 DNS 查找中的键。首先进行的查找是邮件特定的 MX 记录 - 邮件交换器记录类型。MX 条目允许 DNS 操作员指定可以接收某个域邮件的服务器的主机名或主机名。例如,MX记录可用于指定发送给example.com的消息应该发送到mail.example.com。如果收件人域没有MX记录,则尝试查找收件人域的A记录。如果A记录查找成功,则邮件将被发送到主机。如果MXA查找都没有返回任何结果,则消息被视为无法送达,并返回给发件人。

有两个很好的理由拥有MX记录:

  • 首先,强制将域的A记录映射到邮件服务器可能并不理想。例如,公司 Inc.的 WWW 地址www.example.com/希望允许访问者使用更短的example.com/ URL,但不希望在邮件服务器上运行 Web 服务器应用程序(或反之亦然)。

  • 更重要的原因是 MX 查找的结果不仅包含主机名列表,而是包含(主机名,优先级)元组的列表。优先级字段是一个整数,描述了列表中主机名的优先级。优先级数的绝对大小并不重要,但它与其他主机名的优先级相关,以创建在投递消息时尝试的主机名的有序列表。列表按升序排列,因此优先级数最低的主机名将首先被联系。如果两个主机名具有相同的优先级,它们将以随机顺序尝试。

相同优先级的 MX 记录可以用作两个或多个服务器之间非常粗糙的负载平衡形式。这也可以通过映射到多个 IP 地址的 A 记录来实现。使用 MX 记录可以为域设置具有不同优先级的备份邮件服务器的层次结构,而这是无法通过 A 记录实现的。让我们看一个使用大量邮件服务器的组织的构造示例。

优先级 主机名
--- ---
10 mx1.example.com
10 mx2.example.com
20 mx3.example.com
30 mx4.example.com

如果为域 example.com 设置了这个 DNS 配置,SMTP 服务器应该首先尝试将 example.com 的消息投递到 mx1.example.com 或 mx2.example.com。如果两个连接都失败,应该尝试 mx3.example.com,如果甚至该服务器没有及时响应,mx4.example.com 是最后的选择。如果那也失败了,消息将被保留,并在以后重试投递。

备份邮件服务器

拥有一个备份邮件服务器,如果主服务器不可用,可以接收消息听起来是一个非常好的主意,但是如今可靠的互联网连接以及垃圾邮件、蠕虫和其他垃圾大部分情况下使得备份邮件服务器不再必要,甚至经常是有害的。拥有备份服务器的理由是它可以在主服务器宕机时接收消息,然后在主服务器恢复正常时将其投递给主服务器。然而,这样做的优势非常小,因为所有的 SMTP 服务器都要求至少将无法投递的消息队列保存五天,然后再返回给发件人。当然,通过备份服务器可以将不可用消息保存更长时间,但是如果主 SMTP 服务器连续不可用超过五天,那么可能存在比丢失几条消息更大的问题。

因为备份邮件服务器通常没有与主服务器相同的垃圾邮件阻挡配置,所以垃圾邮件发送者通常会专门针对备份服务器,以绕过主服务器的更严格规则。

避免备份邮件服务器的另一个重要原因是它们通常不执行收件人验证。这意味着它们不知道哪些收件人地址对它们充当备份服务器的域是有效的。这要求备份服务器接受备份域的所有消息,并尝试将它们投递到主服务器。主服务器将拒绝无效的收件人,导致备份服务器将这样的消息反弹给发件人。这被称为回溯,有两个坏处:

  • 发件人地址经常被伪造,因此反弹的消息可能会发送给无辜的旁观者。

  • 它可能会填满邮件队列,因为无法将消息投递到接收服务器不可用。

一个繁忙的服务器,如果不执行收件人验证并且受到大量垃圾邮件攻击,可能会在队列中存放成千上万封无法投递的消息。

总结

在本章中,我们首先讨论了为什么您甚至应该考虑托管自己的电子邮件服务器。然后,我们看了一些在开始使用服务器之前需要回答的问题——网络连接类型、计算机性能和磁盘空间需求等。

要成功管理电子邮件服务器,了解所使用的网络通信协议是很重要的。我们概述了 POP 和 IMAP,并更深入地探讨了其中最重要的 SMTP。

最后,我们看了 DNS 在将消息路由到正确的服务器或备用服务器(如果有的话)中所起的关键作用。

第二章:设置 Postfix

邮件传输代理MTA)可能是邮件系统中最重要的部分。它负责接收来自互联网或您自己用户的消息,并尽其所能确保消息到达其目的地——其他邮件服务器或您用户的邮箱。

Postfix 被选为本书涵盖的邮件传输代理。Postfix 具有丰富的功能集,具有出色的安全记录,运行速度快,易于配置,并处于活跃的开发中。

本书假设您正在运行 Postfix 2.0 或更高版本。将注意到 Postfix 的任何特定于 2.0 之后版本的功能或行为。

Postfix 简介

本节首先简要介绍了 Postfix 的工作原理,并描述了如何控制其行为。

什么是 Postfix

Postfix是由 IBM 研究员 Wietse Venema 开发的模块化邮件传输代理。它是免费软件,于 1998 年首次以VMailer的名称公开发布。它用C编写,目前包含约 105,000 行代码(不包括注释),因此相当小。它适用于大多数非历史性的 UNIX 和 Linux 变体。

作为纯邮件传输代理,Postfix 不提供任何允许用户通过POP 或 IMAP协议收取邮件的服务。这项任务必须由其他软件完成。本书讨论的用于从主机检索邮件的软件是Courier IMAP

所有官方的 Postfix 文档,以及源代码和第三方软件的链接以及非常活跃的邮件列表的存档,都可以在 Postfix 网站www.postfix.org/.找到。

Postfix 架构:概述

本节将描述 Postfix 邮件传输代理的不同部分,并解释当您通过系统发送消息时实际发生了什么。虽然这可能不是您读过的最激动人心的文本,但如果您希望成功管理 Postfix 服务器,了解 Postfix 工作原理的基础是必不可少的。

Postfix 分为多个独立的守护程序或后台进程,它们相互通信。这些守护程序有不同的责任领域,可能在不同的安全上下文中运行,并可能具有不同的规则,以限制其类型的进程数量。所有守护进程都是根据需要创建的,并由母守护进程master监督。一些守护程序很少或从不重新启动,但它们中的大多数在服务了可配置数量的请求或在可配置的空闲时间后会自杀。下图显示了消息如何在 Postfix 系统中流动,并可用于随后的文本。实线显示了消息内容的路径,而虚线显示了其他形式的通信。

Postfix 架构:概述

这里不会描述所有 Postfix 守护程序,只会描述重要的守护程序。可以在www.postfix.org/OVERVIEW.htmlPostfix 架构概述文档中找到所有守护程序的完整介绍。

新消息到达

新消息可以通过三种方式进入 Postfix 系统。最常见的方式当然是通过简单邮件传输协议SMTP)。负责通过 SMTP 接收消息的守护程序被命名为smtpd。不常见的QMQP 提交协议,由 Daniel J. Bernstein 的 MTA qmail引入,也支持qmqpd守护程序。然而,本书不会讨论 QMQP。

消息到达的第三种方式是通过 sendmail 程序进行本地提交。这是从在 UNIX 主机上运行的程序和脚本提交邮件消息的标准方式。Postfix 提供了一个 sendmail 程序,它在大多数方面与 sendmail 邮件传输代理的 sendmail 程序兼容(www.sendmail.org/)。许多 UNIX 邮件用户代理,如 Mail、Pine 和 Mutt,以及诸如 SquirrelMail 和 IMP 等的网络邮件软件,使用 sendmail 接口提交新消息,尽管一些软件提供通过 SMTP 提交消息的选项。

sendmail 程序将消息传递给 postdrop 程序,后者将消息文件放在 Postfix queue 目录中的 maildrop 目录中。pickup 守护进程等待消息到达 maildrop 目录,并将其传递给 cleanup 守护进程。从那里开始,通过 sendmail 提交的消息与通过 SMTP 或 QMQP 提交的消息走同样的路线。即使 Postfix 当时没有在机器上运行,也可以通过 sendmail 提交消息。当 Postfix 下次启动时,pickup 将发现排队的消息文件并处理它们。

smtpd, qmqpdpickup 接收到新消息时,它将其交给 cleanup 守护进程。该守护进程对消息的大小施加限制,执行用户配置的任何内容限制,根据配置需要重写发件人和/或收件人地址,添加任何缺失的必需标头,并执行其他一些操作。cleanup 守护进程使用 trivial-rewrite 守护进程进行一些地址重写操作。完成其任务后,cleanup 将队列文件放入传入队列并通知队列管理器。

调度消息交付

队列管理器 qmgr 负责安排消息的交付。为了决定如何将消息交付给每个收件人(即交付方法和下一个目的地),qmgrtrivial-rewrite 获取帮助。队列管理器从 master 守护进程请求交付代理进程,并收集交付结果。

队列管理器负责从 cleanup 守护进程交接消息开始直到它们从队列中被移除。移除可能是因为它们已成功交付给所有收件人,也可能是因为它们在队列中停留的时间太长,Postfix 决定它们无法交付。默认情况下,消息将在队列中最多保留五天。队列管理器调用 bounce 守护进程向发件人发送 bounce 消息。

队列管理器用于不同目的的多个目录。传入队列会监视新消息,下一个目的地是活动队列。活动队列包含准备交付的消息,并等待被派发到交付代理。如果交付尝试失败,消息将被移动到延迟队列。该队列将定期扫描,如果是重新尝试消息交付的时间,消息的队列文件将被移回活动队列。当队列扫描时,消息的交付是否应该重新尝试取决于两个因素:消息到达以来经过了多长时间以及设置重新尝试之间的最小和最大时间间隔的两个配置参数。

除了这些队列,还有一个名为hold的特殊用途队列。这个队列包含了被系统管理员使用postsuper命令暂停的消息。Postfix 不会触及这些消息,直到它们被相同的命令解除暂停。暂停队列可用于暂时延迟某些消息的投递,例如因为它们很大,需要在非高峰时段投递,或者因为它们被视为可疑,需要在允许投递之前进行检查。

Postfix 使用的不同队列在QSHAPE_README文档中有详细描述(www.postfix.org/QSHAPE_README.html)。该文档还描述了qshape,这是一个随 Postfix 一起提供的脚本,用于分析队列的内容,并帮助您识别瓶颈。

消息投递

Postfix 配备了许多投递代理,用于使用各种方式和协议投递消息。这些投递代理是在消息离开您的系统之前最后接触消息的守护程序。

Postfix SMTP 客户端smtp(不要与 SMTP 服务器smtpd混淆)用于通过 SMTP 协议将消息传递到其他主机。它与 LMTP 客户端lmtp非常相似,后者通过本地邮件传输协议LMTP)传递消息。作为一种网络协议,LMTP 与 SMTP 非常相似,但 SMTP 用于在 MTA 之间传输消息,而 LMTP 用于将消息最终传递到用户可以访问消息的邮件存储中。

本地投递代理local将消息投递给系统上具有普通帐户的用户。它支持简单邮件列表或角色地址的别名,以及.forward文件,以便用户自己设置消息的转发。

如果您有虚拟邮箱用户,即在系统上没有真实帐户(例如 shell 帐户)的用户,他们的消息将通过virtual Postfix 守护程序传递。

如果 Postfix 的标准投递代理不够用,您可以编写自己的投递代理,并让 Postfix 为一些(或全部)消息调用它。在这种情况下,您可以使用pipe守护程序,通过标准输入流将消息正文传递给您的投递代理,或者您可以使用spawn守护程序,如果您想编写一个通过某种网络协议接收消息的投递代理。

支持程序

Postfix 包含许多支持程序,您可以使用这些程序来控制、测试和调试您的 Postfix 系统。这个列表并不详尽,只是对每个程序进行了简要描述,但其中一些程序将在本章后面使用。熟悉它们是个好主意,这样至少您知道它们可以帮助您解决什么样的问题。

程序 描述
mailq 查看 Postfix 队列的当前内容。输出包括每条消息的大小、到达时间、发件人地址和收件人地址。内部上,mailq只是调用postqueue命令,仅用于与sendmail邮件传输代理的向后兼容性。
newaliases 使用postalias命令重建所有本地别名文件。本地别名将在虚拟别名域和本地别名部分介绍。
postalias 重建单个别名文件或查询别名查找表。
postcat 显示存储在 Postfix 队列中的二进制队列文件的内容。
postconf 显示 Postfix 配置参数的当前或默认值。也可以修改主配置文件,在脚本中很有用。
postfix 启动、停止或重新启动 Postfix,或重新加载其配置。也可用于检查队列目录的完整性和一些其他很少使用的管理任务。
postmap 重建用于表查找或查询任何查找表的索引数据库文件。使用 postmap 调试 Postfix 设置的查找表故障部分讨论了如何使用它来调试 Postfix 设置。
postqueue 除了执行mailq程序的工作外,postqueue还可以用于刷新队列。刷新队列意味着将延迟队列中的所有消息移动到活动队列中。这可以用于安排立即发送消息,但要小心。如果您的服务器负载过重且性能不佳,刷新队列只会使情况变得更糟。sendmail程序也可以用于刷新队列,出于兼容性原因。
postsuper 允许您对已排队的消息采取行动,例如删除或重新排队。它还可以对队列目录进行结构检查,并修复诸如队列文件名称错误之类的问题。例如,如果整个队列目录已经移动或从备份中恢复,这样的检查是必要的。

安装和基本配置

在本节中,我们将介绍如何获取和安装 Postfix 以及如何进行基本配置更改。在本节结束时,您将能够使用 Postfix 发送和接收电子邮件。

选择 Postfix 版本

Postfix 开发有两个独立的分支——官方发布和实验发布。官方发布有时被称为稳定发布,但这有点误导,因为这意味着实验发布不稳定。事实并非如此。实验发布用于引入所有新的 Postfix 功能。当功能的实现和它们的接口(例如它们的配置参数)已经足够稳定时,它们将被引入官方发布。通常,官方发布只会进行错误修复和可移植性问题的修复。

实验发布可在生产环境中使用,但代码当然经过的测试较少,配置参数及其语义可能会在发布之间发生变化。如果您运行实验构建,您更有可能遇到稳定发布不应该有的错误和其他奇怪的问题。另一方面,您可以在使用稳定构建之前访问新功能。如果选择使用实验发布,您应该从源代码构建和安装 Postfix,而不是使用某些软件包管理系统(例如 RPM)。这将使您能够轻松应用任何新发现问题的补丁。

实验发布的版本号表示即将发布的官方版本的编号以及所讨论的实验发布的发布日期。例如,在撰写本文时,当前的官方发布版本是 2.6.3,当前的实验发布版本是 2.7-20090807。

从软件包安装

大多数 Linux 发行版都将 Postfix 作为一个可以轻松安装的软件包。除非您习惯于从源代码构建软件并且必要时调试可能出现的任何构建问题,否则最好使用发行版的软件包。大多数软件包都预先构建了一些额外的功能,否则需要更复杂的构建过程。

由于存在许多不同的打包系统,本书不会涵盖安装 Postfix 软件包的实际过程。请查阅您的软件包管理系统的文档以获取详细信息。

提示

对于允许同时安装多个邮件传输代理的发行版的用户,需要注意一点:如果您安装 Postfix 以替换另一个邮件传输代理,您应该确保以前的软件已从系统中正确删除。因为几乎所有的邮件传输代理都提供sendmail程序,这个文件安装时的名称可能是sendmail.postfix,并且符号链接从sendmail指向sendmail.postfix或者选择为主要邮件传输代理的任何邮件传输代理的sendmail程序。如果该符号链接不指向 Postfix 的sendmail程序,当您尝试发送消息时可能会感到惊讶。

从源代码安装

从原始源代码安装 Postfix 并不是很困难,这样可以让您运行任何您想要的版本,而不仅仅是您的 Linux 发行版的软件包维护者选择的版本。Postfix 源代码可以从主要 Postfix 网站www.postfix.org/download.html上访问的多个镜像中下载。

一旦您在一个合适的目录(例如/usr/local/src)中下载并解压了存档文件,您会注意到 Postfix 构建系统不使用 GNU autotools,因此在解压后的源代码存档的根目录中通常找不到configure脚本。Postfix 构建系统将自动处理此步骤。如果您想要在一些非标准位置安装 Postfix,不用担心,您将有机会稍后设置各种安装目录。

如果您需要启用非标准功能,例如对 MySQL 或 LDAP 查找的支持,您必须通知构建系统,并告知每个功能的库和头文件的位置。有关每个非标准功能的确切说明和详细信息,请查看每个README文件。例如,在README_FILES/MYSQL_README中找到的 MySQL 说明告诉您在构建 Postfix 时运行以下命令以启用 MySQL 支持:

$ make -f Makefile.init makefiles \ 'CCARGS=-DHAS_MYSQL -I/usr/local/mysql/include' \ 'AUXLIBS=-L/usr/local/mysql/lib -lmysqlclient -lz -lm'

调整路径,使其指向系统中的 MySQL 头文件和共享库所在的位置。您必须安装了 MySQL 的开发头文件和库。根据您的 Linux 发行版,这些可能需要单独安装。

如果您需要多个额外功能,您将不得不结合每个README文件中给出的命令。在这样做时要特别注意。所有引号、等号和空格都需要放在准确的位置。CCARGSAUXLIBS变量只能设置一次,因此结合几个配置命令的一般形式是这样的:

$ make -f Makefile.init makefiles \ 'CCARGS=<feature 1 CCARGS setting> <feature 2 CCARGS setting>' \ 'AUXLIBS=<feature 1 AUXLIBS setting> <feature 2 AUXLIBS setting>'

完成后,您可以使用以下命令构建 Postfix:

$ make

构建完成后(希望没有错误),是时候创建一个用户和一些组,供 Postfix 的许多守护程序使用。首先添加两个组——postfixpostdrop。例如,您可以使用您的 Linux 发行版中可能可用的groupadd工具。

$ groupadd postfix 
$ groupadd postdrop

通过检查/etc/group的内容来验证。现在它应该包含类似于这样的行:

postfix:x:123:
postdrop:x:321:

下一步是创建一个名为postfix的用户。这个用户既不需要 shell 访问权限,也不需要有效的主目录。这个新用户的主要组应该是新创建的postfix组。以下是使用useradd工具执行此操作的方法:

$ useradd -c postfix -d /tmp -g postfix -s /bin/false postfix

再次通过检查/etc/passwd的内容来验证:

postfix:x:12345:123:postfix:/tmp:/bin/false

下一步,也是最后一步,是安装您新构建的 Postfix。如果您是在此特定的 Linux 安装中首次安装 Postfix,请运行以下命令:

$ make install

此命令将引导您完成交互式安装过程,在此过程中,您可以选择各种安装目录和文件位置。

如果您正在从以前的版本升级 Postfix,请运行以下命令:

$ make upgrade

好了!Postfix 现在已经安装在您的系统上,很快您就可以开始使用它了。

为了确保 Postfix 在系统启动时启动,需要采取一些额外措施。大多数 Linux 系统都有SysV-style init,因此您需要构建一个init脚本,并在运行级别目录中创建适当的链接。

Postfix 配置

像大多数 UNIX 软件一样,Postfix 从存储在/etc目录或其子目录中的文本文件中读取其配置。Postfix 配置文件通常存储在/etc/postfix中,但您可以配置 Postfix 使用任何其他目录。Postfix 使用两个主要配置文件,master.cfmain.cf,以及您自己设置的任何辅助文件。

对这些文件的任何更改后,必须重新加载 Postfix。可以使用启动 Postfix 的相同程序来执行此操作,可以通过init脚本或您的发行版提供的其他一些服务管理工具来执行。

postfix reload
/etc/init.d/postfix reload
/etc/rc.d/init.d/postfix reload

注意

更改后需要重新启动 Postfix

如果更改inet_interfaces参数,则仅重新加载是不够的。必须停止并重新启动 Postfix 才能使更改生效。对于 Postfix 2.2 中引入的inet_protocols参数也是如此。

main.cf

您将经常编辑的文件是main.cf。该文件定义了控制 Postfix 守护进程行为的参数。每行的格式如下:

parameter = value

这只是意味着名为parameter的配置参数被赋予内容value。在main.cf中,一个参数只能被指定一次。如果您在main.cf的不同位置错误地给出相同参数不同的内容,Postfix 将使用最后出现的内容。除此之外,在main.cf中列出参数的顺序是无关紧要的。但是,在参数内容中,关键字的顺序可能很重要。例如,以下两个参数设置不一定是等价的:

parameter = A, B
parameter = B, A

如果在main.cf中未指定参数的值,Postfix 将使用默认值。大多数参数的默认值在源代码中是硬编码的,但有些默认值是在构建时确定的,还有一些是在运行时确定的。

main.cf中以#开头的行可以标记为注释。

# These two lines are comments. They can be used to temporarily
# disable parameters, or to explain the configuration.
mydomain = example.com
mydestination = $mydomain, localhost

这个简短的例子还展示了在设置参数值时如何插入另一个参数的当前值;只需直接输入一个美元符号,后面紧跟您希望获得其值的参数的名称。前面代码片段中的最后一行等同于以下内容:

mydestination = example.com, localhost

有时将所有内容放在一行上并不方便。通过以空格开头的方式,您告诉 Postfix 该行是前一行的延续。例如,以下两种方式是等价的:

smtpd_recipient_restrictions = permit_mynetworks, reject
smtpd_recipient_restrictions =
permit_mynetworks,
reject

从 Postfix 2.1 开始,main.cf配置文件的格式在postconf(5)手册页面中有详细说明,该页面还描述了所有可用的配置参数。该手册页面可从www.postfix.org/postconf.5.html在线获取。

postconf程序非常有用,可以用来检查main.cf参数的当前值和默认值。使用一个或多个参数名称作为选项启动程序,它将报告 Postfix 将使用的值。如果使用-d选项,postconf将报告您列出的参数的默认值。

例如,以下是如何比较mydestination的当前值与其默认值:

$ postconf mydestination
mydestination = $mydomain, localhost.$mydomain
$ postconf -d mydestination
mydestination = $myhostname, localhost.$mydomain, localhost

使用这种方法通常比查看main.cf或浏览庞大的手册页面以找到默认值更快。它还揭示了 Postfix 认为参数具有的实际值,使得更容易发现打字错误。

除了显示main.cf配置参数外,postconf程序还可以为您编辑main.cf。如果您想要在脚本中自动化配置更改,这将非常有用。这是通过-e选项完成的,该选项需要接下来的一个或多个参数赋值。

$ postconf relay_domains
relay_domains =
$ postconf -e relay_domains=example.com
$ postconf relay_domains
relay_domains = example.com

master.cf

master.cf文件配置了前面讨论过的 Postfix 主守护程序。对于大多数简单的 Postfix 设置,master.cf根本不需要被修改。

master.cf中的每一行定义了某个程序执行的服务。例如,接收和处理 SMTP 连接的守护程序smtpd是一个服务。将消息传递给本地用户的程序local是另一个服务。除了 Postfix 从一开始就定义的 15 到 20 个服务之外,您还可以添加自己的服务。

master.cf中的第五列控制每个服务是否应在chroot环境中运行。chroot是 UNIX 的一个功能,它改变了文件系统的根目录,使得即使运行中的进程被具有 root 权限的恶意用户入侵,也无法访问新根目录之外的文件。Postfix 的源代码发行版默认完全禁用chroot,但一些 Linux 发行版已启用它。尽管chroot是一个非常有用的安全功能,可以作为额外的安全网,但它会使 Postfix 更难以维护,并且在其余系统没有得到严格保护的情况下几乎没有用处。

在 Postfix 2.2 及更高版本中,master.cf配置文件的格式在master(5)手册页中有文档记录。在早期版本中,大部分信息可以在master.cf文件的注释中找到。

查找表

一些信息无法方便地表示在main.cfmaster.cf中。Postfix 的查找表概念允许将信息存储在外部文件、关系数据库或 LDAP 目录中。

对于 Postfix 来说,查找表是一个将一个字符串(查找键)映射到另一个字符串(查找结果)的抽象实体。数学倾向的人可能会将其视为一个函数或(键,值)元组的集合,程序员可能会将其视为哈希表。基本上,它的功能类似于电话簿;您查找一个名字,然后得到一个电话号码或地址。

Postfix 支持许多不同类型的查找表。其中一些被称为索引,这意味着使用postmap命令将用户编写的输入文件编译为 Postfix 读取的二进制格式。出于性能原因,这样做可以使表包含成千上万甚至数十万条目而不影响性能。这意味着您需要记住在编辑文件后使用postmap

以下表格描述了最重要的查找表类型:

类型 描述
cdb 使用 CDB 库的索引映射类型。对于大量条目非常快速。由 Postfix 2.2 及更高版本支持。
cidr 允许使用 CIDR 表示法查找 IP 地址。由 Postfix 2.1 及更高版本支持。
dbm DBM 是一种经典的 UNIX 索引数据库格式,在 Linux 上也可用,但不建议使用,因为它使用两个文件来表示数据库。这增加了不一致性的风险,因为无法原子更新这两个文件。请改用哈希或 cdb。
hash 这种索引查找表类型可能是最常用的,它利用了 Berkeley DB 库。
ldap LDAP 目录通常用于企业和大学环境中存储用户数据库。Microsoft 的 Active Directory 也可以通过 LDAP 访问,简化了在异构环境中使用 Postfix 的操作。
mysql 支持广为人知的 MySQL 关系数据库引擎,允许您进行几乎任何类型的 SQL 查询。
pcre 允许将查找的字符串与一系列正则表达式进行匹配,第一个匹配的表达式获胜。使用广泛使用的Perl 兼容正则表达式PCRE)库。
pgsql 也支持 PostgreSQL 关系数据库引擎。
proxy 代理类型是一种特殊的查找表类型,用于包装其他查找表。这对于减少并发连接的数量很有用,当查找表从具有高进程计数的服务中使用时。例如,从 SMTP 服务器访问 LDAP 目录可能会导致 LDAP 服务器的最大连接数达到上限,但通过代理查找表访问 LDAP 目录将减少并发性。
regexp 类似于pcre,但不依赖于 PCRE 库。支持的正则表达式语法有限,性能可能不如pcre。如果可能的话,选择pcre而不是regexp
static 这种类型是一个特殊用途的类型,无论查找什么,它总是返回一个给定的字符串。这可以用在 Postfix 期望查找表引用而不是固定字符串的情况下,但您确实想要指定一个固定字符串时使用。

您可以为任何目的使用任何类型的查找表;Postfix 不会强加任何限制,除了安全考虑要求在某些情况下禁用正则表达式表的某些功能。也就是说,并非所有查找表类型都适合用于每个目的。

Postfix 始终支持许多查找表类型,但其中一些是可选的,并且需要支持才能编译到 Postfix 中。许多 Linux 供应商提供了额外的软件包,您可以安装以获取,例如,LDAP 支持。要找出您的 Postfix 安装支持哪些查找表类型,请使用postconf命令。

$ postconf -m
static
cidr
nis
regexp
environ
proxy
btree
unix
hash
pcre
ldap
sdbm

在大多数情况下,简单的索引查找表类型将是最方便的。索引查找表只是一个文本文件,您可以使用您喜欢的文本编辑器进行编辑。每行的第一部分,直到第一个空格或制表符,将被视为查找键,而行的其余部分将被视为相应的值。

key value

索引查找表类型的一个可能的缺点是,当您更新表时,您必须记得运行postmap。更新带有postmap的索引文件后,您不必重新加载或重新启动 Postfix。Postfix 将自行发现更新的文件,并根据需要重新启动其守护进程。

查找表的主题本身就可以填满一整章,因此本节只是简单涉及。我们将在本章的后面的一些地方使用查找表,例如,当我们设置垃圾邮件控制策略时。

有关查找表的更详细讨论和所有可用查找表类型的列表,请参阅DATABASE_READMEwww.postfix.org/DATABASE_README.html)和记录一些更复杂的查找表类型使用的手册页面。

启动 Postfix。

既然您已经安装了 Postfix,让我们进行一些基本配置更改,启动它,并进行测试。如果您从软件包中安装了 Postfix,您可能已经回答了一些配置问题,并已经启动了 Postfix。

域名和主机名

在启动 Postfix 之前,让我们在main.cf中审查一些基本设置。首先是您的域名和邮件主机的名称。mydomain参数应设置为您的主要互联网域。如果您运行 Example Inc.,拥有域http://www.example.com/,以下设置是合理的:

mydomain = example.com

mydomain的值将影响 Postfix 如何转换未完全合格的主机名。这意味着在发件人和收件人地址等位置遇到的所有裸主机名都将用该域进行修饰——在这种情况下,诸如jeeves的主机名将变成jeeves.example.com。我们还将在其他参数中使用$parameter符号提到mydomain。请注意,通过将append_dot_mydomain参数设置为NO可以禁用附加mydomain的功能,一些 Linux 发行版默认进行此修改。通常情况下,该值应保留为YES

与之相关的一个参数是myhostname,它偶然告诉 Postfix 机器的主机名。主机名在其他情况下用作默认值,当 Postfix SMTP 服务器向客户端问候时以及 SMTP 客户端向服务器问好时。Postfix 通常能够自行确定这一点,但有时您可能想要覆盖这一点。使用postconf命令查看当前值是否合适。

$ postconf myhostname
myhostname = jeeves

是的,这看起来不错。请注意,此主机名未完全合格,因此各处使用的实际主机名将包括mydomain

mydomain相关的一个参数是myorigin。该参数指定应该用于修饰没有域部分的电子邮件地址的域。这可能看起来非常不规则,但实际上相当常见。使用sendmail程序提交的消息将默认以当前用户名作为发件人地址,因为用户名没有域,所以在消息传递到任何地方之前,用户名将在myorigin之前进行修饰。默认情况下,myorigin设置为与myhostname相同的值。

$ postconf -d myorigin
myorigin = $myhostname

这应该没问题,但您可能希望将其设置为mydomain

myorigin = $mydomain

我们将要关注的下一个参数是mydestination。该参数非常重要,因为它告诉 Postfix 哪些域被视为本地域,也就是说,哪些域应该传递到此计算机上的 UNIX 帐户。与mydomainmyorigin不同,mydestination可以包含由空格或逗号分隔的多个域。通过在此处列出example.com,Postfix 将接受发送到joe@example.com的消息并将其传递给 UNIX 用户“joe”。

本地域的一个重要特性是它们都被视为相等。如果example.comexample.net都在mydestination中列出,那么joe@example.com将等同于joe@example.net。如果您需要其他用户不相等的域,即joe@example.comjoe@example.net应该导致不同的邮箱,您需要实现虚拟别名域,该域在虚拟别名域部分中有描述。

回到 Example Inc.,您将希望在mydestination中列出example.com作为您的主要域。旧域example.net也应该在一段时间内有效,因此也应该包括在内。此外,明智的做法是在mydestination中列出myhostname的值,并确保发送到localhost的邮件能够正确传递。这就得到了 Example Inc.的本地域的完整列表:

mydestination = $mydomain, example.net, $myhostname, localhost.$mydomain

那么为什么是localhost.$mydomain而不只是localhost,如果我们希望将root@localhost的消息传递到本地?请记住,mydomain用于修饰所有未完全合格的主机名(有人可能会争辩说localhost实际上已经是一个完全合格的主机名,但 Postfix 并没有为该主机名做特殊处理)。地址root@localhost将被重写为root@localhost.example.com,因此localhost.example.com是我们想要列在mydestination中的内容。

两个非常重要的 Postfix 参数mynetworksmynetworks_style控制着允许哪些主机将您的服务器用作中继。设置这些参数不正确可能会导致您的服务器被垃圾邮件发送者等滥用,因此很重要确保设置正确。默认情况下,将允许直接连接到您的服务器的子网上的所有主机访问。在大多数情况下,这应该是安全的。这些参数和其他允许中继访问的方法将在第五章中深入讨论。

通过 ISP 间接邮件传递

一些互联网服务提供商(ISP)不允许他们的客户通过标准的 SMTP 端口(25)直接访问远程邮件服务器。相反,他们提供一个中继服务器,所有出站消息都必须通过该服务器。这种政策在家庭电缆或 DSL 连接中很常见,但是一些提供商对商业级连接也有相同的政策。如果是这种情况,您需要配置 Postfix 通过 ISP 的中继服务器间接地传递所有出站消息。

这是使用包含中继服务器的主机名或 IP 地址的relayhost参数完成的。允许以下形式:

relayhost = example.com
relayhost = [mail.example.com]
relayhost = [1.2.3.4]

第一种形式将导致 Postfix 对主机名执行 MX 查找,就像对正常消息传递一样。在第二个示例中将主机名括在方括号中会抑制 MX 查找。当指定 IP 地址时,第三种情况也需要使用方括号。

可选地,主机名或地址后面可以跟着:port来指定替代的 TCP 端口。请注意,您不能指定多个主机名或地址以实现备用或负载平衡行为。如果在正常中继服务器不可用时需要备用主机,请查看fallback_relay参数。有关其他参数的其他信息,请参阅其他有用的配置参数部分。

选择网络接口

inet_interfaces参数决定了 Postfix 将用于监听新连接和发送消息的网络接口。如果您有多个网络接口,并且不希望 Postfix 使用所有这些接口,您可以调整此参数以列出您希望 Postfix 使用的接口的地址或主机名。

一些 Linux 发行版默认将inet_interfaces设置为localhost,这意味着 Postfix 只会在环回接口上监听。这对工作站至少有些意义,但显然对需要从外部主机接收消息的服务器来说完全无法使用。如果您的 Linux 发行版的 Postfix 打包具有此功能,只需从main.cf中删除或注释inet_interfaces行以禁用它。然后 Postfix 将使用all的默认值,这当然意味着应该使用所有接口。

注意

更改inet_interfaces需要重新启动 Postfix。重新加载是不够的。

选择本地投递的邮箱格式

默认情况下,Postfix 将本地消息(发送到mydestination中列出的域的消息)以mbox格式传递到文件中。mbox邮箱格式将邮箱中的所有消息存储在单个文本文件中。这些文件以用户命名,并放入mail_spool_directory指定的目录中(通常为/var/mail/var/spool/mail)。如果用户希望有额外的邮箱来存储消息,这些文件将存储在用户的主目录中的某个位置(通常为$HOME/mail$HOME/Mail)。

mbox格式有一些缺陷,使其相当不理想。单文件格式使得删除消息变得昂贵,因为除非删除的消息是最后一条消息,否则整个文件必须完全重写,此时文件可以被截断。当多个进程需要同时访问同一个邮箱时,mbox也会出现障碍,当用户使用 POP 服务器检索和删除消息时,同时新邮件正在投递时会发生这种情况。这要求使用某种排他性锁定方法来避免可能损坏文件的并发访问。如果所有软件在同一台机器上运行,访问相同的本地文件系统,并且就使用哪种锁定方法达成一致,这样的锁定并不是一个大问题,但如果需要通过网络访问邮箱,比如通过 NFS 这样的网络文件系统,可靠的文件锁定可能会成为一个问题。最后,如果与磁盘配额一起使用,mbox会出现问题。当邮箱被重写时,它将使用原始存储的两倍。

为了避免这些问题,软件作者 D.J. Bernstein 设计了maildir格式的邮箱,例如qmaildjbdns。顾名思义,maildir使用目录和每个消息一个文件。删除消息总是非常快的,但另一方面,扫描邮箱并生成所有消息列表可能需要更长时间,因为所有消息文件都必须被打开和读取。maildir在 NFS 上使用是安全的。在maildir投递格式中,用户的收件箱通常位于$HOME/Maildir中。

要配置 Postfix 将新邮件投递给本地用户的$HOME/Maildir,请设置home_mailbox参数如下:

home_mailbox = Maildir/

请注意行尾的斜杠;这很重要!Postfix 遵循许多其他程序使用的惯例,即以斜杠结尾的邮箱位置表示maildir。如果省略斜杠,Postfix 将尝试将邮件投递到mbox文件$HOME/Maildir

home_mailbox参数仅在 Postfix 自行进行投递时对本地域有效。如果投递由其他投递代理(如 Procmail 或 Maildrop)进行,您必须为maildir投递配置该软件。

本书的其余部分假定您已选择maildir投递。稍后将介绍的 IMAP/POP 服务器 Courier IMAP 根本不支持mbox格式。在mboxmaildir之间转换邮箱并不困难,因此如果以后想要切换格式,那也不是问题。

错误报告

最后一步是确保 Postfix 和世界各地的真实用户可以通知您作为 postmaster 的错误情况。互联网标准要求所有域都有一个 postmaster 地址,但您不需要创建具有该名称的帐户。相反,您可以使用 Postfix 的别名功能将发送到 postmaster 地址的消息重定向到您自己和管理邮件系统的其他人。此外,您还应该将消息重定向到 root 帐户。

本地别名将在本地别名部分中更详细地讨论,但现在应该立即完成此步骤,因此我们将快速浏览一下。为了使 Postfix 重定向 root 的消息并接受发送到 postmaster 的消息(即使不存在这样的用户帐户),必须修改本地别名表。配置参数alias_maps控制定义此类映射的查找表的位置:

$ postconf alias_maps
alias_maps = hash:/etc/aliases

在这个特定的系统上,本地别名存储在文件/etc/aliases中。编辑该文件,使其包含类似于以下两行的内容:

postmaster: root
root: jack, jill

这意味着发送到 postmaster 的消息将被发送到 root 用户,发送到 root 用户的消息将被重定向到用户"jack"和"jill"。保存文件并运行newaliases命令,以便 Postfix 可以应用对文件的更改。

注意

请注意,别名查找是递归的——Postfix 在postmaster查找成功后不会停止,它会继续查找root,最后是jackjill。如果jackjill没有别名条目,那么 Postfix 将停止查找递归。

Postfix 将向邮件管理员报告的问题类型可以通过notify_classes参数进行配置。默认情况下,只会报告资源问题,如磁盘空间不足问题和软件问题,但您可以配置 Postfix 以报告更多类型的问题。例如,您可能还想了解有关 SMTP 协议违规的信息:

notify_classes = resource, software, protocol

当 Postfix 报告问题时,会包括 SMTP 会话的转录。这可以是有价值的调试辅助工具。

选择更详尽的错误报告而不是简洁的报告。如果您收到太多错误报告,请查看是否可以使用投递代理或邮件客户端的过滤功能来删除您不感兴趣的错误报告。由糟糕编写的垃圾邮件软件生成的入站垃圾邮件的协议违规通常可以被忽略,但如果您自己的计算机行为不端,您会想知道这一点。

其他有用的配置参数

除了到目前为止涵盖的配置参数外,还将提到一些其他可能有用的参数。您很可能会使用它们的默认值。如果您想了解更多关于它们的信息,请查阅随您版本或 Postfix 一起提供的文档,或在线阅读文档www.postfix.org/documentation.html

参数 描述
always_bcc 将每个消息的副本发送给指定的收件人。这可用于电子邮件归档。如果您需要更精细的控制哪些消息被复制,请查看sender_bcc_mapsrecipient_bcc_maps。后两个参数要求 Postfix 2.1 或更高版本。
defer_transports 包含传输(投递代理)的名称,其投递应该被暂时推迟。这允许您暂停本地消息投递,如果家目录的文件系统损坏或不可用,但系统的其余部分正常工作。
delay_warning_time 默认情况下,Postfix 在消息无法投递一段时间后不会发送警告。将此参数设置为特定的持续时间,例如5h表示五小时,将导致 Postfix 为每个已经无法投递的消息发送一条警告消息。但要注意:您的用户可能无法正确解释此警告消息。尽管 Postfix 明确表示这只是一个警告,消息不需要重新发送,但许多用户不理解这一点,仍然重新发送延迟的消息。
mailbox_size_limit 此参数控制本地邮箱的最大大小或使用maildir邮箱时消息的最大大小。如今,默认值 50 MB 可能太低,特别是如果您正在使用默认的mbox格式进行邮箱存储。
maximal_queue_lifetime 指定 Postfix 在将消息的投递失败重试返回给发件人之前的时间。默认值为五天是合理的,不应该没有充分理由而更改。从 Postfix 2.1 开始,还有bounce_queue_lifetime,它是相同的东西,但用于带有空发件人地址的退信消息。
message_size_limit 此参数控制消息的最大大小。默认值为 10 MB 是合理的(邮件不是大文件的最佳传输方法),但可能需要调整。请记住,消息仅使用 7 位发送,因此如果您想允许 20 MB 的二进制文件,您必须增加约 35%以补偿文件的 7 位编码的开销。
proxy_interfaces 如果您的服务器通过代理或 NAT 设备连接到互联网,以至于 Postfix 无法确定可以用于到达服务器的所有网络地址,请将这些地址添加到此参数中。

启动 Postfix 并发送第一条消息

有了这些设置,现在是时候启动 Postfix 了。使用以下 Postfix 命令来执行此操作:

$ postfix start
postfix/postfix-script: starting the Postfix mail system

要验证 Postfix 是否正在运行,请查看日志文件。Postfix 通过标准的syslog接口记录日志,日志文件的确切位置取决于syslog守护程序的配置。邮件日志通常被命名为/var/log/maillog/var/log/mail.info或类似的名称。您的syslog守护程序的配置,通常位于/etc/syslog.conf中,包含了详细信息。在启动 Postfix 后,您将在邮件日志的末尾找到以下内容:

Jan 3 21:03:28 jeeves postfix/postfix-script: starting the Postfix mail system
Jan 3 21:03:29 jeeves postfix/master[22429]: daemon started -- version 2.1.5

Postfix 现在已准备好接收和传递消息。要尝试它,请使用您喜欢的邮件客户端并向自己发送一封测试邮件。如果您的邮件客户端使用 SMTP,请记得重新配置它以使用您的服务器。

如果您在尝试发送测试消息时从邮件客户端收到错误消息,请再次阅读您的日志。它是否显示来自运行邮件客户端的主机的连接的任何痕迹?如果是,是否记录了任何错误消息?要获取有关如何调试 Postfix 问题的提示,请参阅调试 Postfix 问题部分。

一旦您成功发送了消息,您还需要检查它是否被正确传递。由于您尚未配置 POP 或 IMAP 服务器,因此这条路不可行。但是,如果您在服务器上安装了直接从文件系统读取邮件的邮件客户端(如 mail、Pine、Mutt 等),只要您的邮件客户端配置为在与 Postfix 传递消息相同的位置查找新消息,事情应该会顺利进行。如果您选择了maildir交付,那么您的邮件客户端的默认设置可能行不通。

无论如何,直接从文件系统读取邮箱始终是一个选择。对于普通的mbox交付,邮箱文件与用户名称相同,并位于mail_spool_directory配置参数指向的目录中。对于maildir交付,消息通常会在$HOME/Maildir/new目录中的自己的文件中找到。

如果一切顺利,消息将被传递到预期的位置。无论您选择哪种交付方式,请确保您知道传递的消息最终会到哪里。当您需要调试传递问题时,这种知识将非常有价值。

停止垃圾邮件和其他不需要的消息

本节将讨论 Postfix 提供的各种方法,以帮助阻止不需要的消息。垃圾邮件或未经请求的商业电子邮件可能是电子邮件服务器管理员面临的最大问题,但也可能存在其他类型的不想接收的消息。

Postfix 本身不能完全阻止所有垃圾邮件,但它可以捕捉到许多垃圾邮件。对于一些人来说,这可能是足够的,但如果您需要对抗大量的垃圾邮件,您可能需要像 SpamAssassin 这样的工具,该工具在第八章中有描述。即使您使用 SpamAssassin,Postfix 自己的轻量级方法也可以帮助减少服务器的负载,拒绝消息甚至在它们到达 SpamAssassin 之前。

Postfix 的反垃圾邮件方法:概述

没有万能的方法可以阻止所有垃圾邮件,但 Postfix 提供了许多方法,可以帮助解决这个问题:

  • SMTP 限制: SMTP 限制允许您定义控制 Postfix 是否接受消息的规则。这些规则不能考虑消息的内容,只能考虑信封信息。SMTP 限制不仅仅是阻止垃圾邮件的工具,还是定义邮件系统使用政策的一般方法。

  • DNS 阻止列表:DNS 阻止列表是全球发布的包含已知垃圾邮件发送者和其他可能的垃圾邮件来源的 IP 地址的阻止列表。Postfix 允许您使用此信息来拒绝消息。

  • 匹配头表达式:可以将头字段和消息正文与正则表达式进行匹配,从而拒绝某些类型的电子邮件。

  • 队列之后的内容过滤:在 Postfix 接受消息后,不会立即将其传递到目的地。相反,它将被传递到一个内容过滤器,该过滤器可以对消息进行任何操作 - 删除它,扫描病毒,剥离不需要的附件等。内容过滤器有责任将消息重新提交到 Postfix,然后将其视为任何其他消息。

  • 在队列之前的内容过滤:在队列之后的内容过滤的缺点是,Postfix 在将消息发送到内容过滤器之前始终接受消息。这意味着 Postfix 无法根据内容过滤器的判决拒绝消息。在 SMTP 会话期间,队列之前的内容过滤器接收消息并可以选择拒绝它们。由于每个打开的 SMTP 会话都需要一个队列之前的内容过滤器连接,这种类型的内容过滤器对于高流量站点来说更难扩展,并且需要额外的容量来处理流量突发。此功能要求 Postfix 2.1 或更高版本。

  • Milters:从 Postfix 2.3 开始,支持用于电子邮件内容检查的 Milter 插件协议。Milters 是在sendmail邮件传输代理中引入的,有许多可用的 Milters 用于垃圾邮件保护,防病毒检查,消息真实性和签名,例如符合 DKIM 标准。第三方 Milters 可以从www.milter.org/milters.下载。

  • 访问策略委托:如果 SMTP 限制不够富有表现力,您可以构建自己的访问策略服务器,Postfix 可以在每个 SMTP 会话期间联系该服务器。使用此工具,您可以强制执行几乎任何您想要的专门策略,只要该策略可以通过查看消息信封来执行。访问策略服务器将不会提供任何消息内容。Postfix 附带了一个非常简单的策略守护程序,用于实现灰名单,但其他人制作了几个其他策略守护程序。这些守护程序和其他 Postfix 附加软件的链接可以在www.postfix.org/addon.html找到。

理解 SMTP 限制

Postfix 有一个简单但富有表现力的符号来定义将应用于通过 SMTP 到达的消息的规则。例如,您可以表达一个拒绝从某些网络发送的消息的策略,从声称使用特定主机名的客户端发送的消息,或者没有在 DNS 中有反向记录的客户端,除非它们是您自己的客户端。

Postfix 定义了许多配置参数,每个参数都可以包含一系列限制。每个限制列表可能包含零个或多个限制,每个限制在评估时可能会返回或不返回结果。与 Postfix 的其他一些地方一样,“第一个匹配获胜”的原则也适用于这里。这意味着限制按照它们指定的顺序进行评估,并且返回结果的第一个限制会终止当前限制列表的评估。

限制列表在 SMTP 会话期间进行评估。以下表格包含 Postfix 使用的限制列表,并显示它们在 SMTP 会话的哪个阶段进行评估:

参数 评估点
smtpd_client_restrictions 直接连接时。
smtpd_data_restrictions 当客户端发送DATA命令时。
smtpd_end_of_data_restrictions 当客户端发送完整消息时。此限制列表适用于 Postfix 2.2 及更高版本。
smtpd_etrn_restrictions 当客户端发送了ETRN命令时。这个命令在正常的 SMTP 会话中不会被使用。
smtpd_helo_restrictions 当客户端发送了带有HELOEHLO的问候语时。
smtpd_recipient_restrictions 当客户端发送了带有RCPT TO的收件人地址时。
smtpd_sender_restrictions 当客户端发送了带有MAIL FROM的发件人地址时。

smtpd_delay_reject参数的默认值是yes,这意味着所有拒绝都将推迟到RCPT TO之后。这样做的原因是,一些客户端软件不喜欢在RCPT TO之前被拒绝,因此它们会断开连接并重试。另一个很好的原因是,推迟的拒绝给了 Postfix 记录更多信息的机会。这使得管理员更容易确定消息是否被拒绝,尽管不应该被拒绝。

一个常见的误解是,只有收件人地址的限制可以放在smtpd_recipient_restrictions中,只有发件人地址的限制可以放在smtpd_sender_restrictions中,等等,但由于smtpd_delay_reject的默认值,这并不正确。限制列表的名称只表示在 SMTP 会话的哪个阶段将应用列出的限制。

让我们探索 Postfix 默认施加的限制。我们可以使用postconf命令来检查最常用的限制列表的默认值。

$ postconf -d smtpd_client_restrictions smtpd_helo_restrictions \
smtpd_sender_restrictions smtpd_recipient_restrictions
smtpd_client_restrictions =
smtpd_helo_restrictions =
smtpd_sender_restrictions =
smtpd_recipient_restrictions = permit_mynetworks, reject_unauth_destination

这告诉我们,默认情况下,Postfix 没有任何客户端、HELO或发件人限制。但是,它确实有两个收件人限制。第一个permit_mynetworks,如果连接的客户端在mynetworks指定的网络中,则允许当前收件人。这个限制给了您自己的客户端中继访问权限。如果连接的客户端不在mynetworks中,将评估限制列表中的下一项。reject_unauth_destination将拒绝那些其域不是 Postfix 接受邮件的域之一的收件人。换句话说,reject_unauth_destination拒绝中继尝试。如果这里没有发生拒绝,那么限制列表的末尾已经到达。如果发生这种情况,Postfix 将接受消息。

一个限制列表中的permit结果不会导致整个消息被接受。只有同一列表中剩余的限制将被绕过。对于返回reject的限制,这一点并不成立——该结果始终是终端的,并停止对所有限制列表的评估。

有 50 多个标准的 SMTP 限制可供选择,在这里没有足够的空间来涵盖它们。这个表将呈现一些有用的限制。其他限制将在本章后面介绍。

限制 描述
permit_inet_interfaces 如果连接的客户端位于inet_interfaces中列出的网络中,则允许。这通常涵盖了运行 Postfix 的服务器连接到的所有网络。
permit_mynetworks 如果连接的客户端在mynetworks中列出,则允许。
permit_sasl_authenticated 如果连接的客户端已经进行了身份验证,则允许。(SMTP 身份验证在第五章中介绍。)
reject 无条件拒绝请求。
reject_invalid_hostname 如果客户端提供的HELO/EHLO主机名的语法不正确,则拒绝。
reject_non_fqdn_hostname 如果客户端提供的HELO/EHLO主机名不是完全合格的域名,则拒绝。
reject_non_fqdn_recipient 如果收件人地址的域部分不是完全合格的域名,则拒绝。
reject_non_fqdn_sender 如果发件人地址的域部分不是完全合格的域名,则拒绝。
reject_unauth_destination 拒绝请求,除非收件人域是 Postfix 服务器托管的域之一,或者出于某种原因,将接受邮件。
reject_unknown_client_hostname 如果无法确定连接的客户端主机名,则拒绝。如果满足以下任一条件,则会发生这种情况:a) 客户端的 IP 地址无法解析为主机名,即 PTR 查找失败。b) 结果主机名的 A 记录查找失败。c) 从 A 记录查找中获得的 IP 地址均不匹配输入 IP 地址。在 Postfix 2.3 之前,此限制被命名为reject_unknown_client
reject_unknown_recipient_domain 如果收件人地址的域部分在 DNS 中没有 A 记录或 MX 记录,则拒绝。
reject_unknown_reverse_client_hostname 如果连接的客户端 IP 地址无法解析为主机名,即 PTR 查找未返回结果,则拒绝。此功能在 Postfix 2.3 及更高版本中可用。
reject_unknown_sender_domain 如果发件人地址的域部分在 DNS 中没有 A 记录或 MX 记录,则拒绝。
reject_unlisted_recipient 如果收件人地址的域部分是由 Postfix 托管的域,且完整地址不是有效的收件人地址,则拒绝。默认情况下,此限制隐式地在smtpd_recipient_restrictions末尾进行评估。此行为由smtpd_reject_unlisted_recipient参数控制。通过使用reject_unlisted_recipient,您可以更早地实施限制。此限制在 Postfix 2.1 及更高版本中可用。之前的 Postfix 版本可以使用check_recipient_maps参数。
reject_unlisted_sender 如果发件人地址的域部分是由 Postfix 托管的域,并且完整地址不可接受为收件人地址,则拒绝。此功能背后的想法是没有理由接受已知不正确的发件人地址的消息。此限制在 Postfix 2.1 及更高版本中可用。另请参阅smtpd_reject_unlisted_sender参数。

访问映射

除了已经讨论的限制之外,Postfix 还定义了许多在访问映射中查找信息的限制。访问映射是一个查找表,其内容影响邮件是否会被接受。限制的名称控制了用作查找键的信息。

例如,check_client_access限制在查找表中查找客户端 IP 地址和主机名,允许您禁止已知发送垃圾邮件的某些客户端。除了限制名称,您还需要说明查找表的类型和名称。

smtpd_client_restrictions =
check_client_access hash:/etc/postfix/client_access

尽管不是详尽的列表,但以下是使用访问映射的最重要的限制:

限制名称 查找键
check_client_access 客户端 IP 地址和主机名。
check_sender_access 发件人地址。
check_sender_mx_access 发件人域的邮件交换主机名(MX 查找的结果)。此功能在 Postfix 2.1 中添加。
check_sender_ns_access 发件人域的名称服务器主机名(NS 查找的结果)。此功能在 Postfix 2.1 中添加。
check_recipient_access 收件人地址。
check_helo_access HELO/EHLO主机名。

除了regexppcre之外的所有查找表类型,Postfix 对于每个限制进行多次查找,略有不同,取决于所查找的数据类型(例如电子邮件地址或主机名)。这样可以进行不精确的通配符匹配,例如匹配域中的所有电子邮件地址。

对于check_client_access,Postfix 会分别查找客户端 IP 地址、客户端主机名和 IP 地址的部分,后者使得可以匹配整个 A、B 或 C 类网络(为了更好的粒度和完整的 CIDR 表示法,请使用cidr查找表类型)。对于具有地址 1.2.3.4 和主机名mail.example.com的客户端,按以下顺序尝试以下查找键:

  • mail.example.com

  • example.com

  • com

  • 1.2.3.4

  • 1.2.3

  • 1.2

  • 1

项目 2 和 3 假设使用parent_domain_matches_subdomains参数的默认值。Postfix 作者已指出这种行为可能会在将来发生变化。

对于查找键为电子邮件地址的限制,例如check_sender_access,Postfix 首先查找整个电子邮件地址,然后是域部分,然后是本地部分和@。然后,电子邮件地址user@example.com的完整查找列表变为:

  1. user@example.com

  2. example.com

  3. com

  4. user@

再次强调,项目 2 和 3 假设parent_domain_matches_subdomains的默认值。

出于简洁原因,省略了 IPv6 地址和包含接收者分隔符的电子邮件地址的查找。

对于给定查找键,识别以下结果(这还不是详尽的列表)。

Result Description
OK 允许请求。
REJECT [optional text] 使用永久错误代码拒绝请求,并附带指定的错误消息或通用消息。
DUNNO 假装查找键未找到,并且不继续进行其他查找键。例如,如果查找user@example.com返回DUNNO,Postfix 将不会像通常那样查找example.comuser@
DISCARD [optional text] 如果消息最终被接受,它将被丢弃而不会被传递。
HOLD [optional text] 将消息放在暂留队列中。被暂留的消息将不会被传递,并且可以使用postcat程序进行检查,随后释放以进行传递或删除。这可以作为隔离可能不需要的消息的简单方法。
REDIRECT email address 丢弃所有当前消息的收件人,并将消息仅发送到指定的地址。此功能在 Postfix 2.1 中添加。
PREPEND header: text 向消息添加额外的标题。此功能在 Postfix 2.1 中添加。
WARN [optional text] 在日志文件中放置警告消息。此功能在 Postfix 2.1 中添加。
restriction, restriction, … 应用一个或多个限制并使用它们的结果。除非使用限制类,否则此处只允许不涉及任何查找表的简单限制。这本书没有涵盖这些内容,但您可以在www.postfix.org/RESTRICTION_CLASS_README.html上阅读有关它们的RESTRICTION_CLASS_README文档。

访问映射查找键和可能的结果值的完整文档可以在access(5)手册页或www.postfix.org/access.5.html上找到。

访问映射示例

以下是一系列示例,其中包含访问映射,讨论它们如何可以单独使用以及如何与其他限制一起形成相当表达的策略:

smtpd_client_restrictions =
check_client_access hash:/etc/postfix/client_access

在第一个示例中,查找将针对hash-type查找表/etc/postfix/client_access进行。此文件不是由 Postfix 创建的,您可以给它任何名称。我们回顾了查找表部分,hash-type查找表只是文本文件,应该使用postmap命令构建二进制文件(在本例中文件扩展名为.db),每当源文件更改时。

postmap hash:/etc/postfix/client_access

以下是一个client_access文件的示例:

# Block RFC 1918 networks
10 REJECT RFC 1918 address not allowed here
192.168 REJECT RFC 1918 address not allowed here
# Known spammers
12.34.56.78 REJECT
evil-spammer.example.com REJECT

这意味着什么?前两行非注释行用于拒绝似乎是从10.0.0.0/8192.168.0.0/16网络连接的客户端。这些都不是有效的互联网地址,因此没有合法的客户端会从这些地址中的任何一个连接。拒绝将使用错误消息RFC 1918 地址不允许在此处。如果您自己的客户端具有此类 RFC 1918 地址,则需要在check_client_access之前放置permit_mynetworks限制。否则,您将拒绝自己的客户端。

smtpd_client_restrictions =
permit_mynetworks,
check_client_access hash:/etc/postfix/client_access

索引访问映射支持在八位字节边界上进行网络块匹配,但不支持 CIDR 表示法(如10.0.0.0/8)。如果您需要使用 CIDR 表示法指定网络块,请考虑在 Postfix 2.1 及更高版本中提供的CIDR查找表类型。较早的版本可以使用 Rahul Dhesi 的cidr2access脚本(www.rahul.net/dhesi/software/cidr2access)来将 CIDR 块扩展为适用于索引访问映射的表示法。

注意评论是如何用来解释为什么和何时添加条目的。如果有多个人在维护文件,这可能是有价值的。

最后几行用于匹配一对臭名昭著的垃圾邮件发送者(当然是虚构的),并演示这里接受完整的 IP 地址和主机名。这些拒绝将使用通用错误消息进行。

以下是另一个例子:

smtpd_sender_restrictions =
check_sender_access hash:/etc/postfix/sender_access

/etc/postfix/sender_access的内容:

hotmail.com reject_unknown_client
example.com permit_mynetworks, reject

如果有人尝试发送一个带有hotmail.com发件人地址的消息,试图传递消息的客户端将受到reject_unknown_client限制的约束,您可能还记得,该限制拒绝没有有效的 IP 地址和主机名之间的映射的客户端。

第二行举例说明了一个有用的策略,允许只有来自您的网络的客户端在发件人地址中使用您的域。

最后,如果您只在您的网络内部使用 Postfix,并且不需要允许其他人连接,以下两个限制将执行此策略:

smtpd_recipient_restrictions = permit_mynetworks, reject

实施新策略

在实施新策略时要小心。Postfix 的一些限制对于一般用途来说太严格,可能会拒绝大量合法的电子邮件。对于您计划实施的每个新限制,要检查消息被拒绝的条件,并尝试找出合法消息满足这些条件的情况。为了帮助您确定限制是否安全使用,可以使用warn_if_reject限制。此限制会影响限制列表中紧随其后的限制,如果紧随其后的限制应该导致拒绝,则将其转换为拒绝警告。拒绝警告会在邮件日志中放置一行,但不会拒绝消息。

例如,您可能希望评估reject_unknown_client限制,因为您已经注意到许多垃圾邮件消息是从没有在 DNS 中有反向指针的客户端接收的,也就是说,从它们的 IP 地址到映射回该 IP 地址的名称之间没有映射。

以下是一种方法:

smtpd_client_restrictions = warn_if_reject reject_unknown_client

这将导致类似于这样的日志消息:

Dec 31 16:39:31 jeeves postfix/smtpd[28478]: NOQUEUE: reject_warning: RCPT from unknown[222.101.15.127]: 450 Client host rejected: cannot find your hostname, [222.101.15.127]; from=<jdoe@example.com> to=<me@example.com> proto=SMTP helo=<222.101.15.127>

这些日志消息包含有关邮件信封的所有已知信息,这应该足够让您决定消息是否合法。几天后,检查您的邮件日志,并尝试确定被拒绝的不需要的消息和被拒绝的合法消息之间的比率是否可接受。

有许多具有良好准确性的垃圾邮件对策,其中一些在本书中有所涵盖。其他将根据垃圾邮件发送者的行为而在未来出现。在发明自己的识别垃圾邮件的方法时要非常小心——从少量垃圾邮件中挑选特征并得出这些特征是良好的垃圾邮件指标的结论是危险的,很可能会导致合法电子邮件的丢失。明智地选择并避免准确性低的方法。不要忘记检查合法的电子邮件,确保它们没有您所关联的垃圾邮件特征。

使用 DNS 黑名单

自 1997 年以来,域名系统DNS)已被用于阻挠垃圾邮件。基于 DNS 的黑名单DNSBL)或实时黑名单RBL),也称为黑名单拦截列表,使用 DNS 发布有关某些客户端或发件人域的信息。当您自己的邮件服务器被客户端联系时,您的服务器可以将客户端的 IP 地址或给定的发件人地址与一个或多个 DNSBL 的域结合起来,并执行 DNS 查找。如果 DNSBL 列出了该地址,查找成功,您的服务器可以选择,例如,拒绝客户端。

例如,假设您已经配置了 Postfix 使用广泛使用的zen.spamhaus.org黑名单。如果连接的客户端具有地址 1.2.3.4,Postfix 将在 DNS 中查找地址4.3.2.1.zen.spamhaus.org的 A 记录。如果存在这样的记录,Postfix 将不接受来自客户端的消息。

Postfix 支持三种类型的 DNSBL 查找——客户端主机地址、客户端主机名和发件人域。每种查找类型都有自己的限制,它们都要求您在限制名称之后指定 DNSBL 域的名称。

DNSBL 类型 语法 描述
客户端主机地址 reject_rbl_client rbl_domain 查找连接客户端的 IP 地址。这是最原始且最常见的 DNSBL 类型。
客户端主机名 reject_rhsbl_client rbl_domain 查找连接客户端的主机名。
发件人地址域 reject_rhsbl_sender rbl_domain 查找给定发件人地址的域。

请随意列出多个 DNSBL 限制。确保您使用与 DNSBL 类型对应的限制——对发件人地址域 DNSBL 使用reject_rbl_client是没有意义的。

以下代码显示了配置 Postfix 使用zen.spamhaus.org标准类型 DNSBL 和dsn.rfc-ignorant.org发件人域 DNSBL 的一种方法:

smtpd_recipient_restrictions =
permit_mynetworks,
reject_unauth_destination,
reject_rbl_client relays.ordb.org,
reject_rhsbl_sender dsn.rfc-ignorant.org

请注意,这些限制是在permit_mynetworksreject_unauth_destination之后列出的。这是因为 DNSBL 查找相对昂贵,对于您自己的客户端或可能被拒绝的客户端进行这样的查找是没有意义的。为了避免不必要的延迟,请确保首先列出阻止最多消息的 DNSBL 限制。

选择 DNS 黑名单

起初,DNSBL 仅列出了开放中继,即接受来自所有客户端发往所有目的地的所有消息的 SMTP 服务器。开放中继曾经是垃圾邮件的主要来源,但近年来情况已经改变。如今,许多垃圾邮件是从无辜和无知的人的被劫持的家用计算机发送的。

不同的黑名单对于列出主机和删除列出的主机有不同的政策。自然而然,黑名单越大,您可能拒绝的合法消息就越多。在开始使用特定的 DNSBL 拒绝消息之前,您应该仔细检查这些政策,并最好在实际拒绝任何消息之前尝试一段时间。warn_if_reject限制可以帮助您进行此操作。

对于一些人效果很好并拒绝大量垃圾邮件但不拒绝合法消息的黑名单,对其他人可能没有多少价值,实际上可能拒绝更多的合法消息而不是垃圾邮件。在选择黑名单时要非常小心,并避免盲目地从其他人那里复制据称很好的 DNSBL 集。谨慎的另一个很好的理由是,DNSBL 有时会因为它们一直受到垃圾邮件发送者的攻击而被迫关闭。这在 2006 年发生在著名的relays.ordb.org DNSBL 上。被关闭的黑名单可能在一段时间后被重新配置为始终指示 IP 地址在黑名单中,也就是说,如果配置为使用该黑名单,您将拒绝所有邮件。

目前,用于reject_rbl_client的可能是最好的通用 DNSBL 是zen.spamhaus.org。误报率,即被错误拒绝的真实电子邮件的比例,可以预期非常低,同时捕获垃圾邮件的准确性保持很高。除非您有特殊需求,否则这可能是您唯一需要使用的 DNSBL。

在实施任何 DNSBL 之前,请确保您知道如何豁免某些客户端或域免受拒绝。迟早会有合法消息被阻止,无论您选择使用哪个 DNSBL。当这种情况发生时,开始查看文档并试图找出可以采取的措施已经太迟了。

解决问题的方法是在 DNSBL 限制之前拥有白名单访问映射。您应该使用哪种类型的访问映射取决于 DNSBL 类型,但在大多数情况下,check_client_access将是合适的,尽管如果您使用reject_rhsbl_sender,则更适合使用check_sender_access

继续上一个例子,这是您可以豁免某些客户端和发件人地址免受后续限制拒绝的操作:

smtpd_recipient_restrictions =
permit_mynetworks,
reject_unauth_destination,
check_client_access hash:/etc/postfix/rbl_client_exceptions,
check_sender_access hash:/etc/postfix/rhsbl_sender_exceptions,
reject_rbl_client zen.spamhaus.org,
reject_rhsbl_sender dsn.rfc-ignorant.org

/etc/postfix/rbl_client_exceptions:中:

# Added 2005-01-10 to avoid blocking legitimate mail. /jdoe
1.2.3.4 OK
example.net OK

/etc/postfix/rhsbl_client_exceptions:中:

mybusinesspartner.com OK

基于内容停止消息

通常,无法在查看其内容之前发现不需要的消息。Postfix 为此提供了一些简单但非常有用的工具。其思想是将消息中的行与您提供的一组正则表达式进行匹配,如果匹配成功,则将执行一个操作。这称为头部检查正文检查,具体取决于正在检查的消息的哪个部分。通常情况下,您使用头部和正文检查来拒绝消息,但消息也可以被丢弃或重定向到另一个收件人。头部和正文检查可以帮助您解决以下问题,所有这些问题将在以下部分中讨论:

  • 对包含禁止文件名附件的消息做出反应

  • 快速停止大规模病毒爆发

  • 自定义记录某些头部字段

  • 删除某些消息头

介绍正则表达式超出了本书的范围。如果您还没有这方面的知识,互联网上有许多正则表达式资源和教程,例如gnosis.cx/publish/programming/regular_expressions.htmlwww.codeproject.com/KB/dotnet/regextutorial.aspx。如果您正在寻找这方面的书籍,Jeffrey E. F. Friedl 的Mastering Regular Expressions(O'Reilly,2006)非常全面。

配置头部和正文检查

main.cf参数用于头部和正文检查body_checks, header_checks, mime_header_checksnested_header_checks,可以包含一个或多个对正则表达式查找表(regexppcre)的引用,在接收消息时将被考虑。从技术上讲,您可以使用任何其他查找表类型,但只有正则表达式表才真正有用。以下参数用于消息的不同部分:

参数 适用于消息的部分
body_checks 每个消息部分的正文。
header_checks 所有非 MIME 顶级标题。

| mime_header_checks | 在任何消息部分中找到的所有 MIME 头。以下标题被认为是 MIME 头:

  • 内容描述

  • 内容分发

  • 内容 ID

  • 内容传输编码

  • 内容类型

  • MIME 版本

|

nested_header_checks 附加到接收消息的消息中的所有非 MIME 消息头。

这意味着对于每个标题行,将针对header_checks中指定的查找表进行查找,消息正文中的每一行将导致针对body_checks中的查找表进行查找,依此类推。

正则表达式查找表的格式与普通索引查找表非常相似。一个很大的区别是它们不是索引的,也不应该通过postmap程序运行。在许多情况下,当守护程序重新启动时,Postfix 将再次读取正则表达式查找表。如果您需要立即更新,您必须重新加载 Postfix。

正则表达式查找表不仅适用于标题和正文检查。它们可以在 Postfix 期望查找表的任何地方使用。

用于标题和正文检查的查找表的右侧可以包含先前描述的许多允许在访问映射中使用的操作,但是一个操作IGNORE仅在这里可用。IGNORE操作只是从消息中删除匹配的行。

例如以下示例中的消息头被包装成多个物理行,将在使用作为查找键之前连接在一起。

Received: by jeeves.example.com (Postfix, from userid 100)
id 2BB044302; Sat, 1 Jan 2005 20:29:43 +0100 (CET)

标题和正文检查示例

现在,让我们具体看看如何使用标题和正文检查。除非另有说明,所有这些示例都适用于regexppcre查找表类型。

许多计算机病毒通过电子邮件传播,其中大多数通过附加到消息的程序或脚本传播。尽管对包含禁止文件名附件的消息做出反应是一种粗糙和不精确的工具,但这是一种简单的方式,即使在消息到达任何防病毒扫描程序之前,也可以处理这些不需要的消息。通过避免大量的开销扫描,您的服务器可以处理更大的病毒爆发。没有完整的可以被禁止的文件名列表,但是只需阻止.exe,.scr,.pif,.bat等几个文件名可能对大多数人来说就足够了。如果您的用户需要发送或接收具有这些文件名扩展名的文件,则可能需要放松政策。要在 Postfix 中实现这一点,您需要认识到附件的文件名位于 Content-Disposition 或 Content-Type 头中。这些是 MIME 头,因此表达式需要放在mime_header_checks中。在这个例子中,消息将被拒绝,并显示指示违规文件名的文本。如果一个合法的邮件被拒绝,发件人希望能够解释错误消息并重新发送邮件。

/^Content-(Disposition|Type).*name\s*=\s*"?(.*\.(
ade|adp|bas|bat|chm|cmd|com|cpl|crt|dll|exe|hlp|hta|
inf|ins|isp|js|jse|lnk|mdb|mde|mdt|mdw|ms[cipt]|nws|
ops|pcd|pif|prf|reg|sc[frt]|sh[bsm]|swf|
vb[esx]?|vxd|ws[cfh]))(\?=)?"?\s*(;|$)/x
REJECT Attachment not allowed. Filename "$2" may not end with ".$3".

请注意除第一行外所有行的缩进。需要将这些行视为单行。查找表在这方面与main.cfmaster.cf配置文件的工作方式相同。/x修饰符将导致所有空格被忽略。这个表达式最初由 Russell Mosemann 构建,后来由 Noel Jones 进一步完善,需要一个pcre查找表,但可以重写表达式以使用regexp

body_checks可以是一个快速阻止大型病毒爆发的有用工具。以前的许多病毒爆发都有带有某些特征的消息,这使得它们很容易被阻止。如果文件名阻止不是一个选项,您可以尝试找到这些消息中独特的行,并构造合适的表达式。

/^Hi! How are you=3F$/ REJECT SirCam virus detected.
/^ I don't bite, weah!$/ REJECT Bagle.H virus detected.

如果您不确定一个表达式是否过于宽泛并捕获了合法消息,您可以使用HOLDWARN而不是REJECTHOLD将暂停消息,允许您检查它们并释放消息或删除消息。WARN将接受消息但记录该事件。

当新病毒刚开始传播且您使用的防病毒软件尚未更新以捕获它时,这种阻止病毒的方法也可能很有用。

WARN操作也可以用于获取某些标题字段的自定义日志记录。

/^Subject: / WARN

header_checks中有这个表达式将导致所有主题头被记录为类似于以下的警告消息:

Jan 2 00:59:51 jeeves postfix/cleanup[6715]: 6F8184302: warning:
header Subject: Re: Lunch? from local; from=<jack@example.com>
to=<jill@example.com>

有时删除某些消息头可能是有用的。例如,一些提供 SMTP 客户端的编程库会向所有发送的消息添加一个 X-Library 头。显然,许多垃圾邮件发送者使用这些库,因此 SpamAssassin 对包含此头的消息给出了相当高的分数。如果您需要使用这样的库,并且无法或不愿意修改源代码以避免首次添加头,Postfix 可以帮助您删除它。这个header_checks表达式将删除通过 Postfix 的所有X-Library头:

/^X-Library: / IGNORE

注意事项

标题和正文检查是检查消息内容的简单而直接的工具。它们对许多事情都很有用,但不要试图过度使用它们来进行通用垃圾邮件打击。许多人尝试不正确地使用这些工具,本书将试图消除一些常见的误解。

标题和正文检查将逐行检查,不会在不同行之间保留任何状态。这意味着您无法拒绝包含一行上的一个不良词语和消息中其他地方的另一个不良词语的消息。不要被正则表达式查找表中允许的if...endif结构所迷惑!您不能以这种方式使用它们:

if /^From: spammer@example\.com/
/^Subject: Best mortgage rates/ REJECT
endif

记住,查找是逐行进行的。显然,以From开头的行不可能以Subject开头。

许多垃圾邮件消息的邮件正文采用Base64编码。由于 Base64 的工作原理,一个单词有许多可能的 Base64 表示。在消息内容被提供给标题和正文检查之前,Postfix 不执行任何解码。

这意味着使用body_checks来阻止包含不良词语的消息并不是普遍有效的。如果body_checks是您打击垃圾邮件的唯一工具,您将每天花费几个小时来维护您的正则表达式,以便捕获当天的垃圾邮件,但您仍然无法获得高准确性。

标题和正文检查适用于所有消息。您无法将某个发件人或某个客户端列入白名单。如果您托管多个域,您可以通过运行多个cleanup守护程序和多个监听不同 IP 地址的smtpd守护程序,或者您可以运行多个 Postfix 实例来为您托管的域使用不同的标题和正文检查。后者意味着您有多个队列目录和多个 Postfix 副本同时运行。这对于一些复杂的设置是必需的,但实际上可以简化使用单个实例可能实现的设置。

您无法使用标题和正文检查来检查某些内容的不存在,因此无法拒绝具有空正文或不包含秘密密码的消息。

body_checks中有大量正则表达式不仅维护起来很麻烦,而且可能严重降低服务器的性能。一个合理的配置不应该需要超过,比如说,10 到 20 个表达式。如果您有太多的表达式,Postfix 的cleanup进程将使用大量的 CPU 时间。

虚拟别名域和本地别名

在本节中,将讨论一些 Postfix 的特性,用于地址重写以允许托管多个域并实现组地址(或分发列表)。

此外,本节将介绍如何使用 Postfix 在 MySQL 数据库中查找信息。练习的目标是使用 MySQL 查找别名查找,但您可以获得的知识将适用于所有其他可能希望与 Postfix 一起使用 MySQL 的情况。假设您具有基本的 SQL 知识,并且能够设置和操作 MySQL 服务器。

虚拟别名域

正如前面所解释的,即使您可以拥有多个本地域(在mydestination中列出的多个域),它们始终是等效的-它们共享单个本地部分命名空间。换句话说,joe@localdomain1.com就是joe@localdomain2.com就是joe@localdomain3.com。显然,这是不够好的。为了托管具有不同本地部分命名空间的多个域,您需要虚拟别名域。

注意

虚拟别名域是一个域,其中每个有效地址映射到一个或多个其他电子邮件地址,可能在其他域中。与地址通常直接映射到 UNIX 系统帐户的本地域相比。joe@virtualdomain1joe@virtualdomain2可能导致完全不同的邮箱。

虚拟别名域有时被称为虚拟域,但为了避免与虚拟邮箱域混淆,有时也被称为虚拟域,将使用完整术语。

为了展示虚拟别名域在 Postfix 中的工作原理,让我们回到 Example Inc.的朋友,看看他们如何通过使用虚拟别名域来增强邮件系统的一些示例。

多个虚拟别名域映射到一个本地域

Example Inc.的董事们现在已经大幅扩大了业务,并希望为他们的分公司设置子域,以避免当不同办公室的两个人共享相同名称时出现名称冲突。对于他们在伦敦、巴黎和柏林的办公室,他们分别希望使用域gb.example.com, fr.example.comde.example.com。他们有一个接收所有消息的单个 Postfix 服务器。

Example Inc 的问题的解决方案是让gb.example.com, fr.example.comde.example.com都成为虚拟别名域。原始的example.com域应保持为本地域。Postfix 在virtual_alias_domains参数中查找虚拟别名域。

virtual_alias_domains = gb.example.com, fr.example.com, de.example.com

确保不要在mydestination中列出任何这些域。下一步是告诉 Postfix 虚拟别名域中的地址映射到example.com域中的哪些地址。这是通过在virtual_alias_maps参数中指定一个或多个查找表来完成的。首先,Example Inc.将只使用一个简单的hash类型查找表。当事情按我们的预期进行时,他们将创建一个等效的配置,该配置在 MySQL 数据库中查找数据。

virtual_alias_maps = hash:/etc/postfix/virtual

现在,Postfix 将使用他们放在/etc/postfix/virtual中的虚拟别名。虚拟别名查找表的格式非常简单;收件人地址是查找键,应将收件人地址重写为的地址/地址是结果。

joe@gb.example.com joe1@example.com
joe@de.example.com joe2@example.com
jane@fr.example.com jane@example.com

编辑/etc/postfix/virtual文件后,必须运行postmap以将文件转换为/etc/postfix/virtual.db

$ postmap /etc/postfix/virtual

虚拟别名查找表的格式在virtual(5)手册页中有描述。

在上面的示例中,所有发送到joe@gb.example.com的消息将最终进入用户"joe1"的邮箱,所有发送到joe@de.example.com的消息将最终进入用户"joe2"的邮箱,所有发送到jane@fr.example.com的消息将最终进入用户"jane"的邮箱。请注意,引入虚拟别名域不会导致原始本地域停止接受消息。

简和我们的两个 Joe 也将收到发送到他们在example.com的实际用户名的消息。(joe1@example.com, joe2@example.com, 和 jane@example.com)。如果这是不希望的,可以使用smtpd_recipient_restrictionscheck_recipient_access来拒绝发送消息给这些收件人的尝试。将限制添加到main.cf中的smtpd_recipient_restrictions设置(如果有的话):

smtpd_recipient_restrictions =
...
check_recipient_access hash:/etc/postfix/recipient_access
...

然后将以下内容放入/etc/postfix/recipient_access并在文件上运行postmap

example.com REJECT Email to this domain prohibited

一个虚拟别名域映射到多个本地域

在之前的设置运行一段时间后,Example Inc.的员工决定要回到所有员工都使用单一域的旧设置。名字冲突可以通过在地址中包含用户的姓氏来解决。他们还希望每个分支办公室都有一个邮件服务器,以避免用户访问邮箱时出现延迟和网络负载。伦敦用户的帐户将驻留在伦敦服务器上,巴黎用户在巴黎服务器上,柏林用户在柏林服务器上。这个问题是一个机会,可以看看使用虚拟别名域的不同方式。

在这个设置中的想法是,example.com将是虚拟域,每个 Postfix 服务器将有自己的本地域。伦敦办公室的服务器将把gb.example.com列为本地域。虚拟别名将用于将example.com地址映射到特定办公室的子域。这种映射可以在主服务器上独占地进行,也可以在每个分支办公室的服务器上进行。拥有单个主服务器会引入在服务器之间同步数据的问题,但这个问题可以通过将数据存储在关系数据库中轻松解决。如何在章节后面的介绍 MySQL 查找部分中讨论如何使用 MySQL 进行别名查找。

要实现这一点,首先从mydestination中删除example.com,然后将其添加到virtual_alias_domains中。这需要在所有服务器上完成。分支办公室服务器——其中一个很容易可以是主服务器——应该在mydestination中列出自己的域(gb.example.com等等)。不要忘记设置 DNS 服务器,以便将消息路由到分支办公室域的服务器。最后,虚拟别名表应该是这样的:

joe.smith@example.com joe1@gb.example.com
joe.schmidt@example.com joe2@de.example.com
jane.doe@example.com jane@fr.example.com

这个问题说明了一个重要的观点;虚拟别名表右侧的地址/地址不一定是本地的。任何域都可以放在那里。当主服务器收到发送到joe.smith@example.com的邮件时,情况就是这样:

  1. Postfix 在virtual_alias_domains中查找example.com是否是虚拟别名域,结果是肯定的。

  2. 接下来,在virtual_alias_maps中查找joe.smith@example.com。查找返回joe1@gb.example.com

  3. 主服务器上的 Postfix 决定gb.example.com不是它托管的域,并使用 DNS 解析消息的目的地,最终将其传递给伦敦分支办公室服务器。

组地址

这个第三个也是最后一个虚拟别名的例子将只是简单地说明虚拟别名表的右侧可能包含多个地址,这些地址可以是其他别名的名称,而不是实际的帐户名称。

all@example.com managers@example.com,finance@example.com
managers@example.com joe.smith@example.com,joe.schmidt@example.com
finance@example.com jane.doe@example.com,jack.black@example.com

在这个例子中,发送到all@example.com的消息将发送给所有管理人员和所有财务人员,这意味着 Joe Smith、Joe Schmidt、Jane Doe 和 Jack Black。

也许不希望任何人发送消息到大型分发列表。幸运的是,您可以使用 Postfix 的 SMTP 限制来限制对敏感地址的访问。如果您只希望您自己的用户(mynetworks内的客户)被允许发送消息到一个地址,解决方案非常简单。在main.cf中,使用check_recipient_access限制来禁止访问该地址,但使用permit_mynetworks来豁免您自己的客户。

smtpd_recipient_restrictions =
permit_mynetworks,
check_recipient_access hash:/etc/postfix/restricted_recipients,
reject_unauth_destination

如果您已经在main.cf中使用smtpd_recipient_restrictions,则必须修改该参数,而不仅仅是添加上面示例中列出的内容。关键特性是在permit_mynetworks限制之后列出check_recipient_access限制。

/etc/postfix/restricted_recipients的内容:

all@example.com REJECT

在更复杂的情况下,比如当您想要禁止所有寄件人地址,除了少数寄件人地址或客户端,您可能需要使用 Postfix 的限制类功能。它在RESTRICTION_CLASS_README中有描述(www.postfix.org/RESTRICTION_CLASS_README.html),以及针对这种特殊情况的示例。

引入 MySQL 查找

如果您的组织很大,使用别名的平面文本文件可能会很繁琐。将数据存储在真实数据库中有许多优势—许多用户可以同时编辑数据,用户们自己可以通过 Web 界面执行一些任务,数据可以轻松地在网络上共享等等。

Postfix 支持在多种复杂查找表类型中查找数据。这些包括 MySQL、PostgreSQL 和 LDAP。它之所以复杂,不是因为设置起来非常困难,而是因为本质上有更多可能出错的地方,是的,简单的索引文件(hash, dbm, btree, cdb)更容易正确获取。如果您想用查找表解决问题,始终从索引文件开始。当您让事情运转并理解为什么以及如何运转时,尝试将相同的想法转换为复杂的查找表类型。

Postfix 不要求您遵守某种特定的数据库模式。对于每个使用 MySQL 的查找表,您可以使用单独的配置,给出您选择使用的任何模式(或多或少—当前版本的 Postfix 并不完全允许任意的 MySQL 查询),返回所需的结果。每个配置都存储在一个单独的文件中,可以具有限制性权限,因为它们包含数据库密码。要在main.cf中使用 MySQL 查找虚拟别名,可以使用以下设置:

virtual_alias_maps = mysql:/etc/postfix/mysql-virtual.cf

配置文件遵循与main.cf相同的格式,并包含进行查找所需的所有信息—在本例中是虚拟别名查找。以下表格描述了您可以放入配置文件中的参数。这些参数将用于构建SELECT查询。在 Postfix 2.1 及更高版本中,此类配置文件的格式可以在mysql_table(5)手册页中找到。

参数 描述
hosts Postfix 将联系以执行查询的 MySQL 主机列表。可以包含 IP 地址、主机名或(以unix:为前缀时)本地 UNIX 域套接字的路径。如果指定多个主机,它们将以随机顺序尝试。任何 UNIX 域套接字主机将首先尝试。
user 应用于登录到 MySQL 服务器的用户名。
password 应用于登录到 MySQL 服务器的密码。
dbname 要使用的数据库的名称。
select_field 将查找结果取自的列名。
table 将搜索数据的表。
where_field 将查找键与之进行比较的表列。
additional_conditions 如果您需要在构建的查询末尾附加一些额外条件,可以在这里放置它们。
query 要执行的 SQL 查询,其中%s是要查找的字符串的占位符。此参数与select_field, table, where_fieldadditional_conditions是互斥的。此参数在 Postfix 2.2 中引入,并且是配置 MySQL 查询的推荐方式。

让我们从一个简单的例子开始。您有一个包含两列的别名表——“别名”和“地址”。 “别名”列是虚拟查找表的左侧(带有虚拟别名域的地址),而“地址”列是右侧(新地址)。

mysql> SELECT * FROM aliases;
+---------------------+------------------+
| alias | address |
+---------------------+------------------+
| joe@gb.example.com | joe1@example.com |
| joe@de.example.com | joe2@example.com |
| jane@fr.example.com | jane@example.com |
+---------------------+------------------+
3 rows in set (0.00 sec)

需要以下简单的 SQL 查询来查找虚拟域中的地址是否存在,并且是否应该重写为其他地址:

SELECT address FROM aliases WHERE alias = 'lookup key'

将其翻译成 Postfix MySQL 查找表配置将得到以下结果:

hosts = localhost
user = postfix
password = secret
dbname = mail
select_field = address
table = aliases
where_field = alias
additional_conditions =

使用 Postfix 2.2 的query参数的另一种解决方案将如下所示:

hosts = localhost
user = postfix
password = secret
dbname = mail
query = SELECT address FROM aliases WHERE alias ='%s'

为简洁起见,“主机、用户、密码”和dbname参数将在示例配置中被省略。

有时现实情况可能比这个简单的例子更复杂,所以我们将转向一些更困难的内容。

“select_field,table,where_field”和additional_conditions参数实际上只是直接插入到以下SELECT查询模板中,与查找字符串一起:

SELECT select_field FROM table WHERE where_field = 'lookup key' additional_conditions

这意味着select_field不一定是一个单独的列;它可以指定多个列组合成一个值,table可以是多个表,其中包括additional_conditions中的连接条件。例如,考虑这个稍微复杂的查询:

SELECT CONCAT(aliases.user, '@example.com') FROM aliases, domains
WHERE CONCAT(aliases.name, '@', domains.name) = 'lookup key'
AND aliases.domain = domains.id

需要以下查找表配置来执行它:

select_field = CONCAT(aliases.user, '@example.com')
table = aliases, domains
where_field = CONCAT(aliases.name, '@', domains.name)
additional_conditions = AND aliases.domain = domains.id

或者,使用query参数:

query = SELECT CONCAT(aliases.user, '@example.com')
FROM aliases, domains
WHERE CONCAT(aliases.name, '@', domains.name) = '%s'
AND aliases.domain = domains.id

在将新的 MySQL 查找表配置投入使用之前,您应该确保它对所有查找键返回所需的结果。这可以通过postmap程序完成,其过程在使用 postmap 调试查找表部分中有描述。

本地别名

本地别名是虚拟别名的一种替代方案。本地别名基本上以相同的方式工作,但它们只适用于本地域。本地别名表还提供了一些额外的功能。我们甚至在第一次启动 Postfix 之前的错误报告部分简要介绍了本地别名。

本地别名的查找表在alias_maps参数中指定。这些查找表的格式与虚拟别名略有不同,原因是为了与sendmail邮件传输代理的文件格式保持兼容。因此,您不应该使用postmap命令重建别名文件,而应该使用postalias。您也可以发现newaliases命令很方便。

许多人对两个类似的参数alias_mapsalias_database感到困惑。它们之间的区别在于,alias_maps包含 Postfix 将用于执行本地别名重写的查找表,alias_database包含在调用时newaliases命令将重建的查找表。只有索引查找表(hash, btree, dbm, cdb)需要重建,因此在那里列出 MySQL 查找表是没有意义的。

通常,您希望alias_mapsalias_database指向相同的查找表:

alias_maps = hash:/etc/aliases
alias_database = $alias_maps

与虚拟别名表相比,本地别名表中的查找键不包括域部分。该信息将是无用的,因为所有本地域都具有相同的本地部分命名空间。当使用索引文件进行本地别名时,查找键必须以冒号结尾,例如见下文:

$ cat /etc/aliases
postmaster: jack, jill
$ postalias /etc/aliases

这将把发送到任何本地域中的 postmaster 地址的消息发送给两个用户jackjill,假设myorigin中的域是本地的。下一节将解释为什么这个假设很重要。

别名表的右侧不一定要指向本地用户。实际上,它们可以指向任何域中的任何有效地址。本地别名表的格式在aliases(5)手册页中有描述。

命令传递

到目前为止,所有可以使用本地别名完成的工作都可以使用虚拟别名完成。那么,本地别名的意义何在?一个重要的区别是本地别名支持将消息传递给命令。这通常是邮件列表管理软件所需的。Postfix 通过将消息内容传递到标准输入流来将消息传递给命令。

在传递消息时运行命令,使用以下语法:

mylist: |"/usr/local/mailman/bin/wrapper post mylist"

双引号仅在命令包含空格时才是必需的。

但是,如果您想在虚拟域上运行邮件列表怎么办?您将需要使用虚拟别名将虚拟域中的地址重写为本地别名。比如,您希望发送到地址mylist@virtual.example.com的消息被发布到mylist邮件列表,该列表通过命令传递接受消息。为了实现这一点,您将需要一个虚拟别名,如下所示:

mylist@virtual.example.com mylist@localhost

注意程序将以哪个用户身份运行。Postfix 通常使用别名文件的所有者,但如果所有者是 root 用户,则不会使用。在这种情况下,将使用default_privs参数中的用户(通常为“nobody”)来运行程序。

如果您编写自己的程序,希望 Postfix 将消息传递给该程序,请确保在发生错误时返回适当的退出状态。Postfix 使用sysexits.h中的错误状态常量来确定如果程序以非零退出状态退出时该做什么。根据退出状态,Postfix 将消息返回给发件人或让其保留在队列中并稍后重试传递。

常见陷阱

虚拟别名不仅适用于虚拟别名域,还适用于通过 Postfix 传递的所有消息。不认识这一点可能会导致意外。例如,如果您托管许多虚拟别名域,所有这些域都应该有一些共同的别名,比如root, postmasterabuse,您可能会倾向于使用正则表达式查找表(regexpPCRE)将这些地址的别名用于所有虚拟别名域。到您自己。

# Warning! Does not work!
/^abuse@/ abuse@example.com

不要这样做!由于虚拟别名适用于所有消息,您或您的用户发送的任何消息,例如abuse@aol.comabuse@mindspring.com都将发送到您而不是预期的收件人。

一个非常常见的陷阱是认为右侧的非限定地址隐含地指向本地用户。例如,joe总是指本地用户 joe。这对虚拟别名和本地别名同样不正确。回想一下本章开头讨论myorigin参数时。就像在所有其他地方一样,Postfix 将使用myorigin来限定裸用户名。如果您的myorigin值恰好是列在mydestination中的本地域,那么joe确实将指本地用户 joe。为了避免意外,如果您有时将myorigin设置为非本地域,最好始终使用本地域限定右侧地址。由于localhost.$mydomain几乎总是列在mydestination中,一个很好的选择可能是localhost

postmaster@example.com jack@localhost, jill@localhost

其他地址重写机制

虚拟和本地别名并不是 Postfix 提供的地址重写的唯一机制。最值得注意的是,规范重写可用于重写信封和标题中的发件人和/或收件人地址。这种类型的重写由参数canonical_maps,sender_canonical_mapsrecipient_canonical_maps提供,除其他事项外,还可以用于将发件人地址重写为joe@example.comJoe.User@example.com,如果您不想公开用户的实际用户名。

Postfix 如何重写地址以及重写发生的顺序在ADDRESS_REWRITING_README中有描述,可在www.postfix.org/ADDRESS_REWRITING_README.html上找到。

故障排除 Postfix 问题

Postfix 提供了许多工具来简化问题解决。在 Postfix 邮件系统中实施新功能时,一定要一步步来。你对自己所做的事情越不确定,你所采取的步骤就应该越小。如果遇到问题,你会很早就发现它们,更容易找出问题出在哪里。特别是在使用 MySQL 数据库实现复杂查找表时,这一点尤为重要。

注意

如果你对复杂查找表感到稍微不舒服,永远不要同时引入新功能和复杂查找表配置。如果出现问题,你将更难找出从哪里开始。

在尝试新配置时,保持谨慎不会有坏处,直到配置完全测试通过。通过设置以下功能,所有永久错误将被转换为临时错误:

soft_bounce = yes

这意味着您的服务器拒绝的任何消息的传输都将被重试,并且 Postfix 将重试发送任何被远程服务器拒绝的消息。在这种设置下,密切监视日志,并寻找看起来不正常的拒绝。在测试完成后不要忘记关闭此功能!

阅读和解释日志文件

解决 Postfix 问题的一个关键元素是能够阅读和解释 Postfix 生成的日志消息。因为它们是纯文本文件,每行一个日志消息,所以不需要任何特殊的程序进行检查。日志已经被查看了几次,但本节将解释这些消息,并举例说明成功的邮件投递和失败的情况。阅读示例时,请参考“Postfix 架构:概述”部分中的图表,并注意日志条目的顺序如何紧随邮件通过 Postfix 的路径。

Kyle Dent 的文章使用 Postfix 日志进行故障排除中也讨论了理解 Postfix 的日志记录,网址为www.onlamp.com/pub/a/onlamp/2004/01/22/postfix.html

消息队列 ID

每条接收和处理的消息的一个重要属性是队列 ID。队列 ID 是一个长度不等的十六进制数,用于标识消息。具有消息上下文的日志消息也将记录队列 ID。如果您有队列 ID(需要调整日志文件路径以适应您的系统),这将使您很容易找到与消息相关的所有日志消息。

$ grep 92AFD4302 /var/log/maillog

cleanup守护程序在 Postfix 队列目录之一中创建队列文件时,将分配队列 ID。队列文件将保留在系统中,直到所有收件人已交付或消息过期,之后qmgr守护程序将删除队列文件。在 Postfix 的最新版本中,这个删除事件会被记录,我们将在示例中看到。

有时你会发现日志中没有队列 ID,而是出现NOQUEUE这个词,就像我们之前看到的例子一样:

Dec 31 16:39:31 jeeves postfix/smtpd[28478]: NOQUEUE: reject_warning: RCPT from unknown[222.101.15.127]: 450 Client host rejected: cannot find your hostname, [222.101.15.127]; from=<jdoe@example.com> to=<me@example.com> proto=SMTP helo=<222.101.15.127>

原因是这条消息尚未被分配队列文件,因此尚未被分配队列 ID。当第一个收件人被接受时,cleanup守护程序会创建队列文件。这有助于性能优化。

不要将队列 ID 与消息 ID 混淆。后者包含在每个消息的 Message-ID 标头中,并且通常在消息交给 Postfix 之前由邮件客户端添加。如果没有这样的标头字段,则 Postfix 的cleanup守护程序会为您添加一个。接收到的消息的cleanup守护程序将始终记录消息 ID。

Jan 5 23:49:13 jeeves postfix/cleanup[12547]: 92AFD4302:
message-id=<20041214021903.243BE2D4CF@mail.example.com>

Message-ID 标头包含计算机的主机名和当前日期和时间,对于每个消息都是唯一的。不要误以为队列 ID 也是唯一的。队列 ID 可以并且将被不同的消息重复使用,理论上每秒钟都可能发生(但这必须是在一个非常繁忙的系统上)。

SMTP 提交和本地交付

让我们首先看两个成功的邮件交易示例。第一个示例显示通过 SMTP 接收消息并将其交付到本地邮箱,第二个示例将显示通过 SMTP 交付到外部邮箱的本地提交消息。

第一个示例显示了在通过 SMTP 接收消息并将其交付给本地用户后日志中包含的内容。

Jan 5 23:49:13 jeeves postfix/smtpd[12546]:
connect from mail.example.com[1.2.3.4]

smtpd守护程序已从客户端接收到连接。

Jan 5 23:49:13 jeeves postfix/smtpd[12546]: 92AFD4302:
client=mail.example.com[1.2.3.4]

Postfix 现在已接受此消息的第一个收件人,并从cleanup守护程序请求队列文件。这是包含队列 ID 的此消息的第一个日志条目。

Jan 5 23:49:13 jeeves postfix/cleanup[12547]: 92AFD4302:
message-id=<20041214021903.243BE2D4CF@mail.example.com>

cleanup守护程序已从smtpd守护程序接收到整个消息,并记录了消息 ID。

Jan 5 23:49:13 jeeves postfix/smtpd[12546]:
disconnect from mail.example.com[1.2.3.4]

客户端从 SMTP 服务器断开连接。

Jan 5 23:49:13 jeeves postfix/qmgr[22431]: 92AFD4302:
from=<joe@example.com>, size=4258, nrcpt=1 (queue active)

消息已进入活动队列,因此有资格进行交付(除非队列拥挤,否则交付将立即开始)。队列管理器记录了发件人地址、消息大小(以字节为单位)和总收件人数。报告的大小将略大于消息中的实际字节数以及存储在磁盘上的消息大小。这是因为报告的大小是队列文件中消息内容记录的总大小,这会产生一些额外开销。

Jan 5 23:49:14 jeeves postfix/local[12653]: 3C21A4305:
to=<jack@example.net>, orig_to=<postmaster@example.net>,
relay=local, delay=0.1, delays=0.04/0.03/0/0.03, dsn=2.0.0,
status=sent (delivered to maildir)

本地交付代理成功将消息交付给本地用户“jack”的maildir。消息最初是寄给postmaster@example.net的,但是某些地址重写机制(通常是本地或虚拟别名)重写了收件人地址。最后,消息在接收后约一秒钟后被交付(delay关键字)。

请注意,此消息在交付完成时记录。如果交付代理在交付过程中调用另一个程序,并且该程序记录了自己的消息,则这些消息将在此交付完成消息之前记录在日志中。

每个交付的收件人都会发出一条日志消息:

Jan 5 23:49:26 jeeves postfix/qmgr[22431]: 92AFD4302: removed

这条最终消息表示所有收件人都已交付,因此队列文件被删除。

本地提交和 SMTP 交付

我们的下一个示例与上一个示例有些相反。在这里,通过sendmail命令提交的消息通过 SMTP 交付到另一台主机:

Jan 6 01:41:29 jeeves postfix/pickup[12659]:
CBA844305: uid=100 from=<jack>

提交的消息已由pickup守护程序处理。消息由用户 ID 为100的用户提交,发件人是未经修饰的地址jack:

Jan 6 01:41:30 jeeves postfix/cleanup[13190]: CBA844305:
message-id=<20050106004129.CBA844305@example.net>

同样,消息已被cleanup守护程序读取,并记录了消息 ID:

Jan 6 01:41:30 jeeves postfix/qmgr[12661]: CBA844305:
from=<jack@example.net>, size=1309, nrcpt=1 (queue active)

请注意,先前未经修饰的发件人地址现在已被重写为完全合格的地址,可能是因为myorigin参数等于example.net

Jan 6 01:41:31 jeeves postfix/smtp[13214]: CBA844305:
to=<joe@example.com>, relay=mail.example.com[1.2.3.4],
delay=1.3, delays=0.03/0.03/0.97/0.22, dsn=2.0.0,
status=sent (250 Ok: queued as DD8F02787)

该消息已通过mail.example.com SMTP 中继成功交付给收件人joe@example.com。在接受消息时,远程服务器说:

250 Ok: queued as DD8F02787

现在我们知道我们的消息在另一端的队列 ID。如果我们需要联系example.com的邮件管理员有关此消息,这些信息可能会有用:

Jan 6 01:41:31 jeeves postfix/qmgr[12661]: CBA844305: removed

交付完成,队列文件已删除。

希望您开始对消息发出的日志条目的一般格式有所了解,因此下一个示例将仅显示日志片段。

SMTP 交付时的连接问题

以下示例显示了当多个主机在 DNS 中设置为接收某个域的消息,但其中一些主机暂时无法访问时会发生什么,导致 Postfix 在交付之前尝试其中一些主机。我们只会查看交付代理的日志:

Jan 2 14:19:46 poseidon postfix/smtp[998]: connect to
mx4.hotmail.com[65.54.190.230]: Connection timed out (port 25)
Jan 2 14:20:16 poseidon postfix/smtp[998]: connect to
mx1.hotmail.com[64.4.50.50]: Connection timed out (port 25)
Jan 2 14:20:46 poseidon postfix/smtp[998]: connect to
mx3.hotmail.com[64.4.50.179]: Connection timed out (port 25)
Jan 2 14:20:47 poseidon postfix/smtp[998]: 940C132ECE:
to=<postmaster@hotmail.com>, relay=mx4.hotmail.com[65.54.167.230],
delay=92, delays=92/0/0.27/0.28, dsn=2.0.0,
status=sent (250 <20050102131914.B7C4B32ECF@example.com> Queued mail for delivery)

显然,当 Postfix 尝试传递时,hotmail.com的三个接收邮件主机都无法访问。请注意连接尝试是如何均匀分布在 30 秒间隔的。这不是巧合;控制 Postfix 等待连接的smtp_connect_timeout参数的默认值确实是 30 秒。这三个 30 秒的超时也解释了最后一条消息记录的传递延迟为 92 秒。还要注意,Hotmail 给我们的接受消息不包含任何队列 ID,而是消息 ID——250 状态代码后的文本消息格式尚未标准化。

获取更详细的日志消息

在大多数情况下,Postfix 的默认日志记录足以解决问题,但有时需要更多的细节。对于这些罕见的情况,您可以通过确保 Postfix 的守护进程至少给出一个-v启动选项来要求守护进程记录更详细的消息。这是通过编辑master.cf并在您想要获得更详细日志记录的守护进程的行末尾添加-v来完成的。例如,要从 SMTP 服务器smtpd获取详细的日志记录,更改该行:

smtp inet n - n - - smtpd

对此:

smtp inet n - n - - smtpd -v

根据您的配置,第一行可能略有不同,但重要的部分是最后一列中的内容,即守护程序的名称。在 SMTP 服务器的情况下,繁忙的服务器可能会产生大量的日志记录。如果是这种情况,debug_peer_list参数会很有用。

此参数接受一个或多个主机名或网络地址,其日志记录级别将增加。这只在存在网络对等体的情况下才有意义,比如在 SMTP 服务器和 SMTP 客户端中。

如果您在向特定的远程服务器发送消息时遇到问题,比如mail.example.com,可以设置以下规则:

debug_peer_list = mail.example.com

然后,您可以在 Postfix 连接到特定主机时观察增加的日志记录。使用debug_peer_list时,无需修改master.cf

使用 Postmap 进行查找表故障排除

postmap命令不仅用于重建索引查找表,还可以用于查询查找表,以检查查找是否按预期工作。这对于正则表达式查找表和复杂的查找表类型(如 MySQL、LDAP 和 PostgreSQL)尤其有用。在 Postfix 中使用新的查找表之前,应该先用postmap进行测试。要使用postmap执行查找,使用-q选项:

$ postmap -q postmaster@example.com mysql:/etc/postfix/ mysql-aliases.cf
jack@example.com

这将查询/etc/postfix/mysql-aliases.cf中描述的 MySQL 查找表,查找字符串postmaster@example.com,模拟 Postfix 的虚拟别名查找。

您还可以检查命令的退出状态,以确定查找是否成功。与往常一样,零退出状态表示成功。UNIX shell 将最后一个进程的退出状态存储在$?环境变量中。运行postmap后,您可以使用 echo shell 命令查看$?变量的内容:

$ postmap -q badaddress@example.com mysql:/etc/postfix/mysql-aliases.cf
$ echo $?
1

如果查找不按预期工作,您可以(就像 Postfix 守护进程一样)使用一个或多个-v启动选项来增加消息的详细程度。

请注意,postmap 执行原始查询。例如,如果您想知道 IP 地址1.2.3.4是否与以下访问映射行匹配:

1.2.3 REJECT

您不能使用以下命令进行测试:

$ postmap –q 1.2.3.4 hash:/etc/postfix/client_access

postmap命令不知道 Postfix 在访问映射上下文中如何匹配 IP 地址的规则,即使知道,也无法知道1.2.3.4是一个 IP 地址。

从 Postfix 邮件列表获取帮助

Postfix 的邮件列表称为 Postfix-users,是解决 Postfix 问题时非常有价值的资源。可以在www.postfix.org/lists.html找到列表的存档链接以及订阅说明。

尽管列表上的人们非常乐于助人,但他们希望在请求帮助之前你能做好功课。这意味着你应该搜索列表存档,看看你的问题是否以前被问过,最重要的是,你应该先阅读文档。

在提问时,不要忘记说明你试图实现的更大目标。这经常被忽视,问题太具体了。了解更大的情况不仅会让帮助你变得更容易,而且还会揭示你选择的解决方法是否完全错误。然而,在描述时不要太啰嗦!毕竟,阅读 Postfix 用户列表的人也是人类,他们会厌倦过长的帖子。

因为他们是人类,他们也不是通灵者。因此,请确保提供完整的配置和可能与你的问题相关的任何日志消息。通过运行postconf -n来获取你的配置。该命令将打印出你在main.cf中设置的所有参数的值。不要发布main.cf的完整内容,也不要发布postconf的输出(不带-n)。master.cf的内容很少需要。

总结

现在是时候总结本章学到的东西了。我们首先快速了解了 Postfix 邮件传输代理的工作原理,然后看了如何安装软件并准备基本配置。

然后,我们研究了各种方法来阻止垃圾邮件和其他不需要的消息。我们引入了虚拟别名域,以完全使你的邮件服务器能够托管许多域。最后,我们看了一些结构化的技术,帮助你分析和解决 Postfix 的问题。

第三章:使用 POP 和 IMAP 接收邮件

现在您有一个运行的电子邮件服务器,下一阶段是让用户访问他们的电子邮件。在本章中,您将学习以下内容:

  • 什么是 POP 和 IMAP,以及如何选择应该实施哪种

  • 如何安装和配置 Courier-IMAP,它可以提供 POP 和 IMAP 功能

  • 如何配置电子邮件服务器以供客户端访问

  • 如何配置流行的电子邮件客户端以使用电子邮件服务器提供的服务

在 POP 和 IMAP 之间进行选择

Postfix 将接收电子邮件并将其传递到用户的收件箱,但需要其他软件才能让用户轻松阅读他们的电子邮件。有两种从主机检索电子邮件的标准。第一种称为邮局协议POP)。POP3 是最常用的 POP 版本。通常用于从服务器下载电子邮件,将其存储在客户端应用程序中,并从服务器中删除电子邮件。这通常由互联网服务提供商使用。然后,电子邮件由客户端应用程序进行操作,例如 Windows Live Mail 或 Mozilla Thunderbird。

第二个协议称为Internet Message Access ProtocolIMAP)。当您希望每封电子邮件都保留在服务器上时,通常使用 IMAP 系统。IMAP 允许用户为电子邮件创建文件夹,并在文件夹之间移动或复制电子邮件。客户端应用程序访问服务器上的电子邮件,但不必将其存储在客户端机器上。电子邮件服务器必须能够为所有用户存储所有电子邮件,并且数据量通常会随着时间的推移而增加。用户很少删除电子邮件。因此,在具有集中式 IT 设施的大型组织中更频繁地使用 IMAP。

一个名为电子邮件客户端的程序代表用户从邮件服务器检索邮件,电子邮件客户端与之通信的程序称为电子邮件服务器。有许多 POP3 和 IMAP 服务器。有些只执行其中一项任务。Courier-IMAP 软件套件包含了一个 POP3 和一个 IMAP 服务器,并在本章中进行了详细介绍。

Courier-IMAP 通过访问用户的maildir来运行。操作概述如下图所示:

在 POP 和 IMAP 之间进行选择

下载和安装 Courier-IMAP

Courier是一套程序,包括一个完整的 MTA。本书假定使用的 MTA 是Postfix。重要的是只安装和配置 Courier 的 POP3 和 IMAP 组件——如果同时有两个 MTA 运行,电子邮件系统将非常不稳定。

注意

术语“Courier”通常用于指代 Courier 软件的完整套件,包括 MTA。Courier-IMAP 通常用于指代服务器的 IMAP 和 POP3 部分。Courier Authentication Library是 Courier-IMAP 所需的另一个 Courier 模块。确保只安装 Courier Authentication Library 和 Courier-IMAP。

有几种安装 Courier-IMAP 的方法。Courier-IMAP Redhat Package Managers (RPMs)适用于多种不同的 Linux 发行版。这些将从发行版的制造商处获得,或者可能已经由第三方构建,通常是 Courier 的爱好者或开发人员。如果您的 Linux 发行版基于 RPM,但 RPM 中没有 Courier-IMAP 的软件包,则必须从源代码构建。

如果您的 Linux 发行版基于 Debian 软件包格式,则可能会提供 Courier-IMAP 的软件包。如果没有,则 Courier-IMAP 必须从源代码构建。

从发行库安装 Courier-IMAP

最好使用 Linux 发行商构建的软件包,如果可能的话。他们提供的任何软件包都应该是稳定的,性能良好,并且使用默认路径和文件位置以适应其余软件。如果您的发行版有软件包管理器,那么您应该使用它,因为它会自动安装 Courier-IMAP 需要的任何软件包。

重要的是要获得与正在使用的发行版匹配的软件包。针对不同发行版的软件包可能无法正常工作,并且可能使现有软件不稳定。

从 RPM 安装 Courier-IMAP

重要的是要获得与正在使用的发行版匹配的 RPM。针对其他发行版的 RPM 可能无法正常工作,并且可能使现有软件不稳定。

如果您的 Linux 发行版包含用于管理软件包的图形前端(例如 gnorpm),最好使用它,因为它会自动管理软件包之间的任何依赖关系。

要查找 Courier-IMAP 的 RPM,请首先检查 Linux 发行商是否提供了该软件包。如果是这样,请下载并使用它。如果分销商没有提供软件包,则可能由其他组织或个人提供适当的软件包。要检查这一点,请搜索网络。在www.rpmfind.net上有一个 RPM 数据库,搜索courier-imap和发行版的名称(例如 Fedora 或 Mandriva)将找到任何合适的软件包。最好使用设计用于特定版本发行版的软件包。例如,Mandriva Linux 2008.1 的软件包不应该用于 Mandriva Linux 2009.1。如果不确定,则最好按照下一节中的说明从源代码安装 Courier-IMAP。

如果无法使用 RPM 的前端,则首先下载 RPM 并使用命令提示符切换到包含文件的目录,然后以 root 身份使用rpm命令安装 RPM。

# rpm -ivh courier-imap-4.4.1-1mdv2009.1.i586.rpm

如果缺少所有先决条件软件,则 RPM 命令可能会失败。在这种情况下,输出将列出所需的软件。可以使用之前看到的rpm命令下载并安装适当的软件包。安装了所有先决条件软件后,可以使用之前解释的rpm命令安装 Courier-IMAP。

如果使用rpm命令安装 Courier-IMAP,则也可以使用它来卸载。命令如下:

# rpm -e Courier-IMAP

使用 Debian 软件包格式安装 Courier-IMAP

如果您的 Linux 发行版包含用于管理软件包的图形前端(例如 gnorpm),则可以使用它,如果您感到舒适的话。

您可以在任何基于 Debian 的系统上使用以下命令来安装 Courier-IMAP:

# apt-get install courier-imap

从源代码安装 Courier-IMAP

在现代 Linux 发行版上从源代码安装 Courier-IMAP 并不是一项困难的任务。在较旧版本的 Linux 以及其他 UNIX 平台(如 AIX、Solaris 和 HP-UX)上,可能会出现问题,特别是如果系统的其余软件不是最新的。

先决条件

安装 Courier-IMAP 需要满足以下先决条件:

  • 一个可用的 C++编译器:我们建议使用GNU C++编译器,它是GNU 编译器集合GCC)的一部分,又是几乎每个 Linux 发行版的一部分,并且大多数平台都可以免费获得。如果有 GCC 的 RPM 或其他软件包(几乎肯定会有),应优先使用它,而不是从源代码构建 GCC。

  • 一个 make 实用程序:我们建议使用 GNU 的make实用程序,它将在大多数 Linux 发行版中提供,或者可以从gcc.gnu.org/下载。

  • GNU 链接器:可在www.gnu.org/software/binutils/上获得。

  • GNU Libtool:可在www.gnu.org/software/libtool/上获得。

  • Berkeley DB 库或 gdbm 库:这些库允许程序在文件中创建数据库。同样,这些库应该以打包形式提供,但也可以从www.sleepycat.com/www.gnu.org/software/gdbm/gdbm.html下载。这两者中的一个或两者几乎肯定已经安装。

  • Courier-IMAP 源代码。

要成功安装 Courier-IMAP,必须首先安装所有这些先决条件。

构建 Courier 身份验证库

安装 Courier-IMAP 有两个阶段。首先,必须构建 Courier 身份验证库,通常称为Courier-authlib。完成此操作后,可以安装 Courier-IMAP。

注意

虽然这里提供了安装 Courier-IMAP 的说明,但是阅读随包提供的README, READ.MEINSTALL文件总是一个好主意。如果在安装软件时遇到问题,请始终检查问题是否在任何提供的文档中提到。

Courier-authlib源可以从www.courier-mta.org/authlib/下载。与许多开源软件包一样,Courier 身份验证库使用配置脚本来检测系统功能,然后使用make命令来构建和安装软件。

要构建 Courier 身份验证库,请输入以下命令。您应该看到类似以下的响应:

$ cd /tmp
$ tar xfj /path/to/courier-authlib-0.62.4.tar.bz2
$ cd courier-authlib-0.62.4/
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
... (lots more output appears)
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
config.status: executing depfiles commands
config.status: executing libtool commands
$
$ make
$ make
/bin/sh ./config.status --file=authlib.html
config.status: creating authlib.html
echo "#define AUTHLDAPRC \"\"" >authldaprc.h
...(lots more output)
/bin/sh ./config.status --file=authlib.3
config.status: creating authlib.3
make[2]: Leaving directory `/tmp/courier-authlib-0.62.4'
make[1]: Leaving directory `/tmp/courier-authlib-0.62.4'
$ su -c make install (enter the root password)
# make install
make install-recursive
make[1]: Entering directory `/tmp/courier-authlib-0.62.4'
Making install in libltdl
make[2]: Entering directory `/tmp/courier-authlib-0.62.4/libltdl'
make install-am
...(lots more output)
make[4]: Leaving directory `/tmp/courier-authlib-0.62.4'
make[3]: Leaving directory `/tmp/courier-authlib-0.62.4'
make[2]: Leaving directory `/tmp/courier-authlib-0.62.4'
make[1]: Leaving directory `/tmp/courier-authlib-0.62.4'
#

命令成功执行后,Courier 身份验证库将被安装。在启动之前,需要进行一些配置。

请注意,如果您使用的是 Red Hat Linux 或其衍生产品之一,如 Fedora Core 或 CentOS,则./configure脚本会检测到这一点,并建议您使用 RPM 或--with-redhat参数:

$ ./configure
configure: WARNING: === I think you are trying to run this configure script
configure: WARNING: === on Red Hat/Fedora. You're doing too much work!
configure: WARNING: === It's much faster to create installable binary RPMs
configure: WARNING: === like this: http://www.courier-mta.org/FAQ.html#rpm
configure: WARNING: === When you do this you may find that RPM will tell you
configure: WARNING: === to install some other software first, before trying to
configure: WARNING: === build this one, and even tell you the name of RPMs you
configure: WARNING: === build this one, and even tell you the name of RPMs you
configure: WARNING: === need to install from the distribution CD. That's much
configure: WARNING: === easier than trying to figure out the same from some
configure: WARNING: === cryptic error message.
configure: WARNING:
configure: WARNING: === Even if you don't intend to use everything you need to
configure: WARNING: === have in order to build via RPM, you should still do as
configure: WARNING: === you're told. All the extra stuff (LDAP, SQL, etc...)
configure: WARNING: === goes into RPM sub-packages, which do not need to be
configure: WARNING: === installed.
configure: WARNING: === But, if you insist, you can simply add '--with-redhat'
configure: WARNING: === parameter to this configure script and not see this
configure: WARNING: === error message. You should also do this when upgrading
configure: WARNING: === and you didn't use RPM with the older version.
configure: error: ... in either case you better know what you're doing!

在这种情况下,将--with-redhat参数传递给./configure

$ ./configure --with-redhat

配置 Courier 身份验证库

一旦安装了身份验证库,就需要做出一些决定。

Courier 身份验证库为系统管理员提供了在对用户进行身份验证时的灵活性。身份验证是用户通过提供有效的用户名和相应密码来证明其身份的过程。提供以下身份验证方法:

身份验证方法 描述
authshadow 默认情况下,大多数 Linux 发行版将用户密码存储在/etc/shadow系统文件中。使用authshadow进行身份验证会验证系统帐户的密码。只有当用户拥有系统帐户时,即可以使用 telnet 或ssh登录到计算机时,才适用。
authpwd 在旧系统上,密码存储在/etc/passwd文件中。 authpwd模块允许用户根据其系统密码进行身份验证。同样,用户必须拥有系统帐户。
authuserdb authshadow不同,每个用户都需要一个系统帐户,authuserdb将用户详细信息存储在系统帐户之外。这允许虚拟邮箱功能,其中可以定义用户,而无需在计算机上拥有真实帐户。一些脚本用于管理数据库,通常保存在/etc/userdb中。(许多发行版将其放在/etc/courier/authlib/userdb中。)
authmysql 这类似于authuserdb,但是使用 MySQL 数据库而不是authuserdb中使用的文件。 MySQL 是大多数 Linux 发行版提供的流行关系数据库,并且相对于其他方法,它既有优势又有劣势。使用诸如 MySQL 之类的关系数据库会增加电子邮件服务器的复杂性,但可能会使身份验证更快,并且关系数据库将允许数据与其他应用程序共享(如果需要)。
authpam 身份验证由可编程访问方法(PAM)库提供。PAM 是一个常用的库,大多数 Linux 发行版都提供。PAM 是灵活的,可以从各种来源对用户进行身份验证,包括系统密码数据库(通常是/etc/passwd文件)。
authcustom 这允许系统管理员开发自己的自定义身份验证方法。

选择身份验证方法可能是一个困难的决定。以下是一些指导原则:

  • 如果所有用户都有系统帐户,则可以使用authshadow、authpwdauthpam。如果 PAM 已安装和配置,应优先使用 PAM 而不是其他方法。

  • 如果需要虚拟电子邮件系统,请使用authdbauthmysql。对于小型站点,选择authmysql而不是authdb没有太大优势。

在本书中,仅涵盖了使用authshadowauthpwd进行简单身份验证。虽然如果安装和配置了 PAM,则不需要额外的配置。authuserdbauthmysql需要进一步配置,这在身份验证库的文档中有描述。

/usr/local/etc/courier/authlib目录包含 Courier 身份验证库的配置文件。出于安全考虑,最好将整个目录设置为仅由属于mail组的用户可读。默认的authdaemonrc文件可以从安装目录复制过来。

# mkdir -p /usr/local/etc/courier/authlib
# chown mail:mail /usr/local/etc/courier/authlib/
# chmod 755 /usr/local/etc/courier/authlib/
# cp /tmp/courier-authlib-0.52/authdaemonrc /usr/local/etc/courier/
authlib

要以 root 用户身份完成配置,请编辑/usr/local/etc/courier/authlib/authdaemonrc文件,并更改以下条目:

authmodulelist="authshadow"
daemons=3
authdaemonvar=/var/lib/courier/authdaemon
DEBUG_LOGIN=0
DEFAULTOPTIONS=""

在以authmodulelist开头的行中,输入您希望使用的模块。

daemons=行列出了应该运行多少个进程,等待对用户进行身份验证。除非有非常多的用户,否则35之间的值应该足够了。守护进程的数量越多,身份验证库使用的内存就越多。其他进程可用的内存也会减少,这可能会影响整个系统的性能。

authdaemonvar行列出了 Courier 身份验证库放置其运行时文件的位置,特别是用于连接的套接字。此处列出的目录(在本例中是/var/lib/courier/authdaemon)应存在,并且只能由 root 用户读取。使用以下命令作为root创建目录:

# mkdir -p /var/lib/courier/authdaemon
# chmod 750 /var/lib/courier/authdaemon
# chown mail:mail /var/lib/courier/authdaemon

出于安全考虑,最好将authdaemonrc文件设置为只有特定用户可读。

# chown mail:mail /usr/local/etc/courier/authlib/authdaemonrc

系统启动时需要启动身份验证守护程序。通常,会在/etc/init.d/中放置一个脚本,以便轻松启动和停止守护程序。在身份验证库的源代码中包含了一个示例脚本/courier-authlib.sysvinit。应将此文件放置在/etc/init.d中。

# cd /tmp/courier-authlib-0.52
# cp courier-authlib.sysvinit /etc/init.d/courier-auth

将来,可以使用以下命令启动和停止服务:

# /etc/init.d/courier-auth start
# /etc/init.d/courier-auth stop

最初,应该直接从命令行运行守护程序。如果有任何错误,它们将被显示出来。

# /usr/local/sbin/authdaemond start
/usr/local/sbin/authdaemond: line 16: /usr/local/etc/authlib/authdaemonrc: No such file or directory

在刚刚显示的示例中,/usr/local/etc/authlib/authdaemonrc文件丢失,因为默认的authdaemonrc文件没有从安装目录复制过来。

如果服务已正确启动,可以通过传递stop参数来停止服务。

# /usr/local/sbin/authdaemond stop

请参考发行版的文档,以便在 Linux 启动时自动启动服务。在 Red Hat 系统上,可以使用service命令来配置服务自动启动。

# service courier-auth add default

对于其他发行版,可能会使用chkconfig命令。

# chkconfig -add imapd

解决错误

在构建的每个阶段都可能会产生错误。在运行configure脚本时出现错误可能与缺少的依赖关系有关。检查软件提供的READMEINSTALL文件,并确保安装了所有依赖项。如果问题不明显,可以通过互联网搜索确切的错误消息来找到解决方案。

在构建时出现错误是不太常见的,因为大多数错误都会被 configure 脚本阻止。同样,错误消息应该提供关于错误来源的良好线索,并且使用互联网搜索引擎可能会有所帮助。

运行时错误通常是由于错误的配置。Courier Authentication Library 中有一些配置选项,但可能会出现错误。

如果找不到答案,可以向 Courier 邮件列表寻求帮助。与往常一样,首先搜索列表存档以寻求帮助,并查阅常见问题解答。对于 Courier-IMAP,邮件列表位于 lists.sourceforge.net/lists/listinfo/courier-imap/,可搜索的列表存档位于:sourceforge.net/mailarchive/forum.php?forum_id=7307/,常见问题解答位于 www.courier-mta.org/FAQ.html

构建 Courier-IMAP

Courier-IMAP 源代码以一个 tarball 的形式提供,其中包含所有文件,类似于 ZIP 文件。可以从 www.courier-mta.org/imap/ 下载,但要小心下载 Courier-IMAP 的源代码,而不是 Courier MTA 的源代码。

注意

尽管这里提供了有关如何安装 Courier-IMAP 的详细信息,但始终建议阅读随软件包提供的 README, READ.MEINSTALL 文件。如果在安装软件时遇到问题,请始终检查所提供的文档中是否提到了该问题。

要安装 Courier-IMAP,必须输入一些命令。与大多数以源代码形式提供的软件一样,首先运行配置脚本。配置脚本检查我们的计算机上安装的软件,并配置软件以便正确构建。

当 Courier-IMAP 用作 IMAP 服务器时,默认情况下假定其客户端将严格遵循 IMAP 标准。不幸的是,这通常不是情况,如果 Courier-IMAP 期望客户端严格遵循 IMAP 标准,可能无法将邮件传递到电子邮件客户端。Courier-IMAP 的开发人员意识到了这一点,并通过将 --enable-workarounds-for-imap-client-bugs 标志传递给配置脚本来构建与非标准客户端一起工作的能力。

Courier-IMAP 在构建时包含一个特殊的 check 功能。不幸的是,使用 --enable-workarounds-for-imap-client-bugs 会阻止检查成功运行。由于 check 功能很有用,我们将软件构建两次。首先不使用 --enable-workarounds-for-imap-client-bugs 构建,然后运行 check,然后再次使用该标志构建并安装软件。

要构建 Courier-IMAP,请输入以下命令。选择一个合适的目录来构建软件。在这个例子中,我们选择了 /tmp,软件会解压到 courier-imap-3.0.8 目录中。与 Courier Authentication Library 一样,配置脚本将检测是否正在使用 Red Hat 派生的 Linux 发行版,并且可以传递 --with-redhat 标志给配置。

$ cd /tmp
$ tar xfj /path/to/courier-imap-4.5.1.tar.bz2
$ cd /tmp/courier-imap-4.5.1
$ ./configure --with-redhat
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
... (a lot more output follows)
config.status: creating config.h
config.status: executing depfiles commands
config.status: executing libtool command
$ make check
Making check in numlib
make[1]: Entering directory `/tmp/courier-imap-4.5.1/numlib'
make[1]: Nothing to be done for `check'.
make[1]: Leaving directory `/tmp/courier-imap-4.5.1/numlib'
Making check in md5
... (a lot more output appears)
make[2]: Leaving directory `/tmp/courier-imap-4.5.1/imap'
make[1]: Leaving directory `/tmp/courier-imap-4.5.1/imap'
make[1]: Entering directory `/tmp/courier-imap-4.5.1'
make[1]: Nothing to be done for `check-am'.
make[1]: Leaving directory `/tmp/courier-imap-4.5.1'
$ ./configure --enable-workarounds-for-imap-client-bugs
checking for gcc... gcc
checking for C compiler default output file name... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
... (a lot more output follows)
config.status: creating config.h
config.status: executing depfiles commands
config.status: executing libtool command
$ make
$ make
make all-recursive
make[1]: Entering directory `/tmp/courier-imap-4.5.1'
make all-gmake-check FOO=BAR
----------------------------------------------------
(lots more output appears)
cp imap/imapd.cnf .
cp imap/pop3d.cnf .
cp -f ./maildir/quotawarnmsg quotawarnmsg.example
make[2]: Leaving directory `/tmp/courier-imap-4.5.1'
make[1]: Leaving directory `/tmp/courier-imap-4.5.1'
$ su -c "make install"
Password: (enter password for root)
Making install in numlib
make[1]: Entering directory `/tmp/courier-imap-4.5.1/numlib'
make[2]: Entering directory `/tmp/courier-imap-4.5.1/numlib'
make[2]: Nothing to be done for `install-exec-am'.
make[2]: Nothing to be done for `install-data-am'.
(lots more output appears)
Do not forget to run make install-configure
test -z "/usr/lib/courier-imap/share" || /bin/mkdir -p "/usr/lib/courier-imap/share"
/usr/bin/install -c mkimapdcert mkpop3dcert '/usr/lib/courier-imap/share'
make[2]: Leaving directory `/tmp/courier-imap-4.5.1'
make[1]: Leaving directory `/tmp/courier-imap-4.5.1'
$ su -c "make install-configure"
Password: (enter password for root)
make[1]: Entering directory `/tmp/courier-imap-4.5.1/numlib'
make[1]: Leaving directory `/tmp/courier-imap-4.5.1/numlib'
make[1]: Entering directory `/tmp/courier-imap-4.5.1/md5'
make[1]: Leaving directory `/tmp/courier-imap-4.5.1/md5'
(lots more output appears)
make install-configure-local DESTDIR=
make[1]: Entering directory `/tmp/courier-imap-4.5.1'
make[1]: Leaving directory `/tmp/courier-imap-4.5.1'
$

如果输出看起来类似于所示的内容,Courier-IMAP 已成功安装,您可以跳过下一节关于错误处理的部分。

处理错误

configure 命令可能会失败。配置尝试检测现有软件,并确保 Courier-IMAP 与其一起工作,但偶尔会出现错误。

处理错误

在这个例子中,configure 命令假设 vpopmail 已安装,并且在找不到 vpopmail 的部分时失败。实际上,vpopmail 没有安装,也无法被检测到。我们从 INSTALL 文件中得到以下信息:

...configure should automatically detect if you use vpopmail, and compile and install the authvchkpw authentication module.

这表明 authvchkpw 用于 vpopmail。在 INSTALL 文件的前面,我们读到:

* authvchkpw - this module is compiled by default only if the vpopmail account is defined.

检查/etc/passwd文件后,我们发现有一个解释检测的vpopmail帐户。缺少vpopmail文件解释了configure脚本的失败。在INSTALL文件中,描述了配置脚本的参数。

Options to configure:
...
* --without-module - explicitly specify that the authentication module named "module" should not be installed. See below for more details.
Example: --without-authdaemon.

因此,解决方案是使用--without-authvchkpw选项:

$ ./configure –without-authvchkpw

大多数问题可以用类似的方法解决。最好不要被不理解的术语和名称吓倒。不需要了解任何关于vpopmail的东西,只需搜索术语"vpopmail"(原始错误消息中提到的),就可以通过阅读文档解决错误。

如果找不到答案,可以通过 Courier 邮件列表寻求帮助。详细信息请参阅解决错误部分。

使用 POP3

如介绍中所述,当电子邮件要存储在客户端计算机上时,通常使用 POP3。当与电子邮件服务器的连接是间歇性的时,例如,使用拨号线访问 ISP 的电子邮件帐户时,通常会使用它。这种方法的优势在于电子邮件始终可供客户端使用,客户端可以在未连接到电子邮件服务器时工作。用户下次上线时可以阅读电子邮件并创建回复。

使用 POP3 的主要缺点是电子邮件通常只能在客户端 PC 上使用。如果客户端 PC 出现故障或被盗,除非进行了备份,否则电子邮件将丢失。

可以配置 POP3 客户端在服务器上保留电子邮件,以便其他客户端访问,但在这种情况下更常用的是 IMAP。

为 POP3 配置 Courier-IMAP

如果 Courier-IMAP 是从源代码构建的,则配置文件位于/usr/lib/courier-imap/etc/courier-imap/中。如果您使用的是打包的发行版,则可能位于/etc/courier-imap中。pop3d文件包含了 POP3 服务器的设置。

如果您使用的是 Courier-IMAP 的打包发行版,则可以使用以下命令找到配置文件:

# find / -name pop3d 2>/dev/null
/usr/lib/courier-imap/etc/pop3d
/usr/lib/courier-imap/bin/pop3d

编辑文件,找到并更改以下设置:

设置 描述
PIDFILE pop3d守护程序跟踪其使用的进程 ID。它指定一个有效的路径和一个暗示文件使用的名称。通常,这可能是/var/run/pop3d.pid。确保变量指向一个现有的目录(如/var/run)或创建指定的目录。
MAXDAEMONS 这指定可以同时运行的pop3d进程的最大数量。这个数字限制了可以同时连接的用户数量。比预期用户数量更高的数字可能是浪费的,但尝试连接的用户也包括在此数字中。将其设置为接近可能同时连接的最大用户数量的数字,或者稍微高一些。请注意,这是启动的进程的最大数量,而不是初始进程的数量。
MAXPERIP 这指定每个 IP 地址的最大连接数。低数字(如 4)可以防止恶意行为,例如拒绝服务攻击,其中试图使用邮件服务器上的所有连接。
POP3AUTH 如果使用 Courier 身份验证库守护程序,则将其设置为空,否则将其设置为指示执行的登录验证类型。对于 4.0 之前的版本,应将其设置为LOGIN
PORT 这指定守护程序监听的端口。标准端口是110,只有在所有客户端软件都配置为使用非标准端口时才应选择不同的端口。
ADDRESS 这指定要监听的 IP 地址。如果机器有多个网络接口,Courier-IMAP 可以配置为仅在其中一个地址上监听。值为0表示应使用所有网络接口。
TCPDOPTS 这些是要使用的选项。通常使用的选项包括nodnslookup,它可以防止 POP3 守护程序尝试解析每个连接的名称,以及-noidentlookup,它可以防止它尝试对传入连接进行ident查询。指定这两个设置可以减少身份验证用户连接所需的时间。
MAILDIRPATH 这是典型用户的maildir路径。指定适合您系统的值,例如.maildir

此处显示了一个示例pop3d配置文件:

PIDFILE=/var/run/pop3d.pid
MAXDAEMONS=40
MAXPERIP=4
POP3AUTH=""
PORT=110
ADDRESS=0
TCPDOPTS="-nodnslookup -noidentlookup"
MAILDIRPATH=.maildir

一旦配置了 POP3 服务器,就该是时候测试了。如果您使用的是由分发商提供的 Courier-IMAP 版本,请使用名为/etc/init.d/courier-imap的分发商启动脚本。这将尝试启动imapd以及pop3d,但由于大部分配置将由分发商完成,因此 IMAP 应该能够成功启动。

注意

如果您使用的是 Courier-IMAP 4.0 或更高版本,则必须在启动 POP3 或 IMAP 服务之前运行courier-authdaemon。确保按照前面描述的方式启动它们。

要启动 POP3 服务进行测试,请运行以下命令:

# /usr/lib/courier-imap/libexec/pop3d.rc start

一旦正确配置了 POP3 和 IMAP 服务,它们可以在机器启动时自动启动。这在测试 IMAP 服务部分有解释。即使不需要 IMAP,也可以按照说明进行操作。

测试 POP3 服务

测试 POP3 等服务的最简单方法是使用telnet实用程序并连接到适当的端口。这样可以避免网络连接或客户端配置可能出现的任何问题。POP3 使用端口110,因此将 telnet 连接到本地机器的端口110

$ telnet localhost 110
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
+OK Hello there.
USER username
+OK Password required.
PASS password
+OK logged in.
STAT
+OK 82 1450826
LIST
+OK POP3 clients that break here, they violate STD53.
1 5027
2 5130
3 6331
4 3632
5 1367
... all e-mails are listed, with their sizes in bytes
82 6427
.
RETR 1
+OK 5027 octets follow.
Return-Path: <user@domain.com>
X-Original-To: user@localhost
Delivered-To: user@machine.domain.com
Received: from isp (isp [255.255.255.255])
... e-mail is listed
.
QUIT
+OK Bye-bye.

外部主机关闭连接。

POP3 协议基于文本命令,因此可以使用 telnet 模拟客户端。最初,使用USERPASS命令对用户进行身份验证。如果用户成功通过身份验证,则STAT命令将列出所有电子邮件及其组合大小(以字节为单位)。LIST列出每封电子邮件及其大小。RETR命令在指定命令时检索(或列出)一封电子邮件。DELE命令(未在示例中显示)将从服务器中删除一封电子邮件。

现在 POP3 正在运行,是时候配置电子邮件客户端来收取电子邮件了。

通过 Windows Live Mail 使用 POP3 检索电子邮件

Windows Live Mail 是一款流行的电子邮件客户端,适用于从 XP 版本开始的 Windows 版本。它包括连接到支持 POP3 和 IMAP 的服务器的功能。可在download.live.com/wlmail下载。

以下是配置它的步骤:

  1. 通过在开始菜单层次结构中找到它来启动 Windows Live Mail:开始 | 所有程序 | Windows Live | Windows Live Mail。第一次运行时,界面将自动显示创建新帐户的向导。否则,点击导航栏中的添加电子邮件帐户通过 Windows Live Mail 使用 POP3 检索电子邮件

  2. 将显示新帐户向导的第一页。通过 Windows Live Mail 使用 POP3 检索电子邮件

  3. username@domain格式输入完整的电子邮件地址。您可以决定是否要输入密码 - 如果不勾选记住密码,则每次启动 Windows Live Mail 时都会提示输入密码。显示名称应为您的名字和姓氏 - 这将显示在发件邮件上。无需勾选手动配置电子邮件帐户的服务器设置复选框。

  4. 点击下一步。向导的下一页需要一些服务器详细信息。通过 Windows Live Mail 使用 POP3 检索电子邮件

  5. 默认情况下是POP3服务器。传入服务器应该是邮件服务器的名称(或 IP 地址)。默认的端口110可以保持不变。不要勾选 SSL 连接的框,并将认证方法保持为明文认证登录 ID应保持为在第一个屏幕中输入的电子邮件地址的用户名部分。传出服务器应该与传入服务器提供的相同,表单的其余部分可以保持默认值。

  6. 按下下一步。您将看到一个确认屏幕。使用 Windows Live Mail 通过 POP3 检索电子邮件

  7. 当您单击完成时,Windows Live Mail 将尝试连接到电子邮件服务器并下载电子邮件。

  8. 如果有任何错误,那么右键点击导航窗格中的帐户,然后选择属性。然后您可以检查和修改任何设置。

现在 POP3 已经成功配置,是时候转向 IMAP 了。

使用 IMAP

如前所述,使用 IMAP,邮件保存在服务器上,可能不保存在客户端上。这使得它非常适合具有中央管理功能的组织,因为它简化了备份,并且还允许用户更改他们工作的客户计算机。但是,这也意味着随着时间的推移,存储整个组织电子邮件所需的磁盘存储空间将不可避免地增加。当发送或接收大附件时,这一点尤为真实。如果用户依赖于能够访问他们的邮箱,那么如果邮件服务器在他们的工作时间内不可用,他们将感到不便。一些电子邮件客户端可以配置为复制电子邮件,从而避免中断。通过使用 IMAP 的创建文件夹和在它们之间移动电子邮件的能力,有时可以以相对简单的方式实现这一点。

配置 IMAP 的 Courier

安装了 Courier-IMAP 后,无论是从软件包还是从之前描述的源,都需要在使用之前进行配置。

注意

如果您已经按照前面描述的方式配置和测试了 POP3,那么在配置 IMAP 时应停止 Courier-IMAP 守护程序。如果您使用的是 Courier-IMAP 大于 4.0 的版本,则可以保持认证守护程序运行。

如果 Courier-IMAP 是从源代码构建的,则配置文件位于/usr/lib/courier-imap/etc/courier-imap/。在打包的发行版中,它们可能位于/etc/courier-imapimapd文件包含 IMAP 服务器的设置。

如果您使用的是 Courier-IMAP 的打包发行版,则可以使用此命令找到配置文件:

# find / -name imapd 2>/dev/null

/usr/lib/courier-imap/etc/imapd
/usr/lib/courier-imap/bin/imapd

找到文件后,可以根据需要进行修改。以下是主要的配置指令:

设置 描述
PIDFILE imapd守护程序跟踪其使用的进程 ID。它指定一个有效的路径和名称,这表明文件的使用。通常,这可能是/var/run/imapd.pid。确保设置指向有效的目录。
MAXDAEMONS 这指定可以同时运行的imapd进程的最大数量。此数字限制可以同时连接的用户数量。高于预期用户数量的数字可能是浪费的,但尝试连接的用户也包括在此数字中。将其设置为最大可能同时连接的用户数量左右,或者略高一些的数字。
PORT 这指定守护程序侦听的端口。标准端口是143,只有当所有客户端软件都配置为使用非标准端口时,才应选择不同的端口。
ADDRESS 这指定要侦听的 IP 地址。如果机器有多个网络接口,Courier-IMAP 可以配置为仅侦听其中一个地址。值0表示应使用所有网络接口。
TCPDOPTS 这些是要使用的选项。典型的选项包括-nodnslookup,它可以防止 IMAP 守护程序尝试解析每个连接的名称,以及-noidentlookup,它可以防止它尝试对传入连接进行ident查询。指定这两个设置可以减少身份验证用户连接所需的时间。
MAILDIRPATH 这是典型用户的maildir的路径。为您的系统指定适当的值,例如.maildir
MAXPERIP 这指定了每个 IP 地址的最大连接数。较低的数字可以防止恶意行为,例如拒绝服务攻击,即试图使用邮件服务器上的所有连接。一些电子邮件客户端会对服务器进行多次连接,因此诸如5之类的较低值可能会影响客户端软件的操作。
IMAP_CAPABILITY 这描述了服务器向客户端报告的 IMAP 功能。应该保持默认设置。
IMAP_EMPTYTRASH 这指定了电子邮件消息在某些文件夹中应保留的时间。在指定日期之前的消息在用户登录或注销时将自动删除。这可以用于在一定时间后自动从Trash文件夹中删除电子邮件。这适用于所有文件夹,因此Sent items文件夹中的电子邮件可以在较长时间后被删除。例如,IMAP_EMPTYTRASH=Trash:7,Sent:30指定Trash文件夹中的电子邮件在 7 天后被删除,Sent文件夹中的电子邮件将在 30 天后被删除。如果指定的文件夹中存在大量电子邮件,性能将受到影响,因为每次用户登录或注销 IMAP 服务器时都会检查每个文件。在这种情况下,最好禁用此设置,并定期运行一个单独的脚本来删除旧文件。
IMAP_IDLETIMEOUT 这是客户端在空闲状态(未向服务器发出任何请求)多长时间(以秒为单位)之后,连接将被关闭。低于默认值 60 的数值可能会导致客户端连接过早终止,但是一个良好编写的客户端将会在不通知用户的情况下重新连接。如果用户报告问题,应该使用更高的数值。
IMAP_TRASHFOLDERNAME 这指定了在删除电子邮件时要使用的文件夹。
SENDMAIL 这指定了用于发送电子邮件的sendmail的路径。您应该确保它指向由第二章中安装的 Postfix 安装的可执行文件。

这是一个imapd配置文件示例:

ADDRESS=0
IMAP_CAPABILITY="IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE"
IMAP_EMPTYTRASH=Trash:7
IMAP_IDLE_TIMEOUT=60
IMAP_TRASHFOLDERNAME=Trash
MAILDIRPATH=.maildir
MAXDAEMONS=40
MAXPERIP=10
PIDFILE=/var/run/imapd.pid
PORT=143
SENDMAIL=/usr/sbin/sendmail
TCPDOPTS="-nodnslookup -noidentlookup"

测试 IMAP 服务

要启动 IMAP 服务进行测试,请运行以下命令:

/usr/lib/courier-imap/libexec/imapd.rc start

测试 IMAP 等服务的最简单方法是使用 telnet 实用程序并连接到适当的端口。这样可以避免与网络连接或客户端配置相关的任何问题。IMAP 使用端口143,因此在本地机器上 telnet 到端口143

$ telnet localhost 143
Connected to localhost.
Escape character is '^]'.
* OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION STARTTLS] Courier-IMAP ready. Copyright 1998-2004 Double Precision, Inc. See COPYING for distribution information.
1 capability
* CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE
THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION STARTTLS
1 OK CAPABILITY completed
2 login "username" "password"
2 OK LOGIN Ok.
3 namespace
* NAMESPACE (("INBOX." ".")) NIL (("#shared." ".")("shared." "."))
3 OK NAMESPACE completed.

每个命令都以标识符为前缀-这里我们使用递增的数字。第一个命令要求 IMAP 服务器列出其功能。第二个命令是用户登录,包括用户名和密码。如果成功,那么最终的命名空间命令将显示服务器已接受登录,并且客户端可以确定用户在文件夹层次结构中的位置。

这足以确认用户可以登录并发出命令。整个 IMAP 命令集非常庞大和复杂,并不适合通过 telnet 使用。

一旦正确配置了 POP3 和 IMAP 服务,它们可以在计算机启动时自动启动。如果您是从软件包安装的,那么发行商可能已经在/etc/init.d中创建了一个适当的启动脚本。根据发行版的不同,这可能会在计算机启动时启动。对于 Red Hat Linux,命令将是:

# service courier-imap add default

对于其他发行版,可能会使用chkconfig命令:

# chkconfig -add imapd

现在 IMAP 已经正确配置,是时候配置电子邮件客户端了。

使用 Mozilla Thunderbird 通过 IMAP 检索邮件

Mozilla Thunderbird 是一款流行的开源电子邮件客户端,可以从www.mozilla.org/下载。它可以与各种操作系统一起使用,包括 Windows 和 Linux。

以下是配置连接到 Courier-IMAP 服务器的步骤:

  1. 从主 Thunderbird 屏幕中,选择工具|账户设置

使用 Mozilla Thunderbird 通过 IMAP 检索邮件

  1. 点击添加帐户...按钮。在下一个屏幕上,选择电子邮件帐户,然后点击下一步。身份屏幕将打开。输入您的用户名和电子邮件地址,然后点击下一步

使用 Mozilla Thunderbird 通过 IMAP 检索邮件

  1. 服务器信息屏幕上,选择IMAP作为服务器类型,并输入传入电子邮件服务器的名称或 IP 地址。然后点击下一步按钮。

使用 Mozilla Thunderbird 通过 IMAP 检索邮件

  1. 在下一个屏幕上,输入传入用户名。这通常是 Linux 帐户名。

使用 Mozilla Thunderbird 通过 IMAP 检索邮件

  1. 最后,在账户名称字段中为电子邮件帐户提供一个有用的标签,以防将来定义另一个帐户。点击下一步

使用 Mozilla Thunderbird 通过 IMAP 检索邮件

  1. 在下一个屏幕上,总结了详细信息。点击完成保存帐户详细信息并退出帐户向导

使用 Mozilla Thunderbird 通过 IMAP 检索邮件

  1. 最后,显示账户设置屏幕,列出您刚刚定义的帐户。点击确定

使用 Mozilla Thunderbird 通过 IMAP 检索邮件

要检索消息,点击文件|获取新消息,并从菜单中选择您刚刚创建的帐户。

Thunderbird 将提示您输入密码。输入正确的密码并按Enter。然后 Thunderbird 将连接到 Courier-IMAP 并检索所有电子邮件的详细信息。如果您点击一封电子邮件,Thunderbird 将使用 IMAP 协议检索它。

摘要

在本章中,我们看到了用于检索电子邮件的两种邮件协议,POP3 和 IMAP,并解释了一些优缺点。然后介绍了 Courier-IMAP,它可以提供 POP3 和 IMAP 服务,并建议您使用 Linux 发行版的软件包。我们还描述了如何根据需要从源代码构建它。然后讨论了如何配置和测试 POP3 和 IMAP 服务,包括配置流行的电子邮件客户端。

第四章:提供 Webmail 访问

您在之前的章节中学习了如何设置和配置电子邮件服务器。现在您的电子邮件服务器已经准备好服务了,您的用户将如何访问它?在本章中,您将学习以下内容:

  • webmail 访问解决方案的好处和缺点

  • SquirrelMail webmail 软件包

  • 设置和配置 SquirrelMail

  • 什么是 SquirrelMail 插件以及它们能做什么

  • 如何使 SquirrelMail 更安全

在下一节中,我们将介绍 SquirrelMail 软件包,并检查这个和其他 webmail 访问解决方案的优缺点。之后,我们将逐步跟进 SquirrelMail 的安装和配置。接下来,我们将检查插件的安装,并包括有用插件的参考。最后,我们将提供一些关于如何保护 SquirrelMail 的提示。

webmail 解决方案

Webmail 解决方案是在服务器上运行的程序或一系列脚本,可以通过网络访问,并提供类似于传统邮件客户端的电子邮件功能。它被 Yahoo! Mail、Microsoft Hotmail、Microsoft Outlook Web Access 和 Gmail 用作其电子邮件解决方案的主要界面。您可能已经熟悉各种形式的 webmail。

尽管我们将专门研究 SquirrelMail webmail 解决方案,但 SquirrelMail 的好处和缺点适用于市场上大多数 webmail 系统。从这个角度来看,我们将从一个一般的角度来处理这个问题,然后详细介绍 SquirrelMail 软件包。

好处

本节将重点介绍安装和维护 webmail 解决方案所提供的优势。与任何列表一样,它并不完全全面。许多好处将特定于特定情况;重要的是仔细审查和考虑以下特质如何影响您的个人情况。

我们将在本节中探讨的主要优点如下:

  • 简单快捷的访问,几乎不需要设置

  • 简单的远程访问

  • 无需维护客户端软件或配置

  • 提供用户界面来配置邮件服务器选项

  • 可能的安全好处

简单快捷的访问

尽管传统的邮件访问解决方案非常适合某些情况,但通常很难设置和维护。通常,这涉及在客户端的本地计算机上安装软件并进行配置。这可能很困难,特别是在用户需要自己设置软件的情况下。配置通常更加困难,因为一些用户可能不够有能力甚至无法遵循非常详细的一系列说明。这些说明还需要为许多不同的邮件客户端在许多不同的平台上提供和维护。

然而,webmail 解决方案并没有大多数这些问题。所有用户的设置都可以在服务器上配置,因为应用程序本身驻留在服务器上。这意味着用户几乎不需要设置时间。一旦他们收到登录凭据,他们就可以访问 webmail 站点,并立即访问所有的邮件。用户能够立即访问站点发送和接收电子邮件。

由于互联网现在如此普遍,许多用户将熟悉 Google Mail 和 Windows Live Hotmail 等提供免费电子邮件服务的 webmail 站点。然而,开源软件包提供的用户界面可能更为原始,缺乏一些视觉特性。Squirrelmail 提供了访问电子邮件的功能,包括发送和接收附件,并提供了良好的用户界面。

值得一提的是,webmail 解决方案可以提供某些传统邮件客户端所称的 groupware 功能。这些功能让群体以补充电子邮件通信的方式进行沟通和协调。groupware 组件的示例包括私人日历、共享日历、会议安排、待办事项列表和其他类似工具。

这些应用程序可以预先配置,以便用户可以立即开始使用它们,而无需自行配置。SquirrelMail 网站提供了实现这些功能的多个插件。

便捷的远程访问

传统邮件访问软件的另一个问题是它不具备可移植性,因为电子邮件客户端需要在计算机上安装和配置。一旦在特定计算机上下载、安装和配置,它只能在该计算机上访问。没有网络邮件,外出的用户将无法从朋友的计算机、移动设备或机场的互联网亭访问电子邮件。

然而,在网络邮件解决方案中,可以从任何具有互联网连接的位置访问电子邮件。员工可以从任何具有互联网连接和合适浏览器的计算机访问他们的工作电子邮件。

作为管理员,您可以选择允许或拒绝用户在不安全的情况下访问电子邮件。通过要求连接加密,您可以确保用户在远程位置与服务器的通信是安全的。

无需维护客户端

即使软件邮件客户端已安装并正确配置,也必须进行维护。当发布新版本时,所有客户端都必须更新。这并不一定是一项容易的任务。软件如果不能按预期工作,可能会导致大量的支持呼叫。

在每个客户端上更新软件可能是一个非常大的管理负担。事实上,许多昂贵的软件包都是专门用于自动更新单个机器上的软件。尽管如此,通常会出现特定于每台本地机器的问题,必须单独解决。向远程分支位置或远程工作者传达说明或通知可能也很困难。使用网络邮件解决方案,这是不必要的。

与此相反,网络邮件解决方案是集中维护和管理的。网络邮件应用程序驻留在服务器上。使用网络邮件,只需要升级网络服务器和网络邮件包。任何异常或问题都可以在升级之前或期间处理。软件升级本身可以在测试系统上运行,然后再部署到实际系统上。虽然 SquirrelMail 的设置更改很少,但可以更新用户的设置,使其与更新版本中引入的更改兼容。

此外,在升级或更改邮件服务器平台时,测试工作量可以大大减少,因为只需要测试受支持的浏览器版本。建议对企业计算机强制使用特定的浏览器版本。与电子邮件客户端不同,无需在所有可能的客户端和软件平台上进行测试。

通过用户界面配置邮件服务器接口

许多传统的桌面电子邮件客户端只提供电子邮件功能,没有其他支持任务的功能(例如更改访问密码),这些任务是代表邮件用户执行的。服务器上的某些配置选项可能需要额外的软件应用程序或外部解决方案来满足这些需求。可能需要配置的邮件服务器选项的示例包括每个用户的密码和垃圾邮件过滤设置。

在 SquirrelMail 网络邮件应用程序的情况下,已开发了许多插件提供这些功能。例如,用户可以直接从网络邮件界面更改密码。此外,还有插件和系统允许用户轻松注册,无需任何直接人工干预。如果您有兴趣提供一项服务,用户可以在不需要管理开销的情况下注册,这可能很有用。

可能的安全好处

这个问题可以从两个不同的角度来看——这也是标题列为“可能的”安全好处的原因。尽管如此,这仍然是一个有趣的观点需要审查。

在软件客户端访问模型中,电子邮件传统上被下载到本地用户的计算机上,存储在一个或多个个人文件夹中。从安全的角度来看,这可能是一件坏事。系统的用户可能没有受过专业计算机管理员那样的计算机安全意识或知识。未经授权访问最终用户的计算机通常比访问配置正确且安全的服务器容易得多。这意味着偷走公司的笔记本电脑的人可能能够访问该计算机上存储的所有电子邮件。

与客户端访问模型相关的另一个缺点是,即使员工被解雇,他/她仍然可能访问存储在他/她本地办公室计算机上的所有电子邮件。在重要信息得到保护之前可能需要一定的时间。一名不满的工人可能轻松地连接一个外部存储设备到他们的本地办公室计算机,并下载他们想要的任何数据。

值得注意的是,在网络邮件模型中,所有电子邮件都是集中存储的。如果攻击者能够访问中央电子邮件服务器,他/她可能会访问该服务器上存储的所有电子邮件。然而,即使不使用网络邮件系统,如果中央邮件服务器受到损害,攻击者也可能会访问所有电子邮件。

缺点

本节重点讨论提供和支持网络邮件解决方案所带来的缺点。前一节中提到的警告适用:这个列表并不完全全面。每种情况都是独特的,可能带来独特的缺点。

我们将讨论网络邮件解决方案的以下缺点:

  • 性能问题

  • 与大量电子邮件的兼容性

  • 与电子邮件附件的兼容性

  • 安全问题

性能

传统的电子邮件客户端是按照客户端-服务器模型设计的。一个邮件服务器接受并传递电子邮件到其他邮件服务器。然而,桌面邮件客户端可以提供许多额外的提高生产力的功能,如消息排序、搜索、联系人列表管理、附件处理,以及更近期的功能,如垃圾邮件过滤和消息加密。

这些功能中的每一个可能需要一定的处理能力。当在台式电脑上存储一个用户的电子邮件时,所需的处理能力可能微乎其微,但是如果将这些功能应用到单个服务器上的大规模操作,可能会出现问题。

在审查性能问题时,重要的是考虑将访问网络邮件应用程序的潜在用户数量,并相应地调整服务器的大小。一个单一的服务器可能很容易处理大约 300 个用户,但如果用户数量显著增加,服务器负载可能会成为一个问题。

例如,搜索几年的存档邮件可能需要客户端计算机几秒钟。当一个用户使用网络邮件执行此任务时,负载将是相似的。然而,如果许多客户端在短时间间隔或同时请求此操作,服务器可能难以及时处理所有请求。这可能导致页面以较慢的速度提供,或者在极端情况下,服务器无法响应。

如果担心服务器可能无法处理特定用户负载,最好在适当条件下进行负载测试。

与大量电子邮件的兼容性

网络邮件解决方案不太适合大量邮件。这个缺点与前一个问题有关,但更多地与发送的数据量有关。即使是在相对较少的用户数量下,大量的电子邮件在网络邮件应用程序中可能很难管理。主要有以下两个原因:

  • 首先,每次查看电子邮件和列出文件夹都必须从服务器发送。在传统的邮件客户端中,客户端软件可以管理电子邮件消息,创建适合用户的列表和视图。然而,在网络邮件解决方案中,这是在服务器上执行的。因此,如果有很多用户,这种开销可能会占用服务器资源的相当大比例。

  • 其次,与网络邮件应用程序的每次交互都需要一个超文本传输协议HTTP)请求和响应。这些消息通常比电子邮件服务器和桌面邮件客户端之间的消息要大。当使用网络邮件客户端时,可能会出现较少的并行性,换句话说,较少的同时进行的事情。桌面邮件客户端可能能够同时检查几个文件夹中的新电子邮件,但网络邮件客户端通常会依次执行这些任务,如果它们自动发生的话。

与电子邮件附件的兼容性

网络邮件解决方案不太适合电子邮件附件。由于网络邮件应用程序位于远程服务器上,任何和所有的电子邮件附件都必须首先上传到该服务器上。由于一些原因,可能很难或不可能完成这个操作,特别是对于太多附件或者尺寸较大的附件来说。

由于网络邮件服务器上的存储空间有限,可能会出现上传大附件的困难。大附件可能需要很长时间通过 HTTP 协议上传,甚至在 HTTPS 上需要更长的时间。此外,上传文件可能会受到许多文件大小限制的限制。与 SquirrelMail 一起使用的编程语言 PHP 在其默认配置中对上传文件施加了 2MB 的限制。

上述问题的解决方案可能在于网络邮件访问解决方案的性质——电子邮件和邮件访问软件位于服务器上。在传统的邮件客户端中,用户通常在意识到特定电子邮件消息的内容或大小之前下载电子邮件。与此相反,在网络邮件的情况下,用户可以在不下载附件的情况下查看带有大附件的电子邮件——这对于没有高速互联网连接的用户来说是一个特别的好处。

最后,从服务器下载和上传大型电子邮件附件可能会导致用户界面的性能问题。许多用户对网络邮件应用程序中附件的上传时间感到沮丧,特别是因为在附件上传之前无法发送消息。在传统的邮件客户端中,附件会立即附加,而消息需要时间发送。

安全问题

我们将要检查的最后一个问题是安全缺陷的潜在可能性。网络邮件访问解决方案的一个重要特性也带来了潜在的问题。远程访问的好处让用户访问其邮件的本地计算机存在潜在的不安全性。

一个不受直接控制的计算机可能会被第三方控制,意图访问您的信息。通常,计算机不会记录用户的个别按键。网吧和信息亭,甚至员工的家用计算机都可能运行恶意软件。这种恶意软件可能会监视按键和访问的网站。用户必须输入他/她的密码或登录凭据才能访问系统。当这些凭据被恶意软件记录并存储在计算机上时,它们可以被第三方拦截并用于未经授权的访问。

即使我们排除恶意意图,仍然有一些情况可能会构成安全风险。例如,许多现代网络浏览器提供了在输入密码时保存密码的选项。这个密码存储在访问网站的本地计算机上。如果用户登录到网络邮件应用程序并意外地将密码保存在本地计算机上,那么任何可以访问该本地计算机的用户可能可以访问这个密码。

最后,用户可能会无意中保持登录到网络邮件应用程序。在未注销的情况下,任何可以访问该特定计算机的用户可能能够访问用户的邮件帐户。

SquirrelMail 网络邮件包

以下屏幕截图显示了 SquirrelMail 的登录界面:

SquirrelMail 网络邮件包

选择 SquirrelMail 是基于它提供的以下功能的组合:

  • 它是一个经过验证、稳定和成熟的网络邮件平台。

  • 它已经被下载了两百多万次。

  • 它是基于标准的,以纯 HTML 4.0 呈现页面,无需使用 JavaScript。

SquirrelMail 还包括以下功能(以及许多其他功能,通过灵活的插件系统):

  • 强大的 MIME 支持

  • 地址簿功能

  • 拼写检查器

  • 支持发送和接收 HTML 电子邮件

  • 模板和主题支持

  • 虚拟主机支持

以下屏幕截图显示了收件箱,您可以看到其中一些功能:

SquirrelMail 网络邮件包

SquirrelMail 安装和配置

如果您不熟悉安装网络应用程序,SquirrelMail 的安装和配置可能看起来令人生畏。但是通过接下来将要讨论的说明,可以轻松安装 SquirrelMail。

安装前提条件

在继续之前,SquirrelMail 需要安装 PHP 和支持 PHP 脚本的网络服务器。在我们的情况下,我们将使用 Apache2 网络服务器,尽管其他服务器也可以工作。

首先,我们将讨论基本要求,以及如果不符合要求该怎么办。然后,我们将讨论一些可能影响 SquirrelMail 内某些功能的更高级要求。

基本要求

在撰写本文时,SquirrelMail 的最新稳定版本是 1.4.19。以下说明适用于此版本。SquirrelMail 安装有两个基本要求。

安装 Apache2

任何支持 PHP 的现代版本的 Apache,无论是 1.x 系列还是 2.x 系列,都可以胜任。这里我们提供了使用 Apache2 的说明。要查询基于 RPM 软件包管理的系统上是否安装了 Apache,请在提示符下发出以下命令:

$ rpm -q apache

apache-1.3.20-16

如果像刚才看到的示例一样返回了 Apache 的版本,则系统上已安装了 Apache 网络服务器。

要查询基于 Debian 软件包管理的系统上是否安装了 Apache,请在提示符下发出以下命令:

$ apt-cache search --installed apache2 | grep HTTP
libapache2-mod-evasive - evasive module to minimize HTTP DoS or brute force attacks
libpoe-component-server-http-perl - foundation of a POE HTTP Daemon
libserf-0-0 - high-performance asynchronous HTTP client library
libserf-0-0-dbg - high-performance asynchronous HTTP client library debugging symbols
libserf-0-0-dev - high-performance asynchronous HTTP client library headers
nanoweb - HTTP server written in PHP
php-auth-http - HTTP authentication
apache2 - Apache HTTP Server metapackage
apache2-doc - Apache HTTP Server documentation
apache2-mpm-event - Apache HTTP Server - event driven model
apache2-mpm-prefork - Apache HTTP Server - traditional non-threaded model
apache2-mpm-worker - Apache HTTP Server - high speed threaded model
apache2.2-common - Apache HTTP Server common files

其他使用其他软件包管理系统的发行版也有类似的命令。

如果您没有安装 Apache,最好首先查看您的发行版,寻找 Apache 的副本,比如在您的操作系统安装光盘上或使用在线软件包存储库。或者,您可以访问 Apache 基金会的主页www.apache.org

PHP

安装 SquirrelMail 需要 PHP 编程语言(版本 4.1.0 或更高版本,包括所有 PHP 5 版本)。要检查系统是否安装了 PHP,只需尝试使用以下命令运行它:

$ php -v

如果命令成功,您将看到一条描述已安装的 PHP 版本的消息。如果存在 PHP 版本 4.1.0 或更高版本,则您的系统具有所需的软件。否则,您需要安装或升级当前的安装。与 Apache 一样,最好查找您的发行版以安装副本。或者,您也可以访问www.php.net

Perl

SquirrelMail 不需要 Perl 编程环境,但有它可用会使 SquirrelMail 的配置更加简单。在本章中,我们假设您将有 Perl 可用以便轻松配置 SquirrelMail。

要在基于 RPM 的系统上查询 Perl 安装,只需尝试使用以下命令运行它:

$ perl -v

如果命令成功,您将看到一条描述已安装的 Perl 版本的消息。

如果存在任何版本的 Perl,则您的系统具有所需的软件。否则,您需要安装或升级当前的安装。与 Apache 一样,最好查看您的发行版以安装副本。或者,您也可以访问www.perl.com/get.html

审查配置

您需要查看 PHP 配置文件php.ini,以确保设置正确。在大多数 Linux 系统上,此文件可能位于/etc/php.ini

php.ini是一个文本文件,可以使用 Emacs 或 vi 等文本编辑器进行编辑。首先,如果您希望用户能够上传附件,请确保选项file_uploads设置为On

; Whether to allow HTTP file uploads.
file_uploads = On

php.ini文件中您可能想要更改的下一个选项是upload_max_filesize。此设置适用于上传的附件,并确定上传文件的最大文件大小。将其更改为合理的值可能会有所帮助,例如10M

; Maximum allowed size for uploaded files.
upload_max_filesize = 10M

安装 SquirrelMail

SquirrelMail 可以通过软件包或直接从源代码安装。虽然在任一方法中都不会进行源代码编译,但使用软件包可以更轻松地进行升级。

许多不同的 Linux 和 Unix 发行版都包括 SquirrelMail 软件包。从您的发行版安装适当的软件包以使用二进制方法。在许多 Linux 发行版上,这可能是一个以squirrelmail…开头的 RPM 文件。

但是,更新的 SquirrelMail 版本可能不包括在您特定的发行版中或不可用。

以下是使用 Linux 发行版提供的 SquirrelMail 版本的优点:

  • 安装 SquirrelMail 将非常简单。

  • 它将需要更少的配置,因为它将被配置为使用 Linux 发行版选择的标准位置。

  • 更新将非常容易应用,并且迁移问题可以由软件包管理系统处理。

以下是使用 Linux 发行版提供的 SquirrelMail 版本的缺点:

  • 它可能不是最新版本。例如,可能已发布了一个修复安全漏洞的更新版本,但 Linux 发行商可能尚未创建新的软件包。

  • 有时,Linux 发行版会通过应用补丁来更改软件包。这些补丁可能会影响软件包的操作,并可能使获取支持或帮助变得更加困难。

源安装

如果您没有通过发行版安装 SquirrelMail,您将需要获取适当的 tarball。要这样做,请访问 SquirrelMail 网站www.squirrelmail.org,然后单击在此下载。在撰写本文时,此链接为www.squirrelmail.org/download.php

有两个可供下载的版本,一个是稳定版本,另一个是开发版本。除非您有特定原因选择其他版本,通常最好选择稳定版本。下载并将此文件保存到中间位置。

$ cd /tmp
$ wget http://squirrelmail.org/countdl.php?fileurl=http%3A%2F%2Fprdownloa
ds.sourceforge.net%2Fsquirrelmail%2Fsquirrelmail-1.4.19.tar.gz

接下来,解压缩 tarball(.tar.gz)文件。您可以使用以下命令:

$ tar xfz squirrelmail-1.4.19.tar.gz

将刚创建的文件夹移动到您的 Web 根文件夹。这是 Apache 提供页面的目录。在这种情况下,我们将假设/var/www/html是您的 Web 根。我们还将把笨拙的squirrelmail-1.4.3a文件夹重命名为更简单的mail文件夹。在大多数系统上,您需要超级用户root权限才能执行此操作。

# mv squirrelmail-1.4.19 /var/www/html/mail
# cd /var/www/html/mail

在这里,我们使用了名称mail,因此用户将使用的 URL 将是http://www.sitename.com/mail。您可以选择另一个名称,比如webmail,并在输入的命令中使用该目录名称,而不是mail

为 SquirrelMail 创建一个data目录,这样这个文件夹将无法从 Web 访问,也是有用且安全的。

# mv /var/www/html/mail/data /var/www/sqmdata

重要的是要使这个新创建的文件夹对 Web 服务器可写。为了能够做到这一点,您必须知道您的 Web 服务器所在的用户和组。这可能是nobodynobody, apacheapache,或者其他内容。您需要验证这一点;它将在您的httpd.conf文件中列出为UserGroup条目。

# chown -R nobody:nobody /var/www/sqmdata

最后,我们将创建一个目录来存储附件。这个目录很特别,虽然 Web 服务器应该有写入附件的权限,但不应该有读取权限。我们使用以下命令创建这个目录并分配正确的权限:

# mkdir /var/www/sqmdata/attachments
# chgrp -R nobody /var/www/sqmdata/attachments
# chmod 730 /var/www/sqmdata/attachments

SquirrelMail 现在已经正确安装。所有文件夹都已设置正确的权限,以保护中间文件不受窥视。

注意

如果用户中止包含上传附件的消息,则 Web 服务器上的附件文件将不会被删除。在服务器上创建一个 cron 作业以从附件目录中删除多余的文件是一个好习惯。例如,创建一个名为remove_orphaned_attachments的文件,并将其放在/etc/cron.daily目录中。编辑文件,添加以下行:

 #!/bin/sh
#!/bin/sh
rm `find /var/www/sqmdata/attachments -atime +2 | grep -v "\."| grep -v _`

这将每天运行,并搜索 SquirrelMail 附件目录中的孤立文件,并将其删除。

配置 SquirrelMail

SquirrelMail 是通过config.php文件进行配置的。为了帮助配置,还提供了一个conf.pl Perl 脚本。这些文件位于基本安装目录中的config/目录中。

# cd /var/www/html/mail/config
# ./conf.pl

运行此命令后,您应该看到以下菜单:

配置 SquirrelMail

要从菜单中选择项目,请输入适当的字母或数字,然后按Enter键。随着 SquirrelMail 的开发,人们注意到 IMAP 服务器的行为并不总是一致的。为了充分利用您的设置,您应该告诉 SquirrelMail 您正在使用哪个 IMAP 服务器。要加载您的 IMAP 服务器的默认配置,请输入D选项,并输入您安装的 IMAP 服务器的名称。本书涵盖了 Courier IMAP 服务器,所以您应该选择它。再次按Enter,您将返回到主菜单。

我们将在菜单的各个子部分中移动并配置适当的选项。

输入 1,然后按Enter键选择组织首选项。您将得到一个可以更改的项目列表。您可能希望编辑组织名称、组织标志组织标题字段。一旦您对这些进行了满意的修改,输入 R 返回到主菜单。

然后,输入 2 访问服务器设置。这允许您设置 IMAP 服务器设置。重要的是,您要将字段更新为正确的值。

在我们的情况下,更新 IMAP 设置更新 SMTP 设置的值应该是正确的。如果您想要使用位于不同机器上的 IMAP 或 SMTP 服务器,您可能希望更新这些值。

按下 R,然后按Enter键返回到主菜单。

接下来,输入 4 访问常规选项。您需要修改此部分中的两个选项。

  • 数据目录为/var/www/sqmdata

  • 附件目录为/var/www/sqmdata/attachments

  • 输入 R,然后按Enter键返回主菜单。输入 S,然后按Enter键两次将设置保存到配置文件中。最后,输入 Q,然后按Enter键退出配置应用程序。

我们已经完成了配置 SquirrelMail 的基本操作所需的设置。您可以随时返回此脚本以更新您设置的任何设置。还有许多其他选项需要设置,包括主题和插件。

SquirrelMail 插件

插件是扩展或添加功能到软件包的软件。SquirrelMail 是从头开始设计的,非常易于扩展,并包括强大的插件系统。目前,在 SquirrelMail 网站上有 200 多个不同的插件可用。它们可以在www.squirrelmail.org/plugins.php获取。

它们提供的功能包括管理工具、视觉增强、用户界面调整、安全增强,甚至天气预报。在接下来的部分,我们将首先介绍如何安装和配置插件。之后,我们将介绍一些有用的插件,它们的功能,如何安装它们,等等。

安装插件

这些 SquirrelMail 的附加功能旨在简单设置和配置。事实上,它们中的大多数都遵循完全相同的安装过程。但是,有些需要自定义设置说明。对于所有插件,安装过程如下:

  1. 下载并解压插件。

  2. 如有需要,执行自定义安装。

  3. conf.pl中启用插件。

示例插件安装

在本节中,我们将介绍兼容性插件的安装。为了安装为旧版本 SquirrelMail 创建的插件,需要此插件。无论您的安装有多简单,兼容性插件很可能是您设置的一部分。

下载和解压插件

SquirrelMail 的所有可用插件都列在 SquirrelMail 网站上,网址为www.squirrelmail.org/plugins.php

某些插件可能需要特定版本的 SquirrelMail。请验证您已安装此版本。一旦找到插件,请将其下载到 SquirrelMail 根文件夹中的plugins/目录。

您可以通过单击 SquirrelMail 插件网页上杂项类别中的插件页面上的杂项类别来找到兼容性插件。此页面列有杂项类别中的插件列表。找到兼容性,然后单击详细信息和下载,然后下载最新版本。

下载和解压插件

将 tarball 下载到 SquirrelMail 插件目录。

# cd /var/www/mail/plugins
# wget http://squirrelmail.org/countdl.php?fileurl=http%3A%2F%2Fwww.
squirrelmail.org%2Fplugins%2Fcompatibility-2.0.14-1.0.tar.gz

一旦您将插件下载到plugins目录中,使用以下命令解压缩它:

# tar zxvf compatibility-2.0.14-1.0.tar.gz

注意

如果已经安装了同名插件,则可能会覆盖其文件。请验证您是否没有同名插件,或在解压 tarball 之前保存文件。

执行自定义安装

当前版本的兼容性插件不需要任何额外的配置。但是,您应该始终检查插件的文档,因为某些其他插件可能需要自定义安装。一旦您解压了插件包,安装说明将列在新创建的plugin目录中的INSTALL文件中。在在配置管理器中启用插件之前,建议您先检查安装说明,因为某些插件可能需要自定义配置。

在 conf.pl 中启用插件

在配置编辑器的主菜单中,选项号码 8 用于配置和启用插件。启动conf.pl并选择选项8

# cd /var/www/mail/plugins
# cd ../config
# ./conf.pl
SquirrelMail Configuration : Read: config_default.php (1.4.0) 
--------------------------------------------------------- 
Main Menu -- 
[...] 
7\. Message of the Day (MOTD) 
8\. Plugins 
9\. Database
[...] 
Command >>

当您第一次选择此选项时,应该会得到以下显示:

在 conf.pl 中启用插件

所有已安装和启用的插件都列在已安装插件列表下。所有已安装但未启用的插件都列在可用插件列表下。

一旦您在plugins/目录中解压缩了一个插件,它将显示在可用插件下。正如您在上一张图中所看到的,有许多已安装的插件,但没有一个是启用的。由于一个故障或配置错误的插件可能会导致 SquirrelMail 停止正常工作,建议逐个启用插件,并在每个插件启用后验证 SquirrelMail 是否正常工作。要启用兼容性插件,请在可用插件列表中找到它(在本例中,编号4) 并按Enter键。兼容性插件现在已安装。可以通过在已安装插件列表中找到它们并输入其编号并按Enter来禁用插件。

有用的插件

现在我们将看到一些有用的 SquirrelMail 插件,您可能考虑安装。

这些信息已经被编制成一个有用的参考,以便在决定是否安装插件时使用。每个插件包含四个特定的类别:

  • 类别:插件在 SquirrelMail 网站上列出的类别

  • 作者:按时间顺序编写插件的作者

  • 描述:插件功能的简要描述

  • 要求:插件成功安装的先决条件列表

插件名称 类别 作者 描述 要求
兼容性插件 杂项 Paul Lesneiwski 此插件允许任何其他插件访问使其向后(和向前)兼容大多数广泛使用的 SM 版本所需的功能和特殊变量。这消除了在许多插件中重复某些功能的需要。它还提供了帮助检查插件是否已安装和设置正确的功能。
安全登录 登录 Graham Norbury, Paul Lesneiwski 如果 SquirrelMail 登录页面尚未被引用的超链接或书签请求,此插件将自动启用安全的 HTTPS/SSL 加密连接。可选地,在成功登录后可以再次关闭安全连接。 SquirrelMail 版本 1.2.8 或更高版本,具有加密功能的 HTTPS/SSL 能力的 Web 服务器已在 SquirrelMail 安装中正常工作。
HTTP 身份验证 登录 Tyler Akins, Paul Lesniewski 如果您将 SquirrelMail 放在 Web 服务器上受密码保护的目录后,并且如果 PHP 可以访问 Web 服务器使用的用户名和密码,此插件将绕过登录屏幕并使用该用户名/密码对。 SquirrelMail >= 1.4.0
忘记密码 登录 Tyler Akins, Paul Lesneiwski 此插件提供了一个解决方案,用于避免浏览器潜在的漏洞,自动存储输入到网页中的用户名和密码。 SquirrelMail >= 1.0.1
HTML 邮件 撰写 Paul Lesneiwski 此插件允许使用 IE 5.5(及更高版本)和更新的 Mozilla(基于 Gecko 的浏览器,如 Firefox)的用户以 HTML 格式撰写和发送电子邮件。 SquirrelMail >= 1.4.0
快速保存 撰写 Ray Black III, Paul Lesneiwski 此插件会自动保存正在撰写的消息,以防止由于从撰写屏幕浏览到更严重的问题(如浏览器或计算机崩溃)而导致消息内容意外丢失。 SquirrelMail >= 1.2.9,兼容性插件,支持 JavaScript 的浏览器
检查配额使用情况(v) 视觉添加 Kerem Erkan 此插件将检查并显示用户的邮件配额状态。 SquirrelMail 1.4.0+;兼容性插件,版本 2.0.7+,UNIX,IMAP 或已安装和配置的 cPanel 配额
发送确认 杂项 Paul Lesneiwski 在成功发送消息后显示确认消息,以及其他功能。 SquirrelMail >= 1.2.0,兼容性插件
超时用户 杂项 Ray Black III,Paul Lesneiwski 如果用户闲置一段时间,将自动注销用户。 兼容性插件
电子邮件页脚 杂项 Ray Black III,Paul Lesneiwski 此插件会自动在使用 SquirrelMail 发送的消息末尾附加自定义页脚。 SquirrelMail >= 1.4.2
更改密码 更改密码 Tyler Akins,Seth E. Randall 允许用户使用 PAM 或 Courier 身份验证模块更改密码。 SquirrelMail >= 1.4.0
通讯录导入导出 通讯录 Lewis Bergman,Dustin Anders,Christian Sauer,Tomas Kuliavas 允许从CSV(逗号分隔值)文件导入通讯录。 SquirrelMail >= 1.4.4
插件更新(v0.7) 管理员的解脱 Jimmy Conner 检查当前运行的插件是否有更新。 SquirrelMail >= 1.4.2

还有许多其他插件可处理假期消息、日历、共享日历、笔记、待办事项列表、交换服务器集成、书签、天气信息等等。在 SquirrelMail 网站的插件部分查看所有可用的插件。

保护 SquirrelMail

SquirrelMail 软件包本身相当安全。它写得很好,不需要 JavaScript 来运行。但是,可以采取一些预防措施,以使 SquirrelMail 成为一个安全的邮件处理解决方案。

  • 使用 SSL 连接:通过使用 SSL 连接,您可以确保所有通信都将被加密,因此用户名、密码和机密数据在传输过程中不会被拦截。这可以通过安装Secure Login 插件来实现。显然,还需要配置用于安全 SSL 访问的 Web 服务器;证书很可能需要生成或获取。

  • 超时未活动用户:用户可能会保持登录状态并在完成后忽略注销。为了对抗这一点,应在一定时间后注销不活动的用户。Timeout User 插件可以实现这一点。

  • 对抗“记住密码”:许多现代浏览器提供记住用户密码的功能。尽管这很方便,但这可能是一个很大的安全漏洞,特别是如果用户位于公共终端。为了对抗这一点,安装Password Forget 插件。该插件将更改用户名和密码输入字段中的名称,使浏览器更难建议给未来的用户。

  • 不要安装危害安全的插件:Quick Save,HTML MailView As HTML这样的插件可能会危害安全。

摘要

现在您已经完成了本章,您应该有一个可用的 SquirrelMail 安装,以及对 Web 邮件解决方案的优缺点有更深入的了解。您应该熟悉 Web 邮件解决方案的优缺点。优点包括远程访问、单一的中心维护点和更简单的测试;而缺点包括潜在的性能问题和允许来自潜在受损计算机的远程访问的安全风险。

现在您已经了解了 SquirrelMail 的主要特点,包括其灵活性和插件的可用性,以及安装 SquirrelMail 的先决条件,以及如何确定它们是否已安装。

您还学会了如何配置 SquirrelMail,包括定位、安装和配置插件。您已经了解了一个关键插件——兼容性插件的安装过程。还介绍了几个其他有用的插件。最后,您还学会了一些提高 SquirrelMail 安全性的方法,包括 Web 服务器配置和一些适当的插件。

第五章:保护您的安装

对于您的 SMTP 服务器可能发生的所有事情,最糟糕的可能就是它被滥用为开放中继-一个未经您许可就向第三方中继邮件的服务器。 这将消耗大量带宽(可能会很昂贵),耗尽服务器资源(可能会减慢或停止其他服务),并且在时间和金钱上都可能很昂贵。 更严重的后果是,您的电子邮件服务器很可能最终会出现在一个或多个黑名单上,任何引用这些列表的电子邮件服务器都将拒绝接受来自您的服务器的任何邮件,直到您证明它是中继安全的。 如果您需要使用电子邮件来开展业务,您将面临一个大问题。

本章将解释如何:

  • 保护 Postfix 免受中继滥用

  • 区分静态分配和动态分配的 IP 地址

  • 使用 Postfix 为静态 IP 地址配置中继权限

  • 使用 Cyrus SASL 进行来自不可预测和动态 IP 地址的身份验证

  • 使用安全套接字层防止用户名和密码以明文形式发送

  • 配置 Postfix 以打败或至少减缓字典攻击,其中电子邮件发送到域内的许多电子邮件地址,希望其中一些能够到达有效的收件人

配置 Postfix 网络映射

当互联网主要由学术界使用时,没有人需要保护他们的邮件服务器免受中继滥用。 实际上,没有多少人拥有邮件服务器,因此允许其他没有电子邮件服务器的人使用您的服务器中继电子邮件被视为对他们的服务。

随着很快就被称为垃圾邮件的人的出现,情况发生了变化。 他们会滥用开放中继向大量远程收件人发送广告,使邮件服务器的所有者为流量付费。

这就是邮件管理员开始限制处理中继权限的时候。 他们过去只允许受信任的 IP 地址进行中继,拒绝来自其他 IP 地址的消息。 在这种情况下,受信任的 IP 地址是指可以静态关联(参见静态 IP 范围部分)到属于已知用户的主机的 IP 地址,或者已知属于受信任网络的 IP 地址范围。 这在大多数计算机上运行良好,因为大多数计算机都会有静态 IP 地址(IP 地址不会随时间改变)。

然而,当用户变得移动并使用拨号提供商访问互联网并希望在未知位置使用邮件服务器时,必须找到一种新的方法。 接入提供商会给这些用户动态 IP 地址,也就是说,他们的 IP 地址每次拨号时都会更改。

突然之间,用来区分好用户和坏用户的标准消失了。 邮件管理员要么必须放宽中继权限,允许整个潜在不受信任的 IP 网络使用中继,要么必须找到另一种处理动态 IP 地址中继的方法。 随着时间的推移,出现了几种处理动态 IP 地址中继的方法,例如:

  • SMTP-after-POP

  • 虚拟专用网络

  • SMTP 身份验证

这三种方法在其要求和工作方式上有所不同。 以下各节详细介绍了每种方法。

SMTP-after-POP

从历史上看,许多互联网连接都是拨号连接; 如果一个人希望发送电子邮件,他/她必须离线撰写邮件,启动拨号连接,然后告诉电子邮件客户端“发送和接收”邮件。 在这种情况下,邮件客户端首先发送邮件(通过 SMTP),然后检查服务器(通过 POP)是否有新邮件- SMTP 部分发生在 POP 部分之前。

这使得 SMTP 服务器无法找出发件人是否应该被允许中继,因为动态 IP 与使发件人成为受信任主机的任何其他标准无关。ISP 将能够识别拨号连接的 IP 地址作为他们自己的 IP 地址,并允许中继。来自他们自己网络之外的任何连接通常都会被拒绝。对于一个有着企业网络之外用户的小组织来说,要跟踪所有潜在的有效源 IP 地址是不可能的。

然而,交易可以被颠倒过来,检查邮件可以在发送邮件之前进行。检查邮件需要密码,这意味着用户可以被认证。流行的电子邮件客户端现在可以在启动时检查电子邮件,并定期检查新的电子邮件。如果 SMTP 服务器可以被告知特定 IP 地址的用户已通过 POP 服务器进行了身份验证,它可以允许中继。这就是 SMTP-after-POP 的本质。SMTP 服务器需要知道特定 IP 地址是否有经过身份验证的 POP 用户连接到它。

在最后一次连接到 POP 服务器之后,用户连接的有效时间必须有一个时间限制,否则一个旅行推销员可能会留下一百个不同的 IP 地址作为一个星期的有效中继主机,其中一个以后可能被垃圾邮件发送者占用。如今,电子邮件通常是在用户在线时编写的,并在定期自动检查新邮件之间发送。因此,发送到 SMTP 服务器的任何已编写的电子邮件通常会在进行 POP3 请求后的几分钟内发送,因此时间段可以很短,通常是几十分钟。

SMTP-after-POP 的缺点是,即使您只想允许中继消息,您也需要一个 POP 服务器。如果您不需要它,POP 服务器将使服务器的设置变得复杂。它还可能将您的 SMTP 服务器的更新绑定到您的 POP 服务器以保持兼容性。而且 POP 不是一种安全的身份验证方法,因为它可以被欺骗。

虚拟专用网络

虚拟专用网络VPN)在验证 VPN 成功后,为客户端分配另一个私有 IP 地址。VPN 服务器将在已知的区块中分配 IP 地址。SMTP 服务器可以配置为允许来自分配给 VPN 的 IP 地址的邮件客户端进行中继。

再次强调,仅仅为了中继邮件而运行 VPN 需要大量的工作。只有在通过 VPN 提供额外的资源和服务时才会有回报,例如访问共享存储、数据库、内部网站或应用程序。

SMTP 身份验证

SMTP 身份验证,也称为SMTP AUTH,使用不同的方法来识别有效的中继用户。它要求邮件客户端在 SMTP 对话期间向 SMTP 服务器发送用户名和密码,如果认证成功,它们可以进行中继。

它比运行一个完整的 POP 服务器或 VPN 要简单,而且它解决了在 SMTP 服务器中出现的问题。学会如何为一系列受信任的静态 IP 地址配置服务器后,您将了解如何提供 SMTP AUTH 所需的条件。

静态 IP 范围

默认情况下,Postfix 只允许来自自己的网络的主机中继消息。可信任的网络是您为网络接口配置的网络。运行ifconfig -a以获取已在系统上配置的列表。

如果您想更改默认设置,您可以使用mynetworks_style参数使用一些通用值,或者在main.cf中的mynetworks参数的值中提供显式的 IP 地址范围。

通用中继规则

要配置通用中继规则,您需要将以下值之一添加到main.cf中的mynetworks_style参数中:

  • host: 如果你配置mynetworks_style = host,Postfix 将只允许它运行的主机的 IP 地址发送消息到远程目的地。如果你只提供一个 webmail 界面,这可能是可以接受的,但没有桌面客户端能够连接。

  • class: 如果你配置mynetworks_style = class,Postfix 将允许它服务的网络类(A/B/C 网络类)中的每个主机进行中继。网络类指定了一系列 IP 地址,大约 255 个(C 类),65000 个(B 类),或者 1600 万(A 类)地址。

显式中继规则

显式中继规则允许更精细的中继权限。要使用这个,你需要理解用于指定网络地址范围的符号。如果你的网络跨越了从 192.168.1.0 到 192.168.1.255 的范围,那么这可以被指定为 192.168.1.0/24。24 被用作 32 位网络地址的前 24 位对于每个客户端都是相同的。如果你使用 DHCP 服务器(例如,在你的 Linux 服务器或为 DSL 连接提供防火墙),你的网络地址范围可能会被该设备定义,并且你应该在你的 Postfix 设置中使用适当的值。如果你手动分配 IP 地址并硬编码它们,你可以将每个 IP 地址单独指定为/32 范围,或者你可以确保每个 IP 地址在你分配它们后落入一个易于识别的范围内。A 类网络 10.0.0.0/8,B 类网络范围在 172.16.0.0 到 172.31.255.255 之间的 16 个,以及 C 类网络范围在 192.168.0.0 到 192.168.255.255 之间的 256 个。这些都可以用于私人网络地址,并且可以用于内部网络地址。

你可以在main.cfmynetworks参数中添加一个远程和本地主机和/或网络的列表。如果你想允许本地主机、LAN 中的所有主机(在下面的示例中 IP 地址为10.0.0.010.0.0.254),以及你家中的静态 IP(这里为192.0.34.166)作为一个列表以 CIDR 表示,如下例所示:

mynetworks = 127.0.0.0/8, 10.0.0.0/24, 192.0.34.166/32

一旦你重新加载 Postfix,新的设置就会生效。

动态 IP 范围

在前一节中,你看到了如何允许静态 IP 地址进行中继。本节将展示如何配置 Postfix 允许动态 IP 地址进行中继。

尽管如本章介绍中所述,有几种方法可以实现这一点,但我们只会描述 SMTP 认证的方法。它提供了一个简单而稳定的机制,但设置并不简单。原因是 SMTP AUTH 并不是由 Postfix 自己处理的。另一个软件模块 Cyrus SASL 需要提供和处理 SMTP AUTH 给邮件客户端。你需要配置 Cyrus SASL,Postfix 以及它们之间的相互操作。

Cyrus SASL

Cyrus SASL(cyrusimap.web.cmu.edu/)是卡内基梅隆大学对 SASL 的实现。SASL简单认证和安全层),是在 RFC 2222(www.ietf.org/rfc/rfc2222.txt)中描述的认证框架。

SASL 旨在为任何需要使用或提供认证服务的应用程序提供一个与应用程序无关的认证框架。

Cyrus SASL 并不是今天唯一可用的 SASL,但它是第一个出现并在各种应用程序中使用的。例如 Postfix,Sendmail,Mutt 和 OpenLDAP。为了使用 Cyrus SASL,你需要了解它的架构,各个层是如何协同工作的,以及如何配置层的功能。

SASL 层

SASL 由三层组成——认证接口,机制方法。每个层都在处理认证请求时负责不同的工作。

认证过程通常经历以下步骤:

  1. 客户端连接到 SASL 服务器。

  2. 服务器宣布其能力。

  3. 客户端识别在列出的功能中进行身份验证的选项。它还识别可以选择以处理身份验证的机制列表。

  4. 客户端选择一种机制并计算出一条编码消息。消息的确切内容取决于所使用的机制。

  5. 客户端向服务器发送命令AUTH <机制> <编码消息>

  6. 服务器接收身份验证请求并将其交给 SASL。

  7. SASL 识别机制并解码编码的消息。解码取决于所选择的机制。

  8. SASL 联系身份验证后端以验证客户端提供的信息。它确切地寻找什么取决于所使用的机制。

  9. 如果它可以验证信息,它将告诉服务器,服务器应允许客户端中继消息。如果无法验证信息,它将告诉服务器,服务器可能拒绝客户端中继消息。在这两种情况下,服务器都会告诉客户端身份验证是否成功或失败。

让我们在以下部分更仔细地看一下 SASL 的三个层。

身份验证接口

在我们刚刚讨论的 1 到 5 步和第 9 步中,您可以看到客户端和服务器交换数据以处理身份验证。这部分通信发生在身份验证接口中。

尽管 SASL 定义了必须交换的数据,但它并没有指定数据在客户端和服务器之间如何通信。它将这留给它们特定的通信协议,这就是为什么 SASL 可以被各种服务使用,如 SMTP、IMAP 或 LDAP。

注意

SASL 并不像 SMTP 协议那样古老(参见:RFC 821)。它是后来在 RFC 2554 中添加的(www.ietf.org/rfc/rfc2554.txt),该文档描述了SMTP 身份验证的服务扩展

服务器提供 SMTP 身份验证以及其他功能的 SMTP 对话如下:

$ telnet mail.example.com 25
220 mail.example.com ESMTP Postfix
EHLO client.example.com
250-mail.example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-ENHANCEDSTATUSCODES
250-AUTH PLAIN LOGIN CRAM-MD5 DIGEST-MD5 1)
250-AUTH=PLAIN LOGIN CRAM-MD5 DIGEST-MD5 2)
250 8BITMIME
QUIT

  • 250-AUTH PLAIN LOGIN CRAM-MD5 DIGEST-MD5 1): 这一行告诉客户端服务器提供SMTP AUTH。它由两个逻辑部分组成。第一部分250-AUTH宣布了SMTP AUTH的功能,行的其余部分是客户端可以选择其首选项的可用机制的列表。

  • 250-AUTH=PLAIN LOGIN CRAM-MD5 DIGEST-MD5 2): 这一行重复了上面的行,但在宣布 SMTP 身份验证的方式上有所不同。在250-AUTH之后,它添加了一个等号,就像这样250-AUTH=。这是为了那些不遵循 SASL 最终规范的损坏客户端。

机制

机制(如步骤 4 到 7 中描述的)代表 SASL 的第二层。它们确定身份验证期间使用的验证策略。SASL 已知有几种机制。它们在传输数据的方式和传输过程中的安全级别上有所不同。最常用的机制可以分为明文共享密钥机制。

您永远不应该让 Postfix 向客户端提供的一种机制是匿名机制。我们将首先看一下这个。

  • anonymous: 匿名机制要求客户端发送任何它想要的字符串。它旨在允许匿名访问全局 IMAP 文件夹,但不适用于 SMTP。在 AUTH 行中提供ANONYMOUS的 SMTP 服务器最终会被滥用。您不应该在 SMTP 服务器中提供这个选项!Postfix 在默认配置中不提供匿名访问。

  • plaintext: Cyrus SASL 知道PLAINLOGIN明文机制。LOGINPLAIN几乎相同,但用于不完全遵循最终 SASL RFC 的邮件客户端,如 Outlook 和 Outlook Express。这两种机制都要求客户端计算用户名和密码的 Base64 编码字符串,并将其传输到服务器进行认证。明文机制的好处是几乎每个现在使用的邮件客户端都支持它们。坏消息是,如果在没有传输层安全性TLS)的情况下使用明文机制,它们是不安全的。这是因为 Base64 编码的字符串只是编码,而不是加密——它很容易被解码。但是,在传输层加密会话期间使用明文机制传输一个是安全的。然而,如果使用 TLS,它将保护 Base64 编码的字符串免受窃听者的侵害。

  • shared secret: Cyrus SASL 中可用的共享密钥机制有CRAM-MD5DIGEST-MD5。基于共享密钥的身份验证具有完全不同的策略来验证客户端。它基于客户端和服务器都共享一个秘密的假设。选择共享密钥机制的客户端只会告诉服务器特定的共享密钥机制的名称。然后,服务器将生成一个基于他们的秘密的挑战,并将其发送给客户端。然后客户端生成一个响应,证明它知道这个秘密。在整个认证过程中,既不会发送用户名也不会发送密码。这就是为什么共享密钥机制比之前提到的机制更安全。然而,最受欢迎的邮件客户端 Outlook 和 Outlook Express 不支持共享密钥机制。

注意

在异构网络上,您可能最终会同时提供明文和共享密钥机制。

现在已经介绍了机制,只剩下一层——方法层。这是配置和处理保存凭据的数据存储的查找的地方。下一节将告诉您更多关于方法的信息。

方法

SASL 所指的最后一层是方法层。方法由 Cyrus SASL 安装目录中的库表示。它们用于访问数据存储,Cyrus SASL 不仅将其称为方法,还将其称为认证后端。在 SASL 拥有的许多方法中,最常用的是:

  • rimap: rimap方法代表远程 IMAP,使 SASL 能够登录到 IMAP 服务器。它使用客户端提供的用户名和密码。成功的 IMAP 登录是成功的 SASL 认证。

  • ldap: ldap方法查询 LDAP 服务器以验证用户名和密码。如果查询成功,则认证成功。

  • kerberos: kerberos方法使用流行的 Kerberos 方法,并检查 Kerberos 票证。

  • Getpwent/shadow: getpwentshadow方法访问系统的本地用户密码数据库,以验证认证请求。

  • pam: pam方法访问您在 PAM 设置中配置的任何 PAM 模块,以验证认证请求。

  • sasldb: sasldb方法读取甚至写入 Cyrus SASL 的名为 sasldb2 的数据库。通常,此数据库与 Cyrus IMAP 一起使用,但也可以在没有 IMAP 服务器的情况下使用。

  • sql: 此方法使用 SQL 查询来访问各种 SQL 服务器。目前支持的有 MySQL、PostgreSQL 和 SQLite。

现在您已经了解了 SASL 架构的三个层,是时候来看看处理它们之间所有请求的 SASL 服务了。它被称为密码验证服务,将在接下来的部分中进行描述。

密码验证服务

密码验证服务处理来自服务器的认证请求,进行特定于机制的计算,调用方法查询认证后端,最终将结果返回给发送认证请求的服务器。

注意

在 Postfix 的情况下,处理认证请求的服务器是smtpd守护程序。在Postfix SMTP AUTH 配置部分,您将学习如何配置smtpd守护程序以选择正确的密码验证服务。

Cyrus SASL 2.1.23 版本,目前的最新版本,为我们提供了三种不同的密码验证服务:

  • saslauthd

  • auxprop

  • authdaemond

您的邮件客户端可能成功使用的机制以及 Cyrus SASL 在认证期间可以访问的方法取决于您告诉 Postfix 使用的密码验证服务。

  • saslauthd:saslauthd是一个独立的守护程序。它可以作为 root 运行,这样就具有访问仅限 root 访问的源所需的特权。但是,saslauthd在支持的机制范围上受到限制;它只能处理明文机制。

  • auxprop:auxprop辅助属性插件的简称,这是 Project Cyrus 邮件服务器架构中使用的术语。auxprop代表一个库,被提供认证的服务器使用。它以使用它的服务器的特权访问源。与saslauthd不同,auxprop可以处理 Cyrus SASL 认证框架中提供的每种机制。

  • authdaemond:authdaemond是一个专门编写的密码验证服务,用于使用 Courier 的authdaemond作为密码验证器。这样,您就可以访问 Courier 可以处理的任何认证后端。这个auxprop插件只能处理明文机制。

以下表格为您提供了密码验证服务(方法)可以处理的机制的概述:

方法/机制 PLAIN LOGIN CRAM-MD5 DIGEST-MD5
saslauthd
auxprop
authdaemond

只有auxprop密码验证服务能够处理更安全的机制;saslauthdauthdaemond只能处理明文机制。

现在我们已经介绍了一些 Cyrus SASL 理论,现在是时候安装它了。这正是我们在接下来的部分要做的事情。

安装 Cyrus SASL

您的系统上很可能已经安装了 Cyrus SASL。但是,各种 Linux 发行版已经开始将 Cyrus SASL 安装在与典型默认位置/usr/lib/sasl2不同的位置。要检查 Cyrus SASL 是否安装在您的服务器上,可以运行软件包管理器并查询cyrus-sasl,或者运行find。对于 Red Hat 软件包管理器(在 Fedora Core 11 上)的查询,如果安装了 SASL,将返回类似于以下内容:

$ rpm -qa | grep sasl
cyrus-sasl-2.1.18-2.2
cyrus-sasl-devel-2.1.18-2.2
cyrus-sasl-plain-2.1.18-2.2
cyrus-sasl-md5-2.1.18-2.2

对于 Ubuntu 上的dpkg查询,如果安装了 SASL,将返回类似于以下内容:

$ dpkg -l | grep sasl
ii libsasl2-2 2.1.22.dfsg1-23ubuntu3
Cyrus SASL - authentication abstraction libr
ii libsasl2-modules 2.1.22.dfsg1-23ubuntu3
Cyrus SASL - pluggable authentication module2

查找libsasl*.*的结果如下:

$ find /usr -name 'libsasl*.*'
/usr/lib/libsasl.so.7.1.11
/usr/lib/libsasl2.so
/usr/lib/libsasl.la
/usr/lib/libsasl2.so.2.0.18
/usr/lib/libsasl.a
/usr/lib/libsasl2.a
/usr/lib/libsasl2.la
/usr/lib/sasl2/libsasldb.so.2.0.18
/usr/lib/sasl2/libsasldb.so.2
/usr/lib/sasl2/libsasldb.so
/usr/lib/sasl2/libsasldb.la
/usr/lib/libsasl.so.7
/usr/lib/libsasl.so
/usr/lib/libsasl2.so.2

这证明您的系统上已安装了 SASL。要验证 SASL 库的位置,只需像这样运行ls

安装 Cyrus SASL

如前所述,您的发行版可能会将它们放在其他位置。在这种情况下,find方法将找到正确的位置,或者您的发行版的文档应该提供这些信息。

如果您没有安装 Cyrus SASL,您将需要使用软件包管理器获取它,或者手动安装它。

Cyrus 的最新版本通常可以从cyrusimap.web.cmu.edu/downloads.html下载。要下载 2.1.23 版本(始终选择最新的稳定版本,而不是开发版本),请执行以下命令:

$ cd /tmp
$ wget ftp://ftp.andrew.cmu.edu/pub/cyrus-mail/cyrus-sasl-2.1.23.tar.gz
$ tar xfz cyrus-sasl-2.1.23.tar.gz
$ cd cyrus-sasl-2.1.23

下载并解压源文件后,进入源目录并运行configure。典型的源配置如下:

$ ./configure \
installingCyrus SASL--with-plugindir=/usr/lib/sasl2 \
--disable-java \
--disable-krb4 \
--with-dblib=berkeley \
--with-saslauthd=/var/state/saslauthd \
--without-pwcheck \
--with-devrandom=/dev/urandom \
--enable-cram \
--enable-digest \
--enable-plain \
--enable-login \
--disable-otp \
--enable-sql \
--with-ldap=/usr \
--with-mysql=/usr \
--with-pgsql=/usr/lib/pgsql

这将配置 Cyrus SASL 以为您提供明文和共享密钥机制,并将构建saslauthd并为您提供包括对 MySQL 和 PostgreSQL 的支持在内的 SQL 方法。

configure脚本完成后,运行make,成为root,然后运行make install

$ make
$ su -c "make install"
Password:

Cyrus SASL 将安装到/usr/local/lib/sasl2,但它将期望在/usr/lib/sasl2中找到库。您需要创建这样的符号链接:

$ su -c "ln -s /usr/local/lib/sasl2 /usr/lib/sasl2" 
Password:

最后,您需要检查 SASL 日志消息是否会被syslogd捕获并写入日志文件。Cyrus SASL 记录到syslog auth设施。检查您的syslogd配置,通常是/etc/syslog.conf,看看它是否包含捕获 auth 消息的行。

$ grep auth /etc/syslog.conf
auth,authpriv.* /var/log/auth.log
*.*;auth,authpriv.none -/var/log/syslog
auth,authpriv.none;\
auth,authpriv.none;\

如果找不到条目,请添加以下内容,保存文件,然后重新启动syslogd

auth.* /var/log/auth.log

完成所有这些后,您就可以配置 SASL 了。

配置 Cyrus SASL

在返回到 Postfix 并处理特定于 Postfix 的 SMTP AUTH 设置之前,始终配置和测试 Cyrus SASL 非常重要。

遵循此过程的原因非常简单。一个无法进行身份验证的认证框架对于使用它的任何其他应用程序都没有帮助。当问题与 Cyrus SASL 相关时,您很可能会花费数小时来调试 Postfix。

要了解必须在何处以及如何配置 SASL,需要记住它是一个认证框架,旨在为许多应用程序提供其服务。这些应用程序可能对要使用的密码验证服务以及要提供的机制以及用于访问认证后端的方法有完全不同的要求。

Cyrus 是使用特定于应用程序的文件进行配置的。每个客户端应用程序的配置都在单独的文件中。当应用程序连接到 SASL 服务器时,它会发送其应用程序名称。Cyrus 使用此名称来查找要使用的正确配置文件。

在我们的情景中,需要 SMTP AUTH 的应用程序是 Postfix 中的smtpd守护程序。当它联系 SASL 时,它不仅发送认证数据,还发送其应用程序名称smtpd

注意

应用程序名称smtpd是从 Postfix 发送到 Cyrus SASL 的默认值。您可以使用smtpd_sasl_application_name进行更改,但通常不需要。只有在运行需要不同 Cyrus SASL 配置的不同 Postfix 守护程序时才需要。

当 Cyrus SASL 接收到应用程序名称时,它将附加一个.conf并开始查找包含配置设置的配置文件。

默认情况下,smtpd.conf的位置是/usr/lib/sasl2/smtpd.conf,但出于各种原因,一些 Linux 发行版已经开始将其放在其他位置。在 Debian Linux 上,您将不得不在/etc/postfix/sasl/smtpd.conf中创建配置。Mandrake Linux 希望文件位于/var/lib/sasl2/smtpd.conf。所有其他人都知道它应该位于/usr/lib/sasl2/smtpd.conf

检查您的系统,找出是否已经创建了smtpd.conf。如果没有,一个简单的touch命令(作为 root)将创建它:

# touch /usr/lib/sasl2/smtpd.conf

现在接下来的所有配置都将集中在smtpd.conf上。以下是我们将在其中放置的内容的快速概述:

  • 我们想要使用的密码验证服务的名称

  • SASL 应将日志消息发送到日志输出的日志级别

  • 在向客户端提供 SMTP AUTH 时,Postfix 应该宣传的机制列表

  • 特定于所选密码验证服务的配置设置

最后,我们将配置密码验证服务应如何访问认证后端。这需要根据我们选择的密码验证服务来完成,并将在到达时进行解释。

选择密码验证服务

第一步配置是选择 SASL 在身份验证期间应使用的密码验证服务。告诉 SASL 应该处理身份验证的密码验证服务的参数是pwcheck_method。您可以提供的值是:

  • saslauthd

  • auxprop

  • authdaemond

根据您选择的密码验证服务,您将不得不添加正确的值。名称应该说明情况,并告诉您将调用哪个密码验证服务。使用saslauthd的配置将在smtpd.conf中添加以下行:

pwcheck_method: saslauthd

选择日志级别

Cyrus SASL 的日志处理不一致。Cyrus SASL 将记录取决于密码验证服务和正在使用的方法。定义日志级别的参数是log_level。在设置期间合理的设置将是日志级别 3。

log_level: 3

此行应添加到smtpd.conf中。

以下是 Cyrus SASL 知道的所有日志级别的列表:

log_level value Description
0 无日志
1 记录异常错误;这是默认设置
2 记录所有身份验证失败
3 记录非致命警告
4 比 3 更详细
5 比 4 更详细
6 记录内部协议的跟踪
7 记录内部协议的跟踪,包括密码

选择有效的机制

您的下一步将是选择 Postfix 在向客户端广告 SMTP 身份验证时可以提供的机制。在 Cyrus SASL 中配置有效机制列表的参数是mech_list。这些机制的名称与我们在机制部分中介绍它们时使用的名称完全相同。

重要的是设置mech_list参数,并且只列出您的密码验证服务可以处理的机制。如果不这样做,Postfix 将提供 SASL 提供的所有机制,如果您的邮件客户端选择 SASL 密码验证服务无法处理的机制,身份验证将失败。

注意

请记住,密码验证服务saslauthdauthdaemond只能处理两种明文机制——PLAINLOGIN。因此,这些密码验证服务的mech_list必须只包含PLAINLOGIN这两个值。任何能够处理更强机制的邮件客户端都会优先选择更强的机制。它会进行计算并将结果发送给服务器。服务器将无法进行身份验证,因为Saslauthdauthdaemond都无法处理非明文机制。

以下示例将在smtpd.conf中为saslauthd定义有效机制:

mech_list: PLAIN LOGIN

任何auxprop密码验证服务的有效机制列表可以进一步列出以下机制:

mech_list: PLAIN LOGIN CRAM-MD5 DIGEST-MD5

注意

此列表中机制的顺序不会影响客户端选择的机制。选择哪种机制取决于客户端;通常会选择提供最强加密的机制。

在接下来的部分中,我们将看看如何配置密码验证服务以选择认证后端,以及如何提供额外信息以选择相关数据。如前所述,这由三个密码验证服务以不同方式处理。我们将分别查看每个密码验证服务。

saslauthd

在使用saslauthd之前,您需要检查它是否能够在saslauthd称为state dir的目录中建立套接字。请仔细检查,因为与套接字相关的两个常见问题是:

  • 该目录不存在:在这种情况下,saslauthd将停止运行,并且您将找到指示缺少目录的日志消息。

  • 该目录对于除saslauthd之外的应用程序是不可访问的:在这种情况下,您将在邮件日志中找到指示smtpd无法连接到套接字的日志消息。

要解决这些问题,您首先需要找出saslauthd希望建立套接字的位置。只需像示例中那样以 root 身份启动它,并寻找包含run_path的行:

# saslauthd -a shadow -d

saslauthd[3610] :main : num_procs : 5
saslauthd[3610] :main : mech_option: NULL
saslauthd[3610] :main : run_path : /var/run/saslauthd
saslauthd[3610] :main : auth_mech : shadow
saslauthd[3610] :main : could not chdir to: /var/run/saslauthd
saslauthd[3610] :main : chdir: No such file or directory
saslauthd[3610] :main : Check to make sure the directory exists and is
saslauthd[3610] :main : writeable by the user, this process runs as—If you get no errors, the daemon will start, but the -d flag means that it will not start in the background; it will tie up your terminal session. In this case, press *Ctrl+C* to terminate the process.

如前面的示例所示,saslauthd希望访问/var/run/saslauthd作为run_path。由于它无法访问该目录,它立即退出。现在有两种方法可以解决这个问题。这取决于您是从软件包中获取saslauthd还是从源代码安装它。

在第一种情况下,软件包维护者很可能使用默认设置构建了saslauthd;选择不同的位置作为state dir并配置init-script以通过给出-m /path/to/state_dir选项来覆盖默认路径。

在 Debian 系统中,您通常会在/etc/default/saslauthd中找到命令行选项。在 Red Hat 系统中,您通常会在/etc/sysconfig/saslauthd中找到传递给saslauthd的命令行选项。以下清单为您提供了 Fedora Core 2 的设置概述:

# Directory in which to place saslauthd's listening socket, pid file, and so
# on. This directory must already exist.
SOCKETDIR=/var/run/saslauthd
# Mechanism to use when checking passwords. Run "saslauthd -v" to get a list
# of which mechanism your installation was compiled to use.
MECH=shadow
# Additional flags to pass to saslauthd on the command line. See saslauthd(8)
# for the list of accepted flags.
FLAGS=

就大多数 Linux 发行版而言,state dir的典型位置要么是/var/state/saslauthd,要么是/var/run/saslauthd

现在考虑手动构建saslauthd的情况。然后,您应该创建一个与您在执行configure脚本时使用的--with-saslauthd参数相匹配的目录。

在 SASL 配置示例中,--with-saslauthd的值为/var/state/saslauthd。创建此目录并使其对 root 用户和 postfix 组可访问,如下所示:

# mkdir /var/state/saslauthd
# chmod 750 /var/state/saslauthd
# chgrp postfix /var/state/saslauthd

一旦您验证了saslauthd可以在您的state dir中创建套接字和pid文件,您可以开始配置saslauthd以访问您选择的身份验证后端。

注意

以下示例假定您不必为saslauthd提供额外的运行路径。如果需要,请将其添加到给出的示例中。

使用 IMAP 服务器作为身份验证后端

指定-a选项以及值rimap,使 Cyrus SASL 使用邮件客户端提供的凭据登录到 IMAP 服务器。此外,您必须使用-O选项告诉saslauthd它应该转到哪个 IMAP 服务器,如下所示:

# saslauthd -a rimap -O mail.example.com

成功登录到 IMAP 服务器后,saslauthd将向 Postfix 报告身份验证成功,Postfix 可能允许邮件客户端将凭据交给中继。

使用 LDAP 服务器作为身份验证后端

与 IMAP 服务器验证凭据比与 LDAP 服务器验证凭据稍微复杂一些。它需要更多的配置,这就是为什么您不会在命令行上给出所有选项给saslauthd,而是将它们放入配置文件中。默认情况下,saslauthd期望将 LDAP 配置位于/usr/local/etc/saslauthd.conf。如果选择不同的位置,您需要在命令行上声明它。

# saslauthd -a ldap -O /etc/cyrussasl/saslauthd.conf

在前面的示例中,值ldap告诉saslauthd转到 LDAP 服务器,-O选项提供了配置文件的路径。您的配置文件可能包含以下参数:

ldap_servers: ldap://127.0.0.1/ ldap://172.16.10.7/
ldap_bind_dn: cn=saslauthd,dc=example,dc=com
ldap_bind_pw: Oy6k0qyR
ldap_timeout: 10
ldap_time_limit: 10
ldap_scope: sub
ldap_search_base: dc=people,dc=example,dc=com
ldap_auth_method: bind
ldap_filter: (|(&(cn=%u)(&(uid=%u@%r)(smtpAuth=Y)))
ldap_debug: 0
ldap_verbose: off
ldap_ssl: no
ldap_start_tls: no
ldap_referrals: yes

正如您可能已经预料到的那样,您将不得不调整设置以适应您的 LDAP 树和其他特定于您的 LDAP 服务器的设置。要获取所有 LDAP 相关参数的完整列表(这里列出的远不止这些),请查看随 Cyrus SASL 源代码一起提供的LDAP_SASLAUTHD readme,它位于saslauthd子目录中。

使用本地用户帐户

这是大多数人使用saslauthd的配置。您可以配置saslauthd从本地密码文件或支持影子密码的系统上的本地影子密码文件中读取。

要从/etc/passwd中读取,请使用-a getpwent选项,如下所示:

# saslauthd -a getpwent

大多数现代 Linux 发行版不会将密码存储在/etc/passwd中,而是存储在/etc/shadow中。如果要让saslauthd/etc/shadow中读取,请像这样以 root 身份运行它:

# saslauthd -a shadow

使用 PAM

还可以使用PAM(可插入认证模块)作为认证后端,后者又必须配置为访问其他认证后端。首先像这样运行saslauthd

# saslauthd -a pam

然后创建一个/etc/pam.d/smtp文件或在/etc/pam.conf中添加一个部分,并向其中添加特定于 PAM 的设置。如果您从软件包中安装了 Cyrus SASL,那么您很有可能已经有了这样一个文件。例如,在 Red Hat 上,它看起来像这样:

#%PAM-1.0
auth required pam_stack.so service=system-auth
account required pam_stack.so service=system-auth

注意

配置文件的名称必须是smtp。这在RFC 2554中已经定义,其中指出 SASL 在 SMTP 上的服务名称是smtp。postfix smtpd守护程序将smtp的值作为服务名称传递给 Cyrus SASL。然后saslauthd将其传递给 PAM,后者将在smtp文件中查找认证指令。

auxprop

辅助属性插件(或auxprop)的配置与saslauthd不同。您只需在smtpd.conf中添加特定于 auxprop 的设置,而不是传递命令行选项。您在smtpd.conf中设置的任何 auxprop 配置都应以以下三行开头:

log_level: 3
pwcheck_method: auxprop
mech_list: PLAIN LOGIN CRAM-MD5 DIGEST-MD5

要告诉 Cyrus SASL 要使用哪个插件,您需要向配置中添加一个额外的参数。该参数称为auxprop_plugin,我们将在以下部分中研究其用法。

配置 sasldb 插件

auxprop 插件sasldb是 Cyrus SASL 的默认插件,即使您没有设置auxprop_plugin参数,它也会使用sasldbsasldb是 SASL 自己的数据库,可以使用saslpasswd2实用程序进行操作。

注意

这往往会激怒那些尝试设置不同插件并在其配置中出现错误的人。如果 Cyrus SASL 使用默认配置而不是所需的配置,它将失败。当您收到一个错误消息,说 Cyrus SASL 找不到sasldb时,这可能是您配置错误的错误(除非您选择故意配置sasldb),第一步应该是检查您的配置文件。

要使用sasldb,首先需要创建一个sasldb数据库。使用以下命令作为 root 创建一个sasldb2文件并添加一个用户。

# saslpasswd2 -c -u example.com username

此命令将创建一个sasldb2文件,并将一个用户名为example.com的用户添加到其中。您需要特别注意添加的领域,因为它将成为邮件客户端稍后必须发送的用户名的一部分。

注意

领域是 Kerberose 基础设施概念的一部分。Kerberose 是一种分布式的、加密的认证协议。通过添加领域,您可以定义用户可以在其中执行操作的上下文(例如,域或主机)。如果您不添加领域,saslpasswd2将默认添加服务器的主机名。

现在您已经创建了数据库并添加了一个用户,您需要更改对sasldb的访问权限,以便 Postfix 也可以访问数据库。只需像这样将postfix组对sasldb2的访问权限:

# chgrp postfix /etc/sasldb2

不要因为sasldb被称为sasldb2而感到困惑。当 Cyrus SASL 主要版本 2.x 推出时,sasldb的格式发生了变化。出于兼容性的原因,新的sasldb文件被称为sasldb2。创建完数据库后,您需要告诉 Cyrus SASL 使用它。像这样在smtpd.conf中添加auxprop_plugin参数:

auxprop_plugin: sasldb

这就是您需要做的一切,您应该准备好开始测试了(请参阅测试 Cyrus SASL 认证部分)。如果出于任何原因,您需要将sasldb放在与默认位置不同的位置,您可以使用以下附加参数:

sasldb_path: /path/to/sasldb2

配置 sql 插件

sql auxprop插件是一个通用插件,可以让您访问 MySQL、PostgreSQL 和 SQLite。我们将以配置 sql 插件访问 MySQL 数据库为例进行说明。访问其他两个数据库的配置几乎相同,只有一个我们将注意到的例外。

首先,您需要创建一个数据库。当然,这取决于您使用的数据库。连接到 MySQL,如果还没有数据库,则创建一个数据库。

mysql> CREATE DATABASE `mail`;

然后添加一个包含所有 SASL 用户身份验证所需内容的表。它看起来类似于这样:

CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL auto_increment,
`username` varchar(255) NOT NULL default '0',
`userrealm` varchar(255) NOT NULL default 'example.com',
`userpassword` varchar(255) NOT NULL default 't1GRateY',
`auth` tinyint(1) default '1',
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) TYPE=MyISAM COMMENT='Users';

该表具有用户名、用户领域、用户密码和一个额外的auth字段,我们稍后将使用它来确定用户是否可以中继。这样我们可以将该表用于其他身份验证目的,例如,与 Apache 的mysql模块一起,用于授予对httpd上特定文件夹的访问权限。

提示

不要忘记为userpassword设置默认值,如前面的示例所示,否则获取中继权限所需的只是发送一个有效的用户名。

创建表后,为测试目的添加一个用户,如下所示:

INSERT INTO `users` VALUES (1,'test','example.com','testpass',0);

然后为 Postfix 添加一个用户,以便访问 MySQL 的用户数据库,如下所示:

mysql> CONNECT mysql;
mysql> INSERT INTO user VALUES ('localhost','postfix','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
mysql> UPDATE mysql.user SET password=PASSWORD("bu0tt^v") WHERE user='postfix' AND host='localhost';
mysql> GRANT SELECT, UPDATE ON mail.users TO 'postfix'@'localhost';
mysql> FLUSH PRIVILEGES;

设置 MySQL 完成后,您需要向smtpd.conf添加sql auxprop-specific参数。可用的参数如下:

  • sql_engine: 指定数据库类型。您可以选择mysql, pgsqlsqlite。在本示例中,我们使用mysql。如果选择不同的数据库,您需要相应地更改此值。

  • sql_hostnames: 指定数据库服务器名称。您可以指定一个或多个由逗号分隔的 FQDN 或 IP 地址。即使选择localhost,SQL 引擎也会尝试通过套接字进行通信。

  • sql_database: 告诉 Cyrus SASL 要连接的数据库的名称。

  • sql_user: 您在此处设置的值必须与连接到数据库的用户的名称匹配。

  • sql_passwd: 您在此处设置的值必须与连接到数据库的用户的密码匹配。它必须是明文密码。

  • sql_select: sql_select参数定义了用于验证用户的SELECT语句。

  • sql_insert: sql_insert参数定义了一个INSERT语句,允许 Cyrus SASL 在 SQL 数据库中创建用户。您将使用saslpasswd2程序来执行此操作。

  • sql_update: sql_update参数定义了UPDATE语句,允许 Cyrus SASL 修改数据库中的现有条目。如果您选择配置此参数,您将不得不与sql_insert参数结合使用。

  • sql_usessl: 您可以设置yes, 1, ontrue来启用 SSL 以通过加密连接访问 MySQL。默认情况下,此选项为off

将所有参数组合在一起的简单配置如下:

# Global parameters
log_level: 3
pwcheck_method: auxprop
mech_list: PLAIN LOGIN CRAM-MD5 DIGEST-MD5
# auxiliary Plugin parameters
auxprop_plugin: sql
sql_engine: mysql
sql_hostnames: localhost
sql_database: mail
sql_user: postfix
sql_passwd: bu0tt^v
sql_select: SELECT %p FROM users WHERE username = '%u' AND userrealm = '%r' AND auth = '1'
sql_usessl: no

如您所见,sql_select语句中使用了宏。它们的含义是:

  • %u: 此宏是要在身份验证期间查询的用户名的占位符。

  • %p: 此宏是密码的占位符。

  • %r: r代表领域,客户端提供的领域将插入到%r中。

  • %v: 此宏仅与sql_updatesql_insert语句结合使用。它表示应替换现有值的提交值。

提示

特别注意标记。必须使用单引号(')引用宏。

配置完成。如果您使用auxprop并按照到此为止的说明进行操作,您已准备好开始测试,并且可以跳过关于authdaemond的下一部分。

authdaemond

authdaemond是专门为与 Courier IMAP 配合使用而创建的。如果您配置 Cyrus SASL 使用authdaemond,它将连接到 Courier authlib 的authdaemond套接字,询问 Courier authlib 验证邮件客户端发送的凭据。一方面,Cyrus SASL 受益于 Courier authlib 可以用于用户验证的各种后端,但另一方面,Cyrus SASL 的authdaemond密码验证服务仅限于明文机制,这不如使用auxprop插件时所获得的好处多。

设置 authdaemond 密码验证服务非常简单。我们将在接下来的部分中看一下它。

设置 authdaemond 密码验证服务

您的第一步是配置 Postfix 以使用authdaemond密码验证服务。与saslauthdauxprop一样,您将pwcheck_method参数添加到您的smtpd.conf中,并选择它为authdaemond

log_level: 3
pwcheck_method: authdaemond
mech_list: PLAIN LOGIN

由于authdaemond的限制,您还必须将机制列表限制为PLAINLOGIN——仅有的明文机制。

配置 authdaemond 套接字路径

您需要告诉 Cyrus SASL 它可以在哪里找到由 Courier authlib 的authdaemond创建的套接字。

使用authdaemond_path参数提供包括套接字名称在内的完整路径。

authdaemond-path: /var/spool/authdaemon/socket

最后检查authdaemond目录的权限,并验证至少用户postfix可以访问该目录。完成后,您就可以开始测试了。

测试 Cyrus SASL 身份验证

没有测试工具,但您可以使用示例应用程序sample-serversample-client来测试身份验证,而不会有其他应用程序(例如 Postfix)干扰测试。如果您从源代码构建了 Cyrus SASL,您可以在 Cyrus SASL 源代码的sample子目录中找到它们。基于 Fedora 的 Linux 发行版将示例包含在cyrus-sasl-devel软件包中,因此如果可用,您应该安装该软件包。基于 Debian 的 Linux 发行版没有类似的软件包,因此您现在必须自行构建它们。

要仅构建示例,请找到、下载并提取与您的软件包管理器安装相匹配的 Cyrus SASL 版本的发布版。要定位并安装源代码,请按照Cyrus SASL 安装部分中描述的说明进行操作。然后,不要发出make install命令,而是发出以下命令:

# cd sample
# make

我们将使用这些示例来测试我们在smtpd.conf中创建的 Cyrus SASL 配置。但是,这些程序不希望在smtpd.conf中找到它们的配置,而是在sample.conf中找到。我们将简单地创建一个从sample.confsmtpd.conf的符号链接以满足要求:

# ln -s /usr/lib/sasl2/smtpd.conf /usr/lib/sasl2/sample.conf

接下来,我们需要启动服务器应用程序以便它监听传入的连接。像这样启动服务器:

$ ./server -s rcmd -p 8000
trying 2, 1, 6
trying 10, 1, 6
bind: Address already in use

不要担心bind: Address already in use的消息。服务器继续运行表明它已经成功监听指定的端口。这是因为应用程序启用了 IPv6,而底层系统不支持 IPv6。

如果您收到类似./server: No such file or directory的错误,请检查您是否已从您的发行版安装了cyrus-sasl-devel软件包,或者您从源代码构建的工作是否正确,并且您是否在正确的目录中。

服务器将在端口8000上监听传入的连接。接下来打开一个新的终端,并使用相同的端口和机制PLAIN启动客户端,并指向您的服务器实用程序应该在那里监听的localhost。在提示时,输入test, testtestpass,这些是测试服务器提供的有效值。成功的身份验证看起来像这样:

测试 Cyrus SASL 身份验证

您应该能够在auth日志中看到一些日志记录。如果您要使用saslauthd,请在调试模式下在单独的终端上启动它,您将能够像这样跟踪身份验证:

# saslauthd -m /var/run/saslauthd -a shadow -d

saslauthd[4547] :main : num_procs : 5
saslauthd[4547] :main : mech_option: NULL
saslauthd[4547] :main : run_path : /var/run/saslauthd
saslauthd[4547] :main : auth_mech : shadow
saslauthd[4547] :ipc_init : using accept lock file: /var/run/saslauthd/mux.accept
saslauthd[4547] :detach_tty : master pid is: 0
saslauthd[4547] :ipc_init : listening on socket: /var/run/saslauthd/mux
saslauthd[4547] :main : using process model
saslauthd[4548] :get_accept_lock : acquired accept lock
saslauthd[4547] :have_baby : forked child: 4548
saslauthd[4547] :have_baby : forked child: 4549
saslauthd[4547] :have_baby : forked child: 4550
saslauthd[4547] :have_baby : forked child: 4551
saslauthd[4548] :rel_accept_lock : released accept lock
saslauthd[4548] :do_auth : auth success: [user=test] [service=rcmd] [realm=] [mech=shadow]
saslauthd[4548] :do_request : response: OK

saslauthd[4548] :get_accept_lock : acquired accept lock

如果您能够成功进行身份验证,请继续配置 Postfix 中的SMTP AUTH。如果您的身份验证失败,请跟随日志并按照之前讨论的设置和配置 SASL 的说明进行迭代。

配置 Postfix SMTP AUTH

现在,在设置和配置 Cyrus SASL 后,配置 Postfix 中的 SMTP AUTH 就变得非常简单了。您需要做的第一件事是检查 Postfix 是否构建支持 SMTP 身份验证。使用 ldd 实用程序检查 Postfix smtpd 守护程序是否已链接到 libsasl

# ldd /usr/libexec/postfix/smtpd | grep libsasl
libsasl2.so.2 => /usr/lib/libsasl2.so.2 (0x00002aaaabb6a000)

如果没有任何输出,您可能需要重新构建 Postfix。阅读 Postfix README_FILES 目录中的 SASL_README,以获取有关必须在 CCARGSAUXLIBS 语句中包含的详细信息。

准备配置

一旦您验证了 Postfix 支持 SMTP AUTH,您需要验证在配置 SMTP AUTHsmtpd 守护程序是否未运行 chrooted。许多人在意识到原因是 chroot 监狱之前,花费数小时配置无法访问 saslauthd 套接字的 chrooted Postfix。不运行 chrooted 的 Postfix smtpd 守护程序在 /etc/postfix/master.cf 中的 chroot 列中有一个 n

# ==================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (yes) (never) (100)
# ==================================================================
smtp inet n - n - - smtpd

如果 Postfix 在更改了 smtpdchroot 设置后正在运行 chrooted,请重新加载它并转到 main.cf

启用 SMTP AUTH

您要做的第一件事是通过添加 smtpd_sasl_auth_enable 参数并将其设置为 yes 来启用 SMTP AUTH

smtpd_sasl_auth_enable = yes

这将使 Postfix 向使用 ESMTP 的客户端提供 SMTP AUTH,但在开始测试之前,您仍然需要配置一些设置。

设置安全策略

您将不得不决定 Postfix 应该使用 smtpd_sasl_security_options 参数提供哪些机制。此参数接受以下一个或多个值的列表:

  • noanonymous: 您应该始终设置此值,否则 Postfix 将向邮件客户端提供匿名身份验证。允许匿名身份验证将使您成为开放中继,并且不应该用于 SMTP 服务器。

  • noplaintext: noplaintext 值将阻止 Postfix 提供明文机制 PLAINLOGIN。通常情况下,您不希望这样做,因为大多数广泛使用的客户端只支持 LOGIN。如果设置了此选项,我们将无法对一些客户端进行身份验证。

  • noactive: 此设置排除了容易受到主动(非字典)攻击的 SASL 机制。

  • nodictionary: 此关键字排除了所有可以通过字典攻击破解的机制。

  • mutual_auth: 这种形式的身份验证要求服务器向客户端进行身份验证,反之亦然。如果设置了它,只有能够执行此形式或身份验证的服务器和客户端才能进行身份验证。这个选项几乎从不使用。

smtpd_sasl_security_options 参数的常见设置将在 main.cf 中添加以下行:

smtpd_sasl_security_options = noanonymous

这将防止匿名身份验证,并允许所有其他身份验证。

包括破损的客户端

接下来,您需要决定 Postfix 是否应该向破损的客户端提供 SMTP AUTH。在 SMTP AUTH 的上下文中,破损的客户端是指如果身份验证已按照 RFC 2222 要求的方式提供,它们将不会识别服务器的 SMTP AUTH 能力。相反,它们遵循了 RFC 草案,在显示 SMTP 通信期间的 SMTP AUTH 能力行中具有额外的 =。在破损的客户端中,包括几个版本的 Microsoft Outlook Express 和 Microsoft Outlook。要解决此问题,只需像这样向 main.cf 添加 broken_sasl_auth_clients 参数:

broken_sasl_auth_clients = yes

当 Postfix 列出其功能给邮件客户端时,将打印一个额外的 AUTH 行。此行将在其中具有额外的 =,并且破损的客户端将注意到 SMTP AUTH 能力。

最后,如果要限制可以中继到具有相同领域的用户组,添加 smtpd_sasl_local_domain 参数,并提供该值作为领域,如下所示:

smtpd_sasl_local_domain = example.com

Postfix 将在成功通过邮件客户端发送的所有用户名后附加该值,成功限制中继到那些用户名中包含 smtpd_sasl_local_domain 值的用户。

完成所有配置步骤后,重新加载 Postfix 以使设置生效并开始测试。作为 root 用户,发出以下命令:

# postfix reload

测试 SMTP AUTH

在测试 SMTP 身份验证时,不要使用常规邮件客户端,因为邮件客户端可能会引入一些问题。而是使用 Telnet 客户端程序并在 SMTP 通信中连接到 Postfix。您将需要以 Base64 编码形式发送测试用户的用户名和密码,因此第一步将是创建这样的字符串。使用以下命令为用户test使用密码testpass创建 Base64 编码的字符串:

$ perl -MMIME::Base64 -e 'print encode_base64("test\0test\0testpass");'
dGVzdAB0ZXN0AHRlc3RwYXNz

注意

请注意,\0将用户名与密码分开,用户名将需要重复两次。这是因为 SASL 期望两个可能不同的用户名(userid, authid)来支持未用于 SMTP 身份验证的附加功能。

还要记住,如果您的用户名或密码包含@$字符,您将需要使用前置的\进行转义,否则 Perl 将解释它们,这将导致一个无法正常工作的 Base64 编码的字符串。

一旦您手头有 Base64 编码的字符串,使用 Telnet 程序连接到服务器的端口25,如下所示:

$ telnet mail.example.com 25
220 mail.example.com ESMTP Postfix
EHLO client.example.com
250-mail.example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-AUTH LOGIN PLAIN DIGEST-MD5 CRAM-MD5
250-AUTH=LOGIN PLAIN DIGEST-MD5 CRAM-MD5
250-XVERP
250 8BITMIME
AUTH PLAIN dGVzdAB0ZXN0AHRlc3RwYXNz
235 Authentication successful
QUIT
221 Bye

您可以看到在前面的示例中,身份验证是成功的。首先,邮件客户端在介绍时发送了EHLO,而 Postfix 则以一系列功能的列表做出了回应。如果您像我们的示例中所做的那样将broken_sasl_auth_clients参数设置为yes,您还会注意到包含=的额外AUTH行。

认证发生在客户端发送AUTH字符串以及它想要使用的机制时,对于明文机制,还附加了 Base64 编码的字符串。如果您的身份验证没有成功,但您能够在 SASL 测试期间进行身份验证,请查看main.cf中的参数,并仔细检查master.cfsmtpdchroot状态。

为经过身份验证的客户端启用中继

如果身份验证成功,我们只需告诉 Postfix 允许已经经过身份验证的用户中继消息。这是通过编辑main.cf并在smtpd_recipient_restrictions的限制列表中添加permit_sasl_authenticated选项来完成的,如下所示:

smtpd_recipient_restrictions =
...
permit_sasl_authenticated
permit_mynetworks
reject_unauth_destination
...

重新加载 Postfix 并开始使用真实的邮件客户端进行测试。如果可能,请确保其 IP 地址不是mynetworks的一部分,因为 Postfix 可能被允许中继的原因不是因为SMTP AUTH成功。您可能希望在测试期间将中继限制为仅限服务器。更改mynetwork_classes = host设置,以便来自其他计算机的客户端不会自动成为 Postfix 网络的一部分。

如果您仍然遇到SMTP AUTH问题,请查看saslfingerpostfix.state-of-mind.de/patrick.koetter/saslfinger/)。这是一个脚本,它收集有关SMTP AUTH配置的各种有用信息,并为您提供输出,您可以在向 Postfix 邮件列表询问时附加到您的邮件中。

保护明文机制

我们已经注意到,使用明文机制的SMTP AUTH实际上并不安全,因为在身份验证期间发送的字符串仅仅是编码而不是加密。这就是传输层安全TLS)派上用场的地方,因为它可以保护编码字符串的传输免受好奇的眼睛。

启用传输层安全

要启用 TLS,您必须生成密钥对和证书,然后修改 postfix 配置以识别它们。

要生成 SSL 证书,并使用 SSL,您需要安装 OpenSSL 软件包。在许多情况下,这将被安装,否则请使用您的发行版软件包管理器进行安装。

要创建证书,请以 root 身份发出以下命令:

启用传输层安全 SASL 层明文机制

这将在/etc/postfix/certs中创建名为smtpd.keysmtpd.crt的证书。将smtpd_use_tls参数添加到main.cf并将其设置为yes

smtpd_use_tls = yes

然后,您需要告诉smtpd在哪里可以找到密钥和证书,通过添加smtpd_tls_key_filesmtpd_tls_cert_file参数:

smtpd_tls_key_file = /etc/postfix/certs/smtpd.key
smtpd_tls_cert_file = /etc/postfix/certs/smtpd.crt

发送证书以证明其身份的邮件服务器还必须随时保留认证机构的公共证书的副本。假设您已经将其添加到服务器的本地 CA 根存储中的/usr/share/ssl/certs,使用以下参数:

smtpd_tls_CAfile = /usr/share/ssl/certs/ca-bundle.crt

如果 CA 证书不是在一个文件中,而是在同一个目录中的单独文件中,例如/usr/share/ssl/certs,则使用以下参数:

smtpd_tls_CApath = /usr/share/ssl/certs/

一旦您完成了所有这些配置,您就完成了基本的 TLS 配置,可以开始保护明文认证。

配置安全策略

有几种方法可以使用 TLS 保护明文认证。最激进的方法是使用smtpd_tls_auth_only参数,并将其设置为yes。如果使用它,只有在邮件客户端和邮件服务器建立了加密通信层后,才会宣布SMTP AUTH。通过这样做,所有用户名/密码组合都将被加密,不容易被窃听。

然而,这惩罚了所有其他能够使用其他更安全机制的邮件客户端,比如共享密钥机制。如果你想更有选择性地处理这个问题,你应该采取以下方法,禁用未加密传输中的明文认证,但一旦建立了加密通信,就允许它。

首先,您需要重新配置您的smtpd_sasl_security_options参数,以排除向邮件客户端提供明文机制:

smtpd_sasl_security_options = noanonymous, noplaintext

然后设置额外的smtpd_sasl_tls_security_options参数,该参数控制相同的设置,但仅适用于 TLS 会话:

smtpd_sasl_tls_security_options = noanonymous

正如您所看到的,smtpd_sasl_tls_security_options参数不会排除明文机制。这样一来,可以使用其他非明文机制的客户端无需使用 TLS,而只能使用明文机制的客户端一旦建立了加密会话就可以安全地使用它。

一旦您重新加载了 Postfix,您就可以开始测试了。

注意

不要忘记将签署您服务器证书请求的认证机构的证书添加到您的邮件客户端的 CA 根存储中,否则它至少会抱怨无法验证服务器的身份。

字典攻击

字典攻击是指客户端试图向无数潜在收件人发送邮件,这些收件人的电子邮件地址是从字典中的单词或名称派生的:

anton@example.com
bertha@example.com
...
zebediah@example.com

如果您的服务器没有有效收件人地址列表,它必须接受这些邮件,无论收件人是否真的存在。然后,这些邮件的攻击需要像往常一样进行处理(病毒检查、垃圾邮件检查、本地投递),直到系统在某个阶段意识到收件人甚至不存在!

然后将生成一个非投递报告并发送回发件人。

因此,对于每个不存在的收件人,都会接受和处理一封邮件,并且另外生成一封邮件(退信),并且会进行投递尝试。

正如您所看到的,这种做法浪费了服务器的宝贵资源。因为服务器正忙于尝试传递本来不应该接受的邮件,合法的邮件在垃圾邮件的洪流中落后。垃圾邮件发送者还可以使用退信消息来确定进一步攻击的合法电子邮件地址。退信消息还可以提示使用哪个 SMTP 服务器,从而使他们能够针对特定版本中已知的任何漏洞。

收件人映射

Postfix 能够在接受消息之前验证收件人地址。它可以对本地域(列在mydestination中)和中继域(列在relay_domains中)运行检查。

检查本地域收件人

local_recipient_maps参数控制 Postfix 将保留为有效本地收件人的收件人。默认情况如下:

local_recipient_maps = proxy:unix:passwd.byname, $alias_maps

通过这个设置,Postfix 将检查本地/etc/passwd文件的收件人名称,以及已分配给main.cf中的alias_maps参数的任何映射。添加虚拟用户超出了本书的范围,但如果您需要扩展此列表,您可以创建一个包含用户的数据库,并添加路径到保存额外本地收件人的映射。

检查中继域收件人

relay_recipient_maps参数控制中继域的有效收件人。默认情况下为空,为了让 Postfix 获得更多控制权,您需要构建一个 Postfix 可以查找有效收件人的映射。

假设您的服务器中继邮件到example.com,那么您将创建以下配置:

relay_domains = example.com
relay_recipient_maps = hash:/etc/postfix/relay_recipients

relay_domain参数告诉 Postfix 中继example.com域的收件人,relay_recipient_maps参数指向一个保存有效收件人的映射。在映射中,您可以创建以下列表:

adam@example.com OK
eve@example.com OK

然后运行postmap命令创建一个索引映射,如下所示:

# postmap /etc/postfix/relay_recipients

为了让 postfix 识别新的数据库,重新加载它:

# postfix reload
postfix/postfix-script: refreshing the Postfix mail system

这将只允许adam@example.comeve@example.com作为example.com域的收件人。发送到snake@example.com的邮件将被拒绝,并显示User unknown in relay recipient table错误消息。

限制连接速率

拒绝发送到不存在收件人的邮件会有很大帮助,但是当您的服务器受到字典攻击时,它仍会接受所有客户端的连接并生成适当的错误消息(或者接受邮件,如果偶然命中了有效的收件人地址)。

Postfix 的 anvil 服务器维护短期统计数据,以防御您的系统受到在可配置的时间段内以以下任一情况轰炸您的服务器的客户端:

  • 同时会话过多

  • 过多的连续请求

硬件和软件的限制决定了您的服务器每个给定时间单位能够处理的邮件数量,因此不接受超出服务器处理能力的邮件是有意义的。

anvil_rate_time_unit = 60s

上一行指定了用于所有以下限制的时间间隔:

  • smtpd_client_connection_rate_limit = 40: 这指定了客户端可以在anvil_rate_time_unit指定的时间段内建立的连接数。在这种情况下,是每 60 秒 40 个连接。

  • smtpd_client_connection_count_limit = 16: 这是任何客户端允许在anvil_rate_time_unit内建立的最大并发连接数。

  • smtpd_client_message_rate_limit = 100: 这是一个重要的限制,因为客户端可以重复使用已建立的连接并仅使用这个单个连接发送许多邮件。

  • smtpd_client_recipient_rate_limit = 32: 这是任何客户端允许在anvil_rate_time_unit内发送到此服务的最大收件人地址数量,无论 Postfix 是否实际接受这些收件人。

  • smtpd_client_event_limit_exceptions = $mynetworks: 这可以用来豁免某些网络或机器免受速率限制。您可能希望豁免邮件列表服务器免受速率限制,因为它无疑会在短时间内向许多收件人发送大量邮件。

anvil将发出关于最大连接速率(这里是5/60s)以及哪个客户端达到了最大速率(212.227.51.110)以及何时(Dec28 13:19:23)的详细日志数据。

Dec 28 13:25:03 mail postfix/anvil[4176]: statistics: max connection rate 5/60s for (smtp:212.227.51.110) at Dec 28 13:19:23

这第二个日志条目显示了哪个客户端建立了最多的并发连接以及何时:

Dec 28 13:25:03 mail postfix/anvil[4176]: statistics: max connection count 5 for (smtp:62.219.130.25) at Dec 28 13:20:19

如果任何限制被超出,anvil也会记录这一点:

Dec 28 11:33:24 mail postfix/smtpd[19507]: warning: Connection rate limit exceeded: 54 from pD9E83AD0.dip.t-dialin.net[217.232.58.208] for service smtp
Dec 28 12:14:17 mail postfix/smtpd[24642]: warning: Connection concurrency limit exceeded: 17 from hqm-smrly01.meti.go.jp[219.101.211.110] for service smtp

任何超出这些限制的客户端都将收到临时错误代码,因此表示它在稍后重试。合法的客户端将遵守并重试。开放代理和特洛伊木马可能不会重试。

摘要

在本章中,我们讨论了如何保护您的安装。涵盖了几个不同的主题,首先是配置 Postfix 只接受来自特定 IP 地址的电子邮件,这在所有用户都是办公室用户时非常有用。接下来,本章介绍了使用 SASL 对可能来自任何 IP 地址的用户进行身份验证。然后,我们看了如何使用 TLS 加密客户端和服务器之间的身份验证。最后,我们看了如何限制表现不佳的客户端,使用anvil守护程序限制在一定时间内连接过于频繁的客户端,以及一次打开太多连接的客户端。

本章介绍的措施将使您作为邮件管理员的生活更轻松,并有助于限制用户遭受的垃圾邮件数量,如果您无意中配置了开放中继,还可以限制传递给其他互联网用户的垃圾邮件数量。有关限制垃圾邮件的更多细节,请移步到描述使用开源垃圾邮件过滤工具 SpamAssassin 的第八章。或者继续阅读第六章,介绍使用 Procmail 在电子邮件到达时对其进行操作。

第六章:开始使用 Procmail

Procmail 是一种多功能的电子邮件过滤器,通常用于在将消息传递到用户收件箱之前处理消息。

本章包括以下主题:

  • Procmail 的简要介绍

  • Procmail 可以执行的典型过滤任务

  • 如何在服务器上安装和设置邮件过滤系统,以处理您每天不愿意花时间处理的重复分类和存储任务

  • Procmail 食谱中规则和操作的基本结构

  • 如何在我们的食谱中创建和测试规则

  • 最后,一些执行过滤的示例食谱

通过本章结束时,您将了解过滤过程的基础知识,如何设置系统执行过滤以及如何对自己的邮件执行许多非常简单但极其有用的过滤操作。所有这些都将帮助您掌握已经或即将收到的所有邮件。

介绍 Procmail

Procmail 是一个邮件过滤器,它在邮件到达邮件服务器后但在最终交付给收件人之前执行。Procmail 的行为由许多用户编写的食谱(或脚本)控制。每个食谱可以包含许多模式匹配规则,以至少基于收件人、主题和消息内容选择消息。如果规则中的匹配条件选择消息作为候选项,食谱可以执行许多操作,将消息移动到文件夹,回复发件人,甚至在交付之前丢弃消息。与规则一样,操作是用户在食谱中编写的,可以对消息执行几乎任何操作。

Procmail 的主页位于www.procmail.org.

谁写的以及何时写的

1.0 版本于 20 世纪 90 年代末发布,并发展成为基于 UNIX 的邮件系统中最好和最常用的邮件过滤解决方案之一。Procmail 最初由 Stephen R. van den Berg(<srb@cuci.nl>)设计和开发。1998 年秋,他意识到自己没有时间独自维护 Procmail,于是创建了一个用于讨论未来发展的邮件列表,并委任 Philip Guenther(<guenther@sendmail.com>)为维护者。

自 2001 年 9 月发布的 3.22 版本以来,Procmail 一直很稳定,因此大多数最近的安装将安装此最新版本,这也是我们在整本书中将使用的版本。

过滤系统如何帮助我?

到目前为止,您应该已经建立并运行了一个电子邮件系统,并发送和接收电子邮件。您可能已经注册了一些有用的邮件列表,消息以不同的间隔到达。您还应该收到通知您系统状态的消息。所有这些额外的、低优先级的信息很容易分散注意力,妨碍您阅读其他重要的电子邮件。

如何组织您的邮件取决于您个人的口味;如果您非常有条理,您可能已经在电子邮件客户端中设置了一些文件夹,并在阅读后将消息移动到适当的位置。尽管如此,您可能已经意识到,能够让系统自动将一些消息存储在与您重要电子邮件不同的位置将非常有用。

在设置自动流程时,您需要考虑的是如何识别邮件项目的内容。最重要的指标是发送给谁,标题或主题行,以及发件人详细信息。如果您现在花几分钟时间记录一下您已经处理邮件的方式,到达的消息类型以及您对它们的处理方式,您将更清楚地了解您可能想要设置的自动流程。

一般来说,您可能会收到几种不同类别的消息。

  • 邮件列表成员资格:来自邮件组或邮件列表的邮件通常很容易通过发件人信息或主题行进行识别。一些组每隔几分钟发送一封消息,而其他组可能每个月只发送几封消息。通常,不同的邮件组项目由不同的信息片段进行识别。例如,一些组发送的消息的“发件人”地址是真实发件人的地址,而其他组会添加一个虚假或系统生成的“发件人”地址。例如,一些组可能会自动向“主题”字段添加前缀。

  • 自动系统消息:您的服务器每天会生成大量消息。尽管通常只发送给系统管理员或 root 用户,但首先要做的一件事是确保您收到邮件的副本,以便及时了解系统状态和事件。您可以通过编辑/etc/mail/aliases/etc/aliases文件(取决于系统设置)来做到这一点。这些系统生成的消息几乎总是可以识别出来自少数特定系统用户 ID。这些通常是rootcron

  • 未经请求的大量电子邮件:被识别为垃圾邮件的消息通常被认为不重要。因此,您可以选择将这些项目移动到一个单独的文件夹以供稍后查看,甚至完全丢弃它们。不建议自动丢弃垃圾邮件,因为任何误识别的邮件将永远丢失。

  • 个人消息:来自客户、同事或朋友的邮件通常被认为是重要的。因此,通常会将其投递到收件箱,让您有机会提供更及时的回复。个人消息更难以通过过滤器识别,尤其是来自新客户或同事的消息,因此不属于前述任何一类的消息应该正常投递。

完成本章的工作后,您应该具备工具和知识,可以开始更详细地检查邮件并设置一些基本的过滤操作。

邮件过滤的潜在用途

您已经设置的基本邮件系统具有其自己的内置能力,可以根据用户设置处理传入的邮件。默认操作是将消息发送到收件箱;其他选项是自动将所有邮件转发给另一个用户。假设您在不同系统上有多个邮件帐户,并且希望所有邮件最终都发送到一个特定的邮件帐户。然后,您可以将该邮件发送到特定文件,或者将其传递给程序或应用程序,以便让其自行处理。

这种设置的缺点是所有邮件必须遵循一个特定的路线,因此随着时间的推移,已经创建了许多智能过滤邮件的选项。其中最强大和最受欢迎的之一是 Procmail。

过滤和分类邮件

Procmail 旨在处理系统内用户接收的邮件的各种处理和过滤任务。过滤仅适用于在系统上拥有帐户的用户,而不适用于虚拟用户,并且可以应用于所有用户或个别用户可以添加自己的过滤器。

对于系统管理员,Procmail 提供了一系列设施,用于对系统用户接收的所有邮件应用规则和操作。这些操作可能包括为了历史目的而复制所有邮件,或者在邮件内容可能在某种法律或商业情况下使用的企业中使用。

在本书的其他地方,我们将讨论识别电子邮件病毒和垃圾邮件的方法。Procmail 可以利用这些过程提供的信息,并根据这些过程添加的信息执行操作,例如将包含病毒的所有邮件存储在系统管理员检查的安全邮件文件夹中。

对于系统用户来说,对收件箱中的邮件进行的最常见操作是将其分类整理,以便根据您感兴趣的主题区域轻松找到所需的项目。典型的组织布局可能是一个分层的布局,类似于以下内容:

/mailgroups/Procmail
/mailgroups/postfix
/mailgroups/linux
/system/cron
/system/warnings
/system/status
/inbox

如果您计划长时间保留邮件以供历史参考,可能值得增加一两层来将邮件分隔成年份和月份。这样将来存档或清除旧邮件会更容易,同时搜索和排序也会更快。

转发邮件

有时您可能会收到很多很容易识别需要发送到另一个用户的另一个电子邮件地址的电子邮件。在这种情况下,您可以设置一个规则,将电子邮件转发到一个或多个其他电子邮件地址,而不是将文件存储在系统上。当然,您需要小心确保转发不会最终回到您,从而创建一个永无止境的循环。

以这种方式转发邮件比在邮件客户端软件内手动转发邮件具有很大的优势,除了不需要任何手动干预之外。通过 Procmail 转发的邮件是透明的,对收件人来说,它看起来就像邮件直接从原始发件人那里到达一样。而如果使用邮件客户端转发,它看起来就好像是由进行转发的人或帐户发送的。

如果需要将单个地址的所有邮件转发到单个其他地址,更有效的方法是使用 Postfix 邮件系统的别名机制。只有在需要根据在接收消息时才能确定的因素进行智能过滤邮件时,才应该使用 Procmail。

在应用程序中处理邮件

有些邮件可能适合传递到一个应用程序,应用程序可以对电子邮件进行一些处理。也许它可以阅读内容,然后将信息存储在错误跟踪数据库中,或者更新客户活动的公司历史记录。这些是在下一章中简要介绍的更高级的主题。

确认和离职/度假回复

如果您想要对某些消息发送自动回复,可以设置一个过滤器或规则来发送这样的消息。当您长时间离开办公室度假、休假或者生病时,可以设置一个自动回复服务,通知发件人在您能够回复他们的邮件之前需要一些时间,并可能提供其他联系方式或要求他们联系其他人。

重要的是要仔细组织这样的功能。您不应该向邮件组发送这样的回复,也不应该向已经知道您离开但需要在您回来后发送信息的人重复发送回复。这需要保留发送消息的地址日志,以避免重复发送消息。我们将在下一章中探讨设置这样一个服务。

文件锁定和完整性

在您使用 Procmail 的所有工作中要牢记的一个重要概念是,总是可能有多封邮件同时到达,争相处理。因此,很可能会有两封或更多邮件同时存储在同一位置,这是灾难的开始。假设有两封邮件同时到达的简单例子。第一封邮件打开存储位置并开始写入邮件内容,然后第二个进程也这样做。从中可能产生各种可能的结果,从完全丢失一封邮件,到两封邮件交织存储且完全无法阅读。

为了确保这种情况不会发生,需要遵守严格的锁定协议,以确保只有一个进程可以同时写入,所有其他应用程序都需要耐心等待轮到它们。Procmail 本身具有强制执行适用于所应用的进程类型的锁定协议的能力,并且默认情况下会锁定存储邮件的物理文件。

在一些情况下,邮件正在被应用程序处理,可以通过规则中的标志指示 Procmail 使用适当的锁定机制。这将在第七章中更全面地介绍。

Procmail 不适用于哪些情况

Procmail 可能被认为适用于一些非常特定的邮件过滤和处理需求。在大多数情况下,它足够灵活和能干,至少可以在基本水平上执行任务。这些任务可能包括过滤与垃圾邮件相关的电子邮件,过滤病毒或运行邮件列表操作。对于每一个任务,都有一些超出仅使用 Procmail 过滤器能力的解决方案可用。我们将在第八章后面讨论使用 SpamAssassin 进行垃圾邮件过滤以及病毒过滤解决方案。

我们已经提到 Procmail 只适用于在 Procmail 运行的系统上拥有账户的用户。尽管如此,值得强调的是,Procmail 无法处理发送到虚拟用户的邮件,这些邮件最终会被发送到另一个系统上。如果需要处理这样的用户的邮件,可以在系统上创建一个真实的用户账户,然后使用 Procmail 作为其过滤过程的一部分来执行最终的转发。这并不是一个理想的用法,因为如果允许 Postfix 系统执行这项工作,它会比使用 Procmail 更有效率。

下载和安装 Procmail

由于软件现在已经相当成熟,Procmail 通常可以在大多数 Linux 发行版上安装,并且可以通过软件包管理器安装。这是安装 Procmail 的推荐方法。如果您的 Linux 发行版的软件包管理器中没有 Procmail,也可以从源代码安装。

通过软件包管理器安装

对于 Fedora 用户,如果尚未安装 Procmail,可以使用以下yum命令简单安装:

yum install procmail

对于基于 Debian 的用户,可以使用以下命令:

apt-get install procmail

这将确保 Procmail 的二进制文件正确安装在您的系统上,然后您可以决定如何将其集成到您的 Postfix 系统中。

从源代码安装

Procmail 可以从多个来源获取,但官方发布的版本由www.procmail.org维护和提供。在那里,您会找到一些镜像服务的链接,可以从中下载源文件。本书中使用的版本可以从www.procmail.org/procmail-3.22.tar.gz下载。

可以使用wget命令下载如下:

wget http://www.procmail.org/procmail-3.22.tar.gz

下载并解压缩存档后,cd到目录,例如procmail-3.22。在开始构建和安装软件之前,值得阅读INSTALLREADME文档。

对于大多数 Linux 系统,最简单的安装方法可以通过按照这里列出的步骤来简化:

  1. 运行configure命令来创建正确的构建环境:
$ ./configure

  1. 配置脚本完成后,您可以运行make命令来构建软件可执行文件:
$ make

  1. 最后一步,作为root,是将可执行文件复制到系统上的正确位置以进行操作:
# make install

在最后一步,软件被安装到/usr/local目录中。

在所有阶段,您应该检查进程输出是否有任何重要的错误或警告。

安装选项/注意事项

对于本书中的大多数人,您将是您正在管理的机器或机器的系统管理员,并且可能会应用安装以处理系统上所有用户的所有邮件。如果您不是管理员,或者您希望系统上只有有限数量的人利用 Procmail 的功能,您可以为单个用户安装 Procmail。

个别安装

如果您为自己使用或仅为服务器上的少数人安装 Procmail,则最常见的方法是在服务器上的家目录中直接从.forward文件中调用 Procmail 程序(此文件需要是可全局读取的)。

在使用 Postfix 作为 MTA 时,.forward中的条目应该是这样的:

"|IFS=' ' && exec /usr/bin/procmail -f- || exit 75 *#username*"

引号是必需的,用户名应该替换为您的用户名。其他 MTA 的语法可能不同,因此请查阅 MTA 文档。

您还需要在主目录中安装一个.procmailrc文件—这个文件保存了 Procmail 将用来过滤和传递电子邮件的规则。

系统范围的安装

如果您是系统管理员,可以决定全局安装 Procmail。这样做的好处是用户不再需要拥有.forward文件。只需在每个用户的HOME目录中有一个.procmailrc文件就足够了。在这种情况下,操作是透明的—如果HOME目录中没有.procmailrc文件,邮件将像往常一样传递。

可以创建一个全局的.procmailrc文件,该文件在用户自己的文件之前生效。在这种情况下,您需要小心确保配置包含以下指令,以便消息以最终用户的权限而不是 root 用户的权限存储。

DROPPRIVS=yes

这也有助于保护系统安全性的弱点。该文件通常存储在/etc目录中,如/etc/procmailrc,旨在为添加到系统的所有用户提供一组默认的个人规则。值得配置一个.procmailrc文件在用于系统的add user功能的骨架帐户中。请查阅 Linux 文档,了解如何设置这一点。

与 Postfix 集成以进行系统范围的传递

将 Procmail 集成到 Postfix 系统中很简单,但是,与任何其他配置更改一样,必须小心。Postfix 以 nobody 用户 ID 运行所有外部命令,例如 Procmail。因此,它将无法将邮件传递给用户root。为了确保重要的系统消息仍然可以收到,您应该确保配置了别名,以便将所有发送给 root 用户的邮件转发到一个真实的用户,该用户将读取邮箱。

为系统帐户创建别名

要为 root 用户创建别名,您必须编辑适当的alias文件,通常位于/etc/aliases/etc/mail/aliases中。

如果找不到文件,请使用以下命令:

postconf alias_maps

别名文件中的条目应该如下所示,冒号(:)和电子邮件地址的开头之间只有一个制表符,并且没有尾随空格:

root: user@domain.com

创建文本条目后,您应该运行newaliases命令,将文本文件转换为数据库文件,以便供 Postfix 读取。

值得为可能接收邮件的任何其他系统帐户添加额外的别名。例如,您可能最终会得到类似以下的aliases文件:

# /etc/aliases
postmaster: root
nobody: root
hostmaster: root
usenet: root
news: root
webmaster: root
www: root
ftp: root
abuse: root
noc: root
security: root
root: user@example.com
clamav: root

将 Procmail 添加到 Postfix 配置

对于由 Procmail 进行系统范围的邮件投递,需要修改 Postfix 的main.cf文件,以指定将负责实际投递的应用程序。

编辑/etc/postfix/main.cf文件并添加以下行:

mailbox_command = /path/to/procmail

进行更改后,您需要使用以下命令指示 Postfix 文件已更改:

postfix reload

由 Postfix 提供的环境变量

Postfix 通过使用多个环境变量导出有关邮件包的信息。这些变量被修改以避免任何 shell 扩展问题,方法是用下划线字符替换所有可能对 shell 具有特殊含义的字符,包括空格。以下是导出的变量及其含义的列表:

变量 含义
DOMAIN 收件人地址中@右侧的文本
EXTENSION 可选的地址扩展部分
HOME 收件人的主目录
LOCAL 收件人地址中@左侧的文本,例如,$USER+$EXTENSION
LOGNAME 收件人用户名
RECIPIENT 整个收件人地址,$LOCAL@$DOMAIN
SENDER 完整的发件人地址
SHELL 收件人的登录 shell

基本操作

当邮件到达并传递给 Procmail 程序时,操作的顺序遵循一组固定的格式。它从加载各种配置文件开始,以获取为特定用户设置的规则。然后依次通过每个规则测试消息,当找到合适的匹配时,应用规则。一些规则在完成后终止,而其他规则返回控制,以便对消息进行潜在处理的剩余规则进行评估。

配置文件

通常在/etc/procmailrc中进行系统范围的配置,而个人配置文件通常存储在用户的主目录中,称为.procmailrc。个别规则可以存储在单独的文件中,或者分组存储在多个文件中,然后作为主.procmailrc文件的一部分包含在邮件过滤过程中。通常,这些文件将存储在主目录的Procmail子目录中。

文件格式

配置文件中的条目以简单的文本格式按照基本布局进行。允许注释,并且由#字符后的文本组成;空行将被简单地忽略。规则本身不必按任何特定格式布局,但为了便于维护和可读性,值得以一致和简单的格式编写规则。

配置文件解剖

Procmail 配置文件内容可以分为三个主要部分:

  • 变量:Procmail 需要执行其工作所需的信息可以被分配到配置文件中的变量中,类似于它们在 shell 编程中的使用方式。一些变量是从 Procmail 正在运行的 shell 环境中获取的,另一些是由 Procmail 自己创建的,用于脚本内部使用,而其他变量可以在脚本内部分配。变量的另一个用途是设置 Procmail 本身的操作方式的标志。

大多数脚本中可以设置一些有用的变量:

PATH=/usr/bin: /usr/local/bin:.
MAILDIR=$HOME/Maildir # Make sure it exists
DEFAULT=$MAILDIR/ # Trailing / indicates maildir format mailbox
LOGFILE=$HOME/procmail.log
LOG="
"
VERBOSE=yes

  • VERBOSE变量用于影响执行的日志级别,而LOG变量中嵌入的NEWLINE是故意的,旨在使日志文件更易于阅读。

  • 第七章还包括一个简短的脚本,显示了在 Procmail 中分配的所有变量。

  • 注释: #字符和后续的所有字符直到NEWLINE都将被忽略。这不适用于无法被注释的条件行。空行将被忽略,并且可以与注释一起用于记录您的配置并提高可读性。您应该注释您的规则,因为今天写规则时显而易见的东西,也许在六个月后不查看手册就无法解释了。

  • 规则或配方: 配方是我们创建的规则的常见名称。以冒号(:)开头的行标志着配方的开始。配方的格式如下:

:0 [flags] [ : [locallockfile] ]
<zero or more conditions (one per line)>
<exactly one action line>

:0是 Procmail 早期版本的遗留物。冒号后面的数字最初是用来指示规则中包含的动作数量,现在由 Procmail 解析器自动计算。然而,为了兼容性,仍然需要:0

分析一个简单的规则

假设我们从一个特定的邮件组收到大量邮件,我们订阅了这个邮件组。这些邮件很有趣,但不重要,我们更愿意在闲暇时阅读它们。主题是“神话怪兽”,来自这个邮件列表的所有电子邮件都有一个“To”地址<mythical@monsters.com>。我们决定创建一个专门的文件夹来存放这些邮件,并将所有邮件复制到这个文件夹中。这是一个简单的规则,您将能够轻松复制和修改以处理将来的邮件。

规则结构

以下是一个非常简单的.procmail文件的示例副本,取自用户的主目录,并旨在解释 Procmail 配置的一些基本特性。规则本身旨在将发送到特定电子邮件地址<mythical@monsters.com>的所有邮件存储在一个名为monsters的特殊文件夹中。大多数邮件将发送给多个人,包括您自己,而“To”地址可以提供有用的邮件内容指示。例如,邮件可能发送到info@yourcompany.com的分发列表,您需要对这封邮件进行优先处理。

花点时间阅读文件的内容,然后我们将依次分解并分析每个部分的功能。

#
# Here we assign variables
#
PATH=/usr/bin: /usr/local/bin:.
MAILDIR=$HOME/Maildir # Make sure it exists
DEFAULT=$MAILDIR/ # Trailing / indicates maildir format mailbox
LOGFILE=$HOME/procmail.log
LOG="
"
VERBOSE=yes
#
# This is the only rule within the file
#
:0: # Anything to mythical@monsters.com
* ^TO_ mythical@monsters.com
monsters/ # will go to monsters folder. Note the trailing /

变量分析

要详细检查这个文件,我们可以从定义语句开始,其中变量被赋予特定值。这些值将覆盖 Procmail 已经分配的任何值。通过进行手动赋值,我们可以确保路径针对脚本操作进行了优化,并且我们确定使用的值而不是假设 Procmail 可能分配的值。

PATH=/usr/bin: /usr/local/bin:.
MAILDIR=$HOME/Maildir
DEFAULT=$MAILDIR/
LOGFILE=$HOME/procmail.log
LOG="
"
VERBOSE=yes

这些设置指令用于定义一些基本参数:

  • PATH指令指定了 Procmail 可以找到任何可能需要执行的程序的位置。

  • MAILDIR指定了所有邮件项目将存储的目录。这个目录应该存在。

  • DEFAULT定义了如果为单独的规则定义了特定位置,则邮件将存储在哪里。根据 Postfix 章节中关于选择邮箱格式的建议,尾部的/(斜杠)表示 Procmail 应以 Maildir 格式传递邮件。

  • LOGFILE是存储所有跟踪信息的文件,以便我们可以看到发生了什么。

规则分析

接下来是以:0开头的配方说明。第二个:指示 Procmail 创建一个锁定文件,以确保一次只写入一个邮件消息到文件中,以避免消息存储的损坏。单行规则可以分解如下:

  • *:所有规则行都以*开头。这是 Procmail 知道它们是规则的方式。每个配方可能有一个或多个规则。

  • ^TO_:这是一个特殊的 Procmail 内置宏,用于搜索大多数可能携带您地址的标题,例如To:,Apparently-To:,Cc:,Resent-To:等等,如果找到地址<mythical@monsters.com.>,则会匹配。

最后一行是操作行,默认情况下指定了MAILDIR变量指定的目录中的邮件文件夹。

提示

对于 Maildir 格式邮箱,文件夹名称末尾的斜杠是必需的,否则邮件将以不受 Courier-IMAP 支持的 unix mbox 格式传递。如果您正在使用 IMAP,文件夹名称还应以.(句号)为前缀,因为句号字符被指定为层次分隔符。

创建和测试规则

Procmail 允许您将规则和配方组织成多个文件,然后依次处理每个文件。这样可以更容易地管理规则,并根据需要打开或关闭规则。对于这个第一个测试案例,我们将创建一个特殊的规则集进行测试,并将所有规则组织在我们的主目录的子目录中。通常,子目录称为Procmail,但您可以自由使用自己的名称。

我们将从查看一个简单的个人规则并为单个用户进行测试开始。在本章后面,当我们涵盖了所有基础知识并且您对创建和设置规则的过程感到满意时,我们将展示如何开始将规则应用于所有系统用户。

一个“hello world”示例

几乎所有关于编程的书都以非常简单的“hello world”示例开始,以展示编程语言的基础知识。在这种情况下,我们将创建一个简单的个人规则,处理用户收到的所有电子邮件,并检查主题是否包含“hello world”这几个词。如果邮件主题包含这些特定词,邮件消息将存储在一个特殊的文件夹中。如果不包含这些魔术词,邮件将存储在用户的正常收件箱中。

创建 rc.testing

在生产环境中工作时,重要的是要确保编写和测试的规则不会干扰您的日常邮件活动。控制这一点的一种方法是创建一个专门用于测试新规则的特殊文件,并且只在实际进行测试工作时将其包含在 Procmail 处理中。当您对规则操作满意时,可以将其移动到自己的特定文件中,或者将其添加到其他类似或相关的规则中。在这个例子中,我们将创建一个用于测试规则的新文件rc.testing。在$HOME/Procmail目录中,使用您喜欢的编辑器创建文件rc.testing并输入以下行:

# LOGFILE should be specified early in the file so
# everything after it is logged
LOGFILE=$PMDIR/pmlog
# To insert a blank line between each message's log entry,
# Use the following LOG entry
LOG="
"
# Set to yes when debugging; VERBOSE default is no
VERBOSE=yes
#
# Simple test recipes
#
:0:
* ^Subject:.*hello world
TEST-HelloWorld

到目前为止,您可能已经开始认识到规则的结构。这个规则可以分解如下。

前几行设置了适用于我们测试环境的变量。由于它们是在测试脚本中分配的,因此它们只适用于脚本被包含在处理中的时候。一旦我们排除了测试脚本,测试设置当然就不适用了。

匹配所有以Subject:开头并包含字符串hello world的行。我们故意没有使用诸如test之类的字符串,因为少数系统可能会剥离看起来是测试消息的消息。请记住,Procmail 的默认操作是不区分大小写的,因此我们不需要测试所有变体,例如Hello World.

最后一行指示 Procmail 将输出存储在TEST-HelloWorld文件中。

$HOME/Procmail目录中创建testmail.txt,使用您喜欢的编辑器创建文件testmail.txt并输入以下行:

From: me@example.com
To: me@example.com (self test)
Subject: My Hello World Test
BODY OF TEST MESSAGE SEPARATED BY EMPTY LINE

主题行与rc.testing中的规则不一致,该规则包含了候选字符串,以演示不区分大小写的匹配。

对脚本进行静态测试

Procmail目录运行以下命令将生成调试输出:

formail -s procmail -m PMDIR=. rc.testing < testmail.txt

注意

在静态测试期间,我们已经在上一个命令中定义了变量PMDIR为我们当前的目录。

运行命令后,您可以查看错误消息的日志文件。如果一切正常,您将看到文件TEST-HelloWorld的创建,其中包含testmail.txt的内容以及日志中的以下输出。

procmail: [9060] Mon Jun 8 17:52:31 2009
procmail: Match on "^Subject:.*hello world"
procmail: Locking "TEST-HelloWorld.lock"
procmail: Assigning "LASTFOLDER=TEST-HelloWorld"
procmail: Opening "TEST-HelloWorld"
procmail: Acquiring kernel-lock
procmail: Unlocking "TEST-HelloWorld.lock"
From me@example.com Mon Jun 8 17:52:31 2009
Subject: My Hello World Test
Folder: TEST-HelloWorld 194

如果Subject行没有包含相关的匹配短语,您可能会在日志中看到以下输出:

procmail: [9073] Mon Jun 8 17:53:47 2009
procmail: No match on "^Subject:.*hello world"
From me@example.com Mon Jun 8 17:53:47 2009
Subject: My Goodbye World Test
Folder: **Bounced** 0

配置 Procmail 以处理 rc.testing

您需要编辑.procmailrc配置文件。可能已经有一些条目在里面,所以在进行任何更改之前最好备份文件。确保文件中包含以下行:

# Directory for storing procmail configuration and log files
PMDIR=$HOME/Procmail
# Load specific rule sets
INCLUDERC=$PMDIR/rc.testing

有些行使用#进行了故意注释。如果以后需要进行更详细的调试,可能需要这些行。

测试设置

使用以下命令,给自己发送两条消息:

echo "test message" | mail -s "hello world" $USER

主题行应包含字符串hello world,而另一条消息则不应包含此特定字符串。

当您检查邮件时,您应该发现主题中包含关键字的消息已存储在TEST-HelloWorld邮件文件夹中,而另一条消息则留在了正常的邮件收件箱中。

配置调试

如果一切正常——恭喜!您已经在组织您的邮件的道路上取得了很大进展。

如果结果不如预期,我们可以做一些简单的事情来找出问题所在。

检查脚本中的拼写错误

与任何编程过程一样,如果一开始不起作用,请检查代码,确保在编辑阶段没有引入明显的拼写错误。

查看错误消息的日志文件

如果这没有显示任何问题,您可以查看 Procmail 创建的日志文件。在这种情况下,日志文件称为~/Procmail目录中的pmlog。要查看最后几行,请使用以下命令:

tail ~/Procmail/pmlog

在以下示例中,缺少:0,因此规则行被跳过:

* ^Subject:.*hello world
TEST-HelloWorld

这将导致以下错误:

procmail: [10311] Mon Jun 8 18:21:34 2009
procmail: Skipped "* ^Subject:.* hello world"
procmail: Skipped "TEST"
procmail: Skipped "-HelloWorld"

在这里没有存储指令来遵循规则:0:

:0:
* ^Subject:.*hello world

这将导致以下错误:

procmail: [10356] Mon Jun 8 18:23:36 2009
procmail: Match on "^Subject:.* hello world"
procmail: Incomplete recipe

检查文件和目录权限

使用ls命令检查~/.procmailrc~/Procmail/*文件以及~/ home目录的权限。规则文件应该可以被除所有者以外的用户写入,并且应该具有类似以下的权限:

rw-r--r—

主目录应具有以下权限,其中?可以是r或:

drwx?-x?-x

打开完整日志记录

当您创建更复杂的规则,或者仍然遇到问题时,您需要启用 Procmail 的完整日志记录功能。为此,您需要从~/.procmailrc文件中删除#注释,以便启用它们,如下所示:

# Directory for storing procmail configuration and log files
PMDIR=$HOME/Procmail
# LOGFILE should be specified early in the file so
# everything after it is logged
LOGFILE=$PMDIR/pmlog
# To insert a blank line between each message's log entry,
# add a return between the quotes (this is helpful for debugging)
LOG="
"
# Set to yes when debugging; VERBOSE default is no
VERBOSE=yes
# Load specific rule sets
INCLUDERC=$PMDIR/rc.testing

现在重新发送两条示例消息,并检查输出信息的日志文件。日志文件应指示一些问题区域供您调查。

采取措施避免灾难

.procmailrc文件的开头插入以下配方将确保最近接收的 32 条消息都存储在backup目录中,确保在配方包含错误或产生意外副作用的情况下不会丢失宝贵的邮件。

# Create a backup cache of 32 most recent messages in case of mistakes.
# For this to work, you must first create the directory
# ${MAILDIR}/backup.
:0 c
backup
:0 ic
| cd backup && rm -f dummy `ls -t msg.* | sed -e 1,32d`

现在我们将假设这个工作,并在下一章中详细分析这个规则,看看它是如何工作的以及它的作用。

了解电子邮件结构

为了充分利用 Procmail 的功能,值得花一些时间了解典型电子邮件消息的基本结构。 随着时间的推移,结构变得越来越复杂,但仍然可以分解为两个离散的块。

消息正文

消息正文与标题之间由一个空白行分隔(所有标题必须连续出现,因为在空白行之后的任何标题都将被假定为消息正文的一部分)。

消息正文本身可以是一个由简单 ASCII 字符组成的简单文本消息,也可以是使用称为MIME的东西编码的部分的复杂组合。 这使得电子邮件能够传输从简单文本,HTML 或其他格式化页面到包括附件或嵌入对象(如图像)在内的各种形式的数据。 MIME 编码的讨论超出了本书的范围,并且对于您可能在邮件过滤中遇到的大多数过程来说并不是必要的。

如果您决定尝试处理消息正文中保存的数据,重要的是要记住,您在邮件程序的输出中看到的内容可能与原始邮件消息中传输的实际数据非常不同。

电子邮件标题

标题是电子邮件包含的标签,允许各种邮件组件发送和处理消息。 电子邮件标题的典型格式是由一个关键字组成的简单两部分结构,由:终止,并跟随关键字分配的信息。 标题提供了有关电子邮件是如何创建的,什么形式的邮件程序创建了消息,消息来自谁,应该发送给谁,以及它如何到达您的邮箱的大量信息。

以下邮件头与从freelancers.net的多个邮件列表中收到的一封电子邮件相关。 电子邮件最有用的识别特征是主题行,因为大多数其他邮件组使用了讨论的其他邮件头的相同值。

电子邮件标题

标题结构

先前的示例包含了大量的标题,这些标题是由邮件在从发件人到收件人的过程中经历的一系列过程插入的。 但是,有一小部分关键标题非常有用,用于处理电子邮件,并且在大量的规则中使用。

标题的官方定义

所有不以X-开头的标题都由相关标准机构分配特定功能。 关于它们的更多信息可以在RFC(请求评论)文档 822中找到,网址为www.ietf.org/rfc/rfc0822.txt

X-开头的标题是用户定义的,并且仅适用于特定应用程序。 但是,一些应用程序可能使用与其他应用程序相同的标题标记,但出于不同的原因,并且提供的信息格式也不同。

示例规则集

为了帮助您理解 Procmail 规则的工作方式,我们将介绍几个简单但非常有用的规则集的设计和设置。 这应该有助于让您开始设计自己的规则集,以满足更具体的需要来过滤您的传入邮件项目。

所有这些示例都是基于从 Freelancers 邮件列表收到的邮件消息,先前示例标题取自其中。 它们都实现了相同的结果,并再次证明编程问题没有一个正确的解决方案。

来自标题

此标头解释了电子邮件的发起者是谁。可以使用各种格式,并由各种人类可读和计算机可读的信息项的各种组合组成。当您查看了一些电子邮件后,您将开始看到不同邮件系统和软件可以使用的各种模式。实际的标头格式并不一定重要,因为您要生成规则以匹配特定的电子邮件。

From: Do Not Reply <do-not-reply@freelancers.net>

Return-Path 标头

此字段由最终传输系统添加到将消息传递给其接收者的消息中。该字段旨在包含有关消息的发件人地址和路由的确切信息。

Return-Path: <do-not-reply@freelancers.net>

通过 Return-Path 进行过滤

大多数邮件列表使用Return-Path标头:

:0:
* ^Return-Path: <do-not-reply@freelancers.net>
freelancers//

这是一个方便地过滤邮件列表项的方法。在这里,^字符执行一个特殊功能,指示 Procmail 在新行的开头开始匹配过程。这意味着包含短语的行不会被匹配。Procmail 的默认操作是,如果在标头或邮件正文的任何位置找到字符串,就返回匹配,具体取决于脚本设置的搜索位置。

To 和 Cc 标头

邮件通常发送给一个或多个列在电子邮件的 To:或 Cc:标头中的人。与 From:标头一样,这些地址可能以多种方式格式化。这些标头对所有邮件接收者可见,并允许您查看所有列出的公共接收者。

To:projects@adepteo.net

有一个第三个接收者标头,不像 To:和 Cc:那样常见,但在大量邮件中经常使用。这是 Bcc:(暗送)。不幸的是,正如名称所暗示的那样,这是一个盲标头,因此信息不包含在实际的标头信息中,因此无法用于处理。

通过 To 或 Cc 进行过滤

Procmail 有许多特殊的内置宏,可用于识别邮件项。特殊规则^TO_旨在搜索所有可用的目标标头。规则必须写成四个字符,没有空格,并且 T 和 O 都是大写。匹配的短语必须紧跟在_后面,再次没有空格。

:0:
rule setsCc header, filtering by* ^TO_do-not-reply@freelancers.net
freelancers/

主题头

主题行通常包含在电子邮件头中,除非发件人决定根本不包括主题行。

主题:FN-PROJECTS 自由职业网页设计师

在这个例子中,发送到这个特定列表的所有邮件都以短语“FN-PROJECTS”开头,因此有时适合过滤。

通过主题进行过滤

当邮件列表在主题行中添加前缀时,此前缀可能适用于过滤:

:0:
* ^Subject: FN-PROJECTS
freelancers//

系统范围的规则

现在我们已经涵盖了设置规则、分析电子邮件以及通常看到所有处理操作如何交互的所有基础知识,我们将浏览一些系统范围的过滤、测试和操作的示例。

删除可执行文件

在第九章中,我们将看到如何将完整的病毒检查系统集成到 Postfix 邮件架构中。这将执行准确的病毒签名识别,并向邮件头添加适当的标志以指示邮件中是否存在病毒。但是,如果不可能设置这样的系统,这条规则将提供一种替代但更残酷的方法来阻止所有带有可执行附件的电子邮件。

如果您将以下内容放入/etc/procmailrc,它将影响通过系统传输的所有包含特定类型文档附件的邮件。

# Note: The whitespace in the [ ] in the code comprises a space and a tab character
:0
* < 256000
* ! ^Content-Type: text/plain
{
:0B
* ^(Content-(Type|Disposition):.*|[ ]*(file)?)name=("[^"]*|[^ ]*)\.(bat|cmd|com|exe|js|pif|scr)
/dev/null
}

规则以惯例的:0指令开始。

条件适用如下:

首先,确保我们只过滤大小小于 256 KB 的邮件。这主要是为了效率,大多数垃圾邮件都比这个大小小。如果您收到的病毒更大,您可以显然增加它,但是您的系统可能会负载更高。

下一行表示我们也只查看那些是 MIME 类型的消息(即不是纯文本),因为附件根据定义不能包含在纯文本消息中。

我们在花括号之间有一个子过滤器。:0B表示我们正在处理消息的正文,而不是标题。我们必须这样做,因为附件出现在正文中,而不是标题中。然后我们寻找具有可执行文件 MIME 标题特征的行。如果需要,您可以修改文件扩展名;这些只是常用于传输病毒的扩展名。

在这种情况下的操作是,如果匹配,则将此消息发送到/dev/null。请注意,这意味着不会向发件人发送任何消息反弹或错误消息;消息只是被丢弃,永远不会再次被看到。当然,您可以将消息存储在安全位置,并指定某人监视不包含病毒的有效消息的帐户。对于这个问题的更优雅的解决方案,请记得查看第九章。

大邮件

随着高速、始终在线的互联网连接的日益增加,人们开始发送越来越大的电子邮件。曾经,一个签名文件超过四行被认为是粗鲁的,而如今人们愉快地包含图像和壁纸,并发送电子邮件的 HTML 和文本版本,而不意识到他们发送的邮件大小。

将这样大的消息存储在收件箱中会大大增加搜索邮件消息的处理开销。一个简单的解决方案是将所有超过一定大小的消息移动到一个超大文件夹中。通过使用以下规则,可以非常简单地实现这一点,该规则查找大小超过 100,000 字节的消息,并将它们存储在largemail文件夹中。

:0:
* >100000
largemail/

这条规则的缺点是你的用户需要记住定期检查他们的收件箱和largemail文件夹。更优雅的解决方案将允许你复制消息的前几行以及标题和主题行,并将其存储在收件箱中,并通知你需要检查完整版本。这样的解决方案可以在下一章末尾的示例中看到。

总结

在本章中,我们已经了解了 Procmail 的一些基础知识。到目前为止,你应该熟悉 Procmail 用于加载配方的各种文件,过滤的核心原则以及可用的选项。我们还分析了电子邮件,设置了个人和系统范围的过滤器,并查看了一些简单的测试、日志记录和调试选项,这些选项将帮助我们更有效地管理公司的邮件。

我们只是刚刚触及了可能性的表面,但希望这点小小的尝试已经给你提供了大量关于如何处理和过滤你每天过载的电子邮件的想法。这可能已经给你提供了更高级过滤器的想法,下一章将提供更多关于如何设置这些过滤器的建议和解释。

第七章:高级 Procmail

现在我们已经掌握了 Procmail 的基础知识,我们可以继续开始组建一个更完整的邮件处理系统。本章中的高级技术只有在您需要非常专业的邮件处理时才需要,并且不需要为设置基本电子邮件服务器而需要。您可能希望跳过本章,在服务器完全配置和运行后再返回。

在本章中,我们将使用一些更高级的 Procmail 功能。本章将涵盖:

  • 投递和非投递配方之间的区别

  • 在高级配方中使用变量、替换和伪变量

  • 锁定和使用各种标志来控制执行

  • 如何应用条件来测试消息的各个部分

  • 高级操作,将消息转发、保存或传递给外部程序进行处理

  • 正则表达式简介

  • 使用 Procmail 宏简化电子邮件头部分析

  • 详细分析一些高级配方,包括一些示例配方

到本章结束时,您应该具有一套有用的例程工具箱,用于组合您自己的一套 Procmail 配方,并控制您的邮件。

投递和非投递配方

到目前为止,我们只涵盖了那些要么最终将邮件传递给程序或文件,要么将消息转发给另一个邮件用户的配方。还有另一个选项可用,引用 Procmail 文档:

有两种类型的配方——投递和非投递配方。如果找到匹配的投递配方,Procmail 会认为邮件(你猜对了)已经投递,并在成功执行配方的动作行后停止处理.procmailrc文件。如果找到匹配的非投递配方,此配方的动作行执行后,.procmailrc文件的处理将继续。

非投递示例

我们在上一章中介绍了一个示例,旨在备份邮件项,以防正在测试的配方删除所有邮件。这是一个非常有用的非投递配方示例,可以在 Procmail 手册页procmailex中找到。

如果您对 Procmail 相当陌生,并计划进行一些实验,通常有某种安全网会有所帮助。在插入前面提到的两个配方之前,所有其他配方将确保最后到达的 32 封邮件始终被保留。为了使其按预期工作,我们必须在插入这两个配方之前在$MAILDIR中创建一个名为backup的目录:

:0 c
backup
:0 ic
cd backup && rm -f dummy `ls -t msg.* | sed -e 1,32d`

第二个配方使用了 Procmail 的几个特性,我们将在本章的后续部分中更详细地探讨。

如果我们按照这个配方一步一步地工作,最终会得到一个有用的存档实用程序,记录最后接收的 32 封邮件,并允许我们手动恢复邮件,如果我们创建了一个最终销毁邮件而不是存储它的配方。在繁忙的邮件服务器上,增加这个数字可能是明智的,以保留更大的消息存档。

第一个配方通过将邮件的副本或克隆传递到backup目录来执行简单的备份操作:

:0 c
backup

在添加第二个配方之前,在.procmailrc文件中创建上述配方,并给自己发送几封邮件。我们可以看到每个邮件项都存储在备份目录中(前提是它存在并具有正确的权限)。

第二个配方同样简单,但使用了 Linux 系统命令的一些更复杂的特性,以删除除backup目录中最近的 32 个邮件项之外的所有邮件项。

:0 ic
| cd backup && rm -f dummy `ls -t msg.* | sed -e 1,32d`

让我们看看这个配方是如何工作的。首先,我们将看到规则标志及其含义:

标志 含义
i 忽略后续管道命令的返回代码
c 克隆或复制传入数据,以便不影响原始数据

|指示 Procmail 将匹配的数据传递给以下管道命令。每个命令执行特定的动作。

命令 动作
cd backup 进入backup目录。
ls -t msg.* 获取以msg开头的文件列表,并按时间顺序排序。
sed -e 1,32d 删除除最后 32 行之外的所有内容,即最近的 32 封邮件。
rm -f dummy... 参数dummy是为了防止出现没有要删除的文件时的错误消息,然后rm命令继续删除sed过滤器列出的文件。

这两个配方是无条件配方的例子,它们在每封传入的邮件消息上运行。没有条件行,即以星号(*)开头的行,这意味着这些配方是无条件的。由于这两个配方都包括配方中的c标志,它们也被定义为非投递配方。

一旦我们收集了一些 Procmail 配方,我们会发现处理配方的顺序可能很重要。通过正确设置处理顺序,我们可以提高性能,减少处理传入邮件所需的时间。我们还可以确保更重要的消息适用于重要消息,而不是适用于用于批量消息的更一般的规则。

一个典型的情景可能是按以下顺序应用规则:

  1. 首先处理守护程序或服务器消息。

  2. 邮件列表应尽早处理,但在服务器消息之后,因为我们希望我们的服务首先得到处理。

  3. 应用kill file来阻止任何已知的垃圾邮件发送者。

  4. 在我们处理邮件列表之前,请不要发送度假回复,以防止向邮件列表发送令人讨厌的度假回复。

  5. 保存私人消息。

  6. 检查未经请求的大量电子邮件UBE)-垃圾邮件。这避免了在已知有效的电子邮件上处理垃圾邮件检查的高开销。

Formail

Formail是一个外部实用程序(来自 Procmail),几乎总是在安装了 Procmail 的系统上可用。它的功能是处理邮件消息并从消息头中提取信息。它充当过滤器,可用于强制将邮件格式化为适合存储在 Linux 邮件系统中的格式。它还可以执行许多其他有用的功能,如'From'转义、生成自动回复头、简单的头提取或拆分邮箱/摘要/文章文件。

需要使用标准输入提供输入数据邮件/邮箱/文章内容。因此,formail非常适合在管道命令链中使用。输出数据在标准输出上提供。

在本章中,我们不打算深入讨论formail的微妙之处,但由于它是一个有用的工具,我们将在一些示例中提到它的一些功能。更多信息可以从系统手册页面中获得。

高级配方分析

这里有一个更复杂的配方,实现了一种度假服务,通知发件人你不在,无法回复电子邮件。乍一看,这可能是一个简单的非投递配方,向所有收到的消息发送一条消息。然而,这并不理想,因为有些人可能最终会收到多个投递确认消息,而你也可能会向无法理解你善意回复的系统实用程序发送消息。

该示例基于 Procmail procmailex手册页面上的“度假示例”。

vacation.cache文件由 Formail 维护。它通过提取发件人的姓名并将其插入vacation.cache文件来维护度假数据库。这确保它始终包含最新的名称。文件的大小限制为最大约 8192 字节。如果发件人的姓名是新的,将发送自动回复。

以下配方实现了度假自动回复:

SHELL=/bin/sh # for other shells, this might need adjustment
:0 Whc: vacation.lock
# Perform a quick check to see if the mail was addressed to us
$TO_:.*\<$\LOGNAME\>
# Filter out the mail senders we don't want to send replies to - Ever
* !^FROM_DAEMON
# Make sure that we do not create an endless loop that keeps
# replying to the reply by checking to see if we have already processed
# this message and inserted a loop detection header
* !^X-Loop: your@own.mail.address
| formail -rD 8192 vacation.cache
:0 ehc
# We are pretty certain it's OK to send a reply to the sender of this message
| (
formail -rA"Precedence: junk" \
-A"X-Loop: your@own.mail.address" ; \
echo "Hi, Your message was delivered to my mailbox,"; \
echo "but I won't be back until Monday."; \
echo "-- "; cat $HOME/.signature \
) | $SENDMAIL -oi -t

我们将在本节末尾回到这个配方,并通过使用我们在 Procmail 中学到的一些东西,创建一个稍微更新的版本。目前,这个示例将作为一个参考,以便理解我们在下面对一般配方结构进行分解时探讨的一些概念。

添加注释

对我们的规则和配方进行文档编写或添加注释始终是一项重要任务。所有注释都以#字符开头,并持续到行尾。在大多数情况下,将注释放在行首或在我们希望记录的单行之后的一两个制表符处是有用的。

然而,有一个规则文件的部分,注释必须单独占一行,那就是条件部分。

# Here is a full line comment
MAILDIR=${HOME}/Maildir # This comment spans multiple
# lines for clarity.
:0: # Comment OK here
* condition # BAD comment. NOT allowed.
# Old versions of Procmail don't understand this.
* condition
{ # Comment OK
# Comment OK
do_something # Comment OK
}

变量赋值

为了跟踪设置、测试结果、默认值等信息,我们可以将这些信息存储在变量中。赋值操作很简单,遵循与其他 Linux 脚本语言相同的格式。基本格式是VARIABLENAME=VALUE。

提示

变量名称中不能有空格。如果在分配的值中有空格,则整个变量应存储在双引号之间。

访问变量的正确方式是将VARIABLENAME括在大括号{}中,并在所有内容前加上美元($)符号。在其他赋值中使用变量是完全可以接受的。以下是一些示例:

MAILDIR=${HOME}/Maildir # Set the value of the MAILDIR
LOGFILE=${MAILDIR}/log # Store logfiles in the MAILDIR

请注意在前面的示例中,${HOME}获取了 shell 环境设置的值,就像在启动进程时设置的那样。

谨慎使用变量及其命名可以使配方更容易阅读和维护。

执行替换

有时,将文字元素替换为仅在运行时可以计算或评估的变量将是必要或有用的。Procmail 允许作者在大多数地方用变量替换或命令替换替换大多数文字元素。使用变量的最简单方式是使用$varname格式,这是许多脚本语言共有的。

变量/命令 替换
$VAR | 在配方中,无论何时出现$VAR,都用该变量持有的值替换它。
${VAR}iable * | 当我们需要将变量与文字连接在一起时,使用{}来强制指定名称为${VAR}而不是$VARiable

注意

如果需要将变量与固定文本或值组合,{}元素允许绝对定义变量名称。请注意,除非包含$修饰符,否则这不会发生在条件行中。

使用默认值赋值变量

Procmail 借用了一些标准的 shell 语法来初始化变量。

如果我们希望能够为变量分配默认值,以便在变量未设置或由于某种原因无法计算时使用,可以使用或:-分隔符。如果希望在变量已设置或非空时应用替代值,则使用+:+分隔符。

分隔符 操作
${VAR:-value} 如果VAR未设置或为空,则替换为value的扩展;否则,替换为VAR的值。
${VAR-value} 如果VAR未设置,则替换为value的扩展;否则,替换为VAR的值。
${VAR:+value} 如果VAR已设置或非空,则替换为value的扩展;否则,替换为VAR的值。
${VAR+value} 如果VAR已设置,则替换为value的扩展;否则,替换为VAR的值。

以下是一些示例:

VAR = "" # Set VAR to null
VAR = ${VAR:-"val1"} # VAR = "val1"
VAR = ""
VAR = ${VAR-"val2"} # VAR = ""
VAR = ""
VAR = ${VAR:+"val3"} # VAR = ""
VAR = ""
VAR = ${VAR+"val4"} # VAR = "val4"
VAR = "val"
VAR = ${VAR:+"val3"} # VAR = "val3"
VAR = "val"
VAR = ${VAR+"val4"} # VAR = "val4"
VAR # unset VAR
VAR = ${VAR:-"val1"} # VAR = "val1"
VAR
VAR = ${VAR-"val2"} # VAR = "val2"
VAR
VAR = ${VAR:+"val3"} # no action
VAR
VAR = ${VAR+"val4"} # no action

将命令输出赋值给变量

可以通过使用(反引号)操作符将命令的输出赋值给变量——反引号()是 ASCII 值 96,而不是普通的撇号('),其 ASCII 值为 39。

  `cmd1 | cmd2`

此示例将将两个反引号之间的管道输出分配给相应的变量或内联到代码中。

伪变量

有许多特殊变量或伪变量直接由 Procmail 分配。更改其中一些值实际上可以改变 Procmail 的操作方式。

邮箱变量

以下变量由 Procmail 用于确定它将存储任何已交付邮件的位置。

名称 操作
MAILDIR MAILDIR的默认值取自$HOME环境变量的值。它也是 Procmail 在执行期间的当前工作目录的值。除非输出文件名包括路径组件,否则它们将被创建在这个默认目录中。
MSGPREFIX 当我们希望将文件按顺序写入目录时使用此选项。MSGPREFIX将作为前缀添加到使用此选项创建的文件的名称中。默认前缀是msg.,因此文件将被命名为msg.xyz。将文件传递到maildirMH目录时不使用此选项。
DEFAULT 这是系统上默认邮件存储区的位置。通常情况下,我们不会修改这个变量。
ORGMAIL 这用作DEFAULT无法使用的情况下的灾难恢复位置。这绝对不应该被修改。

程序变量

Procmail 在编译时有合理的默认值。大多数情况下,这些值不需要更改。

名称 操作
SHELL 这是一个标准的环境变量,指定了 Procmail 需要调用子进程的 shell 环境。分配给它的值应该是与 Bourne shell 兼容的,例如/bin/sh
SHELLFLAGS 这指定了在启动SHELL时应传递的任何可选标志。
SENDMAIL 这指示 Procmail 在哪里找到用于将邮件发送给其他用户的sendmail程序。(通常不应该被玩弄)。
SENDMAILFLAGS SHELLFLAGS一样,指定在执行SENDMAIL程序时应传递的任何标志或命令行参数。

系统交互变量

在执行食谱期间,Procmail 可能需要运行外部命令,处理错误或创建文件。这些变量控制了 Procmail 与 shell 的交互方式。

名称 操作
UMASK 创建任何文件时使用的文件权限模式。有关详细信息,请参阅man umask
SHELLMETAS 在执行之前,shell 管道将与SHELLMETAS的内容进行比较。如果在管道命令中找到了来自SHELLMETAS的任何字符,则认为该命令对于 Procmail 来说太复杂,无法自行管理,并且会生成一个子 shell 进程。如果我们知道特定的管道始终对 Procmail 来说足够简单,但包含在SHELLMETAS中的字符,我们可以在处理管道时临时将空字符串分配给SHELLMETAS,然后恢复SHELLMETAS。这将避免生成子 shell 的开销。
TRAP 在 Procmail 执行结束时,我们可以分配一个代码段来执行。例如,可以用它来删除在执行食谱期间创建的临时文件。`TEMPORARY=\(HOME/tmp/pmail.\)
--- ---
UMASK 创建任何文件时使用的文件权限模式。有关详细信息,请参阅man umask
SHELLMETAS 在执行之前,shell 管道将与SHELLMETAS的内容进行比较。如果在管道命令中找到了来自SHELLMETAS的任何字符,则认为该命令对于 Procmail 来说太复杂,无法自行管理,并且会生成一个子 shell 进程。如果我们知道特定的管道始终对 Procmail 来说足够简单,但包含在SHELLMETAS中的字符,我们可以在处理管道时临时将空字符串分配给SHELLMETAS,然后恢复SHELLMETAS。这将避免生成子 shell 的开销。
TRAP="/bin/rm -f $TEMPORARY"
EXITCODE 当 Procmail 退出时,此值将返回给启动 Procmail 的进程。通常,成功返回值为0,非零值表示某种形式的失败。通过修改EXITCODE值,我们可以返回有关执行的特定信息。由 Procmail 启动的程序的退出代码存储在变量$?中。

日志变量

在食谱执行期间,任何日志输出的冗长和位置由以下变量控制:

名称 操作
LOGFILE 这指定了 Procmail 应将其所有日志和调试信息写入的位置。如果此值为空,则输出将发送到标准错误输出,这意味着除非程序以交互方式运行或stderr被重定向到其他位置,否则它将丢失。
LOG 如果我们希望直接写入日志文件,我们可以为LOG变量分配一个值,并且它将附加到LOGFILE。如果我们想要格式化输出并在我们的日志消息之后包括一个空行,我们必须记得在输出的消息中包含一个空行。LOG="Procmail is great"
VERBOSE 这允许输出为基本默认或提供详细信息。设置VERBOSE=1将包括详细的日志信息,有助于调试我们的配方。为了减少输出信息的数量,请记得在配方运行后设置VERBOSE=0
LOGABSTRACT 如果LOGABSTRACT设置为all,所有投递将包含有关发送者、主题和投递邮件大小的信息。如果您希望停止此日志记录,请将LOGABSTRACT=no
COMSAT 如果设置为yes,Procmail 将生成 comsat/biff 通知。有关更多信息,请参阅comsatbiff手册页。

Procmail 的状态变量

在处理配方时,Procmail 使用当前配方的当前状态更新以下变量:

名称 操作
PROCMAIL_OVERFLOW 如果 Procmail 在启动时在 Procmail 配方文件中发现任何行长于缓冲区大小,它将将PROCMAIL_OVERFLOW的值设置为yes。如果正在读取的行是条件或操作行,则操作将被视为失败。但是,如果它是变量赋值或配方开始,Procmail 将停止读取文件并以异常终止退出。
HOST 这保存了进程正在运行的主机的名称。
DELIVERED 如果邮件成功投递,这将设置为yes,并且 Procmail 将通知调用进程。如果我们手动将其设置为yes 并且消息未被投递,它将无迹可寻地丢失,但调用进程仍将认为它已成功投递。
LASTFOLDER 这给出了最后一个写入消息的文件或目录的名称。
MATCH 这保存了上一个正则表达式操作提取的信息。
$= 这保存了最新评分配方的结果。有关更多信息,请参阅procmailsc手册页。

| $1,$2,...; $@; $# | 就像标准 shell 一样,这指定了 Procmail 启动时使用的命令行参数。

  • $1是第一个命令行参数,依此类推。

  • $@包含所有参数。

  • $#包含参数的数量。

另请参阅SHIFT伪变量。 |

$$ 这保存了当前进程 ID。这对于创建与进程唯一相关的临时文件非常有用。
$? 这保存了上一个 shell 命令的退出代码。
$_ 这保存了当前正在处理的 Procmail 文件的名称。
$- | 这是LASTFOLDER的别名。$=$@不能直接使用;在可以用于任何有用的东西之前,我们必须将值分配给另一个变量。

消息内容变量

这些变量的主要用途是访问保存在适当部分中的数据,但是当配方具有限制处理到消息的另一部分的标志时。通过使用HB,我们可以访问整个消息的信息。

名称 操作
H 这保存了当前正在处理的消息的头信息。
B 这保存当前正在处理的消息的正文。

锁定变量

以下表中的每个变量控制任何锁定文件的名称以及配方应等待锁定变为自由的时间。

名称 行动
LOCKFILE 为此变量分配一个值会创建一个全局锁定文件,直到为LOCKFILE分配另一个值为止。此值可以是要创建的另一个锁定文件的名称,也可以是一个空值,以删除任何锁定。
LOCKEXT 为此分配一个值允许我们覆盖作为锁定文件名一部分使用的扩展名。这在识别创建锁定文件的进程方面可能很有用。
LOCKSLEEP 如果 Procmail 想要在另一个进程已经锁定的文件上创建一个锁定,它将进入retry循环。LOCKSLEEP变量指定在重试获取锁之前睡眠和等待的秒数。
LOCKTIMEOUT 这指定了锁定文件必须达到的秒龄,然后假定锁定文件无效并将被覆盖。如果值为0,则永远不会覆盖锁定文件。默认值为1024秒。

错误处理变量

在我们的配方中出现错误时,我们可以使用这些变量中的任何一个来决定采取什么行动。

名称 行动
TIMEOUT 这指定了在告知子进程终止之前等待子进程的时间。默认值为960秒。
SUSPEND 这指定了在NORESRETRY重试之间等待的时间。默认值为16秒。
NORESRETRY 当发生严重的系统资源短缺时(例如磁盘空间不足或系统已达到最大进程数),Procmail 将重试的次数。默认值为4,如果数字为负数,Procmail 将永远重试。如果在重试期间资源仍然不可用,消息将被丢弃并被分类为不可投递。

杂项变量

以下表格包含有关可能在我们的配方中有用的各种 Procmail 变量的信息:

名称 行动
LINEBUF 这为 Procmail 准备处理的配方行的长度设置了一个限制。如果我们需要处理非常大的正则表达式或将大量数据存储到MATCH中,增加此值。
SHIFT 这类似于正常 shell 处理中的shift功能。将正数分配给此变量会使 Procmail 的命令行参数下移。
INCLUDERC 这指示 Procmail 加载另一个包含 Procmail 配方的文件。在 Procmail 继续处理当前文件之前,将加载和处理这个新文件。
DROPPRIVS 这确保了在 Procmail 以setuidsetgid执行时不会有根特权可用。将此值设置为yes将使 Procmail 放弃所有特权。

打印 Procmail 变量

以下示例将打印大部分环境设置的响应,并提供一些在尝试调试 Procmail 问题时可能有用的信息。不希望将其包含在任何生产文件中,否则我们的日志文件可能会迅速变得非常庞大。

在与其他 Procmail 配方文件相同的目录中创建一个名为rc.dump的文件,并将以下行放入文件中:

注意

请注意,下一个示例开头和结尾的引号(")是必需的,以确保配方正常运行。

#
# Simple Procmail recipe to dump variables to a log file
#
LOG="Dump of ProcMail Variables
MAILDIR is currently :${MAILDIR}:
MSGPREFIX is currently :${MSGPREFIX}:
DEFAULT is currently :${DEFAULT}:
ORGMAIL is currently :${ORGMAIL}:
SHELL is currently :${SHELL}:
SHELLFLAGS is currently :${SHELLFLAGS}:
SENDMAIL is currently :${SENDMAIL}:
SENDMAILFLAGS is currently :${SENDMAILFLAGS}:
UMASK is currently :${UMASK}:
SHELLMETAS is currently :${SHELLMETAS}:
TRAP is currently :${TRAP}:
EXITCODE is currently :${EXITCODE}:
LOGFILE is currently :${LOGFILE}:
LOG is currently :${LOG}:
VERBOSE is currently :${VERBOSE}:
LOGABSTRACT is currently :${LOGABSTRACT}:
COMSAT is currently :${COMSAT}:
PROCMAIL_OVERFLOW is currently :${PROCMAIL_OVERFLOW}:
TODO is currently :${TODO}:
HOST is currently :${HOST}:
DELIVERED is currently :${DELIVERED}:
LASTFOLDER is currently :${LASTFOLDER}:
\$= is currently :$=:
\$1 is currently :$1:
\$2 is currently :$2:
\$$ is currently :$$:
\$? is currently :$?:
\$_ is currently :$_:
\$- is currently :$-:
LOCKFILE is currently :${LOCKFILE}:
LOCKEXT is currently :${LOCKEXT}:
LOCKSLEEP is currently :${LOCKSLEEP}:
LOCKTIMEOUT is currently :${LOCKTIMEOUT}:
TIMEOUT is currently :${TIMEOUT}:
NORESRETRY is currently :${NORESRETRY}:
SUSPEND is currently :${SUSPEND}:"

运行以下命令:

# procmail ./rc.dump
<CTRL-D>

这将创建以下输出:

# procmail ./rc.dump
<CTRL-D>
"Dump of ProcMail Variables
MAILDIR is currently :.:
MSGPREFIX is currently :msg.:
DEFAULT is currently :/var/spool/mail/root:
ORGMAIL is currently :/var/spool/mail/root:
SHELL is currently :/bin/bash:
SHELLFLAGS is currently :-c:
SENDMAIL is currently :/usr/sbin/sendmail:
SENDMAILFLAGS is currently :-oi:
UMASK is currently ::
SHELLMETAS is currently :&|<>~;?*:
TRAP is currently ::
EXITCODE is currently ::
LOGFILE is currently ::
LOG is currently ::
VERBOSE is currently :1:
LOGABSTRACT is currently ::
COMSAT is currently :no:
PROCMAIL_OVERFLOW is currently ::
TODO is currently ::
HOST is currently :delta.adepteo.net:
DELIVERED is currently ::
LASTFOLDER is currently ::
$= is currently :0:
$1 is currently ::
$2 is currently ::
$$ is currently :9014:
$? is currently :0:
$_ is currently :./rc.dump:
$- is currently ::
LOCKFILE is currently ::
LOCKEXT is currently :.lock:
LOCKSLEEP is currently ::
LOCKTIMEOUT is currently ::
TIMEOUT is currently ::
NORESRETRY is currently ::
SUSPEND is currently ::

配方

Procmail 配方遵循简单的格式。但是,有许多方法可以指示 Procmail 根据许多标志以及规则和配方的编写方式来解释或实施规则中的指令。

冒号行

正如我们已经发现的,到目前为止,所有规则都以:0开头,后面跟着一个或多个标志和指令。历史上,冒号(:)后面跟着一个数字,用来指定规则中存在的条件数量。当前版本的 Procmail 会自动确定条件的数量,因此始终使用值0

锁定

我们已经讨论过,为了阻止多个进程同时尝试写入同一文件,我们需要使用锁定机制。当然,这个要求随着过滤器试图调用的进程类型而变化。例如,仅仅更改或分配值的过滤器对任何物理文件都没有影响,因此不需要锁定。同样,仅仅将数据转发到另一个进程或另一个接收者的过滤器本质上不需要应用锁定。在大多数情况下,当 Procmail 意识到它正在写入文件并提供文件本身的锁定时,将自动应用锁定。在某些情况下,可能需要显式锁定资源。

以下是一些示例,以便了解何时自动应用锁定,根本不需要,或需要强制手动锁定。

自动锁定

任何以:0:开头的规则都将应用自动文件锁定。在这种情况下,Procmail 将自动确定邮件被传递到的文件的名称,并创建一个锁定文件。如果锁定文件已经存在,它将等待一段时间并重试创建锁定。当它最终创建了锁定文件,它将继续处理。如果无法创建锁定文件,它将报告错误并继续下一个规则。

以下规则使用自动锁定:

:0 <flags>:

强制锁定

可能会有一段时间,特别是在通过外部脚本处理邮件时,需要强制锁定。在大多数情况下,Procmail 将通过检查进程命令行并查看输出的位置来确定最终数据被写入的文件的名称。然而,如果脚本负责选择输出位置本身,或者依赖于可能被另一个 Procmail 进程更改的文件,必须明确请求锁定文件如下:

:0 <flags> :scriptname.lock

在大多数情况下,您不太可能需要在编写的大多数脚本中强制锁定。

无锁定

当转发到执行自己的文件或记录锁定过程的管道时,例如将问题报告存储在数据库中,不需要记录锁定。同样,如果消息被转发给另一个用户,最终交付将处理记录锁定。简单的规则定义是:

:0 <flags>

标志

在我们迄今为止看过的例子中,我们已经允许 Procmail 的默认设置生效。然而,有许多标志可以设置以控制 Procmail 的工作方式。

![标志

默认标志

如果在食谱的冒号行上没有声明标志,Procmail 将假定以下标志(H,hb)已被用作默认值。

标志 动作
H 仅扫描邮件标题。
hb 动作行同时传递邮件数据的标题和正文。
匹配范围:HB

通常,匹配将在整个邮件包括标题和邮件正文之间进行。如果邮件正文可能很大,并且我们知道我们需要对标题进行匹配,那么使用H标志限制匹配范围仅限于标题是明智的。

相反,有时我们可能正在寻找信息项,例如文档正文中仅出现的重复页脚或签名,这种情况下我们可以使用B标志将匹配限制为仅限于正文。

标志 动作
H 仅在邮件标题中进行匹配。
B 仅在邮件正文中进行匹配。
HB 在整个邮件项目中包括标题和正文进行匹配。
操作范围:hb

默认情况下,操作行处理整个电子邮件项目,包括标题和正文。如果需要仅处理邮件数据的一部分,则可以指定将哪一部分传递给操作行。

旗帜 行动
h 仅将标题传递给操作行进行处理。
b 仅将消息正文传递给操作行进行处理。
hb 仅将标题和消息正文传递给操作行进行处理。这是默认范围。

注意

注意在“匹配范围”和“操作范围”之间的区别很重要。在第一种情况下,标志的值确定必须扫描邮件的哪一部分(标题、正文或整个邮件)以进行匹配。在第二种情况下,标志的值确定必须处理邮件的哪一部分。

流程控制:aAeEc

这可能是所有 Procmail 标志中最复杂的一组标志。本章后面的示例将解释使用这些标志的各种方式。简而言之,可以假定每个标志的以下内容:

旗帜 行动
A 只有前一个配方的条件得到满足时,才会处理该配方。
a 如果前一个配方的条件得到满足,并且操作已经完成而没有错误,则将处理该配方。
E 这是A的相反。如果前一个配方的条件未满足,则将处理该配方。
e 如果前一个配方的条件得到满足,但处理未能成功完成,将处理该配方。
c 这指示配方创建原始消息的副本或克隆,并使用任何操作在子进程中处理此副本。父进程继续处理消息的原始副本。

c标志应该被理解为克隆复制。普遍的误解是该标志应该被解释为继续克隆复制操作创建数据的单独副本,并创建一个单独的执行流程来处理该数据,有时作为完全独立的子进程。当此克隆配方完成时,父进程将继续执行,并保持原始数据不变。

区分大小写:D

顽固的 Linux 用户非常了解区分大小写,并始终将大写视为与小写完全不同。然而,Procmail 的默认操作是在匹配字符串时不区分大小写。这意味着对于 Procmail 来说,大写小写是相同的,除非告知应用D标志应用区分大小写。

执行模式:fwWir

我们可以指示 Procmail 如何处理或执行配方,以及在处理过程中遇到错误时要采取的操作。当处理仅在数据的前几行上进行时,较小的邮件消息可能不会出现错误。然而,对于较大的消息,当管道仅读取可用数据的一部分时,Linux shell 可能会认为存在错误。

执行模式的过滤模式很重要。这个术语可能会让人困惑,因为 Procmail 的设计目的只是过滤邮件。以以下方式考虑执行模式“过滤”:我们正在处理的邮件消息将在实际传递到 Procmail(或至少我们配方的其余部分)之前通过操作行上的任何内容进行管道传输。查看过滤模式的另一种方式是将其视为一种转换模式,在这种模式下,数据以某种方式进行修改,然后返回到控制 Procmail 配方以进行进一步执行。

旗帜 行动
f 将消息内容通过配方传递给外部管道进程进行处理,然后获取处理过程的输出行,准备替换原始消息内容。
i 如果 Linux 管道进程只读取其输入的一部分,然后终止,shell 将向 Procmail 程序发送SIGPIPE错误信号——i标志指示 Procmail 忽略此信号。这应该在预期管道进程只处理部分消息后返回时使用。
r 传递给管道进程的数据应该原样传递,不做任何修改。
w 默认情况下,Procmail 进程将生成一个子进程并继续自己的处理。w标志指示 Procmail 等待子进程管道完成后再继续自己的处理。
W 这与w的工作方式相同,但也隐藏了管道进程的任何错误或其他输出消息。

条件

有许多条件类型可以应用于决定给定配方是否适用于特定的邮件项。正确应用条件的想法是减少执行的不必要处理量。

条件行始终以星号(*)字符开头,后面跟着一个或多个空格。可以在一个配方内应用多个条件行,但它们必须全部分组在连续的行上。分组的逻辑操作是执行AND操作,以便在执行动作之前必须应用所有条件。

:0
* condition1
* condition2
action_on_condition1_and_condition2

无条件应用规则

可能需要应用规则到所有消息,而不考虑任何条件。例如,这样的规则可以备份邮件消息到邮件文件夹,或者出于法律或公司政策原因归档所有邮件。

无条件规则是由缺少条件行而暗示的。也就是说,规则总是匹配的。

# Save all remaining messages to DEFAULT
:0:
${DEFAULT}/

无条件规则经常用于嵌套配方链的末尾,以执行最终的默认操作,如果配方未传递邮件。请记住,一旦邮件被传递,处理就会停止。

使用正则表达式进行测试

我们中的一些人熟悉简单的模式匹配操作,比如在文件列表操作中常用的?*,可能会想知道是否可以创建类似的测试来匹配邮件头部或正文的部分。好消息是,有一个称为正则表达式regex的优秀功能。这些提供了一个执行非常复杂的模式匹配操作的机制。总的来说,这个功能与egrep命令行正则表达式非常相似。然而,有一些重要的区别,有经验的regex用户一定要了解,以便了解如何编写适用于 Procmail 操作的表达式。本章后面有一个完整的关于编写regex的部分。

正则表达式可以针对邮件消息的数据部分(标题、正文或两者)运行,如标志所定义,也可以用于测试先前分配的变量。

条件 动作
* 正则表达式 根据标志测试消息的部分与正则表达式。通常这只会处理标题,除非给出B标志以指示匹配范围是处理消息的正文。
* 变量 ?? 正则表达式 这是为了将分配的变量与正则表达式进行比较。

本章前面列出了各种伪变量,代表了访问 Procmail 应用程序中包含的信息的方式。这些伪变量可以与普通变量相同方式进行比较。

以下示例将复制所有包含关键短语的邮件项的副本。

VERBOSE=1
:0cB:
* [0-9]+ Linux Rules [ok!]
${MAILDIR}/linuxrules/
VERBOSE=0

以下是对前面示例操作的快速解释:

  • 我们指定:0cB:以确保我们只搜索邮件正文,并进行复制,以便我们仍然可以得到原始邮件的处理。

  • 如果消息正文中有一个短语,该短语有一个或多个数字,后面跟着<SPACE>Linux Rules<SPACE>,然后是o, k!,则会将副本存储在linuxrules文件夹中。

在处理规则之前设置和取消设置VERBOSE选项允许在日志中更详细地显示该规则,这意味着在调试时需要搜索的日志文件更少。

测试消息部分的大小

在某些情况下,我们可能不希望处理大型消息的配方。在这种情况下,我们可以设置一个限制,使得配方不匹配超过一定大小的消息。如果我们有使用缓慢数据连接的用户,也许使用手机连接的连接,将所有大型邮件项目移动到一个单独的文件夹中以便在用户回到更好的互联网连接时检索,这可能是有用的。

条件 操作
* > number 如果消息大小大于给定的字节数,则返回true
* < number 如果消息大小小于给定的字节数,则返回true

测试外部程序的退出代码

如果运行外部程序来提供处理的一部分,则可能需要检查退出代码,以确保进程正确完成或执行次要操作以完成整体处理。

*? /unix/command/line | another/command*emphasis>

?指示 Procmail 将当前消息数据作为标准输入传递到 Linux 命令行。如果命令行以零退出代码退出,则条件成功满足。虽然命令行是几个进程的管道,但返回的退出代码是管道中最后一个程序的退出代码。

管道打印到标准错误的任何输出都会显示在日志中。

在这个例子中,消息的主体被传递到命令管道中,如果短语确切地在第三行中找到(退出代码为 0),则消息将被存储在文件夹中。

VERBOSE=1VERBOSE=0之间的行的操作将被记录,但此范围之外的所有行将不被记录。这使我们能够控制正在进行的日志记录量,因此更容易跟踪日志文件活动。

VERBOSE=1
:0B:
* ? /bin/sed -n 3p | /bin/egrep "Linux Rules"
${MAILDIR}/linuxrules/
VERBOSE=0

否定

有时,检查特定条件是否不存在以便以某种方式继续处理是有用的。感叹号(!)或有时称为Bang,用于颠倒条件的值,使得假变为真,反之亦然。

* ! condition

这测试条件中的负结果,并且如果条件不满足则返回true

在这里,我们正在寻找任何未直接发送给我们的项目,并将其存储在一个文件夹中以供以后查看。

:0:
* !^TO.*cjtaylor
${MAILDIR}/not_sent_to_me/

条件中的变量替换

多个$标志可以用于强制应用多个替换传递。

* $ condition

$指示 Procmail 使用正常的sh规则处理条件,以执行变量和反引号替换,然后再实际评估条件。替换过程将解析变量($VAR)为它们的值,而不是将它们作为文字处理。任何带引号的字符串都将删除其引号,所有其他 shell 元字符也将被评估。要使这些字符通过此替换过程,它们应该使用标准的反斜杠(\)转义机制进行转义。

下面的例子摘自procmailex手册页,即使在那里也被描述为相当奇异,但它确实作为一个例子。假设您的主目录中有一个名为.urgent的文件,并且该文件中列出的(一个)人是传入邮件的发件人。您希望该邮件存储在$MAILDIR/urgent中,而不是存储在任何正常的邮件文件夹中。那么您可以这样做(注意,$HOME/.urgent的文件长度应远低于$LINEBUF;如有必要,增加LINEBUF):

URGMATCH=`cat $HOME/.urgent`
:0:
* $^From.*${URGMATCH}
$MAILDIR/urgent/

操作行

这是执行所有处理活动的行。在大多数情况下,这意味着写入物理文件或文件夹。但它也可以包括将邮件转发给其他用户,将数据传递给命令或一系列命令的管道,或者在某些情况下,作为复合食谱的一部分执行一系列连续的操作。如果要执行多个操作,不能只是将它们堆叠在一起 - 您需要多个食谱(可能是无条件的,和/或分组在一对大括号中)和每个冒号行(当然还可以是条件)。

还要注意,影响操作行的标志实际上直到尝试执行操作时才会生效。特别是,c标志直到其所有条件都得到满足之前才生成消息的克隆。

转发到其他地址

将用户帐户的所有消息全局转发到另一个用户帐户是由 Postfix 本身处理效率要高得多的过程。但是,如果需要应用一些逻辑来决定发送消息的内容或位置,那么 Procmail 可以提供帮助。

大多数邮件传输都允许我们传递多个电子邮件地址以进行进一步传输。

! user1@domain2.net user2@domain1.com user3 ...

上述操作在功能上与将消息传递到以下管道相同:

| $SENDMAIL "$SENDMAILFLAGS"

这是转发邮件的特殊情况,并指示 Procmail 从原始消息的实际标头中提取收件人列表:

! -t

在这里,我们将邮件转发给我们的支持团队,而不是自己处理。邮件的主题行包含短语support

:0:
* ^Subject.*support
! support@adepteo.net

喂给 shell 或命令管道

Procmail 允许在电子邮件中做几乎无限的事情。与 Procmail 一起工作的更强大的功能之一是其能够根据给定的条件将电子邮件转发到应用程序或脚本。一个可能的例子是跟踪支持请求,并将条目直接存储到数据库系统中,以便在专用应用程序中进行跟踪。

管道过程负责保存其输出。食谱的标志能够告诉 Procmail 期望其他内容。通过使用>>语法,Procmail 可以确定要使用的锁定文件。在写入文件时始终使用锁定非常重要,以避免两个操作同时写入同一个文件并损坏彼此的数据。

| cmd1 param1 | cmd2 -opt param2 >>file

可以将命令管道的输出存储在变量中。这本身使得食谱成为非交付食谱。

VAR=| cmd1 | cmd2 ...

请注意,此语法仅允许在操作行上使用。要在普通赋值中获得相同的结果,我们可以使用反引号(`)运算符。

VERBOSE=1
#Copy the data and pass the headers to the process
:0hc:
* ^Subject: Book Pipeline Example
#Copy so that the next recipe will still work
| cat - > /tmp/cjt_header.txt
#Final recipe so do not copy here, but pass the body
:0b:
| cat - > /tmp/cjt_body.txt
VERBOSE=0

保存到文件夹

这将输出保存到普通文件中。如果只提供文件名,文件将在MAILDIR设置中指定的目录中创建。在写入普通文件时,一定要确保使用某种形式的锁定。

/path/to/filename

保存到目录时,文件将在目录中创建具有顺序编号的文件。

在路径名的末尾使用尾部(/)斜杠指示 Procmail 将项目存储在maildir格式的文件夹中。子文件夹cur, newtmp将自动创建。

directory/

在路径名的末尾使用/指示 Procmail 将项目存储在MH格式的文件夹中。

directory/.

如果要将数据存储到多个MHmaildir文件夹中,可以同时列出它们。结果将是实际只写入一个文件,其余将作为硬链接创建。

复合食谱

如果我们想对匹配项执行一系列条件进程或操作,那么我们可以使用{}字符指定要使用的一组食谱块,而不是单个操作行。{}字符后必须至少有一个空格。

{
# ... more recipes
}

大括号之间的代码可以是任何有效的 Procmail 结构。

注意

请注意,变量赋值的操作必须始终放在一组大括号内:{ VAR=value }。如果只使用VAR=value而不使用大括号,数据将保存在名为VAR=value的文件夹中。

如果我们想要一个实际上不进行任何处理的配方,也许作为if…else操作的一部分,我们可以使用一个空的{ },但是空格的规则仍然适用,我们需要确保两个大括号之间至少有一个空格字符。

下面的例子是基于前面的例子稍作修改,以便只执行一个测试,然后如果测试通过,则运行一系列无条件的测试:

VERBOSE=1
:0:
* ^Subject: Book Pipeline Example
{
#Copy so that the next recipe will still work
:0hc:
| cat - > /tmp/cjt_header.txt
#Final recipe so do not copy here
:0b:
| cat - > /tmp/cjt_body.txt
}
VERBOSE=0

正则表达式

Procmail 实现了一种与其他 UNIX 实用程序略有不同的正则表达式形式。在这里,我们将介绍基本的区别,并引导新用户进入正则表达式的强大世界,它们的含义、实现和用途。

我们已经看到,Procmail 的匹配是不区分大小写的,除非使用了D标志。对于正则表达式也是如此。Procmail 还默认使用多行匹配。

正则表达式简介

对于 Linux 和编程的新用户来说,可能不清楚正则表达式为处理数据带来的强大功能。在其最简单的形式中,正则表达式可以理解为在一组数据中搜索短语或模式。以下简单的例子展示了如何匹配所有邮件项目,其中标题和/或正文包含短语mystical monsters,并将邮件放在相关文件夹中。

:0 HB:
* mystical monsters
${MAILDIR}/monsters/

然而,这个过滤器将无法匹配包含短语mystical monstermystical-monsters的项目,例如。因此,正则表达式的真正威力在于能够以简化的格式描述文本或数据模式,然后在一组数据中搜索与这些模式匹配的内容。但是,你应该小心不要被“简化”这个词所误导。在现实生活中,你遇到的大多数正则表达式可能并不简单,如果用原生格式编写的话。以下面的例子为例,它的目的是确定邮件项目是否是 MIME 编码,并在适当的文件夹中存储它:

:0:
* ^Content-Type: multipart/[^;]+;[ ]*boundary="?\/[^"]+
${MAILDIR}/mime/

字符., [, ^, ;, ], +, ?, \, /"是特殊指令,而不是它们通常表示的字面 ASCII 字符。为了理解这些字符及其含义,我们将快速浏览一下最重要的例子。

句点

这是最简单和最常见的正则表达式形式,简单地意味着匹配任何单个字符(不包括换行符,换行符被视为特殊情况)。考虑以下表达式:

:0
* Dragons ... mystical monsters
${MAILDIR}/result/

这将匹配以下任何短语:

Dragons are mystical monsters
Dragons and mystical monsters
Dragons but mystical monsters

实际上,它将匹配Dragonsmystical之间有一个三个字符的单词的任何短语。如果我们想匹配Dragonsmystical之间长度为三个或更多字符的任何单词,我们可以使用?或量词操作。

如果我们想匹配一个字面上的'.'或多个'.',我们可以通过在特殊意义的字符前加上反斜杠''来转义正则表达式字符串,使得'.'可以字面上匹配'.'(句号),'\'可以字面上匹配''(反斜杠)字符。

量词操作

问号表示前面的字符应该匹配零次或一次。因此,以下代码行将满足我们的要求:

:0
* Dragons ....? Mystical monsters
${MAILDIR}/result/

这个表达式可以理解为,“匹配由三个或更多字符组成的任何单词,后面跟着空白或任何一个字符”。

?前面的字符也可以是一个简单的 ASCII 字符,这样表达式将匹配如下:

:0
* Dragons ..d? Mystical monsters
${MAILDIR}/result/

这可以理解为,“任意两个字符后面跟着空白或字母d.”因此,这将匹配anand,但不会匹配are.

星号

星号修饰符的工作方式类似于量词运算符,但意思是匹配前面的字符的零个或多个实例,当然不包括换行符。.*是一个非常常见的序列,你会在很多配方中找到它。

以下示例将匹配所有包含单词choose后跟其他单词再跟单词online的消息:

:0
* ^Subject: Choose.*online
${MAILDIR}/result/
Subject: Choose discount pharmacy and expedite the service online.
Subject: Choose hassle free online shopping
Subject: Choose reliable online shopping site for reliable service and quality meds
Subject: Choose reliable service provider and save more online.
Subject: Choose the supplier for more hot offers online
Subject: Choose to shop online and choose to save

下一个例子将寻找“任何东西”(.)后跟两个或更多感叹号(!!)和(!):

:0
* ^Subject: .*!!!*
${MAILDIR}/result/
Subject: Breathtaking New Year sale on now!!! Get ready for it!! Subject: Hey Ya!! New Year Sale on right now!! Subject: It Doesn't Matter!!

加号

加号与*非常相似,只是它要求正则表达式中+之前必须至少有一个字符的实例。

如果我们考虑我们之前的例子,下一个例子将寻找“任何东西”.*后跟两个!!和至少一个以上的!感叹号。

:0
* ^Subject: .*!!!+
${MAILDIR}/result/

这将给我们一个更受限制的输出,至少需要三个!连续出现。

Subject: Breathtaking New Year sale on now!!! Get ready for it!!

使用括号进行限制匹配

到目前为止,我们能够创建的匹配模式功能强大,但工作方式不够集中。例如,我们可以轻松地编写一个规则来查找以t结尾的任何三个字母的单词,但无法将匹配限制为仅包含一组给定的以t结尾的单词。为了克服这一点,我们可以用一组字符或字符组的集合替换.或单个字符,然后应用量词操作来准确说明这些字符可以应用多少次。

通过小括号( )的谨慎使用,我们可以创建字符串组,这些组将用于模式匹配规则。例如,假设我们试图拆分由系统脚本频繁发送的电子邮件。脚本格式化主题行,使其包含以下短语之一。

There is only one problem
There are 10 problems

下面的正则表达式将匹配我们要查找的特定字符串,方法是匹配任何字符串,该字符串在thereproblem之间有一个或多个is only one短语的出现。

There (is only one)+ problem

如果我们想要过滤一个词或短语列表,我们需要使用Alternation功能。

There (is only one|are)+ problem

|字符分隔了可以用来匹配模式的单词列表。

以下简单的垃圾邮件过滤器使用了Alternation功能,以搜索常用的文本替换,以避免简单的基于单词的过滤器。

创建一个简单的垃圾邮件过滤器

随着我们每天收到的垃圾邮件数量不断增加,我相信到目前为止阅读到这里的一些人已经想出了我们可以开始过滤我们每天收到的一些常规消息的方法。有许多专门设计用于与 Procmail 紧密配合并为垃圾邮件过滤提供更多测试和覆盖范围的特定垃圾邮件过滤器。其中一个应用程序 SpamAssassin,在第八章中有介绍。

以在线赌场为例——这是垃圾邮件发送者鼓励我们探索的热门主题。这不是我们通常感兴趣的东西,所以我们很高兴地将所有包含“在线”和“赌场”这两个词的消息过滤到一个单独的文件夹中。

Subject: Online Casino

垃圾邮件发送者面临的挑战之一是编写我们可以阅读而垃圾邮件过滤器难以处理的主题行。一个简单的方法是用常见的打字错误字符替换,比如数字零(0)替换字母Oo,数字1替换Ll,数字4替换Aa

因此,我们可以继续编写规则:

Subject: (o|0)n(1|l)ine casin(o|0)

下一个迭代将具体显示这个配方,我们特别寻找包含“在线”和“赌场”这两个词的主题行,但要包括单词可能以不同顺序出现的情况,每个单词都会分别测试。

:0
* ^Subject: (o|0)n(1|l)ine
* ^Subject: casin(o|0)
${MAILDIR}/_maybespam/

虽然这样做效果不错,但是以这种方式工作的规则并不是真正高效的,因为这种替换是正则表达式的常见需求,所以有一种特殊的方式来表达这些术语,即字符类

字符类

方括号[ ]中包含的任何字符序列表示要在表达式中检查每个列出的字符。对于字母表中的字母或数字范围等常见字符序列,可以使用[a-z][0-9]

  • [a-e] 意味着匹配所有字母a, b, c, d, e

  • [1,3,5-9] 意味着匹配数字1, 3, 5, 6, 7, 89中的任何一个。

以下示例将查找在文本字符串中嵌入数字01的消息,以便它们看起来像OLI

:0
* ^Subject: [a-z]*[01]+[a-z]*
${MAILDIR}/_maybespam
Subject: Hot Shot St0ckInfo VCSC loadstone Subject: M1CR0S0FT, SYMANNTEC, MACR0MEDIA, PC GAMES FROM $20 EACH Subject: R0LEX Replica - make your first impressions count! Subject: Small-Cap DTOI St0cks reimburse Subject: TimelySt0ck DTOI Buy of the Week evasive

行首

如果我们想匹配所有范围广泛的字符而不匹配少量范围,那么使用^字符指定负匹配会更容易。

[⁰-9]

这意味着匹配任何以不是09之间的数字开头的字符串。

当我们知道模式应该从行首开始时,将行首锚添加到我们正在搜索的模式中是很有用的。例如,所有标题必须从行首开始,因此搜索以下短语:

Subject: any subject message

也将匹配以短语开头的标题,例如:

Old-Subject:

为了停止这个,我们可以添加行首锚字符(^)并将正则表达式更改为:

^Subject: any subject message

行尾

当我们计划匹配我们知道应该终止的字符串时,我们可以将行尾锚字符($)添加到模式中,以确保我们匹配到字符串的末尾,如下所示:

^Subject:.* now$

这将匹配任何以单词now结尾的主题行。

进一步阅读

正则表达式是一个庞大的主题,但是非常值得学习,因为它们被许多 Linux 工具和应用程序使用。有许多与正则表达式相关的在线资源。以下是一些入门链接:

正如我们在上一章中简要介绍的那样,Procmail 有许多有用的“预先准备好的”正则表达式或宏,提供了一系列在 Procmail 配方中常用的匹配。

^TO 和^TO_

^TO是处理“To”地址的原始 Procmail 宏。这已经被新的^TO_宏所取代,该宏是在 Procmail 版本 3.11pre4 中引入的。

这个万能匹配包括大多数标题,这些标题中可能包含您的地址,例如To:, Apparently-To:, Cc:, Resent-To:等等。

在大多数情况下,您应该使用^TO_选项,因为它的覆盖范围要好得多。

注意

尽管似乎有逻辑地有一个类似的宏来覆盖源地址详细信息,但请注意,没有相应的^FROM^FROM_宏。

这是来自 Procmail 源代码的正则表达式字符串:

"(^((Original-)?(Resent-)?(To|Cc|Bcc)|\
(X-Envelope|Apparently(-Resent)?)-To):(.*[^-a-zA-Z0-9_.])?)"

^FROM_MAILER

这个宏识别了广泛的邮件生成程序,并且是一个有用的万能匹配。但是,新程序一直在被创建,因此几乎总是需要额外的过滤器。

Procmail 将这个简短的宏扩展为从 Procmail 源代码中获取的以下正则表达式。

"(^(Mailing-List:|Precedence:.*(junk|bulk|list)|\
To: Multiple recipients of |\
(((Resent-)?(From|Sender)|X-Envelope-From):|>?From )([^>]*[^(.%@a-z0-9])?(\
Post(ma?(st(e?r)?|n)|office)|(send)?Mail(er)?|daemon|m(mdf|ajordomo)|n?uucp|\
LIST(SERV|proc)|NETSERV|o(wner|ps)|r(e(quest|sponse)|oot)|b(ounce|bs\\.smtp)|\
echo|mirror|s(erv(ices?|er)|mtp(error)?|ystem)|\
A(dmin(istrator)?|MMGR|utoanswer)\
)(([^).!:a-z0-9][-_a-z0-9]*)?[%@> ][^<)]*(\\(.*\\).*)?)?$([^>]|$)))"

^FROM_DAEMON

这采用了与^FROM_MAILER类似的方法,但旨在识别更常见的 Linux 守护程序和系统进程的消息。

给出了来自 Procmail 源代码的正则表达式字符串:

"(^(((Resent-)?(From|Sender)|X-Envelope-From):|\
>?From )([^>]*[^(.%@a-z0-9])?(\
Post(ma(st(er)?|n)|office)|(send)?Mail(er)?|daemon|mmdf|n?uucp|ops|\
r(esponse|oot)|(bbs\\.)?smtp(error)?|s(erv(ices?|er)|ystem)|A(dmin(istrator)?|\
MMGR)\
)(([^).!:a-z0-9][-_a-z0-9]*)?[%@> ][^<)]*(\\(.*\\).*)?)?$([^>]|$))"

以下示例将存储接收到的守护程序消息在包含年份和月份作为路径的文件夹中。这些变量${YY}${MM}在 Procmail 文件中之前分配,并且必要的目录也已创建。

:0:
* ^FROM_DAEMON
${YY}/${MM}/daemon

高级配方

在这里,我们将把 Procmail 的各种功能组合成几个有用的食谱,作为我们自己组织中工具的基础。第一个例子是基于传统的度假食谱,通知发件人可能需要一段时间才能被收件人阅读的电子邮件。第二个示例展示了如何创建支持,根据日期和可能的处理时间自动归档消息。最后,我们将完成上一章开始的规则,通知用户已被过滤到单独文件夹中的大型邮件项目。

创建度假自动回复

这个例子是基于man procmailex中给出的度假示例,并在本章中稍作提及。

正如我们已经讨论过的那样,盲目自动回复电子邮件是一个非常糟糕的主意,并且具有重大影响。首先,我们必须决定是否发送自动回复。为此,我们需要确保条件是合理且满足的。如果是这样,当前消息的标题(由h标志表示)被提供给formail,这是 Procmail 工具套件的一部分。然后,formail检查vacation.cache文件,以查看发件人是否已经收到自动回复。这是为了确保我们不会向用户发送多个报告。在进行这部分处理的同时,我们的食谱将创建一个名为vacation.lock的锁。

这样做的主要原因是为了避免在更新缓存时发生冲突,这可能导致缓存信息损坏。

这个食谱实际上包括两个单独的食谱。第一个提供了检查和记录已发送的回复,以确保我们不发送重复的回复。

这个食谱W,等待formail的返回。没有c,Procmail 在完成这个食谱后会停止处理,因为它是一个投递食谱。它将标题传递给formail

TO_^FROM_DAEMON条件的含义远不止表面上看到的那样。

如果用户的登录名出现在任何接收者标题To:, Cc:, Bcc:中,TO_ $<logname>就会被满足。这样可以避免向地址为别名或邮件列表的消息发送自动回复,而不是明确发送给我们的用户。

!^FROM_DAEMON确保我们不会自动回复来自各种守护程序的消息。

!^X-Loop: $RECIPIENT避免回复我们自己的自动回复;请注意,我们发送的自动回复中插入了这个X-Loop标题。

:0 Whc: vacation.lock
# Perform a quick check to see if the mail was addressed to us
* $^To_:.*\<$\LOGNAME\>
# Don't reply to daemons and mailinglists
* !^FROM_DAEMON
# Mail loops are evil
* !^X-Loop: $RECIPIENT
| formail -rD 8192 vacation.cache

如果第一个食谱在缓存中找不到匹配项,那么食谱的第二部分就会执行。地址找不到的原因有两个——要么从未见过,因此没有发送过回复,要么是很久以前见过,以至于条目已经被强制从缓存中移除。在任何一种情况下,都会发送一份度假消息的副本。发件人永远不会收到他们发送的每一条消息的自动回复——这可能会让频繁发送邮件的人感到不安。

:0 ehc
# if the name was not in the cache
| (
formail -rA"Precedence: junk" \
-A"X-Loop: $RECIPIENT" ; \
cat $HOME/.vacation_message \
) | $SENDMAIL -oi -t

由于e,如果前一个食谱返回错误状态,就会执行前一个食谱。在这种情况下,这并不是真正的错误,只是来自formail的信号,表示地址在缓存文件中不存在,我们可以继续发送自动回复。请注意,如果在前一个食谱中条件不满足导致跳过formail缓存检查,Procmail 足够聪明,会跳过这个食谱。

当前消息的标题被提供给这个食谱中的formail,以便构建自动回复的标题。

这个食谱中的c导致在这个食谱之后处理整个当前消息。通常,这意味着它将在没有进一步食谱的情况下被处理,这就是我们在邮箱中得到一份副本的方式。在执行这个食谱时不需要锁,因此没有使用锁。

向原始消息的发送者发送回复所需的只是消息的副本,该副本保存在用户的主目录中的.vacation_message文件中。

在 Procmail 配方之外存储消息信息使得系统用户可以轻松更新他们发送的消息,而不会破坏实际的配方本身。

按日期组织邮件

您可能不想删除您认为将来可能有用的邮件。这很容易导致大量数据存储在各种位置。可以根据年份、月份和主题的组合将我们的一些或所有传入邮件过滤到文件夹中,以便轻松追踪。

应用于每个邮件过程的通用规则确保必要的目录结构存在。

#Assign the name of the folder by extracting the year and month
# parts from the external date command.
MONTHFOLDER=`date +%y/%m`
#Unconditional rule to create the folder. Using the test
#command. we create the monthly folder if it does not exist.
:0 ic
* ? test ! -d ${MONTHFOLDER}
| mkdir -p ${MONTHFOLDER}
#Alternative way of creating the folder using an assignment operation
DUMMY=`test -d $MONTHFOLDER || mkdir $MONTHFOLDER`
#Now store any email matching 'meeting' in an appropriate folder
:0:
* meeting
${MONTHFOLDER}/meeting/

如果您更喜欢对输出格式或位置有更多控制,可以使用以下规则:

#This obtains the date formatted as YYYY MM DD, e.g. 2009 09 08 date = `date "+%Y %m %d"`
#Now assign the Year YYYY style :0 * date ?? ^^()\/ { YYYY = $MATCH }
#Now assign the Year YY style :0 * date ?? ^^..\/ { YY = $MATCH }
#Now assign the Month MM style :0 * date ?? ^^.....\/ { MM = $MATCH }
#Now assign the Day DD style :0 * date ?? ()\/..^^ { DD = $MATCH }
#Create the various directory formats you are going to use
DUMMY=`test -d ${YYYY}/${MM}/${DD} || mkdir -p ${YYYY}/${MM}/${DD}`
DUMMY=`test -d ${YY}/${MM} || mkdir -p ${YY}/${MM}`
#Now store the data in an appropriate folder using the variables
#YYYY, MM and DD setup above.
:0:
* ^FROM_DAEMON
${YYYY}/${MM}/${DD}/daemon/

通知用户有关大型邮件

在上一章中,我们介绍了一个非常简单的规则,将所有大小超过 100KB 的传入邮件存储在largemail文件夹中。这对于保持单个传入邮件文件夹的大小不会变得太大很有用,但意味着需要定期进行特殊检查,以查看是否已过滤任何邮件。

在这个规则中,我们现在将提取标题和主题行,以及原始大型电子邮件消息的前几行,并创建一个带有修改主题行的新消息。这个修改后的消息将与将大型原始项目过滤到其单独的largemail文件夹中同时存储在用户的收件箱中。

测试的主要部分仅在消息大小超过 100,000 字节时才会应用,因此我们需要类似以下配方的结构来进行初始测试,并决定这是否是一个大项目:

:0:
* >100000
{
MAIN PROCESS WILL GO HERE
}

假设我们确实有一个大项目,我们需要使用c标志复制消息,并将此副本存储在largemail文件夹中:

#Place a copy in the largemail folder
:0 c:
largemail/

接下来是提取消息正文的第一部分,可以使用各种选项来完成。在这种情况下,我们将通过等待传递消息正文的结果来剥离消息的前 1024 字节,只告诉系统头命令返回前 1024 字节。这里使用的标志告诉 Procmail 等待命令行进程的结果,并忽略任何管道错误,因为头命令只会读取提供给它的数据的一部分。

#Strip the body to 1kb
:0 bfwi
| /usr/bin/head -c1024

现在我们需要重写主题行,这是使用formail程序完成的。这一次,我们只传递标题到命令行,并等待响应。

不过,在这种情况下,我们需要获取当前的主题行,以便将其作为修改后的主题行的一部分传递给formail程序。我们通过对主题内容进行简单匹配,然后将$MATCH变量(现在保存主题行内容)作为formail程序的参数传递。为了整洁起见,我们在原始主题行之前添加{* -BIG- *}的措辞,以便轻松排序和识别这些消息。

#ReWrite the subject line
:0 fhw
* ^Subject:\/.*
| formail -I "Subject: {* -BIG- *} $MATCH"

然后将进行消息的正常传递,并将新的较短消息存储在收件箱中。

如果我们将所有这些放在一起,我们最终得到以下完整的配方。

:0: * >100000
{
#Place a copy in the largemail folder
:0 c: largemail/
#Strip the body to 1kb :0 bfwi | /usr/bin/head -c1024 #ReWrite the subject line :0 fhw * ^Subject:\/.* | formail -I "Subject: {* -BIG- *} $MATCH" }

Procmail 模块库

作为一个避免重复造轮子的社区努力的一部分,Procmail 模块库提供了一系列由 Procmail 用户贡献的有用配方。来自 Procmail 模块库的以下介绍freshmeat.net/projects/procmail-lib描述了该软件包:

Procmail 模块库是 Procmail 邮件处理实用程序的许多插件模块的集合。这些模块允许常见任务,如解析日期、时间、MIME 和电子邮件地址,转发邮件,处理 POP3,垃圾邮件屏蔽,运行电子邮件 cron 作业,处理守护程序消息等。

每个模块或 Procmail 包含的文件都有全面的文档和示例用法。它们可以按原样使用,也可以使用各种可配置选项,或者作为您自己食谱的基础。我们在本章中演示的许多技术都在库中使用,还有一些基于消息内容类型的更复杂的过滤方法。

把所有东西整合起来

在本章中,我们涵盖了各种主题,现在我们可以把它们整合起来。以下示例使用了本章中展示的每种技术,并且通常用于电子邮件处理。我希望你能在创建自己的邮件过滤策略时找到它有用。

创建一个基于您自己规则的结构

将 Procmail 规则和配置的相关方面分组将使您的安装更容易维护,并在进行更改时更不太可能出现问题。

在主 Procmail 目录中,创建遵循一致命名约定的单独文件,例如rc.main, rc.spam, rc.lists等。然后将这些文件包含到您的主.procmailrc文件中,如下所示。

#This obtains the date formatted as YYYY MM DD date = `date "+%Y %m %d"`
#Now assign the Year YYYY style :0 * date ?? ^^()\/ { YYYY = $MATCH }
#Now assign the Year YY style :0 * date ?? ^^..\/ { YY = $MATCH }
#Now assign the Month MM style :0 * date ?? ^^.....\/ { MM = $MATCH }
#Now assign the Day DD style :0 * date ?? ()\/..^^ { DD = $MATCH }
#Create the various directory formats you are going to use
DUMMY=`test -d ${YYYY}/${MM}/${DD} || mkdir -p ${YYYY}/${MM}/${DD}`
DUMMY=`test -d ${YY}/${MM} || mkdir -p ${YY}/${MM}`
#Make a backup copy of all incoming mail
:0 c
backup/
#Restrict the history to just 32 mail items
:0 ic
| cd backup && rm -f dummy `ls -t msg.* | sed -e 1,32d`
#Make sure that all mails have a valid From value
:0 fhw
| formail -I "From " -a "From "
#
## Don't include this unless we need to
## INCLUDERC=${HOME}/Procmail/rc.testing
##
## Now include the various process listings
INCLUDERC=${HOME}/Procmail/rc.system
INCLUDERC=${HOME}/Procmail/rc.lists
INCLUDERC=${HOME}/Procmail/rc.killspam
INCLUDERC=${HOME}/Procmail/rc.vacation
INCLUDERC=${HOME}/Procmail/rc.largefiles
INCLUDERC=${HOME}/Procmail/rc.virusfilter
INCLUDERC=${HOME}/Procmail/rc.spamfilter

现在,对于列出的每个include文件,创建与该文件相关的规则,并将其包含在该文件中。然后,暂时隔离用于处理传入邮件的部分的INCLUDERC引用就成了问题。在生产环境中,要小心不要盲目地剪切和粘贴这些示例,而不检查每个食谱是否按预期执行。

Rc.system

将信息系统和守护程序消息存档在一个带日期的文件夹结构中,可以给出如下:

# Filter system mail messages into a dated folder structure.
# The variables YY and MM are defined in the calling recipe
# and each of the directories will have been created if necessary.
:0:
* ^From:.*root@delta.adepteo.net
${YY}/${MM}/daemon/
:0:
* ^From:.*root@ramsbottom.adepteo.net
${YY}/${MM}/daemon/
:0:
* ^TO_pager@adepteo.net
${YY}/${MM}/daemon/
:0:
* ^From:.*MAILER-DAEMON@delta.adepteo.net
${YY}/${MM}/daemon/
:0:
* ^From:.*me@localhost.com
${YY}/${MM}/daemon/

Rc.lists

将所有订阅的邮件列表存档在带日期的文件夹中以供以后阅读。

# Mailing lists
# Store by date folder
# The variables DD and MM are defined in the calling recipe.
# and each of the directories will have been created if necessary.
:0:
* ^From:.*mapserver-users-admin@lists.gis.umn.edu
${YY}/${MM}/mapserver/
:0:
* ^TO_mapserver-users@lists.gis.umn.edu
${YY}/${MM}/mapserver/
:0:
* ^From:.*yourtopjob@topjobs.co.uk
${YY}/${MM}/jobs/
:0:
* ^Subject: silicon Jobs-by-Email Alert
${YY}/${MM}/jobs/
:0:
* ^Reply-To: Axandra Search Engine Facts <facts@Axandra.com>
${YY}/${MM}/lists/
:0:
* ^Subject: A Joke A Day
${YY}/${MM}/lists/
:0:
* ^List-Owner: <mailto:owner-tribune@lists.sitepoint.com>
${YY}/${MM}/lists/
:0:
* ^Reply-To: newsletter@192.com
${YY}/${MM}/lists/
:0:
* ^Subject: Developer Shed Weekly Update
${YY}/${MM}/lists/

Rc.killspam

删除来自与我们的屏蔽文件中地址匹配的发件人的任何邮件。

#Kill file for known spammers
# If the sender is in the killfile then discard the mail into the bit bucket
# Here we use the external command 'grep' to search our killfile for a
# matching sending sending by testing the return status from grep.
:0:
* ? grep -i `formail -rtzxTo:` $HOME/.killfile
/dev/null

Rc.vacation

我们的假期自动回复食谱:

#Vacation Replies
:0 Whc: vacation.lock
# Perform a quick check to see if the mail was addressed to us
* $^To_:.*\<$\LOGNAME\>
# Don't reply to daemons and mailinglists
* !^FROM_DAEMON
# Mail loops are evil
* !^X-Loop: $RECIPIENT
| formail -rD 8192 vacation.cache
:0 ehc
# if the name was not in the cache reply with the contents
# of our vacation message in the body of the email.
| (
formail -rA"Precedence: junk" \
-A"X-Loop: $RECIPIENT" ; \
cat $HOME/.vacation_message \
) | $SENDMAIL -oi -t

Rc.largefiles

为了避免用大量消息堵塞我们的收件箱,我们将大消息存档在一个文件夹中,并发送通知给自己,告知我们收到了一条超大消息。

#Assume that files larger than 100k are not spam
:0: * >100000
{
#Place a copy in the largemail folder
:0 c: largemail/
#Strip the body to 1kb :0 bfwi | /usr/bin/head -c1024
#ReWrite the subject line :0 fhw * ^Subject:\/.* | formail -I "Subject: {* -BIG- *} $MATCH" }

Rc.viruses

任何带有表明该消息为病毒的电子邮件标题的内容,都放在一个文件夹中。

#Virus Filter
#X-Virus-Status: Infected
:0:
* ^X-Virus-Status: Infected
_virus/

Rc.spamfilter

任何带有表明该消息为垃圾邮件的电子邮件标题的内容,都放在一个文件夹中。

#Spam Filter
:0fw
* < 256000
| spamc
# Mails with a score of 15 or higher are almost certainly
# spam (with 0.05% false positives according to
# rules/STATISTICS.txt). Let's put them in a
# different mbox. (This one is optional.)
#
# The regular expression below matches the SpamAssassin
# header with 15 asterisks or more.
#
:0:
* ^X-Spam-Level: \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
_almost-certainly-spam/
# All mail tagged as spam (eg. with a score higher than the
# set threshold)
is moved to "probably-spam".
:0:
* ^X-Spam-Status: Yes
_probably-spam/

总结

在本章中,我们探索了 Procmail,发现了大量的服务和大量的功能,它可以提供帮助我们控制邮件。使用 Procmail 的高级功能,我们发现了:

  • 交付和非交付食谱之间的区别

  • 如何订购每个食谱以避免交货时间过长

  • 使用 Procmail 变量和条件标志来控制交付

  • 使用正则表达式进行复杂的模式匹配

  • 可用的大量 Procmail 宏及其用法

  • 最后,一些示例食谱来有效管理我们的邮件

虽然我们已经涵盖了很多内容,但仍有很多东西有待学习,网上有大量资源专门致力于这一特定应用程序。

希望现在您已经对 Procmail 的核心功能有了深刻的理解,知道如何实施它,以及如何探索您的现实需求,并创建食谱集,以便组合成您自己独特的邮件过滤策略。

第八章:使用 SpamAssassin 打击垃圾邮件

垃圾邮件,或者有时被称为不受欢迎的商业电子邮件(UCE),是互联网的祸害。在过去的十年里,垃圾邮件不断增加,现在占据了超过一半的互联网带宽。六分之一的消费者已经对垃圾邮件采取了行动,因此有充分的商业理由将垃圾邮件从用户的收件箱中清除出去。有各种不同的垃圾邮件解决方案,从完全外包您的垃圾邮件到完全不采取任何行动。然而,如果您有自己的电子邮件服务器,您可以非常容易地添加垃圾邮件过滤。

SpamAssassin 是一个非常受欢迎的开源反垃圾邮件工具。它赢得了 2006 年 Linux New Media 奖,被评为“最佳基于 Linux 的反垃圾邮件解决方案”,被许多人认为是最好的免费、开源的反垃圾邮件工具,比许多商业产品更好。事实上,有几个商业产品和服务都是基于 SpamAssassin 或其先前版本的。

在本章中,您将学习:

  • 为什么垃圾邮件很难处理,为什么垃圾邮件过滤器需要定期更新

  • 如何下载、安装和配置 SpamAssassin

  • 如何使用 SpamAssassin 过滤传入的电子邮件。

  • 如何配置 SpamAssassin 以在每个用户或每个服务器的基础上运行

  • 如何配置流行的电子邮件客户端以识别 SpamAssassin 放置在电子邮件中的标记

  • 如何自定义 SpamAssassin 以自动更新新规则集,以保持系统的垃圾邮件检测良好。

  • 如何使用 amavisd 集成垃圾邮件过滤和病毒识别

为什么过滤电子邮件

如果您没有收到任何垃圾邮件,可能没有必要过滤垃圾邮件。然而,一旦收到一封垃圾邮件,通常会接着收到更多。垃圾邮件发送者有时可以检测出是否查看了垃圾邮件,使用诸如 Web bug 之类的技术,这些技术是 HTML 电子邮件中的微小图像,从 Web 服务器获取,然后知道电子邮件地址是有效的和易受攻击的。如果垃圾邮件被过滤,最初的电子邮件可能永远不会被看到,因此垃圾邮件发送者可能不会再以垃圾邮件的形式攻击该电子邮件地址。

尽管针对垃圾邮件的法律行动,实际上垃圾邮件还在增加。在欧洲和美国,最近针对垃圾邮件的立法(2002/58/EC 指令和 S.877 法案)几乎没有效果,垃圾邮件在这两个地区仍在增加。

这主要原因是垃圾邮件是一个非常好的商业模式。发送垃圾邮件的成本非常低,每封邮件只需千分之一美分,只需低得多的点击率就可以获利。垃圾邮件发送者只需要将十万封垃圾邮件中的一封变成销售,就可以获利。因此,有很多垃圾邮件发送者,垃圾邮件被用来推广各种商品。由于使用恶意软件利用无辜的计算机代表他们发送垃圾邮件,垃圾邮件成本也是微不足道的。

相比之下,垃圾邮件对收件人的成本非常高。估计各种各样,从每封收到的垃圾邮件 10 美分,每年每名员工 1,000 美元,到仅在 2007 年全球总成本高达 1400 亿美元。这个成本主要是劳动力——通过堵塞收件箱和迫使他们处理许多额外电子邮件,分散人们的工作。垃圾邮件干扰日常工作,可能包括大多数人都觉得冒犯的内容。公司有责任保护员工免受这种内容的侵害。垃圾邮件过滤是一种非常廉价的方式,可以最大程度地减少成本并保护劳动力。

垃圾邮件是一个不断变化的目标

垃圾邮件并不是静态的。它每天都在变化,因为垃圾邮件发送者增加了新的方法到他们的武器库中,而反垃圾邮件者则制定了对策。因此,最好的反垃圾邮件工具是那些经常更新的工具。这与防病毒软件类似——病毒定义需要定期更新,否则新病毒将无法被检测到。

SpamAssassin 定期更新。除了软件的新版本外,还有一个积极的社区创建、评论和测试新的反垃圾邮件规则。这些规则可以自动下载,以获得最新的防垃圾邮件保护。

让我们讨论 SpamAssassin 用于打击垃圾邮件的一些措施:

  • 开放中继:这些是允许垃圾邮件发送者发送邮件的电子邮件服务器,即使他们与服务器所有者没有任何联系。为了对抗这一点,反垃圾邮件社区开发了黑名单,也称为黑名单,可以被反垃圾邮件软件用来检测垃圾邮件。这在第五章中提到过,作为您的电子邮件服务器不应出现在的列表,因为它可能限制合法的电子邮件流量。通过黑名单服务器传递的任何电子邮件都比没有传递的电子邮件更加可疑。SpamAssassin 使用多个黑名单来测试电子邮件。

  • 关键词过滤器:这些是对抗垃圾邮件的有用工具。垃圾邮件发送者倾向于一遍又一遍地重复相同的词和短语。SpamAssassin 广泛使用规则来检测这些短语。这些规则占据了大部分测试,先前提到的用户社区规则通常是这种形式。它们允许检测特定的单词、短语或字母、数字和标点符号的序列。

  • 黑名单和白名单:这些用于列出已知的垃圾邮件发送者和良好电子邮件的来源。来自黑名单地址的电子邮件很可能是垃圾邮件,并且会相应地处理,而来自白名单地址的电子邮件则不太可能被视为垃圾邮件。SpamAssassin 允许用户手动输入黑名单和白名单,并根据其处理的电子邮件建立自动白名单和黑名单。

  • 统计过滤器:这些是自动系统,给出电子邮件是垃圾邮件的概率。这种过滤是基于过滤器先前看到的垃圾邮件和非垃圾邮件。它们通常通过查找在一种类型的电子邮件中存在但在另一种类型中不存在的单词,并利用这种知识来确定新电子邮件的类型。SpamAssassin 有一个称为贝叶斯过滤器的统计过滤器,可以非常有效地提高检测率。

  • 内容数据库:这些是大规模电子邮件检测系统。许多电子邮件服务器接收并将电子邮件提交到中央服务器。如果同一封电子邮件发送给成千上万的收件人,那么它很可能是垃圾邮件。内容数据库通过一种称为哈希的技术防止机密电子邮件被发送到服务器,并降低发送到服务器的数据量。SpamAssassin 可以与几个内容数据库集成,特别是 Vipul's Razor (razor.sourceforge.net/)、Pyzor (sourceforge.net/apps/trac/pyzor/)和分布式校验和清除中心,即DCC (www.rhyolite.com/dcc/)。

  • URL 黑名单:这类似于开放中继黑名单,但列出了垃圾邮件发送者使用的网站。几乎所有的垃圾邮件都会包含网址。建立这些网址的数据库可以快速检测垃圾邮件。这是一种非常有效的防止垃圾邮件的工具。默认情况下,SpamAssassin 使用垃圾邮件 URI 实时黑名单SURBLs),无需进一步配置。

垃圾邮件过滤选项

垃圾邮件可以在服务器端或客户端进行过滤。接下来将解释这两种方法。在第一种情况下,垃圾邮件在客户端上进行过滤。

垃圾邮件过滤选项

  1. 邮件由 MTA 处理。

  2. 然后将电子邮件放入适当用户的收件箱。

  3. 电子邮件客户端从收件箱中读取所有新的电子邮件。

  4. 然后,电子邮件客户端将电子邮件传递给过滤器。

  5. 当过滤器返回结果时,客户端可以显示有效的电子邮件,要么丢弃垃圾邮件,要么将其存档到一个单独的文件夹中。

在这种方法中,垃圾邮件过滤始终由客户端完成,并且始终在处理新电子邮件时完成。通常当用户可能在场时,他或她可能会在电子邮件可见之前经历延迟,或者在客户端软件可以过滤垃圾邮件之前,收件箱中可能存在垃圾邮件的时间段。可以在客户端上执行的垃圾邮件过滤量可能有限。特别是,诸如开放中继阻止列表或 SURBL 之类的网络测试可能太耗时或复杂,无法在用户的个人电脑上执行。由于垃圾邮件是一个不断变化的目标,更新许多客户端个人电脑可能成为一项困难的管理任务。

在第二种情况下,垃圾邮件过滤是在电子邮件服务器上执行的。

垃圾邮件过滤选项

  1. 传入的电子邮件由 MTA 接收。

  2. 然后将其传递给垃圾邮件过滤器。

  3. 然后将结果发送回 MTA。

  4. 根据结果,MTA 将电子邮件放入适当用户的收件箱(4a),或者放入一个单独的垃圾邮件文件夹(4b)。

  5. 电子邮件客户端可以访问用户的收件箱中的电子邮件,如果需要,也可以访问垃圾邮件文件夹。

这种方法有几个优点:

  • 垃圾邮件过滤是在收到电子邮件时完成的,可能是一天中的任何时间。用户不太可能因延迟而感到不便。

  • 服务器可以专门用于垃圾邮件过滤。它可以使用诸如开放中继阻止列表、在线内容数据库和 SURBL 之类的外部服务。

  • 配置是集中的,这将简化设置(例如,防火墙可能需要配置为使用在线垃圾邮件测试)以及维护(更新规则或软件)。

另一方面,缺点包括:

  • 现在存在一个单点故障。但是,经过小心配置,可以围绕一个破损的垃圾邮件过滤服务。如果服务不可用,电子邮件仍将被传递,但垃圾邮件将不会被过滤。

  • 所有垃圾邮件必须由一个服务处理。如果此服务不可扩展,大量的电子邮件可能会影响邮件传递时间,导致过滤效果不佳或间歇性过滤,甚至可能导致电子邮件服务丢失。

介绍 SpamAssassin

实际上,垃圾邮件过滤涉及两个阶段——检测垃圾邮件,然后对其进行处理。SpamAssassin 是一个垃圾邮件检测器,它通过放置标头来修改它处理的电子邮件,以标记它是否是垃圾邮件。由 MTA 或电子邮件系统中的邮件传递代理对 SpamAssassin 在电子邮件中创建的标头做出反应,以过滤它。但是,电子邮件系统的另一部分也可能执行此任务。

介绍 SpamAssassin

前面的图示给出了 SpamAssassin 的示意图。SpamAssassin 的核心是其规则引擎,确定调用哪些规则。规则触发各种测试的使用,包括贝叶斯过滤器、网络测试和自动白名单。

SpamAssassin 使用各种数据库来完成其工作,这些也显示出来。规则和分数是文本文件。SpamAssassin 分发中包含默认规则和分数,正如我们将看到的,系统管理员和用户都可以通过将它们添加到特定位置的文件中来添加规则或更改现有规则的分数。贝叶斯过滤器(这是 SpamAssassin 的一个重要部分,稍后将介绍)使用基于先前垃圾邮件和非垃圾邮件电子邮件的统计数据的数据库。自动黑名单/白名单也创建自己的数据库。

下载和安装 SpamAssassin

SpamAssassin 与本书中使用的大多数软件略有不同。它是用一种称为Perl的语言编写的,它有自己的分发方法称为CPANComprehensive Perl Archive Network)。CPAN 是一个大型的 Perl 软件网站(通常是 Perl 模块),术语 CPAN 也是用于下载这些模块并安装它们的软件的名称。尽管 SpamAssassin 作为一个软件包由许多 Linux 发行版提供,但我们强烈建议您从源代码安装它,而不是使用软件包。这样,您将获得最新版本的 SpamAssassin,而不是在您的 Linux 发行商创建其发布时的版本。

大多数 Perl 用户将使用 CPAN 构建 Perl 模块并且不会遇到困难。CPAN 可以自动定位和安装任何依赖项(使所需组件正常工作所需的其他组件)。从 Perl 的角度来看,使用 CPAN 安装 Perl 模块就像在 Linux 中使用rpmapt-get命令一样。基本操作非常简单,一旦系统配置好了,通常每次都能正常工作。

然而,学习和配置一种新的软件安装方式可能会让一些人望而却步。SpamAssassin 的发布以源代码形式分发,但基于Red Hat Package ManagerRPM)的系统的管理员可以轻松地将最新的 SpamAssassin 发布转换为 rpm 格式,然后可以使用常规的rpm命令来安装软件包。当 SpamAssassin 更新时,Debian 存储库也会相当快地更新,可以使用常规的apt-get命令来安装 SpamAssassin。我们强烈建议您优先使用apt-get、CPAN 或使用下面描述的rpmbuild命令来安装,而不是使用发行商提供的 RPM。

由于 SpamAssassin 是一个 Perl 模块,它首先出现在 CPAN 上。事实上,只有到达 CPAN 时才会发布。CPAN 的用户可以在发布几分钟后下载最新版本的 SpamAssassin。

如果从源代码构建 SpamAssassin,支持也更容易获得。一些发行商在创建他们的 SpamAssassin 的 RPM 时可能会做出不寻常的决定,或者可能修改某些默认值。这使得获得支持更加困难。

RPM 包也需要时间交付。发行商需要时间来构建和测试软件的新版本,然后才能发布它们,大多数软件包的更新速度不如 SpamAssassin 快。因此,Linux 发行版可能不提供最新的软件,提供的软件可能已经过时了几个版本。

使用 CPAN

使用 CPAN 安装 SpamAssassin 3.2.5 的先决条件如下:

  • Perl 版本 5.6.1 或更高版本:大多数现代 Linux 发行版将其作为基本软件包的一部分。

  • 多个 Perl 模块:当前版本的 SpamAssassin 需要 Digest::SHA1、HTML::Parser 和 Net::DNS 模块。如果配置 CPAN 以遵循依赖关系,CPAN 将安装这些模块,但还有许多可选的 Perl 模块应该安装以获得最佳的垃圾邮件检测。CPAN 将发出带有模块名称的警告,这将使您能够识别并安装它们。

  • C 编译器:这可能不会默认安装,可能需要使用rpm命令添加。通常使用的编译器将被称为gcc

  • Internet 连接:CPAN 将尝试使用HTTPFTP下载模块,因此网络应该配置为允许此操作。

配置 CPAN

如果您以前使用过 CPAN,可以跳到下一节,使用 CPAN 安装 SpamAssassin

如果 Internet 流量需要代理服务器,CPAN(以及其他 Perl 模块和脚本)将使用http_proxy环境变量。如果代理需要用户名和密码,则需要使用环境变量指定这些信息。由于 CPAN 通常以root身份运行,这些命令应该以root身份输入:

# HTTP_proxy=http://proxy.name:80
# export HTTP_proxy
# HTTP_proxy_user=username
# export HTTP_proxy_user
# HTTP_proxy_pass=password
# export HTTP_proxy_pass

接下来,输入以下命令:

# perl -MCPAN -e shell

如果输出类似于以下内容,则 CPAN 模块已安装并配置,您可以跳过下一节使用 CPAN 安装 SpamAssassin

cpan shell -- CPAN exploration and modules installation (v1.7601)
ReadLine support enabled

如果输出提示手动配置,如下所示,CPAN 模块已安装但未配置。

Are you ready for manual configuration? [yes]

在配置期间,CPAN Perl 模块会提示回答大约 30 个问题。对于大多数问题,选择默认值是最佳响应。必须在使用 CPAN Perl 模块之前完成此初始配置。这些问题主要是关于各种实用程序的位置,可以通过按 Enter 选择默认值。我们应该更改默认值的唯一问题是关于构建先决模块的问题。如果我们配置 CPAN 以遵循依赖关系,它将在不提示的情况下安装所需的模块。

Policy on building prerequisites (follow, ask or ignore)? [ask] follow

配置 CPAN 后,通过输入exit并按Enter退出 shell。我们现在准备使用 CPAN 安装 SpamAssassin。

使用 CPAN 安装 SpamAssassin

要安装 SpamAssassin,请输入以下命令进入 CPAN shell:

# cpan

如果 CPAN 模块已正确配置,则将显示以下输出(或类似内容):

cpan shell -- CPAN exploration and modules installation (v1.7601)
ReadLine support enabled

现在,在cpan提示符处,输入以下命令:

cpan> install Mail::SpamAssassin

CPAN 模块将查询在线数据库以查找 SpamAssassin 及其依赖项的最新版本,然后安装它们。在安装 SpamAssassin 之前将安装依赖项。以下是示例输出:

cpan> install Mail::SpamAssassin
CPAN: Storable loaded ok (v2.18)
Going to read '/root/.cpan/Metadata'
Database was generated on Mon, 03 Aug 2009 04:27:49 GMT
Running install for module 'Mail::SpamAssassin'
CPAN: Data::Dumper loaded ok (v2.121_14)
'YAML' not installed, falling back to Data::Dumper and Storable to read prefs '/root/.cpan/prefs'
Running make for J/JM/JMASON/Mail-SpamAssassin-3.2.5.tar.gz
CPAN: Digest::SHA loaded ok (v5.45)
CPAN: Compress::Zlib loaded ok (v2.015)
Checksum for /root/.cpan/sources/authors/id/J/JM/JMASON/Mail-SpamAssassin-3.2.5.tar.gz ok
Scanning cache /root/.cpan/build for sizes
............................................................................DONE
CPAN: Archive::Tar loaded ok (v1.38)
Will not use Archive::Tar, need 1.00
Mail-SpamAssassin-3.2.5
Mail-SpamAssassin-3.2.5/t
Mail-SpamAssassin-3.2.5/sql
Mail-SpamAssassin-3.2.5/lib
....
CPAN.pm: Going to build F/FE/FELICITY/Mail-SpamAssassin-3.00.tar.gz

SpamAssassin 可能需要用户回答一些问题。提供的答案可能会影响模块配置,或者只是安装前执行的测试的一部分。

CPAN.pm: Going to build J/JM/JMASON/Mail-SpamAssassin-3
What e-mail address or URL should be used in the suspected-spam report
text for users who want more information on your filter installation?
(In particular, ISPs should change this to a local Postmaster contact)
default text: [the administrator of that system] postmaster@myfomain.com
NOTE: settings for "make test" are now controlled using "t/config.dist".
See that file if you wish to customise what tests are run, and how.
checking module dependencies and their versions...

与许多 Perl 模块一样,SpamAssassin 非常灵活。如果可用,它可以利用功能,即使没有也可以工作。在使用 CPAN 时,您可能会看到以下消息:

optional module missing: Mail::SPF
optional module missing: Mail::SPF::Query
optional module missing: IP::Country
optional module missing: Razor2
optional module missing: Net::Ident
optional module missing: IO::Socket::INET6
optional module missing: IO::Socket::SSL
optional module missing: Mail::DomainKeys
optional module missing: Mail::DKIM
optional module missing: DBI
optional module missing: Encode::Detect

如果安装了提到的模块,SpamAssassin 将使用它们,这将改善电子邮件过滤。您可以中止 SpamAssassin 的安装,并使用 cpan install Module::Name命令安装模块。

如果让构建过程完成,它将测试 C 编译器的功能,配置和构建模块,创建文档并测试 SpamAssassin。在构建结束时,输出应类似于以下内容:

chmod 755 /usr/share/spamassassin
/usr/bin/make install -- OK
cpan>

这表明 SpamAssassin 已经正确安装。如果 SpamAssassin 安装成功,您可以跳过测试安装部分。

如果安装失败,输出可能如下所示:

Failed 17/68 test scripts, 75.00% okay. 50/1482 subtests
failed, 96.63% okay.
make: *** [test_dynamic] Error 29
/usr/bin/make test -- NOT OK
Running make install
make test had returned bad status, won't install without force
cpan>

如果输出不以/usr/bin/make install -- OK消息结束,则发生错误。首先,您应该检查所有输出以查找可能的警告和错误消息,特别是先决软件包。如果这没有帮助,那么支持途径将在测试安装部分中描述。

使用 rpmbuild 实用程序

如果使用基于 Red Hat 软件包管理器格式的 Linux 版本,则可以使用rpmbuild命令安装 SpamAssassin。从www.cpan.org/modules/01modules.index.html下载 SpamAssassin 源代码到工作目录,然后发出以下命令构建 SpamAssassin:

# rpmbuild -tb Mail-SpamAssassin-3.2.5.tar.gz
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.ORksvX
+ umask 022
+ cd /root/rpmbuild/BUILD
+ cd /root/rpmbuild/BUILD
+ rm -rf Mail-SpamAssassin-3.2.5
+ /usr/bin/gzip -dc /root/Mail-SpamAssassin-3.2.5.tar.gz
+ /bin/tar -xf -
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ cd Mail-SpamAssassin-3.2.5
+ /bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ exit 0
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.zgpcdd
...
... (output continues)
...
Wrote: /usr/src/redhat/RPMS/i386/spamassassin-3.0.4-1.i386.rpm Wrote: /usr/src/redhat/RPMS/i386/spamassassin-tools-3.0.4-1.i386.rpm Wrote: /usr/src/redhat/RPMS/i386/perl-Mail-SpamAssassin-3.0.4-1.i386.rpm Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.65065 + umask 022 + cd /usr/src/redhat/BUILD + cd Mail-SpamAssassin-3.0.4 + '[' /var/tmp/spamassassin-root '!=' / ']' + rm -rf /var/tmp/spamassassin-root + exit 0

由于缺少依赖关系,安装可能会失败。这些是 SpamAssassin 使用的 Perl 模块,需要单独安装。错误消息通常会暗示依赖项的名称,如以下安装中所示:

# rpmbuild -tb Mail-SpamAssassin-3.2.5.tar.gz
error: Failed build dependencies:
perl(Digest::SHA1) is needed by spamassassin-3.2.5-1.i386
perl(HTML::Parser) is needed by spamassassin-3.2.5-1.i386
perl(Net::DNS) is needed by spamassassin-3.2.5-1.i386

在这种情况下,需要 Perl 模块Digest::SHA1, HTML::ParserNet::DNS。解决方案是使用 CPAN 安装它。在某些情况下,SpamAssassin 可能需要特定版本的软件包,这可能需要升级已安装的版本。

使用 CPAN 安装 SpamAssassin 时,所有依赖项都会自动安装。但是,使用rpmbuild命令时,需要手动安装依赖项。使用 CPAN 通常比rpmbuild更不麻烦。

使用预构建的 RPM

SpamAssassin 打包在许多 Linux 发行版中,并且通常可以从其他来源获得 SpamAssassin 的新版本的软件包。如前所述,RPM 不是安装 SpamAssassin 的推荐方法,但比在不寻常的平台上从源代码构建更可靠。

要安装 RPM,只需下载或在发行 CD 上找到它,并使用rpm命令安装它。以下命令可用于安装 SpamAssassin 的 RPM:

# rpm -ivh /path/to/rpmfile-9.99.rpm

也可以使用图形安装程序来安装 SpamAssassin 的 RPM。SpamAssassin 网站上列出的 RPM 通常是最新版本的 SpamAssassin,并且是完整的。如果无法安装这些 RPM,则应安装 Linux 发行版提供的 RPM。

测试安装

值得进行一些测试,以确保 SpamAssassin 已正确安装并且环境是完整的。如果要测试特定用户帐户,应登录到该帐户执行测试。

SpamAssassin 包括一个样本垃圾邮件和一个样本非垃圾邮件。可以通过处理样本电子邮件来进行测试。这些电子邮件位于 SpamAssassin 分发目录的根目录。如果您使用 CPAN 以root用户安装 SpamAssassin,则该目录的路径可能类似于~root/.cpan/build/Mail-SpamAssassin-3.2.5/,其中3.2.5是安装的 SpamAssassin 版本。如果找不到文件,请从www.cpan.org/modules/01modules.index.html下载 SpamAssassin 源代码并将源代码解压缩到临时目录。样本电子邮件位于解压后源代码的根目录中。

要测试 SpamAssassin,请切换到包含sample-spam.txt的目录,并使用以下命令。每个命令后都显示了示例结果。

$ spamassassin -t < sample-nonspam.txt | grep X-Spam

[22674] warn: config: created user preferences file: /home/user/.spamassassin/user_prefs
X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on
X-Spam-Level:
X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=haX-

$ spamassassin -t < sample-spam.txt | grep X-Spam

X-Spam-Flag: YES
X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on
X-Spam-Level: **************************************************
X-Spam-Status: Yes, score=1000.0 required=5.0 tests=GTUBE,NO_RECEIVED,
X-Spam-Report:
X-Spam-Prev-Subject: Test spam mail (GTUBE)

使用sample-nonspam.txt命令的输出应该是X-Spam-Status: No,使用sample-spam.txt命令的输出应该是X-Spam-Flag: YESX-Spam-Status: Yes

SpamAssassin 可以使用--lint标志验证其配置文件并报告任何错误。默认情况下,干净的 SpamAssassin 安装不应该有任何错误,但一旦网站被定制,一些规则可能会失败。在下面的示例中,score条目与规则不匹配:

$ spamassassin --lint

warning: score set for non-existent rule RULE_NAME
lint: 1 issues detected. please run with debug enabled for more
information

如果输出包含警告,那么出现了问题。在继续使用之前,值得修复 SpamAssassin。最好的参考资料是 SpamAssassin Wiki(wiki.apache.org/spamassassin/)、SpamAssassin 邮件列表的存档(wiki.apache.org/spamassassin/MailingLists)以及您喜欢的搜索引擎。与大多数开源项目一样,开发人员是志愿者,他们欣赏那些在发布求助请求之前搜索解决方案的用户,因为大多数问题以前已经遇到过很多次。

修改后的电子邮件

除了提到的电子邮件标题之外,如果认为是垃圾邮件,SpamAssassin 还会修改电子邮件。它会将原始电子邮件转换为一个简单的电子邮件附件。如果检测到潜在病毒或其他危险内容,SpamAssassin 总是会包装电子邮件。在其默认配置中,它会在垃圾邮件周围添加一个信封电子邮件,但如果需要,可以关闭此功能。请参阅 SpamAssassin 文档中的report_safe指令。信封电子邮件如下所示:

修改后的电子邮件

使用 SpamAssassin

现在 SpamAssassin 已安装,我们需要配置系统来使用它。SpamAssassin 可以以多种方式使用。它可以集成到 MTA 中以获得最大性能;它可以作为守护程序运行或作为简单脚本以避免复杂性;它可以为每个用户使用单独的设置或为所有用户使用单一设置;它可以用于所有帐户或仅用于选择的帐户。在本书中,我们将讨论三种使用 SpamAssassin 的方法。

第一种方法是使用 Procmail。这是配置最简单的方法,适用于低流量站点,例如每天少于 10,000 封电子邮件。

第二种方法是将 SpamAssassin 作为守护程序使用。这更有效,并且如果需要的话,仍然可以与 Procmail 一起使用。

第三种方法是将 SpamAssassin 与诸如 amavisd 之类的内容过滤器集成。这提供了性能优势,但有时内容过滤器与最新版本的 SpamAssassin 不兼容。如果有任何问题,通常会很快解决。

注意

为了帮助您充分利用 SpamAssassin,Packt Publishing 出版了SpamAssassin: A practical guide to integration and configuration,作者是 Alistair McDonald(ISBN 1-904811-12-4)。

使用 Procmail 与 SpamAssassin

Procmail 在第六章和第七章中有所涉及。如果你对 Procmail 有基本的了解,那么接下来的内容应该很容易理解。如果你直接跳到这一章而不了解 Procmail,那么最好先阅读第六章,该章节讨论了 Procmail 的基础知识。

在我们配置系统使用 SpamAssassin 之前,让我们考虑一下 SpamAssassin 的作用。SpamAssassin 不是一个电子邮件过滤器。过滤器是改变电子邮件目的地的东西。SpamAssassin 会向电子邮件消息添加电子邮件头,以指示它是否是垃圾邮件。

考虑一个具有以下头部的电子邮件:

Return-Path: <user@domain.com>
X-Original-To: jdoe@localhost
Delivered-To: jdoe@host.domain.com
Received: from localhost (localhost [127.0.0.1])
by domain.com (Postfix) with ESMTP id 52A2CF2948
for <jdoe@localhost>; Thu, 11 Nov 2004 03:39:42 +0000 (GMT)
Received: from pop.ntlworld.com [62.253.162.50]
by localhost with POP3 (fetchmail-6.2.5)
for jdoe@localhost (single-drop); Thu, 11 Nov 2004 03:39:42 +0000 (GMT)
Message-ID: <D8F7B41C.4DDAFE7@anotherdomain.com>
Date: Wed, 10 Nov 2004 17:54:14 -0800
From: "stephen mellors" <gregory@anotherdomain.com>
User-Agent: MIME-tools 5.503 (Entity 5.501)
X-Accept-Language: en-us
MIME-Version: 1.0
To: "Jane Doe" <jdoe@domain.com>
Subject: nearest pharmacy online
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 7bit

SpamAssassin 将添加头部行。

X-Spam-Flag: YES
X-Spam-Checker-Version: SpamAssassin 3.1.0-r54722 (2004-10-13) on
host.domain.com
X-Spam-Level: *****
X-Spam-Status: Yes, score=5.8 required=5.0 tests=BAYES_05,HTML_00_10,
HTML_MESSAGE,MPART_ALT_DIFF autolearn=no
version=3.1.0-r54722

SpamAssassin 不会改变电子邮件的目的地,它只是添加头部,以便其他东西改变电子邮件的目的地。

判断一封电子邮件是否是垃圾邮件的最佳指标是X-Spam-Flag。如果是YES,SpamAssassin 认为该邮件是垃圾邮件,并且可以被 Procmail 过滤。

SpamAssassin 还为每封电子邮件分配一个分数,分数越高,该邮件是垃圾邮件的可能性就越大。确定一封电子邮件是否是垃圾邮件的阈值可以在系统范围或每个用户的基础上进行配置。如果您使用未经修改的 SpamAssassin 安装和没有自定义规则集,则默认值5.0是一个明智的默认值。

全局 procmailrc 文件

假设我们想要使用 SpamAssassin 检查所有传入的电子邮件是否为垃圾邮件。/etc/procmailrc文件中的命令将为所有用户运行,因此在这里执行 SpamAssassin 是理想的。

将以下简单的配方放置在/etc/procmailrc中,将为所有用户运行 SpamAssassin:

:0fw
| /usr/bin/spamassassin

要将所有垃圾邮件放在单独的垃圾邮件文件夹中,请确保global/etc/procmailrc文件中有一行指定默认目的地。例如:

DEFAULT=$HOME/.maildir/

如果不是,则添加一行指定DEFAULT。要将垃圾邮件过滤到文件夹中,请添加类似以下的配方:

* ^X-Spam-Flag: Yes
.SPAM/new

这假设每个用户已经配置了一个名为SPAM的文件夹。

要将所有垃圾邮件放在一个单一的中央文件夹中,请在配方中使用目的地的绝对路径:

* ^X-Spam-Flag: Yes
/var/spool/poss_spam

这将把所有垃圾邮件放在一个单独的文件夹中,可以由系统管理员进行审核。由于常规电子邮件偶尔会被错误地检测为垃圾邮件,因此该文件夹不应该是全世界可读的,这导致了一个更普遍的陈述。

注意

SpamAssassin 将在由 Postfix 使用的系统帐户下运行。这意味着贝叶斯数据库和自动白名单和黑名单将被所有用户共享。从安全的角度来看,重要的是 SpamAssassin 创建的各种数据库不可被全世界写入。

SpamAssassin 将用户特定文件存储在~/.spamassassin/目录中。以下是用户可能存在的文件列表:

文件 内容
auto-whitelist``aauto-whitelist.db``aauto-whitelist.dir``aauto-whitelist.pag SpamAssassin 创建一个用户发送非垃圾邮件的数据库,并使用它来预测特定发件人的电子邮件是垃圾邮件还是非垃圾邮件。这些文件用于跟踪用户。
bayes_journal``bayes_seen``bayes_toks SpamAssassin 使用一种称为贝叶斯分析的统计技术。这些文件用于此功能。
user_prefs 此文件允许为特定用户覆盖全局设置。该文件可以包含配置设置、规则和分数。

其中一些可能包含机密数据,例如,常规联系人将出现在自动白名单文件中。谨慎使用权限将确保这些文件不可被普通用户帐户读取。

在每个用户的基础上使用 SpamAssassin

也许有些用户不会收到垃圾邮件,或者用户共享白名单和贝叶斯数据库可能存在问题。SpamAssassin 可以通过将配方移动到特定用户的~/.procmailrc来在个人基础上运行。这应该提高每个用户的过滤性能,但会增加每个用户的磁盘空间使用量,并需要通过修改其~/.procmailrc设置每个单独的用户帐户。

典型用户的.procmailrc可能如下所示:

MAILDIR=$HOME/.maildir
:0fw
| /usr/bin/spamassassin
:0
* ^X-Spam-Flag: Yes
.SPAM/cur

如建议的,有时电子邮件可能被错误地检测为垃圾邮件。值得回顾垃圾邮件,以确保合法的电子邮件没有被错误分类。如果用户收到大量垃圾邮件,那么浏览所有这些邮件是耗时、乏味且容易出错的。Procmail 可以通过检查 SpamAssassin 在电子邮件标题中写入的垃圾邮件分数来过滤垃圾邮件。

低分垃圾邮件(例如,得分高达 9 分)可以放在一个名为Probable_Spam的文件夹中,而得分更高的垃圾邮件(更有可能是垃圾邮件)可以放在一个名为Certain_Spam的文件夹中。

为此,我们使用 SpamAssassin 创建的X-Spam-Level标题。这只是与X-Spam-Level值相关的星号数量。通过将具有超过一定数量星号的电子邮件移动到Certain_Spam文件夹,其余的垃圾邮件就是“Probable Spam”。标有X-Spam-Flag: NO的电子邮件显然不是垃圾邮件。

以下的.procmailrc文件将高分垃圾邮件与低分垃圾邮件和非垃圾邮件分开过滤:

MAILDIR=$HOME/.maildir
:0fw
| /usr/bin/spamassassin
:0
* ^X-Spam-Level: \*\*\*\*\*\*\*\*\*\*\*\*\*\*
.Certain_Spam/cur
:0
* ^X-Spam-FLAG: YES
.Probable_Spam/cur

使用 SpamAssassin 作为 Postfix 的守护程序

守护程序是一个后台进程;它等待工作,处理工作,然后等待更多工作。使用这种方法实际上提高了性能(只要有足够的内存),因为响应性得到了改善——程序始终准备好并等待,不需要每次需要标记垃圾邮件时加载。

要将 SpamAssassin 作为守护程序使用,应添加一个用户帐户——以root身份运行任何服务都是危险的。作为root,输入以下命令以创建名为 spam 的用户和组:

# groupadd spam
# useradd -m -d /home/spam -g spam -s /bin/false spam
# chmod 0700 /home/spam

要配置 Postfix 运行 SpamAssassin,使用 SpamAssassin 作为守护程序。必须更改 Postfix 的master.cf文件。编辑文件并找到以'smtp inet'开头的行。修改该行以在末尾添加-o content_filter=spamd

smtp inet n - n - - smtpd -o content_filter=spamd

在文件末尾添加以下行:

spamd unix - n n - - pipe
flags=R user=spam argv=/usr/bin/spamc
-e /usr/sbin/sendmail -oi -f ${sender} ${recipient}

如果文本跨越多行,则任何继续的行必须以所示的空格开头。对文件的更改定义了一个名为spamd的过滤器,该过滤器为每条消息运行spamc客户端,并且还指定应在通过 SMTP 接收电子邮件时运行该过滤器。

在此行上,spamd是过滤器的名称,并与content_filter行中使用的名称匹配。user=部分指定应用于运行命令的用户上下文。argv=部分描述应运行的程序。Procmail 使用其他标志,它们的存在很重要。

使用 SpamAssassin 与 amavisd-new

amavisd-new是 MTA 和内容检查器之间的接口。尽管它的名字是 amavisd-new,但它是一个经过良好维护的开源软件包。内容检查器扫描电子邮件以查找病毒和/或垃圾邮件。amavisd-new 略有不同。就像spamd一样,它是用 Perl 编写的并作为守护进程运行,但它不是通过spamcspamassassin客户端访问 SpamAssassin,而是将 SpamAssassin 加载到内存中并直接访问 SpamAssassin 函数。因此,它与 SpamAssassin 紧密耦合,可能需要与 SpamAssassin 同时升级。

与其他基于 Perl 的应用程序和实用程序不同,amavisd-new 无法从 CPAN 获取。但是,它以源代码和 RPM 形式提供给许多 Linux 发行版,并且也可用于基于 debian 的存储库。可用版本的详细信息在www.ijs.si/software/amavisd/#download上列出。我们建议,如果您的发行商提供的 SpamAssassin 版本是最新的,那么您应该使用他们提供的 SpamAssassin 和 amavisd 的软件包。

从软件包安装 amavisd-new

要从软件包安装 amavisd-new,请在基于 RPM 的发行版上使用rpm命令。amavisd-new 有许多依赖项,所有这些依赖项都是 Perl 模块。每个版本可能具有不同的依赖项,这些依赖项在软件包的安装文件中列出。版本 2.6.2 的 Perl 先决条件如下:

Archive::Zip
BerkeleyDB
Convert::BinHex
Convert::TNEF
Convert::UUlib
Crypt::OpenSSL::Bignum
Crypt::OpenSSL::RSA
Digest::HMAC
Digest::Sha1
IO::Multiplex
IO::Stringy
MIME::Tools
Mail::DKIM
Net::CIDR
Net::DNS
Net::IP
Net::Server
Unix::Syslog

要查看特定版本的 amavisd-new 的先决条件,请按照这里所示的方式下载源代码并解压缩,然后阅读安装文件。

$ cd /some/dir
$ wget http://www.ijs.si/software/amavisd/amavisd-new-2.6.2.tar.gz
$ tar xfz amavisd-new-2.6.2.tar.gz
$ cd amavisd-new-2.6.2
$ vi INSTALL

其中一些依赖项可能已经安装,因为它们也被 SpamAssassin 使用。

安装先决条件

一些基于 RPM 的 Linux 发行版可能会自动安装先决条件作为依赖项。对于其他发行版,必须从 CPAN 下载并安装所有先决条件。使用cpan命令最容易实现这一点。另一种方法是分别下载每个先决条件的源代码,并使用以下命令安装它:

$ cd /some/directory
$ gunzip -c source-nn.tar.gz | tar xf -
$ cd source-nn
$ perl Makefile.pl
$ make test
$ su
# make install

从源代码安装

amavisd-new 没有 makefile、配置脚本或安装例程。要安装它,唯一的可执行脚本应复制到/usr/local/bin,并修改其属性以确保它不能被非 root 用户修改:

# cp amavisd /usr/local/sbin/
# chown root /usr/local/sbin/amavisd
# chmod 755 /usr/local/sbin/amavisd

示例amavisd.conf文件应复制到/etc,并且其属性也应该被修改。

# cp amavisd.conf /etc/
# chown root /etc/amavisd.conf
# chmod 644 /etc/amavisd.conf

amavisd-new 必须配置为作为守护进程运行,因此示例init脚本应复制到适当的目录。

# cp amavisd_init.sh /etc/init.d/amavisd-new

init脚本也应添加到系统启动中。大多数 Linux 发行版使用chkconfig命令来执行此操作。

# chkconfig --add amavisd-new

为 amavisd-new 创建用户帐户

要创建用户帐户,首先使用groupadd命令创建一个专用组,然后使用useradd命令添加用户。

# groupadd amavis
# useradd -m -d /home/amavis -g amavis -s /bin/false amavis

配置 amavisd-new

/etc/amavisd.conf文件需要进行一些更改。该文件将被解析为 Perl 源代码,语法很重要。每行应以分号结尾,大小写很重要。以下变量声明行应更改为包含以下值:

$MYHOME = '/home/amavis';
$mydomain = 'domain.com';
$daemon_user = 'amavis';
$daemon_group = 'amavis';
$max_servers = 5; # number of pre-forked children (default 2)

确保为$mydomain指定了正确的域。为$max_servers指定的数字5是将同时运行的守护进程的数量。如果您有适量的电子邮件,例如每秒少于十封邮件,那么默认设置就足够了。

/etc/amavisd.conf中,有一个关于 SpamAssassin 相关配置设置的部分:

$sa_tag_level_deflt = 2.0;
$sa_tag2_level_deflt = 6.2;
$sa_kill_level_deflt = 6.9;

这三个设置与正在处理的电子邮件相关的 SpamAssassin 分数级别一起使用。$sa_tag_level_deflt设置是将正常邮件与垃圾邮件分开的阈值,并且将X-Spam-StatusX-Spam-Level头添加到电子邮件中。

得分低于此阈值的电子邮件不会添加头,而得分高于阈值的电子邮件将添加头。$sa_kill_level_deflt设置是拒绝垃圾邮件的阈值。

默认配置是拒绝垃圾邮件。要将垃圾邮件转发到另一个电子邮件地址,请找到指定$final_spam_destiny的行,如果不存在则添加一行,并将其设置为以下内容:

$final_spam_destiny = D_PASS; # (defaults to D_REJECT)

必须定义垃圾邮件的接收者。找到指定$spam_quarantine_to的行,并更改它或添加一个以包含电子邮件地址。在此步骤中早期配置的$mydomain变量可以用于引用域名,记得在@符号前加上反斜杠。

$spam_quarantine_to = "spam-quarantine\@$mydomain";

现在,应该启动 amavisd-new。大多数 Linux 发行版使用以下命令:

# /etc/init.d/amavisd-new start

配置 Postfix 以运行 amavisd-new

编辑/etc/postfix/master.cf并找到以下行:

smtp inet n - n - - smtpd

在此之后添加以下内容:

smtp-amavis unix y - 5 smtp
-o smtp_data_done_timeout=1200
-o disable_dns_lookups=yes
127.0.0.1:10025 inet n y-- smtpd
-o content_filter=
-o local_recipient_maps=
-o relay_recipient_maps=
-o smtpd_restriction_classes=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks=127.0.0.0/8
-o strict_rfc821_envelopes=yes

smtp-amavis行中,数字5指定可以同时使用的实例数。这应该对应于amavisd.conf文件中指定的$max_servers条目。

编辑/etc/postfix/main.cf并在文件末尾添加以下行:

content_filter = smtp-amavis:[localhost]:10024

使用postfix reload命令重新启动 Postfix:

# postfix reload

配置电子邮件客户端

通过使用电子邮件客户端而不是使用 Procmail 将垃圾邮件放入单独的文件夹中,可以执行此操作。大多数电子邮件客户端允许创建规则或过滤器。这些通常在阅读新邮件或打开文件夹时生效。

电子邮件客户端中的规则根据电子邮件头的值运行。最好使用X‑Spam-Flag并搜索值YES。将标记的邮件移动到单独的文件夹的过程如下所述:

  1. 为存储垃圾邮件创建文件夹或邮箱。文件夹名称应该直观,例如Spam

  2. 创建一个在电子邮件到达时运行的规则。该规则应该在邮件头中查找文本X‑Spam-Flag

  3. 规则上的操作应该是将电子邮件移动到在第一步创建的“垃圾邮件”文件夹中。

  4. 创建过滤器后,发送测试邮件,包括垃圾邮件和非垃圾邮件,以检查过滤器是否正常工作。

Microsoft Outlook

Microsoft Outlook 在大型组织中很受欢迎。它与 IMAP 服务器集成良好。按照以下步骤配置 Outlook 以根据电子邮件头中的X‑Spam‑Flag过滤垃圾邮件:

注意

这些说明基于随 Microsoft Office XP 一起提供的 Outlook;其他版本具有类似的配置细节。

  1. 创建一个用于存储垃圾邮件的文件夹。单击文件夹列表中的“Inbox”以选择它,右键单击并从菜单中选择“新建文件夹”。选择 Spam 或其他有意义的名称,然后单击“确定”。Microsoft Outlook

  2. 单击“工具”菜单,然后选择“规则和警报”。单击“新建规则”以创建新规则。

Microsoft Outlook

  1. 从“从空白规则开始”下选择“在邮件到达时检查邮件”。单击“下一步”。

  2. 在邮件头中检查特定单词。这将允许 Outlook 检查 X-Spam-Flag 电子邮件头。单击“特定单词”以选择正确的短语。

Microsoft Outlook

  1. 在下一个对话框中,仔细输入“X-Spam-Flag: YES”,然后单击“添加”。然后单击“确定”,再单击“下一步”。

Microsoft Outlook

  1. 下一个窗口提供了选择操作的选项。选择“将其移动到指定的文件夹”,然后单击“指定”,这将显示一个文件夹列表。

Microsoft Outlook

  1. 选择之前创建的文件夹,然后单击“确定”。单击“完成”。没有例外情况,因此再次单击“下一步”。

  2. 规则向导允许立即在收件箱中的任何现有邮件上运行规则。要做到这一点,请确保“启用此规则”旁边的复选框被选中。

  3. 最后,单击“完成”,规则将被创建并应用于收件箱中的所有邮件。

Microsoft Outlook Express

Outlook Express 随 Windows 的大多数版本一起提供,包括 Windows XP。它提供了 POP3 连接和许多功能,如 HTML 电子邮件。一些电子邮件客户端,包括 Outlook Express,不允许对每个电子邮件头部进行过滤,而只能对特定的头部进行过滤,比如From:Subject:头部。默认情况下,SpamAssassin 只写入额外的头部,但可以配置为更改电子邮件的主题、发件人收件人头部。要做到这一点,应该更改/etc/spamassassin/local.cf文件。这种更改也可以通过编辑~user/.spamassassin/user_prefs来实现每个用户的基础上。

将以下行添加到文件中:

rewrite_header Subject *****SPAM*****

这将把电子邮件的标题更改为*****SPAM*****。如果需要,可以更改标签。

现在 SpamAssassin 配置完成,Outlook Express 可以配置为对修改后的消息主题进行操作。按照以下步骤进行:

  1. 为垃圾邮件创建一个文件夹。要做到这一点,选择文件菜单,点击文件夹,然后点击新建。输入垃圾邮件,或者其他描述性的名称作为文件夹名称,然后点击确定

  2. 选择工具菜单,然后选择消息规则,然后选择新建。在下一个窗口中,确保条件包括主题行包含特定单词,操作包括将其移动到指定的文件夹

Microsoft Outlook Express

  1. 点击包含特定单词,并输入SPAM,或者在配置 SpamAssassin 时选择的替代短语。点击确定

Microsoft Outlook Express

  1. 规则描述的下一行中点击指定。选择创建的文件夹,然后点击确定Microsoft Outlook Express

  2. 规则已经总结。给它一个有意义的名称,比如垃圾邮件,然后点击确定保存它。

Mozilla Thunderbird

Mozilla Thunderbird 是一个免费的开源电子邮件客户端,具有大部分 Microsoft Outlook 的功能。可以在www.mozilla.org/products/thunderbird/免费获取。它具有完整的过滤功能。要配置它,请按照以下步骤进行:

  1. 创建一个文件夹来存储垃圾邮件。点击文件菜单,选择新建 | 文件夹。选择一个位置(收件箱应该没问题),然后输入一个名称,比如垃圾邮件。点击确定

Mozilla Thunderbird

  1. 点击工具菜单,选择消息过滤器。点击新建按钮创建一个新的过滤器。

Mozilla Thunderbird

  1. 在下一个对话框中,选择一个过滤器的名称,比如垃圾邮件。然后选择匹配以下任意内容按钮。在左侧列表中,输入X-Spam-Status,在中间列表中选择,在右侧选择。在下面的框中,点击移动消息到,并选择在第一步创建的文件夹。Mozilla Thunderbird

  2. 点击确定,规则摘要将显示规则。点击立即运行以测试规则。

Mozilla Thunderbird

自定义 SpamAssassin

SpamAssassin 非常灵活。几乎每个设置都可以在系统范围或用户特定的基础上进行配置。

自定义的原因

如果 SpamAssassin 如此出色,为什么还要配置它呢?嗯,有几个原因值得改进 SpamAssassin 的垃圾邮件过滤。

  • SpamAssassin 默认情况下(即安装但未自定义时)通常能够检测超过 80%的垃圾邮件。添加一些自定义后,检测率可以超过 95%。

  • 每个人的垃圾邮件都是不同的,一个用户的垃圾邮件可能看起来像另一个用户的正常邮件。试图变得普遍化,SpamAssassin 可能无法为每个用户过滤垃圾邮件。

  • SpamAssassin 的一些功能默认情况下是禁用的。通过启用它们,可以提高垃圾邮件识别率。

本章讨论了以下配置选项:

  • 修改规则的分数:这允许禁用规则,给予较差规则较少的权重,并给予较好规则更高的权重。

  • 获取和使用新规则:这可以改善垃圾邮件检测。

  • 将电子邮件地址添加到白名单和黑名单:这允许来自指定发件人的电子邮件始终被视为垃圾邮件,无论内容如何,或者相反。

  • 启用 SpamAssassin 的贝叶斯过滤器:这可以将过滤准确度从 80%提高到 95%或更高。

规则和分数

标准、站点范围和用户特定设置的配置文件保存在不同的目录中,如下所示:

  • 标准配置设置存储在/usr/share/spamassassin中。

  • 站点范围的自定义和设置存储在/etc/mail/spamassassin/中。SpamAssassin 会检查所有与*.cf匹配的文件。

  • 用户特定设置存储在~/.spamassassin/local.cf中。

大部分标准配置文件用于简单规则及其分数。

规则通常是与字母、数字或其他打印字符匹配。规则使用一种称为正则表达式或简称为 regex 的技术编写。这是一种简写方法,用于指定某些字符组合将触发规则。规则可能尝试检测特定单词,例如“劳力士”,或者可能按特定顺序查找特定单词,例如“在线购买劳力士”。规则存储在文本文件中。

默认文件存储在/usr/share/spamassassin中。这些文件是随 SpamAssassin 一起提供的,并且可能会在每个版本中更改。最好不要修改这些文件或在此目录中放置新文件,因为升级到 SpamAssassin 将覆盖这些文件。SpamAssassin 使用的大部分规则以及应用于每个规则的分数在此目录中的文件中定义。

默认设置可以被站点范围配置文件覆盖。这些文件放置在/etc/mail/spamassassin中。SpamAssassin 将读取此目录中与*.cf匹配的所有文件。在此处进行的设置可以覆盖默认文件中的设置。它们可以包括定义新规则和新规则分数。

用户特定的自定义可以放置在~/.spamassassin/local.cf文件中。在此处进行的设置可以覆盖在/etc/mail/spamassassin中定义的站点范围设置和在/usr/share/spamassassin/中定义的默认设置。可以在此处定义新规则,并覆盖现有规则的分数。

SpamAssassin 首先按字母数字顺序读取/usr/share/spamassassin中的所有文件;10_misc.cf将在23_bayes.cf之前读取。然后,SpamAssassin 再次按字母数字顺序读取/etc/mail/spamassassin/中的所有.cf文件。最后,SpamAssassin 读取~user/.spamassassin/user_prefs。如果在两个文件中定义了规则或分数,则使用最后读取的文件中的设置。这允许管理员覆盖默认设置,用户覆盖站点范围设置。

规则文件中的每一行可以是空白的,也可以包含注释或命令。井号(#)符号用于注释。规则通常有三个部分,规则定义、文本描述和分数或一系列分数。约定规定,SpamAssassin 提供的所有规则分数应该位于单独的文件中。该文件是/usr/share/spamassassin/50_scores.cf

修改规则分数

最简单的配置更改是更改规则分数。这样做的两个原因是:

  • 一个规则非常擅长检测垃圾邮件,但得分很低。触发该规则的电子邮件未被检测为垃圾邮件。

  • 一个规则正在作用于非垃圾邮件。因此,触发该规则的电子邮件被错误地检测为垃圾邮件。

当运行 SpamAssassin 时产生积极结果的规则将列在电子邮件的X-Spam-Status:头中:

X-Spam-Status: Yes, score=5.8 required=5.0 tests=BAYES_05,HTML_00_10,
HTML_MESSAGE,MPART_ALT_DIFF autolearn=no
version=3.1.0-r54722

应用于电子邮件的规则在tests=之后列出。如果一个规则在应该标记为垃圾邮件的电子邮件中不断出现,但实际上没有被标记为垃圾邮件,那么该规则的分数应该增加。如果一个规则经常在错误分类为垃圾邮件的电子邮件中触发,则应该降低分数。

要查找当前分数,请在可以定义分数的所有位置使用grep实用程序。

grep score.*RULE_NAME
$ grep score.*BAYES /usr/share/spamassassin/* /etc/mail/spamassassin/* ~/.spamassassin/local.cf

/etc/mail/spamassassin/local_scores.cf:score RULE_NAME 0 0 1.665 2.599
/etc/mail/spamassassin/local_scores.cf: 4.34

在前面的示例中,该规则具有默认分数,该分数在/etc/mail/spamassassin/local_scores.cf中被覆盖。

该规则的原始分数有四个值。SpamAssassin 根据是否使用网络测试(例如测试开放继电器)以及是否使用贝叶斯过滤器来更改其使用的分数。列出了四个分数,这些分数在以下情况下使用:

未使用贝叶斯过滤器 使用贝叶斯过滤器
未使用外部测试 第一分数 第三分数
使用外部测试 第二分数 第四分数

如果只给出一个分数,如在/etc/mail/spamassassin/local_scores.cf中覆盖,它将在所有情况下使用。

在前面的示例中,系统管理员已经在/etc/mail/spamassassin/local_scores.cf中用一个值覆盖了默认分数。要更改特定用户的此值,他们的~/.spamassassin/local.cf可能如下所示:

score RULE_NAME 1.2

这将从/etc/mail/spamassassin/local_scores.cf中设置的4.34分数更改为1.2。要完全禁用该规则,可以将分数设置为零。

score RULE_NAME 0

可以花费无数小时来配置规则分数。SpamAssassin 包括工具,通过检查现有的垃圾邮件和非垃圾邮件来重新计算最佳的规则分数。这些工具在 Packt 出版的《SpamAssassin》一书中有详细介绍。

使用其他规则集

SpamAssassin 拥有庞大的追随者群,并且 SpamAssassin 的设计使得添加新的规则集变得容易,这些规则集是一组规则和这些规则的默认分数。有许多不同的规则集可用。大多数基于特定主题,例如查找通常与垃圾邮件一起出售的药物名称或在垃圾邮件中找到的电话号码。大多数自定义规则集都列在 SpamAssassin Wiki 的自定义规则集页面上,网址为wiki.apache.org/spamassassin/CustomRulesets

由于对抗垃圾邮件的斗争如此激烈,已经开发了可能每天上传的规则集。SpamAssassin 通过sa-update实用程序提供了这种能力。您可以选择定期使用sa-update,或者下载特定的规则集并保留它,或者手动更新您选择的规则集。为了获得最佳的垃圾邮件过滤结果,建议使用sa-update

如果要手动安装规则集,Wiki 页面提供了每个规则集的一般描述和下载它的 URL。选择了规则集后,我们按以下方式安装它:

  1. 在浏览器中,跟随 SpamAssassin Wiki 页面上的链接。在大多数情况下,链接将指向一个与*.cf匹配的文件,并且浏览器将以文本文件的形式打开它。

  2. 使用浏览器保存文件(通常,文件菜单有另存为选项)。

  3. 将文件复制到/etc/mail/spamassassin - 如果文件放在此位置,规则将自动运行。

  4. 检查文件中是否有分数,否则规则将不会被使用。

  5. 监视垃圾邮件性能,以确保合法的电子邮件不被检测为垃圾邮件。

向 SpamAssassin 添加规则将增加 SpamAssassin 使用的内存,并增加处理电子邮件所需的时间。最好谨慎地逐渐添加新的规则集,以确保了解对机器的影响。

您可以手动监视规则集,并使用相同的过程在您的系统上更新它。

如果选择使用sa-update,应该计划其使用。sa-update 可以使用多个频道,这些频道基本上是规则集的来源。默认情况下,使用 updates.spamassassin.org 频道;另一个流行的频道是 OpenProtect 频道,称为saupdates.openprotect.com

要启用sa-update,必须定期运行它,例如通过 cron。在系统中添加一个 cron 条目,调用以下命令,以更新基本规则集:

sa-update

如果使用额外的频道,命令可能如下:

sa-update –channel saupdates.openprotect.com

提示

为了防止 DNS 中毒和冒充,SpamAssassin 允许对规则集进行数字签名。要使用签名的规则集,请使用—gpgkey参数sa-update。正确的—gpgkey参数值将在 SpamAssassin 维基页面上的规则集中描述。

白名单和黑名单

SpamAssassin 非常擅长检测垃圾邮件,但总会存在错误的风险。通过使用已知的垃圾邮件生产者的电子邮件地址列表(黑名单),可以过滤掉那些一直使用相同电子邮件地址或域名的垃圾邮件发送者的电子邮件。通过使用已知的合法电子邮件发送者的电子邮件地址列表(白名单),可以确保来自常规或重要通讯者的电子邮件被过滤为正常邮件。这可以防止重要电子邮件的延迟或未投递,否则可能被标记为垃圾邮件。

列出单个电子邮件地址的黑名单的用途有限,因为垃圾邮件发送者通常会在每次垃圾邮件运行中使用不同或随机的电子邮件地址。然而,一些垃圾邮件发送者会在多次运行中使用相同的域。由于 SpamAssassin 允许在其黑名单中使用通配符,可以将整个域列入黑名单。这对于过滤垃圾邮件更有用。

手动白名单和黑名单涉及向全局配置文件/etc/mail/spamassassin/local.cf和/或~/.spamassassin/user_prefs中添加配置指令。

白名单和黑名单条目允许使用?*字符分别匹配单个字符或多个字符。因此,如果白名单条目为*@domain.com,那么joe@domain.combill@domain.com都会匹配。对于一个读取*@yahoo?.com的条目,joe@yahoo1.combill@yahoo2.com会匹配,但billy@yahoo22.com不会匹配。*@yahoo*.com将匹配所有三个示例。

白名单和黑名单规则不会立即导致电子邮件被标记为垃圾邮件或正常邮件,尽管分数受到严重加权。USER_IN_WHITELIST规则的默认分数为-100.0。从技术上讲,电子邮件可能会匹配白名单条目,但仍会触发足够多的其他测试,导致其被标记为垃圾邮件。尽管在实践中,这不太可能发生,除非分数已从默认值更改。

要将电子邮件地址或整个域列入黑名单,请使用blacklist_from指令。

blacklist_from user@spammer.com
blacklist_from *@spamdomain.com

要将电子邮件地址或域加入白名单,请使用whitelist_from指令。

whitelist_from user@mycompany.com
whitelist_from *@mytradingpartner.com

SpamAssassin 有更复杂的规则来管理白名单和黑名单,以及自动白名单/黑名单。黑名单和白名单都可以指定为离散项(blacklist joe@domain.combill@another.com)或通配符(黑名单每个joe,并黑名单来自domain.com的所有人)。通配符特别强大,应该注意确保合法的电子邮件不被拒绝。

贝叶斯过滤

这使用统计技术来确定电子邮件是否为垃圾邮件,基于先前的两种类型的电子邮件。在它起作用之前,需要用已知的垃圾邮件和已知的非垃圾邮件对其进行训练。重要的是正确分类电子邮件,否则过滤器的有效性将降低。学习过程是在电子邮件服务器上完成的,样本电子邮件应存储在可访问的位置。

sa-learn命令用于训练贝叶斯过滤器,使用已知的正常邮件或垃圾邮件。SpamAssassin 安装程序通常会将sa-learn放置在路径中,通常在/usr/bin/sa-learn中。

它在命令行上使用,并传递一个目录、文件或一系列文件。为了使其工作,电子邮件必须存储在服务器上或以合适的格式从客户端导出。SpamAssassin 识别mbox格式,许多电子邮件客户端使用兼容的格式。要使用sa-learn,可以将一个目录或一系列目录传递给命令:

$ sa-learn --ham ~/.maildir/.Trash/cur/ ~/.maildir/cur

Learned from 75 message(s) (175 message(s) examined).

如果使用mbox格式,则应使用mbox标志,以便 SpamAssassin 搜索多封电子邮件的文件。

$ sa-learn -mbox --spam ~/mbox/spam ~/mbox/bad-spam

Learned from 75 message(s) (175 message(s) examined).

如果 SpamAssassin 已经从一封电子邮件中学到了东西,sa-learn会检测到这一点,并且不会对其进行两次处理。在上面的示例中,已经处理了 175 封邮件中的 100 封,并在此次运行中被忽略。剩下的 75 封邮件之前没有被处理过。

如果sa-learn传递了多封邮件,可能有一段时间没有反馈。--showdots标志在处理每封电子邮件时提供点(.)的反馈。

$ sa-learn --spam --showdots ~/.SPAM/cur ~/.SPAM/new
.........................

Learned from 20 message(s) (25 message(s) examined).

一旦 SpamAssassin 学习了足够多的电子邮件,它将自动开始使用贝叶斯过滤器。可以通过使用自动学习功能来保持其最新状态。

不应该在没有额外用户输入的情况下使用自动学习。这样做有两个原因。

  • SpamAssassin 偶尔会错误地检测垃圾邮件,将垃圾邮件学习为非垃圾邮件的示例。自动学习会混淆贝叶斯过滤器并降低其效果。

  • 电子邮件被自动学习的分数阈值高于作为垃圾邮件检测的阈值。换句话说,电子邮件可能被检测为垃圾邮件,但不会被自动学习。在这种情况下,SpamAssassin 的其余部分在检测边缘垃圾邮件(得分接近垃圾邮件阈值的邮件)方面做得相当不错,但贝叶斯过滤器没有被教导这些邮件。

要使用自动学习,将bayes_auto_learn标志设置为1。这可以在/etc/mail/spamassassin/local.cf文件中进行全局配置,也可以在用户的~/.spamassassin/user_prefs文件中进行覆盖。另外两个配置标志也会影响自动学习,即学习正常邮件和垃圾邮件的阈值。这些值与 SpamAssassin 对每封电子邮件的分数单位相同。

bayes_auto_learn 1
bayes_auto_learn_threshold_nonspam 0.1
bayes_auto_learn_threshold_spam 12.0

启用自动学习后,任何分配的分数低于bayes_auto_learn_threshold_nonspam的电子邮件都会被学习为正常邮件。任何分配的值大于bayes_auto_learn_threshold_spam的电子邮件都会被学习为垃圾邮件。

建议将bayes_auto_learn_threshold_nonspam阈值保持较低(接近或低于零)。这将避免垃圾邮件逃脱检测后被用作训练贝叶斯过滤器的示例的情况。保持bayes_auto_learn_threshold_spam阈值较高在某种程度上是一个选择的问题;然而,它应该高于过去被错误分类为垃圾邮件的任何邮件的分数。这可能会发生在默认垃圾邮件阈值为5的情况下,直到10的分数。因此,对垃圾邮件使用小于10的自动学习阈值可能会导致非垃圾邮件被错误地学习为垃圾邮件。如果发生这种情况,贝叶斯数据库将开始失去效力,未来的贝叶斯结果将受到影响。

SpamAssassin 将贝叶斯数据库保存在用户主目录中的.spamassassin目录中的三个文件中。通常使用的格式是伯克利 DB 格式,文件命名如下:

bayes_journal
bayes_seen
bayes_toks

bayes_journal文件用作临时存储区域。有时它不存在。这个文件通常相对较小,大约为 10 KB。bayes_seenbayes_toks文件的大小可以达到几兆字节。

其他 SpamAssassin 功能

本章只是揭示了 SpamAssassin 的能力的一部分。如果垃圾邮件对组织造成问题,SpamAssassin 将值得进一步研究。它包含的其他一些功能如下:

  • 网络测试:SpamAssassin 可以与开放中继数据库集成。(3.x 发行版包含对 30 多个数据库的测试,尽管并非所有数据库都是默认启用的。)开放中继测试不需要快速的机器或大量的 RAM,因此是相对廉价的测试。它们具有相当成功的检测率。

  • 外部内容数据库:SpamAssassin 可以与外部内容数据库集成。这些工作在一个参与网络中。所有参与者将他们收到的所有电子邮件的详细信息发送到中央服务器。如果电子邮件以前发送过多次,那么该电子邮件很可能是发送给许多用户的垃圾邮件。这些服务被设计为不发送机密数据。

  • 白名单和黑名单:SpamAssassin 包括自动白名单和黑名单,其工作方式类似于前面描述的手动列表。这在防止常规通信者的电子邮件被错误地检测为垃圾邮件方面特别有效。

  • 创建新规则:可以编写和开发新规则。创建规则并不特别困难,只需一点想象力和合适的垃圾邮件来源。系统管理员可以摆脱任何未能被默认 SpamAssassin 规则检测到的持续垃圾邮件。

  • 可定制的标头:SpamAssassin 添加到电子邮件中的标头可以定制,并且可以编写新的标头。SpamAssassin 还将尝试检测病毒和特洛伊木马软件,并将像这样的电子邮件地址包装在特殊的信封电子邮件中。

  • 多个安装:SpamAssassin 可以安装在多台机器上,为一个或多个电子邮件服务器提供服务。在非常高容量的电子邮件系统中,可以运行许多垃圾邮件服务器,每个服务器只处理垃圾邮件。这导致高吞吐量,高可用性的服务。

  • 可定制的规则分数:SpamAssassin 包括工具,可以根据组织接收到的垃圾邮件和合法电子邮件的样本来定制规则分数。这有助于提高过滤率。使用 SpamAssassin 3.0,这些工具得到了显着改进,执行此操作的过程比早期版本要少得多。

总结

在本章中,您已经了解了如何获取和安装 SpamAssassin。介绍了使用 SpamAssassin 的三种不同方法,并提出了针对特定安装选择哪种选项的建议。

流行电子邮件客户端的配置也被涵盖,即 Microsoft Outlook,Microsoft Outlook Express 和 Mozilla Thunderbird。

第九章:防病毒保护

一种普遍的观点是 Linux 不容易受到病毒攻击,那么为什么要安装防病毒解决方案?虽然 Linux 确实很少受到病毒攻击,但主要目标不是保护邮件服务器免受感染,而是减少或消除对接收者的任何风险。您的组织可能有运行 Windows 的客户端 PC,容易受到病毒攻击,或者您可能收到带有病毒的电子邮件,您可能会将其转发给客户或商业伙伴。

使用 Procmail 进行过滤的众多选项之一是删除电子邮件中的可执行附件,以保护系统免受可能的病毒攻击。这将是一个粗糙的操作,最坏的情况下,它将删除不包含病毒的文件,并可能留下其他受感染的文档,如不是可执行文件的脚本。

也可以在客户端扫描电子邮件。但在公司环境中,不一定能够依赖每个人的机器都是最新的,并且正确安装了适当的病毒检测软件。明显的解决方案是在服务器上运行一个高效的进程,以确保组织发送或接收的所有电子邮件都经过正确的病毒扫描。

针对基于 Linux 的系统有许多防病毒解决方案可用。我们选择专注于 Clam AntiVirus,通常称为 ClamAV。这是一个开源软件,并定期更新病毒数据库,以便在下载之前进行检查。

在本章中,我们将学习:

  • ClamAV 可以检测到可能包含病毒的文档类型

  • 安装和配置 ClamAV 组件以检测病毒

  • 建立程序以维护最新的防病毒数据库

  • 将 ClamAV 与 Postfix 集成,以扫描所有传入的电子邮件消息和附件

  • 通过使用包含测试病毒签名的样本文件和测试电子邮件伯恩病毒来广泛测试我们的安装

  • 将每个 ClamAV 组件添加到我们的系统启动和关闭程序中

ClamAV 简介

Clam AntiVirus 是一个面向 Linux、Windows 和 Mac OS X 的开源防病毒工具包。ClamAV 的主要设计特点是将其与邮件服务器集成,以执行附件扫描并帮助过滤已知病毒。该软件包提供了一个灵活和可扩展的多线程守护程序(clamd)、一个命令行扫描程序(clamscan)和一个通过互联网进行自动更新的工具(freshclam)。这些程序基于一个共享库libclamav,与 Clam AntiVirus 软件包一起分发,您也可以将其与自己的软件一起使用。

我们将在本章中使用的 ClamAV 版本是最新的稳定版本 0.95.2,具有最新的病毒数据库和签名,可以检测超过 580,000 种病毒、蠕虫和特洛伊木马,包括 Microsoft Office 宏病毒、移动恶意软件和其他威胁。虽然本书未涉及,但它也能够在 Linux 下进行适当安装并进行实时扫描。

支持的文档类型

ClamAV 可以提供对大多数文档类型的保护,这些文档类型可能包含或传播病毒:

  • UNIX 和类 UNIX 操作系统(如 Linux、Solaris 和 OpenBSD)使用的ELF可执行和链接格式)文件。

  • 可移植可执行文件PE)文件(32/64 位)使用 UPX、FSG、Petite、WWPack32 压缩,并使用 SUE、Yoda's Cryptor 等进行混淆。这是 Microsoft Windows 可执行文件的标准格式,也是病毒最常见的传输方式之一。

  • 许多形式的 Microsoft 文档可能包含脚本或可执行文件。ClamAV 可以处理以下文档和存档类型:

  • MS OLE2

  • MS Cabinet 文件

  • MS CHM(压缩 HTML)

  • MS SZDD

  • MS Office Word 和 Excel 文档

  • 支持其他特殊文件和格式,包括:

  • HTML

  • RTF

  • PDF

  • 使用 CryptFF 和 ScrEnc 加密的文件

  • uuencode

  • TNEF(winmail.dat)

  • ClamAV 可以处理的其他常见存档格式包括任何形式的文档:

  • RAR(2.0)

  • ZIP

  • gzip

  • bzip2

  • tar

  • BinHex

  • SIS(SymbianOS 软件包)

  • AutoIt

扫描存档还包括扫描存档中保存的支持的文档格式。

下载和安装 ClamAV

由于几乎每天都会发现病毒,因此安装最新稳定版本的 ClamAV 软件非常值得。如果您的系统已经安装了 ClamAV,则可能是基于过时的安装包进行安装。强烈建议您从 ClamAV 网站下载并安装最新版本,以确保系统对病毒具有最高级别的安全性。

添加新的系统用户和组

您将不得不为 ClamAV 系统添加一个新用户和组。

# groupadd clamav
# useradd -g clamav -s /bin/false -c "Clam AntiVirus" clamav

从软件包安装

ClamAV 有许多安装包可供选择,详细信息可以在 ClamAV 网站上找到(www.clamav.net/download/packages/packages-linux.

注意

由于许可限制,大多数二进制软件包没有内置的 RAR 支持。因此,我们建议您在任何许可问题得到解决之前从源代码安装 ClamAV。

如果您使用的是基于 Red Hat 的系统,则可以使用以下选项之一执行安装,具体取决于您安装了哪个发行版:

# yum update clamav

或者

# up2date -u clamav

如果您使用的是基于 Debian 的系统,则可以使用以下命令执行安装:

# apt-get install clamav clamav-daemon clamav-freshclam

注意

确保安装的版本是 0.95.2 或更高版本,因为与以前的版本相比有重大改进。一般来说,您应该始终安装最新的稳定版本。

从源代码安装

从原始源代码安装 ClamAV 并不是很困难,可以让您运行任何您想要的版本,而不仅仅是您的 Linux 发行版的软件包维护者选择的版本。ClamAV 源代码可以从主 ClamAV 网站的多个镜像下载(www.clamav.net/download/sources)。

要求

编译 ClamAV 需要以下元素:

  • zlib 和 zlib-devel 软件包

  • gcc 编译器套件

以下软件包是可选的,但强烈推荐:

  • bzip2 和 bzip2-devel 库

  • 解压包

构建和安装

下载并解压缩存档后,cd到目录,例如clamav-0.95.2。在开始构建和安装软件之前,值得阅读INSTALLREADME文档。

对于大多数 Linux 系统,最简单的安装方法可以通过按照这里列出的步骤来简化:

  1. 运行configure实用程序通过运行configure命令来创建正确的构建环境:
$ ./configure --sysconfdir=/etc

  1. 配置脚本完成后,可以运行make命令来构建软件可执行文件。
$ make

  1. 最后一步是以root身份将可执行文件复制到系统上的正确位置以进行操作。
# make install

在最后一步,软件安装到/usr/local目录,配置文件安装到/etc,如—sysconfdir选项所示。

在所有阶段,您都应该检查进程输出是否有任何重大错误或警告。

与所有从源代码构建的软件包一样,在完成本章的构建、安装和测试步骤后,您可能希望删除解压的存档。

快速测试

我们可以通过尝试扫描源目录中的示例测试病毒文件来验证软件是否正确安装:

注意

提供的测试病毒文件不包含真正的病毒,是无害的。它们包含专门设计用于测试目的的行业认可的病毒签名。

$ clamscan -r -l scan.txt clamav-x.yz/test

它应该在clamav-x.yz/test目录中找到一些测试文件。扫描结果将保存在scan.txt日志文件中。检查日志文件,特别注意任何警告,指示对特定文件或存档格式的支持未被编译进去。日志文件的末尾应该包含类似以下的摘要:

快速测试

编辑配置文件

安装软件后,需要编辑两个配置文件。第一个文件/etc/clamd.conf是用于实际病毒扫描软件的。这个文件的大部分重要配置选项在接下来的章节中讨论。第二个配置文件/etc/freshclam.conf将在本章后面讨论。这是我们添加自动病毒数据库更新的配置选项的地方。

clamd

您必须编辑配置文件才能使用守护进程,否则clamd将无法运行。

$ clamd

ERROR: Please edit the example config file /etc/clamd.conf.

这显示了默认配置文件的位置。该文件的格式和选项在clamd.conf(5)手册中有详细描述。config文件有很好的注释,配置应该是直观的。

检查示例配置文件

提供的示例config文件在每个重要配置值处都有注释,非常详细。以下是一些您可能希望修改的关键值:

##
## Example config file for the Clam AV daemon
## Please read the clamd.conf(5) manual before editing this file.
##
# Comment or remove the line below.
#Example

Example行将导致程序因配置错误而停止运行,并且是故意包含的,以强制您在软件正确运行之前编辑文件。在编辑完文件后,在该行的开头加上#就足以解决这个问题。

# Uncomment this option to enable logging.
# LogFile must be writable for the user running daemon.
# A full path is required.
# Default: disabled
LogFile /var/log/clamav/clamd.log

建立一个日志文件非常值得,以便您可以在运行的最初几周内检查错误并监视正确的操作。之后,您可以决定是否停止记录或保持其运行。

# Log time with each message.
# Default: disabled
LogTime yes

在日志文件中启用时间戳可以确保您可以追踪事件被记录的时间,以帮助调试问题并将事件与其他日志文件中的条目匹配。

# Path to the database directory.
# Default: hardcoded (depends on installation options)
#DatabaseDirectory /var/lib/clamav
DatabaseDirectory /usr/local/share/clamav

确保数据库目录正确配置,以确切地知道病毒签名信息存储在哪里。安装过程将创建文件main.cvd,可能还有daily.cld,作为包含病毒签名的数据库文件。

# The daemon works in a local OR a network mode. Due to security # reasons we recommend the local mode.
# Path to a local socket file the daemon will listen on.
# Default: disabled
LocalSocket /var/run/clamav/clamd.sock

使用本地模式是一个重要的配置更改,也是确保安装了 ClamAV 的系统的安全所必需的。

# This option allows you to save a process identifier of the listening
# daemon (main thread).
# Default: disabled
PidFile /var/run/clamav/clamd.pid

这对于启动和停止脚本非常有用。如前面的示例所示,ClamAV 目录必须是可写的。

# TCP address.
# By default we bind to INADDR_ANY, probably not wise.
# Enable the following to provide some degree of protection
# from the outside world.
# Default: disabled
TCPAddr 127.0.0.1

这是另一个与安全相关的配置项,以确保只有本地进程可以访问该服务。

# Execute a command when virus is found. In the command string %v # will
# be replaced by a virus name.
# Default: disabled
#VirusEvent /usr/local/bin/send_sms 123456789 "VIRUS ALERT: %v"

在某些情况下,这可能是一个有用的功能。然而,由于病毒传递的广泛范围和频率,这可能会成为一个显著的烦恼,因为消息可能会在整个夜晚或白天到达。

# Run as a selected user (clamd must be started by root).
# Default: disabled
User clamav

通过为 ClamAV 创建一个专门的用户,我们可以将文件和进程的所有权分配给这个用户 ID,并通过限制对只有这个用户 ID 的访问来提高文件的安全性。此外,当在系统上列出运行的进程时,很容易识别出 ClamAV 系统拥有的进程。

freshclam

您必须编辑配置文件,否则freshclam将无法运行。

$ freshclam

ERROR: Please edit the example config file /etc/freshclam.conf

源分发中还包括了一个freshclam配置文件的示例。如果您需要更多关于配置选项和格式的信息,您应该参考freshclam.conf(5)手册页。

最近的镜像

互联网上有许多镜像服务器可供下载最新的防病毒数据库。为了避免过载任何一个服务器,配置文件应设置为确保下载来自最近可用的服务器。包含的update实用程序利用 DNS 系统来定位基于您请求的国家代码的合适服务器。

需要修改的配置文件条目是DatabaseMirror。您还可以指定参数MaxAttempts——从服务器下载数据库的次数。

默认的数据库镜像是clamav.database.net,但您可以在配置文件中应用多个条目。配置条目应使用格式db.xx.clamav.net,其中xx代表您的正常两字母 ISO 国家代码。例如,如果您的服务器在美国,您应该将以下行添加到freshclam.conf。两字母国家代码的完整列表可在www.iana.org/cctld/cctld-whois.htm上找到。

DatabaseMirror db.us.clamav.net
DatabaseMirror db.local.clamav.net

如果由于任何原因与第一个条目的连接失败,将尝试从第二个镜像条目下载。您不应该只使用默认条目,因为这可能导致您的服务器或 IP 地址被 ClamAV 数据库管理员列入黑名单,因为过载,您可能无法获取任何更新。

检查示例配置文件

提供的示例config文件在每个重要配置值处都有注释,非常详细。以下是一些您可能希望修改的关键值:

##
## Example config file for freshclam
## Please read the freshclam.conf(5) manual before editing this file.
## This file may be optionally merged with clamd.conf.
##
# Comment or remove the line below.
#Example

确保此行已注释以允许守护程序运行。

# Path to the log file (make sure it has proper permissions)
# Default: disabled
UpdateLogFile /var/log/clamav/freshclam.log

启用日志文件对于跟踪正在应用的持续更新以及在早期测试阶段监视系统的正确操作非常有用。

# Enable verbose logging.
# Default: disabled
LogVerbose

前面的选项使得更详细的错误消息能够包含在更新日志文件中。

# Use DNS to verify virus database version. Freshclam uses DNS TXT # records to verify database and software versions. We highly # recommend enabling this option.
# Default: disabled
DNSDatabaseInfo current.cvd.clamav.net
# Uncomment the following line and replace XY with your country
# code. See http://www.iana.org/cctld/cctld-whois.htm for the full # list.
# Default: There is no default, which results in an error when running freshclam
DatabaseMirror db.us.clamav.net

这是一个重要的配置,可以减少网络流量开销,并确保您从地理位置接近的服务器获取更新。

# database.clamav.net is a round-robin record which points to our # most
# reliable mirrors. It's used as a fall back in case db.XY.clamav.net
# is not working. DO NOT TOUCH the following line unless you know
# what you are doing.
DatabaseMirror database.clamav.net

正如说明所说——不要动这一行。

# Number of database checks per day.
# Default: 12 (every two hours)
Checks 24

对于忙碌的服务器和大量流量的服务器,值得以更频繁的间隔更新病毒数据库。但是,这仅建议适用于运行 ClamAV 软件版本 0.8 或更高版本的系统。

# Run command after successful database update.
# Default: disabled
#OnUpdateExecute command
# Run command when database update process fails..
# Default: disabled
#OnErrorExecute command

为了帮助监控对配置文件的更新,您刚刚看到的选项可用于在更新正确或不正确时应用适当的操作。

文件权限

根据先前的建议,clamd将作为clamav用户运行,并且默认情况下,当启动时,freshclam会放弃权限并切换到clamav用户。因此,应使用以下命令设置在前面示例中看到的配置文件中指定的套接字、PID 和日志文件的所有权,以允许正确访问:

# mkdir /var/log/clamav /var/run/clamav
# chown clamav:clamav /var/log/clamav /var/run/clamav

freshclamclamd运行的用户可以在freshclam.confclamd.conf中更改。但是,如果更改这些参数,您应验证 ClamAV 进程是否可以访问病毒定义数据库。

安装后测试

现在我们已经安装了 ClamAV 的主要组件,我们可以验证每个组件的正确操作。

  • clamscan——命令行扫描程序

  • clamd——ClamAV 守护程序

  • freshclam——病毒定义更新程序

对于这些测试,我们需要一个病毒,或者至少一个看起来像病毒的非破坏性文件。

EICAR 测试病毒

许多防病毒研究人员已经共同努力制作了一个文件,他们(以及许多其他产品)检测到它像是病毒。就此类目的,达成一致意见简化了用户的事务。

这个测试文件被称为EICAR欧洲计算机防病毒研究所)标准防病毒测试文件。该文件本身不是病毒,它根本不包含任何程序代码,因此可以安全地传递给其他人。但是,大多数防病毒产品会对该文件做出反应,就好像它真的是一个病毒,这可能会使它成为一个相当棘手的文件,如果您或接收者已经有良好的病毒防护系统,可能会很难操作或通过电子邮件发送。

该文件是一个完全由可打印的 ASCII 字符组成的文本文件,因此可以很容易地使用常规文本编辑器创建。任何支持 EICAR 测试文件的防病毒产品都应该能够在任何以以下 68 个字符开头的文件中检测到它:

X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

在创建此文件时,您应该注意以下事实。该文件仅使用大写字母、数字和标点符号,并且不包括空格。在重新创建此文件时可能会出现一些常见错误。这些错误包括确保第三个字符是大写字母O,而不是数字零(0),并且所有的 68 个字符都在一行上,这必须是文件中的第一行。

有关 EICAR 防病毒测试文件的更多信息,请访问www.eicar.org/anti_virus_test_file.htm

测试 clamscan

我们需要运行的第一个测试是确保病毒扫描程序已安装,并且病毒定义数据库已正确配置和包含。病毒数据库是安装过程的一部分。

这样做的最简单方法是在服务器上创建 EICAR 测试文件的副本,然后运行clamscan程序。我们使用—i标志,以便只显示感染的文件。您应该得到以下输出:

测试 clamscan

请注意关于过时病毒数据库的警告。这是正常的,在进行freshclam测试期间将会得到纠正。

测试 clamd

通过使用clamdscan程序,我们可以再次扫描测试文件,但是通过指示clamd进程进行扫描。这是一个很好的测试,以确保clamd守护程序进程正在运行。

预期的输出应该看起来像以下内容:

$ clamdscan testvirus.txt

/home/ian/testvirus.txt: Eicar-Test-Signature FOUND
----------- SCAN SUMMARY -----------
Infected files: 1
Time: 0.000 sec (0 m 0 s)

如果 clamd 守护程序没有运行,可以使用# clamd命令启动它。

在运行此测试后,您还应检查clamd日志文件(在clamd.conf中配置)是否包含任何意外的错误或警告。

测试 freshclam

使用freshclam程序进行交互式操作,我们可以使用最新的定义更新病毒数据库。此测试将仅更新数据库一次。稍后我们将看到如何执行自动更新。使用以下命令(作为超级用户),我们期望得到类似以下的输出:

测试 freshclam

从输出中,我们可以看到更新过程成功下载了两个差异更新,并在第三次出现网络问题时失败。下载最新数据库与当前数据库之间的差异有助于减少网络流量和服务器负载。在这种情况下,freshclam检测到了失败,并下载了最新的每日更新,以使病毒数据库更新到具有增加病毒签名数量的状态。

现在如果再次运行clamscan测试,您会注意到不再显示过时警告。

在运行此测试后,您还应检查freshclam日志文件是否包含类似于先前代码的输出。

ClamSMTP 介绍

为了扫描通过服务器的所有电子邮件,需要在 Postfix 和 ClamAV 之间使用软件接口。我们将使用的接口是ClamSMTP。来自 ClamSMTP 网站(memberwebs.com/stef/software/clamsmtp/)的以下介绍描述了 SMTP 病毒过滤器:

ClamSMTP 是一个 SMTP 过滤器,允许您使用 ClamAV 防病毒软件检查病毒。它接受 SMTP 连接并将 SMTP 命令和响应转发到另一个 SMTP 服务器。在转发之前拦截并扫描“DATA”电子邮件正文。ClamSMTP 旨在轻量、可靠和简单,而不是拥有大量选项。它是用 C 编写的,没有主要依赖关系。

Postfix 旨在允许调用外部过滤器来处理邮件消息,并将处理后的数据返回给 Postfix 以进行进一步交付。ClamSMTP 已经被设计为直接在 Postfix 和 ClamAV 之间工作,以确保高效运行。

一些 Linux 发行版可能会维护 ClamSMTP 的软件包,可以通过相关软件包管理器安装。但是,您仍然应该完成后续的配置和集成 ClamSMTP 到 Postfix 的指示。

最新的源代码可以从memberwebs.com/stef/software/clamsmtp/下载,直接使用wget命令下载到您的 Linux 系统上。切换到适当的位置以下载和构建软件。当前版本(1.10)的命令选项将是wget <url>

$ wget http://memberwebs.com/stef/software/clamsmtp/clamsmtp-1.10.tar.gz

您应该检查网站以获取可以下载的最新版本。下载文件后,使用tar命令解压文件的内容。

$ tar xvfz clamsmtp-1.10.tar.gz

这将创建一个目录结构,其中包含当前目录下的所有相关文件。

构建和安装

在构建和安装软件之前,值得阅读INSTALLREADME文档。

对于大多数 Linux 系统,最简单的安装方法如下:

  1. 运行configure实用程序通过运行configure命令创建正确的构建环境。
$ ./configure --sysconfdir=/etc

  1. 配置脚本完成后,您可以运行make命令来构建软件可执行文件:
$ make

  1. 最后一步,作为root,是将可执行文件复制到系统上的正确位置以进行操作:
# make install

在最后一步,软件安装到/usr/local目录,配置文件安装到/etc目录。

在所有阶段,您应该检查进程输出以查找任何重要的错误或警告。

配置到 Postfix

Postfix 通过将邮件项目通过外部进程来支持邮件过滤。此操作可以在邮件排队之前或之后执行。Postfix 与clamsmtp之间的通信方式是假装clamsmtp本身是一个 SMTP 服务器。这种简单的方法提供了一种简单的方式来创建分布式架构,不同的进程可以在不同的机器上工作,以在非常繁忙的网络中分散负载。对于我们的用途,我们将假设我们只使用一台机器,所有软件都在该机器上运行。

clamsmtp过滤器接口是专门设计为在 ClamAV 和 Postfix 邮件系统之间提供接口。该过滤器被实现为用于邮件防病毒扫描的后队列过滤器。

第一个配置选项需要向 Postfix 的main.cf文件添加行:

content_filter = scan:127.0.0.1:10025
receive_override_options = no_address_mappings

content_filter指令强制 Postfix 通过名为scan的服务在端口10025上发送所有邮件。扫描服务将是我们使用clamsmtpd设置的服务。receive_override_options的指令配置 Postfix 执行no_address_mappings。这可以防止 Postfix 扩展任何电子邮件别名或组,否则将导致接收到重复的电子邮件。

第二个配置更改需要在 Postfix 的master.cf文件中进行。

# AV scan filter (used by content_filter)
scan unix - - n - 16 smtp
-o smtp_send_xforward_command=yes
-o smtp_enforce_tls=no
# For injecting mail back into postfix from the filter
127.0.0.1:10026 inet n - n - 16 smtpd
-o content_filter=
-o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
-o smtpd_helo_restrictions=
-o smtpd_client_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks_style=host
-o smtpd_authorized_xforward_hosts=127.0.0.0/8

注意

文件的格式非常重要。您应该确保在您添加的文本中,=(等号)周围没有空格或,(逗号)

“前两行实际上创建了scan服务。其余的行设置了一个服务,用于接受邮件返回到 Postfix 以进行投递。其他选项是为了防止邮件循环发生并放宽地址检查。当这些更改完成后,您需要使用以下命令让 Postfix 重新读取修改后的配置文件:”

**# postfix reload** 

“配置 clamSMTP”

“您必须创建配置文件/etc/clamsmtpd.conf,否则clamsmtpd将无法运行:”

**$ clamsmtpd
clamsmtpd: configuration file not found: /etc/clamsmtpd.conf** 

“源分发doc目录中包含了一个示例clamsmtp.conf配置文件。在clamsmtp软件正常运行之前,需要将其复制到正确的位置并进行编辑。”

**# cp clamsmtpd.conf /etc/clamsmtpd.conf** 

“该文件的格式和选项在clamsmtpd.conf(5)手册中有详细描述。”

“检查样本配置文件”

“提供的示例config文件非常详细地记录了每个重要配置值的注释。以下是您可能希望修改的一些关键值。”

# The address to send scanned mail to.
# This option is required unless TransparentProxy is enabled
**OutAddress: 127.0.0.1:10026** 

“由于我们在此配置中只使用一台机器,因此我们应该将OutAddress选项指定为127.0.0.1:10026,以匹配master.cf中指定的选项。”

# The maximum number of connection allowed at once.
# Be sure that clamd can also handle this many connections
#MaxConnections: 64
# Amount of time (in seconds) to wait on network IO
#TimeOut: 180
# Keep Alives (ie: NOOP's to server)
#KeepAlives: 0
# Send XCLIENT commands to receiving server
#XClient: off
# Address to listen on (defaults to all local addresses on port 10025)
#Listen: 0.0.0.0:10025 

“此地址与main.cf中指定的选项匹配。”

# The address clamd is listening on
**ClamAddress: /var/run/clamav/clamd.sock** 

“这应该与clamd.conf文件中的LocalSocket选项匹配。”

# A header to add to all scanned email
#Header: X-Virus-Scanned: ClamAV using ClamSMTP
# Directory for temporary files
#TempDirectory: /tmp
# What to do when we see a virus (use 'bounce' or 'pass' or 'drop'
**Action: drop** 

“丢弃消息。”

# Whether or not to keep virus files
#Quarantine: off
# Enable transparent proxy support
#TransparentProxy: off
# User to switch to
**User: clamav** 

“重要的是要确保进程以与您用于运行clamd相同的用户身份运行,否则您可能会发现每个进程在访问其他临时文件时出现问题。”

# Virus actions: There's an option to run a script every time a virus is found.
# !IMPORTANT! This can open a hole in your server's security big enough to drive
# farm vehicles through. Be sure you know what you're doing. !IMPORTANT!
#VirusAction: /path/to/some/script.sh 

“现在我们准备启动clamsmtpd进程。您应该以root身份启动此进程,并验证该进程是否存在并以clamav用户 ID 运行。”

**# clamsmtpd** 

“如果启动服务时遇到问题,请确保clamd(ClamAV 守护程序)正在运行,并且它正在监听您指定的套接字。您可以在clamd.conf中使用LocalSocketTCPSocket指令进行设置(确保只取消注释其中一行)。您还应确保ScanMail指令设置为on。”

“# 测试电子邮件过滤”

病毒,根据定义,是我们希望尽量避免接触的东西。但为了确保我们的过滤和检测过程正常运行,并且我们得到充分的保护,我们需要访问病毒进行测试。在现实世界的生产环境中使用真正的病毒进行测试,就像在办公室的垃圾桶里点火来测试烟雾探测器是否正常工作一样。这样的测试会产生有意义的结果,但伴随着令人不愉快的风险和不可接受的副作用。因此,我们需要 EICAR 测试文件,可以安全地发送邮件,并且显然不是病毒,但您的防病毒软件会对其做出反应,就像它是病毒一样。

测试邮件传播的病毒过滤

第一个测试是检查您是否仍然可以收到邮件。

$ echo "Clean mail" | sendmail $USER

您应该收到您的邮件,并在标题中添加以下行:

X-virus-scanned: ClamAV using ClamSMTP

如果您没有收到邮件,请检查系统、postfix 和 clamd 日志文件。如果需要,您还可以使用-d 4选项停止和重新启动clamsmtpd守护进程以获得额外的调试输出。

通过简单地将 EICAR 病毒作为电子邮件附件发送给自己,可以执行第二个简单的测试,以检测邮件传播的病毒。

必须将示例 EICAR 病毒文件创建为电子邮件的附件。从 Linux 命令提示符中执行以下命令链将发送一个非常简单的 uuencoded 附件副本的感染病毒文件。

$ uuencode testvirus.txt test_virus | sendmail $USER

如果一切正常并且配置正确,您不应该收到邮件,因为clamsmtp被指示丢弃该消息。消息的缺失并不意味着一切都正常,因此请检查系统或 postfix 日志文件,查看类似以下条目的内容:

Jul 8 19:38:57 ian postfix/smtp[6873]: 26E66F42CB: to=<ian@example.com>, orig_to=<ian>, relay=127.0.0.1[127.0.0.1]:10025, delay=0.1, delays=0.06/0/0.04/0, dsn=2.0.0, status=sent (250 Virus Detected; Discarded Email)

这证明了检测包含病毒的简单附件的简单情况。

当然,在现实世界中,病毒比你平均的电子邮件附件要聪明一些。需要进行彻底的测试,以确保过滤设置正确。幸运的是,有一个网站(www.gfi.com/emailsecuritytest/)可以向您发送包含 EICAR 病毒的电子邮件,以多种方式编码。目前它支持 17 个单独的测试。

彻底的电子邮件测试

该网站www.gfi.com/emailsecuritytest/要求您注册要测试的电子邮件地址,并向该地址发送确认电子邮件。在这封电子邮件中有一个链接,确认您是控制该电子邮件地址的有效用户。然后,您可以将这些 17 个病毒和电子邮件客户端利用测试中的任何一个或全部发送到这个电子邮件地址。如果任何携带病毒的电子邮件最终未被过滤到您的收件箱中,那么安装就失败了。

注意

然而,该网站上有一些测试消息并不严格是病毒,因此不会被 ClamAV 进程检测到。这是因为这些消息本身并不包含病毒,因此没有东西可以找到,因此也没有东西可以停止。

根据定义,ClamAV 只捕获恶意代码。gfi(www.gfi.com/emailsecuritytest/)网站发送这种类型的测试消息。这些消息的性质是它们有一些格式错误的 MIME 标记,可以欺骗 Outlook 客户端。杀毒软件的工作不是检测这样的消息。

自动更新病毒数据

ClamAV 由志愿者提供,用于分发软件和病毒数据库的服务器和带宽是自愿资助的。因此,重要的是要确保在维护最新数据库的更新频率和过载各种服务器之间保持平衡。

注意

ClamAV 组建议以下操作:如果您运行的是 ClamAV 0.8x 或更高版本,可以每小时检查四次数据库更新,只要您在freshclam.conf中有以下选项:DNSDatabaseInfo current.cvd.clamav.net。

如果您没有这个选项,您必须坚持每小时检查一次。

设置自动更新

ClamAV 的病毒数据库文件可以以多种方式从 ClamAV 服务器下载。这包括使用自动化或手动工具,如wget。但这不是更新的首选方式。

我们之前使用 ClamAV 安装的freshclam实用程序是执行更新的首选方法。它将定期自动下载最新的杀毒软件数据库。它可以设置为自动从cron条目或命令行工作,也可以作为守护进程运行并处理自己的调度。当freshclam由具有 root 权限的用户启动时,它会放弃特权并切换用户 ID 为clamav用户。

freshclam使用 DNS 系统的功能来获取准备下载的最新病毒数据库的详细信息以及可以从哪里获取。这可以显著减少您自己以及远程系统的负载,因为在大多数情况下,执行的唯一操作是与 DNS 服务器的检查。只有在有更新版本可用时,它才会尝试执行下载。

我们现在准备启动freshclam进程。如果您决定将其作为守护进程运行,只需执行以下命令:

# freshclam –d

然后检查进程是否正在运行,并且日志文件是否被正确更新。

另一种可用的方法是使用cron守护程序安排freshclam进程定期运行。为此,您需要为rootclamav用户的crontab文件添加以下条目:

N * * * *       /usr/local/bin/freshclam –quiet

注意

N可以是您选择的159之间的任意数字。请不要选择任何 10 的倍数,因为已经有太多服务器在使用这些时间段。

代理设置仅可通过配置文件进行配置,并且在启用HTTPProxyPassword时,freshclam将要求配置文件的所有者具有严格的只读权限。例如,

# chmod 0600 /etc/freshclam.conf

以下是代理设置的示例:

HTTPProxyServer myproxyserver.com
HTTPProxyPort 1234
HTTPProxyUsername myusername
HTTPProxyPassword mypass

自动化启动和关闭

如果您通过软件包管理器而不是从源代码安装了 ClamAV 和 ClamSMTP 组件中的任何一个或全部组件,则可能已提供必要的启动脚本。请检查是否已将必要的脚本包含在引导启动顺序中。

如果您从源代码安装了 ClamAV,则以下脚本是用于在引导时启动和停止必要守护程序的示例。根据您的发行版,文件位置可能会有所不同,您可能需要执行其他命令来为每个脚本设置运行级别。请参阅您的发行版文档。

ClamSMTP

ClamSMTP 源中提供的一个贡献脚本是用于在系统引导时自动启动和停止操作守护程序的脚本。检查脚本中的路径名是否与配置文件和安装目录中的路径名匹配,然后从 ClamSMTP 源树的根目录执行以下命令:

# cp scripts/clamsmtpd.sh /etc/init.d/clamsmtpd

复制文件后,请确保脚本具有执行权限,并且除系统根用户外,其他人无法修改它。

# ls -al /etc/init.d/clamsmtpd
-rwxr-xr-x 1 root root 756 2009-07-09 15:51 /etc/init.d/clamsmtpd

将脚本添加到系统启动中。

# update-rc.d clamsmtpd defaults

ClamAV

以下是一个示例脚本,用于在引导时启动和停止clamdfreshclamd守护程序。与以前一样,验证路径名,根据需要调整脚本,并在将其添加到系统启动之前将脚本复制到系统初始化目录。

如果freshclam作为cron作业运行,而不是作为守护程序运行,则从脚本中删除启动和停止freshclam进程的行。

#!/bin/sh
#
# Startup script for the Clam AntiVirus Daemons
#
[ -x /usr/local/sbin/clamd ] || [ -x /usr/local/bin/freshclam ] || exit 0
# See how we were called.
case "$1" in
start)
echo -n "Starting Clam AntiVirus Daemon: "
/usr/local/sbin/clamd
echo -n "Starting FreshClam Daemon: "
/usr/local/bin/freshclam -d -p /var/run/clamav/freshclam.pid
;;
stop)
echo -n "Stopping Clam AntiVirus Daemon: "
[ -f /var/run/clamav/clamd.pid ] && kill `cat /var/run/clamav/clamd.pid`
rm -f /var/run/clamav/clamd.socket
rm -f /var/run/clamav/clamd.pid
echo -n "Stopping FreshClam Daemon: "
[ -f /var/run/clamav/freshclam.pid ] && kill `cat /var/run/clamav/freshclam.pid`
rm -f /var/run/clamav/freshclam.pid
;;
*)
echo "Usage: clamav {start|stop}"
;;
esac

监视日志文件

定期监视日志文件非常重要。在这里,您将能够跟踪病毒数据库的定期更新,并确保您的系统受到尽可能多的保护。

定期更新消息应该类似于以下内容:

监视日志文件

偶尔会发布新软件并需要更新。在这种情况下,您将在日志文件中收到警告消息,例如以下内容:

监视日志文件

在出现互联网连接问题或远程文件在更新时不可用的情况下,该过程可能会记录瞬态错误消息。只要这些错误不持续存在,就无需采取任何行动。

文件消毒

常见的请求是在转发给最终接收者之前自动对文件进行消毒。在当前版本(0.95)中,ClamAV 无法对文件进行消毒。以下信息可从 ClamAV 文档中获取。

我们将在接下来的一个稳定版本中添加对 OLE2 文件的消毒支持。没有计划对其他类型的文件进行消毒。原因有很多:清除文件中的病毒在今天几乎是毫无意义的。清理后很少有任何有用的东西留下,即使有,你会相信它吗?

总结

我们现在已经安装并配置了一个非常高效的防病毒系统,用于检查所有传入电子邮件中的感染附件,并且已经显着加强了我们的系统——服务器和工作站——以防止攻击。

我们的邮件传输代理 Postfix 现在可以通过 ClamSMTP 内容过滤接口,使用 ClamAV 守护程序来过滤所有消息,以扫描和检测针对病毒签名数据库的各种威胁。通过使用freshclam,我们已经确保我们的检测数据库始终保持最新,以防范最新的威胁和任何新发布的病毒。在这场持续的战斗中,仍然需要保持不懈的警惕,以确保软件和文件始终保持完全最新。

第十章:备份您的系统

为了从重大硬件或软件故障中的灾难性服务丢失中恢复,绝对必须有备份。备份应该让您恢复软件(或者软件的配置)和其他需要重新建立服务的数据。这包括用户的邮件,系统的邮件队列以及它们的认证数据等。

本章将指导您完成必要的步骤,以防止系统故障,并在发生故障时如何从中恢复。阅读完本章后,您将了解:

  • 可用的备份选项

  • 我们需要备份哪些数据

  • 我们备份介质的存储考虑

  • 如何为邮箱执行增量和完整备份

  • 完成文件系统恢复所需的步骤

  • 如何恢复单个电子邮件

  • 如何备份我们的服务器配置

  • 设置自动备份计划

备份选项

选择最合适的备份选项总是一个权衡。您必须权衡业务停机成本,备份媒体和硬件的价格和可用性,用户数据的价值(在我们的情况下,用户的电子邮件),以及管理备份操作的人员成本。

对于我们的小型办公室电子邮件服务器,我们将提出一个简单但可靠的解决方案,使用多年来许多管理员采用的经过验证的技术和工具。

我们采取的任何备份都需要存储在备份介质上。最方便的解决方案是拥有一个备用的 Linux 机器,配备多个硬盘,与我们的电子邮件服务器相连,最好位于另一栋建筑物中。如果我们想要保护自己免受火灾等灾难性事件的影响,将备份存储在离站位置是必不可少的。

如果远程服务器不可用,另一种选择可能是连接到服务器的一些热插拔外部硬盘,甚至在紧急情况下使用 DVD 刻录机。磁带驱动器也是一个选择,但通常磁带驱动器和介质的成本大于服务器。如果可移动介质是唯一的选择,那么不要把备份堆叠在服务器顶部或桌子抽屉里,将它们移动到一个安全的离站位置。保留最新备份介质的本地副本以更快地应对紧急恢复情况可能更方便。

RAID

RAID 是“冗余磁盘阵列”的缩写。通过在 RAID 设置中使用多个磁盘,数据分布在磁盘上,但操作系统将该阵列视为单个设备。通过在整个阵列中复制和分割数据,可以显著减少磁盘故障的容忍度,提高数据可靠性,可能提高 I/O 性能。如果阵列中的硬盘故障,旧硬盘可以被更换为新硬盘。然后,RAID 控制器(无论是硬件控制器还是软件控制器)会重建数据。有关 RAID 和可用的各种配置选项的更多信息,请访问en.wikipedia.org/wiki/RAID

然而,单独使用 RAID 并不是一个备份解决方案。已删除的文件或电子邮件,无论是意外还是恶意删除,都无法恢复。RAID 无法保护用户错误或严重的硬件故障,例如使服务器烧毁的电涌甚至火灾。

使用 RAID 增加数据可用性是一件好事,但并不是适当备份和恢复策略的替代品。

镜像备份

磁盘镜像备份程序将从硬盘逐扇区地复制数据,而不考虑硬盘上的任何文件或结构。备份是硬盘的精确镜像——主引导记录,分区表和所有数据。

在发生重大硬件故障的情况下,恢复系统的步骤如下:

  1. 更换或修复故障的硬件。

  2. 引导 Linux 光盘,其中包含磁盘镜像恢复程序。

  3. 将每个磁盘的映像写入备份。

  4. 重新启动。

表面上看,这似乎是一种快速恢复服务的吸引人且快速的方法。然而,使用磁盘映像进行备份存在一些问题。

  • 通常无法将磁盘映像恢复到大小或几何不同的新磁盘上。

  • 新硬件几乎肯定会有不同的配置(主板、网络卡、磁盘控制器等),并且恢复的 Linux 内核可能没有必要的驱动程序来成功引导。

  • 磁盘映像很大。映像是磁盘的总大小,而不仅仅是存储在其中的数据大小。多个磁盘映像的空间需求很快就会累积起来。

  • 恢复单个用户文件非常麻烦。需要将磁盘映像恢复到备用磁盘上,挂载到运行中的系统上,然后找到后,复制到所需的位置。

总体系统故障很少发生,映像恢复的感觉上便利和快速通常被文件系统备份的灵活性所抵消。

文件系统备份

与映像备份不同,文件系统备份了解文件系统的结构,因此也了解硬盘上的数据。因此,只复制分配的磁盘部分,而不复制空闲空间。备份是针对文件系统中的所有文件而不是按扇区复制。

因为文件系统备份是这样完成的,这意味着可以仅复制自上次备份以来发生更改的文件,从而产生较小的后续备份文件。

在发生重大硬件故障时,恢复系统的步骤如下:

  1. 更换或修复故障的硬件。

  2. 安装 Linux 发行版。

  3. 安装本书中的邮件服务器应用程序。

  4. 应用任何补丁。

  5. 恢复应用程序配置数据备份。

  6. 恢复用户数据备份。

  7. 重新启动

与映像备份相比,这种方法需要的时间略长,涉及的步骤更多,但确实具有许多优点。

  • 替换磁盘不需要与原来的大小或几何相同。

  • 只要您的 Linux 发行版支持新硬件,就不会出现兼容性问题。

  • 备份文件大小要小得多。

  • 恢复单个文件要简单得多。

如前所述,主要系统故障并不常见。尽管完成完全恢复的步骤比映像备份更繁琐,但较小且更快的备份以及用户数据选择性恢复的优势是显著的。

为了减少意外磁盘故障的可能性,系统工具可用于监视磁盘驱动器的健康状况。有关更多信息,请访问en.wikipedia.org/wiki/S.M.A.R.T.

临时备份

文件系统备份仅备份整个文件系统,而不是单个文件或目录。偶尔,我们可能希望在我们的应用程序的重大配置更改后复制几个文件的副本。

使用标准的 Linux 工具,如tarcp,可以将重要的更改文件复制到正常备份计划的文件系统中的目录。

备份什么

备份始终伴随着一个大问题:“我们应该备份什么?”

有许多因素影响我们最终的决定。当然,我们希望备份服务器的配置,因为这对服务器的功能至关重要。但我们也希望备份用户的数据,因为这是我们业务的宝贵资产。公司是否有政策允许人们使用电子邮件进行私人通信?如果有,我们是否也应该备份这些消息?

我们应该只备份我们需要将系统恢复到正常状态所需的内容。这可以节省备份介质上的空间,并缩短执行备份和必要时恢复所需的时间。

毕竟,备份介质上的空间是有限的,因此宝贵的。备份所有用户的邮件比完全备份/tmp目录更重要。此外,我们备份的数据越少,执行备份所需的时间就越少,因此更快地将系统资源(CPU 周期、I/O 带宽)返回到它们的主要用途——处理用户的邮件。

以下是我们需要备份以获得可用系统的项目列表:

  • 系统清单

  • 服务所需的已安装软件

  • 软件配置文件

  • 用户的凭据

  • 用户的邮箱

  • 日志文件(用于计费目的和最终用户请求)

  • Postfix 邮件队列

以下各节描述了讨论的每个项目。

系统清单

在部分或完全硬件故障的情况下,记录当前系统布局是有用的。在大多数情况下,替换硬件通常会和甚至更好地满足我们当前的设置。为了恢复我们的系统,我们需要知道磁盘如何分区以及挂载点的组织方式。将我们的用户数据恢复到一个太小的磁盘上将会很困难。

使用以下命令的输出,我们将有足够的信息来重新创建我们的磁盘布局:

# fdisk -l > disk_layout.txt

该命令打印出每个磁盘的分区表,并将输出保存到文件中。

# df -h >> disk_layout.txt

该命令将每个挂载点的容量和使用情况追加到我们的文件中。

# mount >> disk_layout.txt

mount命令列出当前的挂载点,我们将其追加到文件中。

可能还有其他信息在文件/etc/fstab中,我们稍后会备份。

获取已安装软件的列表

为了恢复我们安装的软件,我们需要有当前安装的软件列表。

在 Debian 中,可以使用以下命令。文件installed_software.txt包含系统上已安装/未安装的软件的当前状态。

# dpkg --get-selections > installed_software.txt

在基于 RPM 的发行版中,这将是:

# rpm -qa > installed_software.txt

在基于 Debian 的系统中,稍后可以使用此文件安装相同的软件集。

# dpkg --set-selections < installed_software.txt
# dselect

dselect实用程序中,选择i进行“安装”,然后确认安装。

在基于 RPM 的发行版中,这将是:

# yum -y install $(cat installed_software.txt)

注意

刚才讨论的命令仅列出通过软件包管理器安装的软件。如果您从源代码安装了软件,请记下您安装的应用程序和版本。

系统配置文件

如果没有这些,服务器将无法执行预期的职责。至少需要备份的配置文件包括:

  • /etc/courier:该目录保存了 Courier-IMAP 的配置数据。

  • /etc/postfix:该目录保存了 Postfix 的配置数据。

目录树/etc包括诸如网络设置、路由等项目,我们否则需要记住。建议备份整个/etc树。

注意

如果您从非标准位置安装了带有配置文件的软件,请确保将这些配置文件包含在备份候选列表中。

认证数据

用户如果没有这些,将无法使用他们的用户名和密码组合进行身份验证。需要备份的数据取决于认证的方式,并且可能包括三个文件“/etc/passwd,/etc/shadow”和/etc/group,以及一个 MySQL 数据库(如果用户的凭据存储在该数据库中)。

用户的邮箱

这是用户的邮件存储位置。这包括整个/home及其子目录树。这是我们备份的主要内容——大量的数据。

日志文件

我们至少应该存储由 Postfix 和 Courier 生成的日志。这些将需要用于处理用户请求,比如“我的邮件去哪了?”。如果用户根据发送和/或接收的邮件量计费,我们肯定需要备份 Postfix 的日志。

由于 Postfix 和 Courier 的日志通常是由系统的syslogd守护程序写入的,我们需要检查/etc/syslog.conf文件,看看这些日志去哪里。这两个程序都使用syslog邮件设施记录它们的消息。

为了确保完全覆盖,最好备份/var/log的整个目录树。

邮件队列

根据情况,备份工作系统的 Postfix 队列可能有意义,也可能没有意义。

使用 Postfix,电子邮件消息至少会两次进入磁盘。

  • 电子邮件消息第一次到达您的驱动器是在被 Postfix 接受时;它们被写入 Postfix 的queue_directory,然后交付继续进行。

注意

病毒扫描程序或检测垃圾邮件的程序(例如clamavspamassassin)可能会产生更多的磁盘 I/O。

  • 如果是本地域的邮件,我们的服务器是这些邮件的最终目的地,在queue_directory中的寿命极短。它们进入队列,然后立即传递到用户的邮箱。这是它们第二次进入磁盘。

  • 如果是发往其他域的邮件(因为服务器充当中继),那么 Postfix 将立即联系收件人的邮件服务器,并尝试在那里传递消息。只有在出现问题的情况下,队列中才会包含大量尚未传递的电子邮件。这些问题包括:

  • content_filter很慢或者无法运行:例如clamsmtp或其他产品。

  • 远程站点存在问题:大型免费电子邮件提供商经常出现问题,因此可能无法立即接受我们的电子邮件。

在这两种情况下,延迟队列将填满尚未传递的邮件,显然在发生故障时应该备份。如果服务器非常忙,队列中可能会有相当多的延迟邮件。

Postfix 邮件队列包括目录树/var/spool/postfix及其子目录。

不需要备份的内容

我们不需要备份所有已安装的二进制文件,因为这些可以通过前面提到的“已安装软件列表”简单地重新安装。当我们需要重建系统时,这当然假定安装介质是可用的。作为注重安全的管理员,我们通过安装供应商的补丁来保持系统的最新状态。随着时间的推移,已安装和随后打补丁的软件版本将与安装介质上的版本有很大不同。如果这些更新可以通过互联网安装(例如使用 Red Hat 的 up2date 或 Debian 的 apt-get),我们就不必将它们保存在现场。

备份用户的电子邮件

我们将使用 dump 来备份包含我们邮箱的整个分区。dump 命令将文件系统上的文件复制到指定的磁盘、磁带或其他媒体。

使用它的一些原因是:

  • 它非常快(在我的测试中,网络是瓶颈)

  • 它很简单(一个命令就足够了)

  • 它可以无人值守运行(例如,作为cron作业)

  • 它不需要安装任何额外的软件

  • 它不需要图形用户界面

  • 自 1975 年左右的 AT&T UNIX 版本 6 以来,它已经非常成熟了

restore命令执行与dump相反的操作。使用dump备份的文件系统可以作为完整的文件系统进行恢复,或者可以选择性地恢复某些文件或目录。

邮件存储

我们建议将邮箱(/home)放在单独的分区上,有很多原因。

  • 文件系统维护可以独立于系统的其他部分进行(简单地卸载/home,执行fsck,然后再次挂载)。

  • 可以将该分区放在单独的磁盘或 RAID 上,从而将用户的 I/O(在该分区上)与系统的 I/O(日志、邮件队列、病毒扫描程序)分开。

最重要的是:

  • 使用dump/restore,我们可以转储整个分区。(好吧,这并不完全正确,但是只有整个分区才能轻松进行dump/restore。)

  • 包含邮箱的超额分区不会对系统写入日志文件或其他重要系统信息产生负面影响。如果所有数据(日志、邮箱、系统文件)都在一个分区上,填满这个分区将导致日志记录停止。

Courier 和 Postfix 都使用 Maildir 格式存储用户邮箱。它们将每封邮件存储为单独的文件,即使对于单个邮件,也可以轻松进行恢复操作。

使用 Maildir 格式非常容易进行备份操作。

  • “备份电子邮件”对应于“将文件备份到备份介质”。

  • “恢复电子邮件”对应于“从备份介质中恢复文件”。

  • “备份邮箱”对应于“将 Maildir 及其所有子目录备份到备份介质”。

  • “恢复邮箱”对应于“从备份介质中恢复 Maildir 及其所有子目录”。

使用 dump

基本上有两种备份数据的方法。简单的方法是在每次备份时存储所有数据。这称为完整备份。它的优点是简单性,主要缺点是需要存储在备份介质上的大量数据。这个问题通过增量备份的概念得到解决。增量备份仅保存自上次增量(或完整)备份以来的更改。

如果备份介质上的空间允许每天进行完整备份,我们可以为了简单起见这样做。这样我们只需要查看最后一个完整备份来恢复所有数据。

增量备份很简单。备份软件只需要备份自上次备份以来最近创建或更改的文件和目录。

如果空间不允许使用这种简单的解决方案,我们可以使用以下方案:

  • 每周执行一次完整备份

  • 每天进行六次增量备份

如果我们需要从头开始恢复,首先恢复最后一个完整备份,然后恢复最多六个增量备份。这样我们最多会丢失一天的邮件,这是我们在每日备份间隔下能够做到的。稍后我们将看到更复杂的增量备份策略,以减少恢复完整转储后需要的增量恢复次数。

有关dump(8)restore(8)的详细信息,请参阅系统手册页。

现在我们将看一下使用dump命令备份邮箱的实际任务。

完整转储

我们现在将执行包含用户 Maildirs 的分区的完整备份。在这个例子中,这个分区将是/dev/sdb1(我们的 SATA 磁盘的第一个分区)。因此,我们将要备份/dev/sdb1

要找出我们需要在系统上备份的分区,我们需要检查mount命令的输出:

# mount

/dev/sda1 on / type ext3 (rw,relatime,errors=remount-ro)
tmpfs on /lib/init/rw type tmpfs (rw,nosuid,mode=0755)
/proc on /proc type proc (rw,noexec,nosuid,nodev)
sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
varrun on /var/run type tmpfs (rw,nosuid,mode=0755)
varlock on /var/lock type tmpfs (rw,noexec,nosuid,nodev,mode=1777)
udev on /dev type tmpfs (rw,mode=0755)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=620)
fusectl on /sys/fs/fuse/connections type fusectl (rw)
lrm on /lib/modules/2.6.27-14-generic/volatile type tmpfs (rw,mode=755)
/dev/sdb1 on /home type ext3 (rw,relatime)

我们可以看到,/home/dev/sdb1的分区。

我们的计划是使用dump工具为这个分区创建备份。这些备份数据需要传输到我们的备份介质,可以是另一块磁盘、磁带,或者在我们的情况下是远程备份服务器上的磁盘。

有各种方法可以在网络上传输数据,其中之一是ssh。这是一种网络协议,可以促进两个设备之间的安全通信。

为了将我们的备份数据通过网络传输到备份服务器中的另一块磁盘,我们利用 Linux 的强大功能来结合dump程序和ssh协议。

dump程序的输出将被输入到gzip中以压缩转储,然后传输到ssh,然后在备份服务器上生成另一个dd程序,最终将数据写入其磁盘。

以下代码行将分区中的邮箱完全转储到远程系统上的文件。我们假设邮箱位于挂载为/home的分区/dev/sdb1上。

以 root 用户身份运行以下命令:

# dump -0 -u -b 1024 -f - /dev/sdb1 | \
gzip -c | \
ssh user@backup-host.domain.com \
dd of=/backupdirectory/$(date +%Y%m%d%H%M%S).home.dump.0.gz

该命令看起来很复杂,所以让我们逐步分解每个步骤:

  • dump -0 -u -b 1024 -f -执行分区/dev/sdb1(在我们的示例中包含/home)的级别0full)转储,使用块大小1024(以获得最佳性能),并在成功转储后更新(-u)文件/var/lib/dumpdates-u选项很重要,因为它记录了此转储的日期和时间,因此随后的增量转储可以确定自上次转储以来已更改或创建的文件。转储的输出进入指定为(-)的文件(-f),该文件表示stdout,标准输出。

  • 由于dump数据进入标准输出(stdout),我们可以将该输出管道传输到gzip以压缩转储的大小。-c选项告诉gzip将压缩输出写入stdout

  • 然后,压缩级别 0 的转储输出被传送到ssh命令,该命令与系统backup-host.domain.com建立远程连接,以user身份登录。一旦登录,远程系统执行dd命令。我们建议使用ssh提供的基于密钥的身份验证方案。这样,备份可以无人值守运行,因为没有人需要输入登录backup-host.domain.com上的user所需的密码。

  • 在远程服务器上,最后一步是使用dd命令写入输出。输出文件名由ddof选项指定。输出文件名已经构造成易于识别文件系统、转储日期和时间、转储级别以及后缀.gz以指示此转储文件已被压缩。文件名部分$(date +%Y%m%d%H%M%S)是在本地系统上执行的 shell 扩展(而不是远程系统),以输出当前日期和时间以YYYYMMDDHHMMSS格式。最终的输出文件名将类似于20090727115323.dump.0.gz

有关每个命令的更多信息,请参阅dump、gzip、ssh、dddate的系统手册页面。

输出将类似于以下内容:

完整转储

下一个示例将简单地将备份数据写入一个目录,这次没有stdout的巫术!

以下代码行将完整转储包含邮箱的分区写入一个单独的磁盘上的文件以保存备份:

# dump -0 -u -f /backupdirectory/fulldump /dev/sdb1

当然,这比通过ssh将所有数据加密并在传输过程中进行解密(这需要大量时间和 CPU 功率)快得多简单得多,但如果我们的服务器被烧毁,内置硬盘上的备份将毫无帮助。

请记住,/backupdirectory/fulldump也可以是 NFS 挂载或 SMB 挂载。这将为您提供简单命令行和远程备份的优势。因此,请确保您有远程备份。无论哪种方式都很容易。

增量转储

增量转储的执行方式与完整转储完全相同,只是我们将级别选项从 0 更改为 1、2 或 3,等等,具体取决于我们希望备份多少更改。请记住,级别数字大于 0 告诉转储复制自上次较低级别转储以来新建或修改的所有文件。这最好通过一些示例来说明。为了清晰起见,我们将简单地转储到一个文件中,但在实践中,我们通常会使用与使用gzip、ssh、dd等相同的命令序列。

假设我们的级别 0 转储是在星期日晚上进行的。第一个增量转储(级别 1,由-1选项指示)然后在星期一晚上进行,如下所示:

# dump -1 -u -f mon.dump.1 /dev/sdb1

这将保存自上次完整转储以来新建或更改的所有内容到mon.dump.1。这个转储文件将比之前的完整转储小得多,只包含星期一的更改。假设在第二天我们重复这个级别 1 转储

# dump -1 -u -f tue.dump.1 /dev/sdb1

第二个增量转储tue.dump.1将包含周一和周二所做的所有更改,因为 1 级转储将备份自 0 级转储以来发生的所有更改。为了将系统恢复到最新的备份,我们只需要恢复周二的备份。因此,有人可能认为周一的转储现在已经过时;然而,如果用户希望恢复在周一创建并在周二意外删除的文件,我们仍然需要第一次备份。

反复执行 1 级转储允许非常快速的恢复,因为只需要恢复两个转储文件,即 0 级转储和最新的 1 级转储。缺点是每个后续转储文件的大小都会增加,并且完成时间会越来越长。这种方案有时被称为差异备份。

另一种选择是使用额外的转储级别来减少每个备份文件的大小。

例如,以下一系列命令在我们的初始 0 级转储后执行了许多增量备份:

# dump -1 -u -f mon.dump.1 /dev/sdb1
# dump -2 -u -f tue.dump.2 /dev/sdb1
# dump -3 -u -f wed.dump.3 /dev/sdb1
# dump -4 -u -f thu.dump.4 /dev/sdb1

在这个例子中,每天的转储文件只包含自上一个转储以来的新文件和更改文件。从周二开始的每个转储操作将更快地完成,并且生成的文件大小比我们之前的例子要小。然而,恢复将需要更长时间。要恢复到最新的备份,我们需要恢复完整的转储,然后按顺序恢复从周一到周四的每个增量转储。

在一个小的临时文件系统上尝试这些示例可能是一个有用的练习,以便了解不同级别的转储之间的交互。可以使用以下命令检查每个转储文件:

# restore -t -f filename

对于好奇的人,文件/var/lib/dumpdates也可以在每次转储后进行检查,以验证每次转储的日期和级别。

正如本章开头所述,一切都是一种权衡,因此选择适当的备份策略涉及平衡媒体成本、人员成本和恢复时间。

到目前为止,我们所有的备份都是在挂载的磁盘上执行的,这使得验证备份是不可能的。原因是我们刚刚备份的数据不断变化。请记住,每个文件代表一封电子邮件。每当用户收到新邮件或删除旧邮件时,文件系统的状态都会发生变化。用户不断地收邮件、阅读邮件和删除邮件,即使在进行备份之前也是如此。

restore命令确实有-C选项,用于将转储与原始磁盘内容进行比较,但只有在我们转储的文件系统未挂载时才是明智的。在大多数情况下,卸载每个文件系统是不切实际的,并且会显著中断服务。

使用 restore

所有已备份的数据在使用之前都需要被恢复。

这可以通过两种方式完成,交互式或非交互式。

交互式恢复

要交互地从转储中恢复数据,我们需要将转储从备份介质复制到我们的系统上,或者在存储转储的计算机上执行文件选择以进行恢复。如果我们只提取几个文件,可以在临时目录中执行此操作,并在恢复完成后将生成的文件移动到正确的位置。对于更多的文件,例如整个用户帐户,我们可以在开始恢复之前cd到最终目的地。

对于交互式恢复,请运行以下命令:

# restore -i -f /backupdirectory/subdir/dumpfile
>

>是交互式接口的提示符,用于恢复。这是一个简陋的界面,可用的命令有限。它允许通过转储进行导航,就好像我们在实时文件系统上一样。使用lscd来显示目录内容或更改目录。输入?以获取支持的命令列表。

一旦找到要恢复的数据,输入以下命令之一:

  • > add directoryname

  • > add filename

这将把特定的“目录名”和其下所有数据,或者只是“文件名”添加到需要还原的文件集中。对于其他文件或目录重复此操作。

一旦我们添加了所有需要恢复的数据,我们发出extract命令。

交互式还原

前一个屏幕截图中显示的输出与磁带上的卷号有关。一个转储文件可能已经分割成多卷磁带集,但在处理硬盘上的转储文件时,选择卷1。通常我们会选择n以保留工作目录的当前所有权和权限。

一旦必要的文件被提取出来,执行以下命令:

> quit

对于最后一个完整转储和每个增量转储,按顺序进行,直到可用的最后一个增量转储。这样可以确保我们还原自上次完整备份以来的所有更改。

注意

如果我们要还原的数据在两次转储之间没有发生变化,我们在第二个增量转储中将找不到它。

网络上的非交互式还原

如果我们只想还原几个邮箱,手动方法是有意义的。如果我们需要完全恢复所有邮箱,我们需要使用非交互式方案。这不需要目标系统上的额外存储空间,因为转储数据正在通过网络传输。

在我们新安装的、新分区的硬盘上重新创建文件系统并挂载它:

# mke2fs -j /dev/sdb1
# mount /dev/sdb1 /home

-j选项对mke2fs/dev/sdb1上创建一个 ext3 日志文件系统,并将其挂载为/home

请注意,我们需要使用创建备份时使用的相同文件系统来重新创建数据!

让还原开始。

# cd /home
# ssh user@backup-host.domain.com \
dd if=/backupdirectory/20090601030034.home.dump.0.gz | \
gunzip -c | restore -r -f -

就像我们在网络上执行备份时一样,现在我们也要用还原来做同样的操作。

ssh user@backup-host.domain.com

前一行将以下命令作为backup-host.domain.com主机上的user执行,尽管这次使用dd命令使用if选项读取压缩的转储文件并将输出发送到stdout

dd if=/backupdirectory/20090601030034.home.dump.0.gz

输出通过网络传输并输入到gunzip中解压文件,最终传输到restore -r -f --r选项指示还原从转储文件的内容到原始位置使用原始权限和所有权重新构建整个文件系统。如果需要,可以使用restore-v选项进行详细输出。

注意

在发出restore命令之前,必须确保我们位于正确的目录中,否则可能会对现有文件系统造成严重损坏。

还原的输出看起来会像这样:

# ssh backup@nas1 dd if=backups/20090727153909.home.dump.0.gz \ | gunzip -c | restore -r -f -
restore: ./lost+found: File exists
1629153+1 records in
1629153+1 records out
834126574 bytes (834 MB) copied, 71.4752 s, 11.7 MB/s
#

关于lost+found存在的警告是正常的,可以安全地忽略。

然后,这个操作应该对每个需要将系统恢复到所需状态的增量转储文件重复进行。如果我们以错误的顺序还原增量转储,将会出现错误“增量磁带太低”或“增量磁带太高”。一旦我们收到这些错误中的一个,就无法完成完整的还原,必须从级别 0 的转储重新开始还原。

当使用-r选项执行还原命令时,它将创建文件restoresymtable。这是一个检查点文件,还原命令在还原多个转储时使用它来帮助下一个restore命令确定哪些目录或文件需要更新、创建或删除。

一旦文件系统完全还原并验证,我们应该删除restoresymtable文件。如果这个文件包含在下一个转储中,旧的restoresymtable文件可能会覆盖正在创建的文件,并阻止其他转储的还原。

作为最后一步,对新还原的文件系统执行级别 0 的dump

备份配置和日志

备份配置数据和重要日志文件有两种方法。

  • 将数据存储在我们的备份介质上:使用这种方法,我们将直接备份到我们的备份服务器。

  • 将数据添加到我们的备份计划中:这种方法将包括必要的文件作为我们用户数据备份的一部分。

任何一种情况都是同样有效的,实际上是个人偏好的问题。

作为提醒,之前我们列出了需要备份的系统重要部分。这些是:

系统的重要部分 示例命令
--- ---
系统清单 disk_layout.txt
已安装软件列表 installed_software.txt
系统配置文件 /etc
认证数据 /etc/password /etc/groups /etc/shadow
日志文件 /var/log
邮件队列 /var/spool/postfix

由于每个系统都不同,您应该确保下面给出的示例命令涵盖了所有必要的文件。

将配置和日志传输到备份介质

为了简化操作,我们只需使用tar工具创建文件和目录的存档,并将其存储在备份服务器上完整或增量转储的相同目录中:

# tar cz disk_layout.txt installed_software.txt \
/etc /var/log /var/spool/postfix | \
ssh user@backup-host.domain.com \
dd of=/backupdirectory/$(date +%Y%m%d%H%M%S).config.tar.gz

或者,我们可以在/home文件系统上创建tar存档,并将其作为我们正常备份计划的一部分进行备份。

# mkdir -p /home/config
# chmod 600 /home/config
# tar czf /home/config/$(date +%Y%m%d%H%M%S).config.tar.gz \
disk_layout.txt installed_software.txt \
/etc /var/log /var/spool/postfix

在这两种情况下,我们使用tar命令,选项为c创建存档,z压缩,f作为输出存档名称。还要注意,我们已经限制了对/home/config目录的访问,因为它包含了应该受到保护的敏感信息的存档。

有关tar的更多信息,请参阅系统手册页。

恢复配置

根据之前使用的方法,恢复我们的配置和日志文件相对简单。我们可以从备份服务器复制所需的存档,或者直接使用/home/config中的存档。在任何情况下,解压存档都是使用以下命令执行的:

# mkdir tmpdir
# cd tmpdir
# tar xzf xxxxx.config.tar.gz

请注意,在扩展存档之前,我们已经创建并移动到了一个临时目录。如果我们执行tar命令时当前目录是/,我们将覆盖/etc、/var/log/var/spool/postfix中的所有文件,可能会产生不良后果。

现在我们已经解压了存档,我们可以比较并复制我们需要恢复的文件。

自动化备份

现在我们已经看到如何备份我们的系统,我们需要建立一个自动化的程序来消除手动调用dump的繁琐。

转储的手册页确实提供了一些关于多久进行备份以及在哪个级别减少恢复时间的指导。

在发生灾难性的磁盘事件时,通过错开增量转储的时间,可以将将所有必要的备份磁带或文件恢复到磁盘所需的时间最小化。错开增量转储的有效方法以最小化磁带数量如下:

始终从 0 级备份开始。这应该在固定的时间间隔内进行,比如每个月或每两个月一次,并且使用一组永久保存的新磁带

在进行 0 级备份后,每天对活动文件系统进行转储,使用修改后的汉诺塔算法,转储级别的顺序为:3 2 5 4 7 6 9 8 9 9 . . . 对于每天的转储,应该可以使用固定数量的磁带,每周使用一次。每周进行 1 级转储,并且每天的汉诺塔序列从 3 开始重复。对于每周转储,每个转储的文件系统也使用一组固定的磁带,也是循环使用的

几个月后,每天和每周的磁带应该被从转储周期中移出,并带入新的磁带

这一系列转储看起来相当奇怪,需要更多的解释。通过这个过程,我们将说明如何最小化转储的大小并减少恢复所需的数量。

一旦进行了级别 3 的转储,恢复只是恢复转储 0 和 3。第二天后,级别 2 的转储将备份自上次较低级别的转储以来发生的所有更改,即级别 0。这使级别 3 的转储无效。然后,级别 5 的转储将备份自级别 2 转储以来的更改。随着序列的进行,使用更高级别和更低级别来跳过天数,以前的转储变得无效,不再需要完成完全恢复。每个转储仍应保留,以防我们需要在以后的某个时间恢复意外删除的单个文件。

到了周末,执行级别 1 的转储,使之前几周的转储级别都变得过时,然后在月底重新开始,进行新的级别 0 的转储。

以下表格说明了每天采取的转储级别以及恢复数据到最新版本所需的次数:

日期 转储级别 需要的恢复级别
1 0 0
2, 9, 16, 23, 30 3 0, 1*, 3
3, 10, 17, 24, 31 2 0, 1*, 2
4, 11, 18, 25 5 0, 1*, 2, 5
5, 12, 19, 26 4 0, 1*, 2, 4
6, 13, 20, 27 7 0, 1*, 2, 4, 7
7, 14, 21, 28 6 0, 1*, 2, 4, 6
8, 15, 22, 29 1 0,1

注意

在第一周,级别 1 的转储(标有*)在恢复过程中不是必需的。从第八天开始,级别 1 的转储总是必需的。

从表中我们可以看到,即使在月底,只需要几次转储就可以恢复我们的数据,而不是每天创建增量转储时需要的几十次。

通过我们的月度备份计划,一个简单的脚本和添加一些条目到cron,将完成自动备份过程。

备份脚本

以下示例 bash 脚本将存档我们的系统配置和日志文件,并将请求的文件系统转储到远程备份服务器。这只是一个示例脚本,应根据您的需求进行修改。为了清晰起见,任何错误检查和日志记录都已省略。

#!/bin/sh
# The name of the dump, e.g. home or users
NAME=$1
# The partition to dump, e.g. /dev/sdb1
DEVICE=$2
# The dump level, e.g. 0 or 3 etc.
LEVEL=$3
# ssh login name and host
#
USERNAME=user
BACKUPHOST=backuphost
# Take a system inventory.
#
/sbin/fdisk -l > /tmp/disk_layout.txt
/bin/df -h >> /tmp/disk_layout.txt
/bin/mount >> /tmp/disk_layout.txt
# Installed software (Debian)
#
/usr/bin/dpkg --get-selections > /tmp/installed_software.txt
# Archive our system configuration and logs
#
/bin/tar cz /tmp/disk_layout.txt /tmp/installed_software.txt \
/etc /var/log /var/spool/postfix | \
/usr/bin/ssh $USERNAME@$BACKUPHOST \
/bin/dd of=$(date +%Y%m%d%H%M%S).config.tar.gz
# Perform the dump to the remote backup server.
#
/usr/sbin/dump -u -$LEVEL -f - $DEVICE | \
/bin/gzip -c | ssh $USERNAME@$BACKUPHOST \
/bin/dd $(date +%Y%m%d%H%M%S).$NAME.dump.$LEVEL.gz"
# Remove temporary files.
#
rm -f /tmp/disk_layout.txt /tmp/installed_software.txt
exit 0

该脚本需要 3 个参数,转储的名称,要转储的分区和转储级别。

典型的用法如下:

# remote-dump.sh home /home 0

上一个脚本每次运行都会存档/etc。您可能希望将这些命令移到一个单独的脚本中,每周或每月执行此任务。如果脚本将用于转储其他文件系统,则这一点尤为重要。

脚本不会删除以前几个月的旧转储文件,这可能会填满我们的备份服务器,从而阻止未来的备份。最好制定程序,根据组织的数据保留政策,删除或存档旧的转储文件。

添加 crontab 条目

每晚自动运行我们的备份脚本只是使用备份计划表中的条目并执行脚本来转储正确的分区。以下示例crontab条目每晚在 02:10 执行我们的脚本来转储/home。每个月的第一天,执行级别 0 的转储,然后每隔七天执行一次每周级别 1 的转储。其他条目实现了修改后的“汉诺塔”算法。

10 02 1 * * /bin/remote-dump.sh home /home 0
10 02 2,9,16,23,30 * * /bin/remote-dump.sh home /home 3
10 02 3,10,17,24,31 * * /bin/remote-dump.sh home /home 2
10 02 4,11,18,25 * * /bin/remote-dump.sh home /home 5
10 02 5,12,19,26 * * /bin/remote-dump.sh home /home 4
10 02 6,13,20,27 * * /bin/remote-dump.sh home /home 7
10 02 7,14,21,28 * * /bin/remote-dump.sh home /home 6
10 02 8,15,22,29 * * /bin/remote-dump.sh home /home 1

一旦我们的自动备份程序就位,我们需要密切关注任何错误,并验证远程服务器上转储文件的完整性。

验证恢复程序

即使做了最好的计划,事情也会出错,而且总是在最不方便的时候。

采取积极的灾难恢复方法,进行良好的规划和实践,将在太迟之前就能发现任何问题。验证系统备份的完整性只有通过恢复它们并检查恢复的系统是否完全可操作才是真正可能的。

您应该问自己一些问题,比如,“如果远程服务器出现故障,需要采取哪些措施?”您是先修复备份服务器还是切换到另一台服务器以减小没有备份的时间窗口?如果邮件服务器出现故障,您是否熟悉恢复程序?例如,是否可以在短时间内获得替换硬件,比如在星期天?

有许多管理员勤奋地进行备份,却发现在需要时备份无用,因为磁带驱动器错误或备份脚本中的轻微语法错误覆盖了有效的转储文件,导致数据损坏。

为自己构想情景,并在备用硬件上练习完全裸金属恢复,或者恢复单个用户的电子邮件。

验证恢复程序是否有效将使您相信您可以从数据丢失中恢复过来。

总结

在本章中,我们描述了如何备份电子邮件和邮件服务器配置。我们从介绍应该考虑备份的内容开始,最后使用自动完全和增量备份的复杂解决方案结束。

特别是,我们描述了使用dump命令的过程,以及如何复制我们的数据。我们使用restore命令来恢复完整的文件系统和选择性文件。

本章指导您备份和恢复服务器宝贵数据的过程。它展示了为什么要备份,备份哪些数据,不同的备份和恢复方法,以及进行自动每日备份的程序。

在实施本章中向您展示的所有程序之后,您将睡得更香甜,而且无论如何,您的用户都会喜欢系统所能提供的范围和功能。

posted @ 2024-05-16 19:08  绝不原创的飞龙  阅读(20)  评论(0编辑  收藏  举报