LPI Linux 认证/邮件流量管理
权重:3
描述: 候选人应该能够实施客户端电子邮件管理软件来过滤、排序和监控传入的用户邮件。
- 关键知识领域
- procmail 配置文件、工具和实用程序
- 在服务器端和客户端上使用 procmail
- 以下是使用的文件、术语和实用程序的部分列表
- ~/.procmail
- /etc/procmailrc
- procmail
procmail 是由德国的 Stephen van den Berg 编写的邮件处理实用程序语言。本文为中级 Unix 用户提供了一些关于如何使用 procmail 的背景信息。作为一种“小型”语言(使用学术术语),procmail 缺少传统通用语言的许多特性和结构。它没有“while”或“for”循环。但是,它“了解”很多关于 Unix 邮件传递约定和文件/目录权限的信息——特别是关于文件锁定的信息。尽管可以使用大多数 Unix 系统上安装的工具,用任何编程语言编写自定义邮件过滤脚本都是可能的——我们将展示 procmail 是系统管理员和高级 Unix 用户的首选工具。
Unix 邮件系统由 MTA(邮件传输代理,如 sendmail、smail、qmail mmdf 等)、MDA(传递代理,如 sendmail、deliver 和 procmail)和 MUA(用户代理,如 elm、pine、/bin/mail、mh、Eudora 和 Pegasus)组成。
在互联网上的大多数 Unix 系统上,sendmail 被用作集成的传输和传递代理。sendmail 和兼容的 MTA 能够通过两种机制之一(别名和 .forward)*通过*自定义过滤器或程序调度邮件。
别名机制使用单个文件(通常为 /etc/aliases 或 /usr/lib/aliases)来重定向邮件。此文件由系统管理员拥有和维护。因此,您(作为用户)无法修改它。“.forward”机制是分散的。系统上的每个用户都可以在其主目录中创建一个名为 .forward 的文件,该文件包含一个地址、一个文件名或一个程序(过滤器)。通常,该文件*必须*由用户或 root 拥有,并且*不能*被其他用户“写入”(良好的 sendmail 版本出于安全原因会检查这些因素)。
还可以使用某些版本的 sendmail 指定多个地址、程序或文件,这些地址、程序或文件用逗号分隔。但是,我们将跳过这方面的细节。邮件流量管理
您可以通过任何任意程序转发您的邮件,该程序的 .forward 包含如下所示的行
"|$HOME/bin/your.program -and some arguments"
请注意引号和“管道”字符。它们是必需的。“Your.program”可以是 Bourne shell 脚本、awk 或 perl 脚本、已编译的 C 程序或您想要编写的任何其他类型的过滤器。
但是,“your.program”必须编写为处理有关 sendmail 如何将消息(标题和正文)传递给它的众多详细信息,如何将返回值返回给 sendmail,如何处理文件锁定(如果邮件在“your.program”仍在处理一个邮件时到达,等等)。这就是 procmail 为我们提供的功能。
到目前为止,我们看到的是适用于所有 sendmail 兼容 MTA/MDA 的一般信息。邮件流量管理
因此,为了确保邮件被传递给 procmail 进行处理,第一步是创建 .forward 文件。(在您执行任何 procmail 本身的配置之前,执行此操作是安全的——假设软件包的二进制文件已安装)。以下是 procmail 手册页中粘贴的规范示例
"|IFS=' '&&exec /usr/local/bin/procmail -f-||exit 75 #YOUR_USERNAME"
如果您执行此操作且没有其他操作,您的邮件基本上不会受到影响。procmail 将只查找其默认配方文件 (.procmailrc),如果找不到,它将对每条消息执行其默认操作。换句话说,它会将新邮件追加到您的正常邮件池文件中。
您可以将 procmail 作为 sendmail/postfix 中的本地传递代理在系统范围内进行设置。完成此操作后,您可以跳过关于使用 .forward 文件的整个部分——或者您也可以继续使用它。例如,在 sendmail 中,可以通过以下方式更改 sendmail.mc 来完成此操作
MAILER_DEFINITIONS dnl # MAILER(`local')dnl <- comment this one out with dnl MAILER(`procmail')dnl MAILER(`smtp')dnl
在 postfix 中,可以根据postfix 常见问题解答来完成此操作。基本上,只需使用以下内容编辑 /etc/postfix/main.cf 并重新加载 postfix 即可。
/etc/postfix/mail.cf: mailbox_command = /path/to/procmail
无论哪种情况,自动化邮件处理的下一步是在您的主目录中创建一个 .procmailrc 文件。您实际上可以将此文件命名为您想要的任何名称——但随后您必须将名称显式地放入 .forward 文件中(正好在“||”运算符之前)。几乎每个人都只使用默认值。
到目前为止,我们讨论的只是所有内容如何路由到 procmail——这主要涉及 sendmail 和 Bourne shell 的语法。几乎所有 sendmail 都配置为使用 /bin/sh(Bourne shell)来解释别名和 .forward“管道”。
因此,这是一个非常简单的 .procmailrc 文件
:0c: $HOME/mail.backup
这只是将所有传入邮件的额外副本追加到您主目录中名为“mail.backup”的文件中。请注意,为您预设了许多环境变量。有人建议您应该显式设置 SHELL=/bin/sh(或系统上可用的最接近 Bourne Shell 的派生版本)。我从未担心过这个问题,因为我大多数系统上使用的 shell 已经与 Bourne 兼容。
但是,csh 和其他 shell 用户应该注意,我见过的所有 procmail 配方示例都使用 Bourne 语法。
:0 行标志着“配方”(过程、子句,无论什么)的开始。:0 后面可以跟随许多“标志”。组合这些标志的方式不胜枚举。我们在本示例中使用的标志是“c”,表示“复制”。
此行上的第二个冒号标志着标志的结束和锁定文件名称的开始。由于没有给出名称,procmail 将自动选择一个。
这有点复杂。邮件可能会成批到达。如果在您的脚本仍在忙于处理上一条邮件时到达了一条新邮件,您将拥有多个 sendmail 进程。每个进程都将处理一条邮件。这本身并不是问题。但是,如果这两个进程可能尝试同时写入一个文件,它们很可能会以不可预测的方式混淆(结果将不是格式正确的邮件文件夹)。
因此,我们提示 procmail 它将需要检查并创建锁定文件。在本例中,我们不关心锁定文件的名称是什么(因为我们不会让*其他*程序写入备份文件)。因此,我们将最后一个字段(冒号之后)留空。然后,procmail 将选择它自己的锁定文件名。
如果我们从配方标题行中删除 :(完全省略最后一个字段),则不使用锁定文件。当我们打算仅从配方中的文件读取时,或者当我们打算仅按任意顺序(如日志文件条目)向文件写入简短的单行条目时,这适用。procmail 的工作方式是
它从 sendmail(或某些 sendmail 兼容的 MTA/MDA)接收一条消息。由于新邮件可能到达的速度快于处理速度,因此当前可能正在运行多个 procmail 处理。它打开其配方文件(默认为 .procmailrc 或在其命令行上指定),并从第一个到最后一个解析每个配方,直到消息被“传递”(或“处理”)。
任何配方都可以是消息的“处理”或“传递”。一旦消息被“传递”,procmail 就会关闭其文件、删除其锁并退出。
如果 procmail 到达其 rc 文件(以及所有包含的文件)的末尾而没有“处理”消息,则消息将追加到您的邮件池文件(对您和所有“邮件用户代理”(如 Eudora、elm 等)来说,这看起来像正常的传递)。
这解释了为什么 procmail 在您*没有*.procmailrc 时非常宽容。它只是将您的邮件传递到邮件池,因为它已到达所有配方的末尾(没有配方)。“c”标志会导致配方对消息的“副本”进行操作——这意味着该配方采取的任何操作都不被视为消息的“处理”。
如果没有“c”标志,此配方将捕获所有传入消息,并且所有邮件都将最终存储在 mail.backup 中。其中没有任何邮件会进入您的邮件池文件,并且不会解析其他任何配方。
此示例配方中的下一行只是一个文件名。与 sendmail 的别名和 .forward 文件一样,procmail 识别三种对任何消息的处理。您可以将其追加到文件、转发到其他邮件地址或通过程序对其进行过滤。
实际上,procmail 处理了一种特殊的“传递”或“处理”形式。如果您为它提供目录名(而不是文件名),它会将消息作为单独的文件添加到该目录中。该文件的名称将基于您不必担心的几个相当复杂的因素,除非您使用 Rand MH 系统或其他一些相对模糊和“奇特”的邮件代理。
procmail 配方通常由三个部分组成——开始行(带有某些标志的:0)、一些条件(以“*”(星号)字符开头的行)和一个“传递”行,该行可以是文件/目录名称或以“!”(感叹号)字符或“|”(管道)字符开头的行。
以下是一个示例
:0 * ^From.*[email protected] /dev/null
这是一个简单的示例,不包含任何标志、一个条件和一个简单的文件传递。它只是丢弃来自“我不喜欢的人”的任何邮件。(Unix 下的 /dev/null 是一个“位桶”——一个用于丢弃不需要的输出的无底洞,DOS 有类似的概念,但它并不像 Unix 下的那么方便)。
以下是一个更复杂的示例
:0 * !^FROM_DAEMON * !^FROM_MAILER * !^X-Loop: [email protected] | $HOME/bin/my.script
它由一组否定条件组成(请注意,所有条件都以“!”字符开头)。这意味着:对于任何不是来自“守护程序”(某些自动化进程)且不是来自“邮件发送程序”(某些其他自动化进程)并且不包含任何形式为“X-Loop: myadd…”的头行的邮件,将其通过我的 bin 目录中的脚本进行处理。
我可以直接将脚本放在 rc 文件中(大多数 procmail 用户大部分时间都是这么做的)。这个脚本可能会对邮件做任何事情。在这种情况下——无论它做什么,最好是好的,因为 procmail 方式会认为任何这样的邮件都被投递了,并且此后的任何规则只会收到来自 DAEMON、MAILER 和邮件头中具有该特定 X-Loop: 行的邮件。
这两个特定的 FROM_ 条件实际上是“特殊的”。它们由 procmail 预设,实际上指的是几个相当复杂的正则表达式,这些表达式经过精心设计以匹配大多数来自守护进程和邮件发送器的邮件头中发现的内容。
X-Loop: 行是一个正常的 procmail 条件。在 RFC822 文档(定义了电子邮件头在互联网上应是什么样子)中,任何以 X- 开头的行都是“自定义”头。这意味着任何想要添加的邮件程序都可以添加几乎任何 X- 行。
一个常见的 procmail 习惯用法是在我们发送出的任何邮件的标题中添加 X-Loop: 行——并在发送任何内容之前检查我们自己的 X-Loop: 行。这是为了防止“邮件循环”——我们的邮件被转发或“退回”给我们,而我们无限期地回复它。
因此,这是一个关于如何使用 procmail 自动回复特定人员的邮件的详细示例。我们从规则头开始。
:0
…然后我们添加一个条件(邮件似乎来自相关人员)
* ^[email protected]
FROM 是 procmail 的一个“魔法”值——它检查 from、resent-by 和类似的标题行。您也可以使用 ^From:——它只会匹配以字符串“From:”开头的标题行。
^(打嗝或更专业的“脱字符号”) 是一个“正则表达式锚”(一个技术术语,意思是“它指定模式必须在何处才能匹配”。有一整本书讲正则表达式(O'Reilly & Associates)。“正则表达式”渗透到许多 Unix 实用程序、脚本语言和其他程序中。每个应用程序的“正则表达式”语法略有不同。但是,'grep' 或 'egrep' 的手册页是了解更多信息的绝佳场所。
在这种情况下,打嗝表示模式必须出现在一行的开头(这在 grep、ed/sed、awk 和其他上下文中是它的常用含义)。
…我们添加几个条件以避免循环并避免回复自动系统
* !^FROM_DAEMON * !^FROM_MAILER
(这两个是更多“魔法”值。手册页显示分配给这些关键字的确切正则表达式——如果您好奇或需要调整类似于这两个之一的特殊条件)。
…以及另一个防止棘手循环的条件
* !^X-Loop: [email protected]
(所有这些模式都以“感叹号”(感叹号)开头,因为条件是标题的*任何*行都不以这些模式中的任何一个开头。在这种情况下(以及大多数其他正则表达式上下文)中的“感叹号”会“否定”或“反转”模式的含义)。
…现在我们添加一个“处理方式”——自动回复。
| (formail -rk \ -A "X-Loop: [email protected]" \ -A "Precedence: junk"; \ echo "Please don't send me any more mail";\ echo "This is an automated response";\ echo "I'll never see your message";\ echo "So, GO AWAY" ) | $SENDMAIL -t -oi
这非常复杂——但以下是它的工作原理:| 字符告诉 procmail 它应该启动一个程序并将消息馈送到它。左括号是 Bourne shell 结构,它以一种将所有输出组合成一个“流”的方式对一组命令进行分组。
‘formail’ 命令是一个方便的程序,包含在 procmail 包中。它根据其命令行开关和输入“格式化”邮件头。-rk 告诉 'formail' 格式化“回复”并“保留”邮件正文。使用这些开关,formail 期望输入标题和正文。
-A 参数告诉 formail “添加”下一个参数作为标题行。提供给 -A 开关的参数必须用引号括起来,以便 shell 将整个字符串(包括空格)视为单个参数。每行末尾的反斜杠告诉 procmail 邮件将下一行视为此行的一部分。因此,所有以反斜杠结尾的行都被作为一行长行传递给 shell。
这个“尾随反斜杠”或“行延续”字符是许多编程语言和配置文件格式中常见的 Unix 习惯用法。分号告诉 shell 执行另一个命令——它们允许在同一命令行上发出多个命令。
每个 echo 命令都应该相当容易理解。如果我们想的话,我们可以使用 'cat' 命令并将我们的文本放入一个文件中。我们也可以在这里调用其他程序——比如 'fortune' 或 'date',它们的输出将与其余部分结合起来)。
现在我们到了右括号。这标志着我们组合的命令块的结束。所有这些的输出都馈送到下一个管道——它启动本地 sendmail 副本(请注意,这是 procmail 为我们预设的另一个变量)。
sendmail 上的 -t 开关告诉它从其输入的标题('formail -r' 放置的位置)获取“To:”地址,-oi 开关启用 sendmail 的“选项”以“忽略”仅由“点”组成的行(不要担心这方面的细节)。
理解 procmail 的大部分困难与 procmail 本身无关。正则表达式的复杂性(那些奇怪的 '*'——条件行)以及 shell 引号和命令语法,以及如何格式化 sendmail 可以接受的回复标题('formail' 和 'sendmail' 内容)是需要如此多解释的部分。
有关 procmail 的更多信息,请参阅 Era Eriksson 的“迷你常见问题解答”。在http://www.iki.fi/~era/procmail/mini-faq.html或几个镜像之一,如http://www.zer0.org/procmail/mini-faq.htmlhttp://www.dcs.ed.ac.uk/home/procmail/faq/mini-faq.html
关键词、文件和实用程序:Procmail .procmailrc