A-A+

postfix的本地投递与pop/imap

2013年02月28日 综合技术 评论 17 条 阅读 1,476 次

smtp协议要求收下邮件的MTA,必须负责将邮件送到最终目的地--可能是本地系统的邮箱,也可能同一网络上的其他主机,这段工作过程称为投递(delivery)。
本 章探讨postfix将本地邮件投递到邮箱的过程以及pop/imap serer如何访问邮箱。许多用户常误以为收信与寄信是同一套软件在工作,其实不然。让用户能从邮箱取走邮件的协议是IMAP与pop,而postfix 的职责是将收下的邮件放入邮箱。也就是说,pop/imap服务是由postfix之外的软件所提供的。提供pop/imap服务的软件很多,包括 popper、wu imap kit等。
在我们讨论本地邮件的投递过程之前,先让我们理清“本地”、“外地”、“虚拟”这三种邮件的定义以及相关的MDA.

postfix的投递代理程序

postfix依据来信地址来决定是否要收下邮件以及如何选择适当的MDA来执行后续的投递任务。postfix会收下三种网域的邮件,分别是本地(local)、转发(relay)以及虚拟(virtual),它们的定义与相关的MDA,如下:

本地邮件
若邮件终点站是mydestination参数所列出的网域之一,postfix就视其为本地邮件,由local MDA(或是你指定的其他程序)执行投递任务。本地邮件的收件者必须拥有本地系统(postfix server本身所在的主机)的用户账户,或是其名称被定义在别名文件(传统上是/etc/aliases)。本地邮件会被投递到系统的邮件存储目录(通 常是/var/spool/mail/),或个人主目录下的邮件文件(~/mail/)。

转发邮件
若邮件终点站是relay_domains参数所列出的网域之一,postfix就视其为转发邮件,由relay MDA来执行投递任务。一般而言,只有在postfix被当成局域网络的邮件网关使用,而同网络上还有其他网域的邮件服务器时,才会让postfix收下 转发邮件。也就是说,所谓的“转发”,通常是指同局域网络上的其他主机,而relay其实是smtp MDA的翻版,只不过被刻意设计成特别适合传信给局域网络上主机而已。

虚拟网域邮件
一台邮件服务器通常只服务一个范围网域 (canonical domain);如果要同时服务多个网域,则额外的网域称为虚拟网域(virtual domain)。虚拟网域的邮件由virtual MDA负责投递。依据用户是否有服务器的系统账号,虚拟网域的邮件还可分成“虚拟邮箱”与“虚拟别名”两种。虚拟邮箱的收件人没有系统账户,而且每一个虚 拟邮箱网域都有自己的邮箱目录(mail spool),所有虚拟邮箱网域都必须列于virtual_mailbox_domains参数。另一方面,虚拟别名网域的收件人可以拥有本地或非本地系 统账户,postfix会改写这类邮件的收件地址,然后交给smtp MDA递送出去(如果新地址是非本地网域),或重新回到收件队列(如果新地址是本地网域)。

邮箱格式

当postfix投 递本地邮件时,邮件内容会被传送到postfix系统上的适当邮箱。最常见的两种邮箱格式,分别是传统的mbox以及较新的 maildir。两者都是使用一般的文件来储存邮件内容,差别在于文件内部的组织安排有所不同。在postfix中,当你设定任何邮件文件或目录参数时, 如果在路径末端加注一个/符号,表示你想使用maildir格式的邮箱。

mbox格式

传统上,unix系统将同一位用户 的所有邮件都塞在同一个文件里,像这样的邮箱格式通常称为mbox。邮箱文件里的每一封邮件,其第一行的前五个字符必定是“from ”。习惯上,为了方便表示,我们通常将它写成“from_“,以下划线字符强调空格的存在请勿将mbox文件内用来分隔邮件的”from “字样与邮件标头里的”from:“字段混为一谈。邮件在mbox文件里的最后一行必定为空格。因此,一行空格接着一个from_字样,就可视为下一封信 的开始。
postfix将邮件写入mbox文件之前,会先使用信封上的寄件人地址与当时的日期创建好from_文本行,并将该行字符串写到 mbox文件的末端,然后才开始填入邮件内容。如果postfix发现邮件内容本身有任何以“from”开头的文本行,它会在该文本行的开头加一个 >符号,避免该文本行被误以为下一封信的开头。
当pop/imap server读取mbox文件内的邮件时,第一步是扫描文件内容,找出代表邮件开头的from文本行。在读取邮件内容时,如果遇到下一个from_文本行 (或文件结尾),就可断定当前的邮件已经读完了。有些pop/imap server会主动恢复">from"的原状,但有些不会。
由于 postfix和pop/imap server有可能会同时访问同一个mbox文件,所有它们必须使用“文件锁定机制”(file locking)来确保访问权。在local投递本地邮件之前,必须先将该文件加锁,然后才能将邮件内容写入mbox文件。postfix支持多种锁定机 制,视系统平台而定。利用postconf -l命令可查看你的系统提供了哪些锁定机制可供postfix使用:
postconf -l
如果想知道postfix在你系统上列出的各种锁定机制的详细信息,请把锁定机制的名称告诉man:
man folck
如 果你的系统平台支持flock和fcntl,应该就可以找到它们的在线说明文件,因为这两者都是操作系统或函数库提供的功能,而任何系统平台都支持的 dotlock机制,很可能找不到说明文件,因为dotlock只是程序之间一种不成文协议,不需要额外的函数库。dotlock的原理很简单,举个例子 就可以说明清楚。假设postfix要访问user1邮件文件,它必须先检查该文件的同目录下是否存在一个.user1.lock文件,如果存在,表示 user1邮件文件当前被另一个进程占用;如果.user1.lock文件步存在,postfix就自己产生一个,让其他进程知道user1文件当前正被 占用。在postfix关闭user1文件之后,要主动移除.user1.lock文件,让其他进程可以使用user1邮件文件。dotlock锁定机制 的缺点是它没有强制性(任何进程都可以不检查,user1.lock是否存在而径直访问user1文件),而且效率不佳。
通常你可以不必担心锁定机制的细节,也不必理会系统支持哪些类型的锁定机制,因为postfix能自动做出最佳选择。

maildir格式

maildir 邮箱格式不同于mbox之处,在于它使用目录结构来存储邮件。maildir的设计原意为了解决mbox格式的可能性与文件锁定问题。例如,如果在邮件内 容还没完全写入mbox文件之前,系统就死机了,这时候可能只有部分内容在邮箱里。当系统恢复运行,MDA将邮件写入邮箱时,新的内容会接在前次残缺内容 的后面,因而造成问题。
mbox格式的另一个缺点,是发生在pop/imap server与smtp server试图同时开启同一个邮箱时。如果双方没有使用相同的锁定机制,邮箱文件可能因此受损。先前说过,保护文件的锁定机制有好几种,但是并非所有邮 件程序都使用锁定机制。但如果使用maildir格式,则可以不使用文件保护锁,因为每一封邮件都是存放在单独的一个文件里。因此,不用的邮件程序,不可 能同时访问同一个文件。
一个maildir风格的目录,其下有三个子目录:tmp/、new/以及cur/。这些子目录与它们的上层目录必须位于同一个文件系统,习惯上,它们应该放在用户的主目录的邮件目录下
在new/目录下的邮件文件,是MDA已经送达但是尚未被用户阅读的信,文件本身的修改时间,就是收下邮件的时间。邮件文件通常包含RFC 2822格式的邮件,而且不需要“from_"。
用户看过邮件之后,邮件文件会被转移到cur/目录。tmp/目录供MDA将邮件内容存储成文件,在确定全部内柔都写入文件之后,邮件文件会被搬到new/目录。

应该选择mbox还是maildir?

这 个问题没有简单的答案。哪一种邮箱格式最适合你,取决于许多因素。mbox格式的好处是几乎全世界都支持,但也正是因为它有文件锁定问题,而导致了 maildir格式的出现。而maildir格式在规模适合性方面也颇受质疑,因为某些文件系统可能无法应付太多的邮件文件。在效率方面,两种格式各有各 优缺点:搜索、访问、删除特定邮件时,maildir的速度比较快;但是就MDA的投递工作效率而言,直接将邮件内容放入文本未(mbox格式)可能比较 快。实际上,你的选择可能要看你所用的POP/IMAP SERVER而定,如果你架设的POP/IMAP SERVER只支持maildir格式,很显然你没有选择的余地。postfix对两种格式都支持,所以你只要考虑其他因素就行。如果你的环境让你感觉到 为难,建议你测试两种格式,尽量以接近实际的运行环境和工作量来实验,依据实验结果做出选择。

本地邮件的投体操作

若收件 地址的网域部分,是列在mydestination参数的所有网域之一,postfix就将其当成本地邮件交给local MDA进行投递。你可以随意在mydestination列出多个网域,但是本地的个别用户会收到所有网域的邮件。举例来说,如果 oreilly.com、ora.com和oreillymedia.com同时被列在mydestination参数,则寄给 kdent@ora.com、kdent@oreilly.com或kdent@oreillymedia.com的邮件,最后都是进入同一个本地邮箱。 为了避免收下不明用户的邮件,所有本地收件人的名称都必须列在local_recipient_maps参数所指的表中。此参数的默认值是指向unix系 统的密码文件与别名表,所以你通常不需要修改它。
检查邮件地址的人名部分时,postfix先检查别名表。如果发现相符的别名,则以该别名对应的 名称为新的收件人,重新提交邮件,当成新邮件处理;否则,就试着将邮件传给系统上的用户。postfix先检查当地用户是否设置了自己的.forward 文件,如果有,则依据其设定内容来转寄邮件;乳沟没有,则将邮件放入用户的邮箱。

.forward文件

.forward 文件让用户可以设置自己的别名。.forward文件的格式与别名文件的RHS-VALUE部分的格式一样,甚至比其更宽松。比方说,别名文件的RHS- VALUE可以有多个以分号隔开的值,.forward文件也沿用相同惯例,但同时也容许你将值分别写在不同的行。
.forward文件的拥有权 限必须是收件人的系统账户,而且通常放在用户的主目录下。你可以用forward_path参数来改变.forward文件的存放路径。postfix提 供下列8大变量让你表示.forward文件的存放路径,这些变量的实际值,由投递时的系统环境决定:
$user      收件人得账户名称(信息来源:  /etc/passwd)
$home   收件人得主目录(信息来源: /etc/passwd)
$shell      收件人得shell
$recipient      收件人得完整邮件地址
$extension      收件地址得人名部分得扩展部分(不一定有),以+之类得分隔符与人名部分风格开。以                                 user1+labs@example.com为例,$extension等于user1。
$domain      收件地址的网域部分。
$local         收件地址的完整人名部分(包括扩展部分在内--如果有的话)。以user1+                                     labs@example.com为例,$local等于user1+labs。
$recipient delimiter        收件地址得人名部分与扩展部分之间得分隔符(通常是+)如果你增加对一个非标准得.forward文件得支持,可以参考下面得设定;
forward_path = /home/$user/.forward   /home/$user/other_forward

别名投递操作

当 别名文件指定了一个命令或文件时,postfix必须先将自己得执行身份改成别名文件拥有者,然后以该身份得权限来执行命令,或将邮件内容写入文件。唯一 的例外是别名文件拥有者为root时,这时候postfix使用default_privs参数所指定的账户(默认值为nobody)

邮箱投递操作

当postfix 将邮件投递给一位本地用户时,它必须将邮件内容写入该用户在系统上的邮箱。postfix默认使用的邮箱格式是mbox,当你安装postfix时,它会 依据你所用的Unix平台类型来决定邮件存储目录的位置。mail_spool_directory参数可用来指定一个默认之外的目录,而目录路径的指定 方式会影响pstfix选择何种邮件格式。举例来说,假如你这样设定:
mail_spool_directory = /var/spool/mail
这表示postfix应该使用mbox格式将邮件存在/var/spool/mail目录下。如果你想改用maildir格式,则必须在目录名称之后附加一个/符号:
mail_spool_directory = /var/spool/mail/
你也可以要求postfix将邮件放在用户的主目录下。设定一个相对路径给home_mailbox参数,表示你想要哪一个文件来作为邮箱:
home_mailbox = mbox
在路径名称之后附加一个/符号,表示postfix应该使用maildir格式的投递程序:
home_mailbox = maildir/
这会使得postfix将邮件投递到用户主目录下的maildir/子目录。
注意:   使用maildir格式时,postfix通常会自动创建必要的子目录与文件--如果用户的身份权限足够的话。不过,基于安全上的考虑,如果上层目录的权限模式为775,则local MDA不会创建任何额外的文件或目录。

POP与IMAP

postfix 将邮件内容填入邮箱之后,用户还需要一个渠道才能读到邮件。许多站点提供POP/IMAP服务,让用户能通过网络取得他们的邮件。大多数情况下, postfix能够与POP/IMAP servers合作无间,而且两边都不需要有特殊设定,当然,前提是双方必须都使用相同的邮箱格式。

POP与IMAP的比较

对 于不能总是保持网络连接的用户,pop协议比较理想,因为它能让用户连接到邮件服务器,取走所有邮件,然后切断网络连接。用户可以离线读信,因为所有邮件 都已经在用户自己的计算机上了。大部分的pop客户端软件,都可以让用户选择在取走邮件之后,是否要清空服务器上的邮箱。如果一段时间没清理邮箱,邮件会 逐渐累积,占用越来越多的服务器磁盘空间。pop的软件相当容易设计,但是它最大的问题,是你的邮件不见得放在你需要得地方(试想,如果你有一台以上得计 算机)。此外,pop也不支持多邮箱,而且强制要求你必须完整下载邮件之后才能读信。如果想要先看所有邮件得主题,然后再决定要下载哪几封邮件,pop则 无能为力。
IMAP协议是为了克服pop得缺点而设计得。IMAP让所有邮件都留在服务器上,用户看信之前必须先联机,联机之后可在远程进行任何 控制管理动作,就像所有邮件都在本地端一样。由于所有实质动作都发生在服务器上,因此,不管用户是在家里得电脑、办公室里得工作站,还是出差时所带得笔记 本计算机上,都可以看到同样得内容。在功能方面,IMAP比pop更强、更有灵活性。你同样可以离线阅读,而且可以拥有多个邮箱,甚至可以只下载邮件得标 题,然后才决定是否要取得邮件得其余部分。所以,如果你发现某封邮件夹带一个大文件,而你对该文件并没有兴趣,你也必须等漫长得下载过程。

postfix与pop/imap servers

postfix 与pop/imap server之间得合作相当简单。每当postfix收下本地邮件,就将邮件封存在邮箱里。当pop/imap server收到用户的要求时,只要从同样的邮箱取出邮件即可。postfix与pop/imap server双方都必须同意使用相同得邮箱格式以及相同得锁定机制。postfix可搭配任何使用传统邮箱格式得标准POP/IMAP server。你可能会想要调整mail_spool_directory参数,但是对于大多数得POP/IMAP SERVERS而言,只要按照标准安装指示安装并启动服务器即可。对于不支持传统邮件格式的pop/imap servers而言,postfix扔可以使用“本地邮件传输协议”来投递邮件,由POP/IMAP SERVER自己将邮件存入邮箱。

本地邮件传输协议(LMTP)

某 些pop/imap servers使用非标准的邮箱格式。很显然的,没道理要求postfix之类的MTA必须认识多种不同的专属格式。因此,我们需要一种无关邮件格式的传 输渠道,让邮件能从某个邮件程序传到同机器上的另一个邮件程序,这个渠道便是LMTP。LMTP可说是smtp的精简版。LMTP SERVER同样有权决定是否要收下或拒收邮件,不过,LMTP SERVER不负责处理无法立刻投递的邮件。
当MTA传递一封多收件人的邮件给 SMTP server时,若有部分收件人因故不能收下邮件,则smtp server要负责将邮件排入队列以便下次传送,并对LMTP client宣称投递任务已经成功。然而,LMTP server不承担这样的责任,也就是说,每一位收件人的投递状况都必须个别回复给LMTP client知道。对于无法投递的收件人,他们的邮件是放在LMTP client的队列里,由LMTP client负责后续的处理过程。
LMTP 的对话可能发生在同一机器上的不同邮件子系统之间,或是同一局域网上的不同机器之间。但是,如果对话双方中间隔着广域网路,就不保证LMTP一定可靠,因 为此协议是以响应速度的快慢与否来判断邮件是否顺利送达。SMTP已经被发现其收信与送信系统之间有一个同步化问题,偶尔会导致邮件被重复传递。乳沟 LMTP的对话双方居广域网络的两端,相信问题会更严重。
注意:  LMTP除了可让MTA用来将邮件投递到非标准格式的邮箱,其真正的好处是让邮件管理人员可以架设出容易扩展且可靠的邮件系统。比方说,对于邮件量很大的 站点,可以架设一台或多台postfix servers专门接收来自Internet的邮件,然后投递给多个LMTP后台系统。当邮件量提升时,只要多架设几台前台或后台系统即可。

POSTFIX与CYRUS IMAP

Cyrus IMAP是专门提供POP/IMAP服务的服务器,用户不需要系统账户。如果你想要为系统现有的用户架设邮件服务器,或许应该考虑其他比较简单的 POP/IMAP解决方案,像Qualcomm的Qpopper(只有pop功能),或是University of Washington的IMAP Toolkit,这两套软件都不需要你在postfix进行任何特殊设定。
Cyrus IMAP提供两种LMTP投递渠道,分别是Unix-domain socket与TCP socket。你必须知道Cyrus IMAP使用哪一种渠道,才可以适当设定postfix。如果使用Unix-domain socket,则postfix与Cyrus IMAP server两者都必须在同一台机器上;如果使用TCP socket,则postfix与Cyrus IMAP server可以在同一台机器上,也可以分居于局域网络上的不同主机。postfix的LMTP投递参数定义在main.cf配置文件的 transort_maps参数中。
要让postfix收下投递给本地Cyrus IMAP server的邮件,则收件地址的网域名称必须被列在mydestination参数。接着,你必须要求postfix将邮件交给Cyrus IMAP。使用mailbox_transport、local_transport或fallback_transport参数可让postfix知 道,在邮件交给Cyrus之前,要进行多少道本地投递手续。如果你使用了local_transport或fallback_transport,请将 Cyrus的所有用户名称写在local_recipient_maps参数所指的一个查询表里,以免postfix拒收cyrus用户的邮件。
mailbox_transport
邮件先交给local MDA,由local检查别名文件与.forward文件,并展开收件人的邮件地址。经过处理后的邮件会被转交给postfix的LMTP client,由它负责投递道LMTP server。
local_transport
当LMTP用于本地传输时,邮件会被直接交给postfix LMTP client,原本负责处理本地邮件的local MDA完全不插手。因此,别名文件与.forward文件都没有作用。
fallback_transport
当LMTP用于备用传输时,postfix会先将邮件交给local MDA处理,执行别名文件与.forward文件的展开操作。如果收件人有一个正常的系统账户,则邮件会被投递到系统上的适当邮箱;如果收件人没有系统账 户,则邮件会被交给postfix LMTP clenet,由它负责将邮件交给LMTP server。当你的邮件服务器同时以自己的系统账户服务一群用户,而同时又代收另一群Cyrus IMAP server用户的邮件时,就需要以fallback_transport机制来进行LMTP投递操作。
使用下列格式来设定你所选的投递机制:
xxx_transport = service:socket_type[:/path/to/socket]
对于LMTP投递操作而言,service必须为lmtp(代表/etc/postfix/master.cf)所定义的lmtp服务, socket_type必须是unix(代表unix-domain socket)或inet(代表tcp socket)两者之一。默认值为inet,这表示postfix架设你的lmtp server是使用tcp socket来收信。因此,如果默认值符合你的需求,你只要这样设定就可以了:
local_transport = lmtp
如果使用local_transport与unix-domain socket,在/etc/postfix/main.cf中应该做这样的设定:
local_transport = lmtp:unix:/var/imap/sockte/lmtp

postfix与cyrus imap的搭配实例

cyrus imap使用春雨如丝sasl函数库来验证用户的身份,所以,你必须先构建、安装cyrus sasl函数库,然后才可以顺利构建cyrus imap server。此外,cyrus软件至少需要berkeley db 3以上的版本,如果你系统
上的Berkeley DB比第三版还旧,你可能需要全面更新整个系统。在同一个系统上混用不同版本的Berkeley DB,可能会引发难以追查的问题。如果你必须升级DB函数库,建议你重建所有需要用到的Berkeley DB的软件,让系统上的所有软件都使用同版函数库。
为了举例说明,架设我们已经架设好一个能接收example.com网域邮件的postfix server,并以同一台机器来运行Cyrus IMAP server。假设所有邮件用户的账户都已经建在Cyrus SASL的数据库里,但是这台机器上仍有少数几个shell账户。虽然这些账户不会被用来收信,但是我们希望仍然能够使用.foward文件与别名文件, 以便系统产生给root的邮件以及外界寄给postmaster别名的邮件,都可以被转寄到正确的地方。
很显然的,我们不能直接将所有邮件都直接交付给Cyrus IMAP server(所以应该排除local_transport),因为我们扔需要local MDA的别名文件与.forward文件的展开功能(所以只剩下mailbox_transport与fallback_transport可以选择)。 但由于shell账户不是用来收信的,所以,最理想的选择是mailbox_transport。我们应该将此参数指向lmtp MDA,并确定master.cf里的lmtp服务能将邮件投递给Cyrus IMAP server。以下是我们的设定步骤:
1、将Cyrus IMAP安装到系统上。检查Cyrus的配置文件(通常是/etc/cyrus.conf),确定它的服务渠道是Unix-domain socket,并记下socket文件的位置。你应该会看到类似下面这样的内容:
SERVICES {
#  add or remove based on preferences
imap               cmd="imapd"  listen="imap"  prefork=0
pop3               cmd="pop3d"  listen="pop3"  prefork=0
#  LMTP is required for delivery
lmtpunix            cmd="lmtpd"  listen="/var/imap/socket/lmtp"   prefork=0
}
其中,“lmtpunix”那一行就是socket文件的正确位置。
2、依照Cyrus随附文件的指示,在服务器上设置一个供Cyrus IMAP server使用的系统账户。
3、检查/etc/postfix/master.cf的内容,确定lmtp服务的配置符合你的系统环境。一般而言,你应该像这样设定:
lmtp               unix  -    -     n   -      -    lmtp
如果你的postfix是以默认模式安装的,上面这一行应该已经出现在你的/etc/postfix/master.cf配置文件里了。要注意的是第五 栏,它代表lmtp MDA是否要处于chroot环境下。在本例中,由于它必须要能够读取Cyrus IMAP server的socket文件,所以此栏设定为n
4、检查main.cf,确定mydestination参数所列的网域包含了你要的收信网域。你可以直接列出。
5、要求mailbox_transport使用master.cf所指定的lmtp服务,并指向你先前记下的Cyrus IMAP socket文件
6、重新加载postfix

给我留言

Copyright © 2011-2018 零下二十四度Theme By  Ality  京ICP备16007547号   关于本站

用户登录