postfix地址重写的目的:
地址重写是postfix邮件系统的核心。postfix出于许多不同的目的需要重写地址。一些是为了管理或使用上的方便,而另一些是为了把正确格式的邮件传递到正确的目的地。下面是postfix地址重写的例子:

•把不完全的邮件地址补全为完整的邮件地址。例如:把username补全为username@example.com或者把username@hostname补全为username@hostname.example.com

•替换邮件地址。例如:当发送邮件的时候用firstname.lastname@example.com替换useranme@example.com;当接收邮件的时候用useranme@example.com替换firstname.lastname@example.com

•用一个外部地址来替换内部的邮件地址。例如:当内部用户给公网发邮件时,用isp-account@isp.example替换掉内部的username@localdomain.local来发邮件

•用多个地址来替换一个地址。例如:用alias中的地址来替换一个别名地址

•对特定的邮件地址,决定邮件怎样传递以及传递到哪儿。例如:使用smtp(8)传递代理来传递username@example.com的邮件,并传递到DNS中的example.com域mx记录指向的主机上。

尽管postfix目前没有地址重写语言,但是通过表查询,地址重写拥有强大的地址控制能力。一般情况下,postfix使用固定字符串表查询的方法把一个地址匹配为一个或多个地址;使用正则表达式把多个地址匹配为一个或多个地址。固定字符串表查询采用本地文件形式,或者是NIS,LDAP或SQL形式。DATABASE_README文档介绍了关于postfix查询表的内容。

这篇文档的主题包括:
重写消息头,或者标记为无效
postfix地址重写总述
接收邮件时的地址重写
•标准邮件格式的地址重写
•权威地址映射
•地址伪装
•自动的BCC接收者
•虚拟映射
传递邮件时的地址重写
•Resolve address to destination
•邮件传递交换
•重定位的用户表
远程传递的地址重写
•出站邮件的通用映射
本地传递的地址重写
•本地别名数据库
•本地per-user转发文件
•本地catch-all地址
调试你的地址控制

重写消息头或者标记为无效
postfix 2.1及以前的版本总是重写消息头的地址,把postfix的域名附加到那些不完全的邮件地址中去。为来自本地的邮件重写消息头是正常的,但是为来自网络的邮件重写消息头就可能会出错:

•消息头地址重写被邮件地址标准格式所困扰(be frowned upon)
•附加postfix自己的域名会产生一些不正确的结果
•附加postfix自己的域名会产生好像来自本地用户的垃圾邮件

postfix 2.2及以后的版本给出了一个选项,让你决定是不重写来自远程邮件的消息头还是把这样的消息头标记为无效的地址。下面是它的工作原理:

•postfix总是重写来自本地和postfix中sendmail命令的消息头地址,用自己的域名不全那些不完整的邮件地址。local_header_rewrite_clients参数控制postfix认为哪些smtp客户端是本地客户端(默认情况下,来自本地网络接口地址的邮件被认为是本地邮件)

•当remote_header_rewrite_domain参数值为空时(这个值默认为空),postfix从不重写来自远程smtp客户端邮件的消息头。

•如果remote_header_rewrite_domain的值不为空,那么postfix将用remote_header_rewrite_domain的值来重写那些来自远程smtp客户端并且邮件地址不完整的消息头。可以为remote_header_rewrite_domain指定一个类似于domain.invalid的值,这样不完整的邮件地址就不会被误认为是本地地址了。

postfix地址重写总述
下面这张图是地址重写的过程。
 
下面这个表总述了所有的postfix地址控制。如果你第一个阅读这篇文档,请先阅读完“接收邮件的地址重写”部分。一旦你阅读完了这篇文档,下面的这个表将帮助你迅速的找到你需要的内容
 
接收邮件的地址重写
cleanup(8)服务接收来自postfix之外的邮件,同时也接收来自内部的邮件,比如:转发的邮件,反弹至发送者的未送达邮件和关于邮件系统的postmaster提示邮件

cleanup(8)服务把发送者,接收者和消息内容变成一个标准的邮件格式,然后放到入站队列中(incoming queue)。邮件服务器整理消息头和信封中的发送者和接受者邮件地址。并在消息头中添加下面的内容:Form: , Date:(这是邮件标准格式要求的);移除信息头中的Bcc。cleanup(8)服务把更复杂的地址控制交给trivial-rewrite(8)来实现。

这个阶段的地址控制如下:
•把地址重写为标准的邮件地址格式
•权威地址映射
•地址伪装
•自动的BCC接收者
•虚拟别名

把地址重写为标准的邮件地址格式
在cleanup(8)守护进程通过地址映射表查询管理地址前,它首先通过把地址发送给rewrite(8)守护进程来把地址重写为标准的邮件地址格式(如:user@fully.qualified.domain)。把邮件地址重写为标准格式的目的是缩小查询表中的条目数量。

postfix的trivial-rewrite(8)守护进程进行下面的硬编码地址控制:

把"@hosta,@hostb:user@site"重写为"user@site"

上面的地址格式叫做路由邮件地址,指定邮件通过hosta和hostb主机到达user@site邮箱。这种邮件格式已经被废弃很久了。除非去除地址的路由部分,否则postfix无法处理这样的路由地址

注意:postfix 2.2及以后的版本将重写来自远程smtp客户端的消息头地址,只要这个smtp客户端与local_header_rewrite_clients参数的值匹配,或者remote_header_rewrite_domain参数指定了一个非空值。postfix 2.2之前的版本指定local_header_rewrite_clients  = static:all,即可重写远程的smtp地址

把site!user重写为user@site
这个功能是由swap_bangpath布尔参数来控制的(默认值为yes)。这个功能的目的是把UUCP形式的地址重写为域地址形式。当你通过UUCP接收邮件时,这个功能是有用的。

把user%domain重写为user@domain
这个功能是有allow_percent_hack布尔参数控制的(默认值为yes)。一般情况下,这是为了处理像"user%domain@otherdomain"这样的地址的

把user重写为user@$myorigin
这个功能是由append_at_myorigin布尔参数控制的(默认值为yes)。你不能关闭这个这个功能,因为许多postfix组件期望所有的邮件地址都是user@domain这种形式的

如果你的服务器不是$myorigin的主服务器,而且你希望一些用户不经过主服务器直接投递邮件,这时需要在虚拟别名表中把user@$myorigin重定向为user@$myhostname。更详细的内容参见STANDARD_CONFIGURATION_README文档的"delivering some users locally"部分

把user@host重写为user@host.$mydomain
这个功能是由append_dot_mydomain布尔参数控制的。这个功能的目的是:对相同主机名的不同形式采用一致的处理方式。

有些人认为把host重写为host.domain是不好的做法。这是关闭该功能的原因。

把user@site.重写为user@site(结尾没有点号)
结尾的点号将自动的被移除。但是,一个地址如果在结尾有多个点号,postfix将认为该地址无效。

权威地址映射
cleanup(8)进程使用canonical(5)表把消息头和信封的地址重写。默认情况下,所有的消息头和信封地址都被重写;这个是由canonical_classes参数来控制的

这中映射对使用firstname.lastname的地址形式来代替登陆名形式的地址是有用的。对清除由邮件系统产生的无效的域也是有帮助的

权威映射默认是被禁用的。通过编辑main.cf文件中的canonical_maps参数来启用,当有多个值时,用空格或逗号隔开。

示例:
    /etc/postfix/main.cf:
        canonical_maps = hash:/etc/postfix/canonical

    /etc/postfix/canonical:
        wietse        Wietse.Venema

静态的映射表如上面的示例所示,查询表可以用hash,ldap,mysql或pgsql。对于动态的映射表,你可以使用正则表达式。这要求你非常熟悉regexp_table(5), pcre_table(5)和canonical(5)的内容

除了权威映射表可以用于发送者和接收者,你还能单独指定应用于发送者或者接收者的权威映射表。

示例:
    /etc/postfix/master.cf:
        127.0.0.1:10026    inet  n      -      n      -      -     smtpd
            -o receive_override_options=no_address_mappings
注意:在等号两边不要有空格

地址伪装
地址伪装是隐藏邮件网关后面的邮件主机的一个方法。通过这个伪装,使得邮件就好像从邮件网关发过来的一样。

地址伪装默认是被禁用的,它是由cleanup(8)来控制的。通过编辑main.cf中的masquerade_domains参数来启用它,当masquerade_domains有多个值时,用空格或逗号隔开。当postfix要伪装一个域时,postfix将从左到右的方式来处理列表,遇到第一个匹配的时候停止。

示例:
    /etc/postfix/main.cf:
        masquerade_domains = foo.example.com example.com

上面的masquerade_domains会把any.thing.foo.example.com变为foo.example.com;但是吧any.thing.else.example.com为example.com

当一个域名前以!开头意味着不伪装这个域和它的子域
    /etc/postfix/main.cf:
        masquerade_domains = !foo.example.com example.com

不把"any.thing.foo.example.com"改为"foo.example.com", 但是把"any.thing.else.example.com"改为"example.com".

masquerade_exceptions参数指定哪些用户名不受地址伪装的影响。当指定一个或多个用户名时,用空格或逗号隔开。

示例:
    /etc/postfix/main.cf:
        masquerade_exceptions = root
默认情况下,postfix不做任何例外。

Subtle point:默认情况下,地址伪装仅应用于消息头和信封的发件人地址,不应用于信封的收件人地址。这允许你在邮件网关上使用地址伪装,并且仍然可以把来自外部的邮件传递到内部的邮箱用户上。

为了伪装信封收件人地址,指定(postfix 1.1及以后的版本):

    /etc/postfix/main.cf:
        masquerade_classes = envelope_sender, envelope_recipient,
            header_sender, header_recipient

如果你像这样重写了收件人地址,postfix将不能给内部的邮件服务器发送邮件

地址伪装可以通过smtpd(8), qmqpd(8)或者pickup(8)对接收的邮件进行有选择的关闭--master.cf中的设置优先于main.cf中的设置。这个功能在postfix 2.1及以后的版本中生效。

示例:
    /etc/postfix/master.cf:
        127.0.0.1:10026    inet  n      -      n      -      -     smtpd
            -o receive_override_options=no_address_mappings
注意:等号两边不要有空格

自动的BCC接收者
在应用了权威映射和地址伪装后,cleanup(8)守护进程能产生一个可选的BCC(blind carbon-copy)。postfix提供3种机制:

always_bcc = address
传递所有邮件的拷贝到指定邮箱。在postfix 2.1之前的版本,这个功能是通过smtpd(8), qmqpd(8)或者pickup(8)来实现的。

sender_bcc_maps = type:table
用信封发送者的地址来查找"type:table"查询表。postfix 2.1及以后的版本支持该功能。

recipient_bcc_maps = type:table
用信封接收者的地址来查找"type:table"查询表。postfix 2.1及以后的版本支持该功能。

注意:自动BCC接收者仅对新邮件起作用。为了避免邮件的死循环,自动BCC接收者不接收postfix内部转发的邮件,也不接收postfix自己生成的邮件。

自动BCC接收者(包括alway_bcc)能够通过smtpd(8), qmqpd(8)或者pickup(8)对接收的邮件进行有选择的关闭。postfix 2.1及以后的版本支持该功能。

示例:
/etc/postfix/master.cf:
    127.0.0.1:10026    inet  n      -      n      -      -     smtpd
        -o receive_override_options=no_address_mappings
注意:等号两边不要有空格

虚拟别名
在把接收者写入队列文件前,cleanup(8)守护进程使用可选的virtual(5)别名表重定向接收者的邮件。这个映射过程仅影响信封接收者地址;它不影响消息头地址和信封发送者地址。虚拟别名查询对把虚拟别名域的邮件重定向到真实的用户邮箱是有用处的。它对重定向不存在的域邮件也是有用处的。虚拟别名查询能用于把"firstname.lastname"的邮件地址转换回unix登录名的形式,尽管这个功能使用本地别名更合适。关于postfix虚拟域的方法总述参见VIRTUAL_README这篇文档。

默认情况下,虚拟别名是禁用的。通过编辑main.cf文件中的virtual_alias_maps参数值来来启用。当virtual_alias_maps有多个值时,用空格或逗号隔开。

示例:
/etc/postfix/main.cf:
    virtual_alias_maps = hash:/etc/postfix/virtual

/etc/postfix/virtual:
    Wietse.Venema        wietse

虚拟别名地址表中的地址受虚拟别名影响,但是它们不受权威映射影响,这样是为了避免邮件的循环。

正如上个示例的静态映射表所示,查询表可以使用hash:,ldap:,mysql:或者pgsql:。对于动态的映射表,可以使用正则表达式。这要求你熟悉regexp_table(5), pcre_table(5) 和 virtual(5)中的内容。

虚拟映射能够通过smtpd(8), qmqpd(8)或者pickup(8)对接收的邮件进行有选择的关闭。postfix 2.1及以后的版本支持该功能。

示例:
/etc/postfix/master.cf:
    127.0.0.1:10026    inet  n      -      n      -      -     smtpd
        -o receive_override_options=no_address_mappings
注意:等号两边不要有空格

这时,信息将进入postfix的入站队列。

传递邮件时的地址重写
postfix队列管理器依据邮件的目的地址将邮件进行分类,并把邮件交给postfix的投递代理,像:local(8), smtp(8) 和 lmtp(8)。就像cleanup(8)一样,postfix队列管理器把复杂的地址控制交给trivial-rewrite(8)来处理。

这个阶段的地址控制主要有:
•把地址解析目的地址
•邮件传递交换
•重定位用户表

每一个postfix投递代理都尽力把邮件直接投递到目的地,而如何压缩发送者,接收者和信息内容则依据smtp,lmtp等协议的规则。当邮件无法投递到目的地时,该邮件将退给邮件发送者或者被移入延迟队列,稍后再试。

当邮件通过smtp(8)传递代理时的地址控制:
•smtp出站邮件的通用映射

当邮件通过local(8)传递代理时的地址控制:
•本地别名数据库
•本地用户.forward文件
•本地全部(catch-all)地址
 
该文档的接下来部分将以更详细的方式展现每一步的地址控制,主要是以示例的方式。

把地址解析成目的地址
psotfix的qmgr(8)队列管理器从入站队列选取新邮件或者从延迟队列选择被延迟的邮件;然后询问trivial-rewrite(8)地址重写和解析进程来确定邮件应该被投递到哪儿。

postfix 2.0以后的版本区别4种主要的地址类型。每一种类型都有它自己的域名,传递方法。更详细的内容参见ADDRESS_CLASS_README文章。postfix 2.0之前的版本只区别本地传递。
 

邮件传递交换
一旦trivial-rewrite(8)进程确定了默认的传递方法,它会到可选的transprot(5)表中查询(overrides the message destination and/or delivery method)这样的信息。transport(5)表最常用的功能是发送邮件到没有连接到internet网络的系统,或者是为特定需求的客户端配置特定的smtp客户端。更详细的内容参见STANDARD_CONFIGURATION_README 和 UUCP_README文档,示例参见transport(5)手册。

传输表查询默认是禁用的。通过编辑main.cf中的transport_maps参数来启用,对多个值的,用空格或逗号隔开。

示例:
/etc/postfix/main.cf:
    transport_maps = hash:/etc/postfix/transport

重定位的用户表
trivial-rewrite(8)进程通过relocated(5)数据库来处理每一个接收者。这个数据库提供了处理不存在邮箱地址和不存在域的办法。当邮件发送给relocalted(5)数据库中列出的地址时,postfix将会在原来邮件的基础之上附加一些信息,然后退给邮件发送者。

在查询完transport(5)表后查找relocated(5)数据库,in anticipation of transport(5) tables that can replace one recipient address by a different one

重定位用户查询默认是禁用的。通过修改main.cf中的relocated_maps参数来启用,当为多个值时,用空格或逗号隔开。

示例:
/etc/postfix/main.cf:
    relocated_maps = hash:/etc/postfix/relocated

/etc/postfix/relocated:
    username@example.com      otheruser@elsewhere.tld

postfix2.0以后的版本,重定位用户的邮件将会被smtp服务器以“用户移动到otheruser@elsewhere.tld”理由而拒绝。老版本的postfix先会接收邮件,然后以同样的理由发送一份不可达报告给发送者。

出站smtp邮件的通用映射
一些主机没有有效的internet域名,它们的域名类似于localdomain.local。当你想给internet上邮箱发送邮件时,就会产生问题,因为许多邮件服务器拒绝来自无效域名的邮件地址。

通过smtp_generic_maps参数,你能指定generic(5)查询表(这个表的作用是用有效的internet邮件地址来代替本地邮件地址),当邮件通过smtp进入internet时。generic(5)映射表代替信封和消息头地址时不可逆的。当邮件在本地postfix上的不同用户间传递时,generic(5)映射不会发生。

这个功能在postfix 2.2及以后的版本可用。

例如:
/etc/postfix/main.cf:
    smtp_generic_maps = hash:/etc/postfix/generic

/etc/postfix/generic:
    his@localdomain.local        hisaccount@hisisp.example
    her@localdomain.local        heraccount@herisp.example
    @localdomain.local            hisaccount+local@hisisp.example

当邮件通过smtp发送给远程的邮件主机时,会用isp的邮件地址代替his@localdomain.local。

本地别名数据库
当邮件被传递到本地时,local(8)传递代理通过aliases(5)数据库管理每一个本地接收者。这个映射不影响消息头中的邮件地址。本地别名一般用于分发列表,或者用于把类似于postmaster这类标准别名重定向到真实的邮箱地址。这个表也能用于把firstname.lastname转换为登录名。

别名查询默认是启用的。这个默认配置依赖于操作系统的环境,但是通常是下面的一种:

/etc/postfix/main.cf:
    alias_maps = hash:/etc/aliases
    alias_maps = dbm:/etc/aliases, nis:mail.aliases

alias数据库文件的位置是由alias_database参数来控制的。
/etc/postfix/main.cf:
    alias_database = hash:/etc/aliases (4.4BSD, LINUX)
    alias_database = dbm:/etc/aliases (4.3BSD, SYSV<4)
    alias_database = dbm:/etc/mail/aliases (SYSV4)

aliases(5)文件能够指定邮件是传递到本地文件,还是传递一个命令通过标准的输入流来接收信息。基于安全的考虑,命令和文件目的地的传递是用别名数据库所有者权限来执行的。默认的userid和default_privs被用于传递所有者为root的别名文件中的命令或文件。

本地用户的.forward文件
通过local(8)传递代理的传递,用户通过指定家目录中.forward文件的目的地来控制他们自己的邮件传递。除了alias(查询值和冒号)左边没有之外,.forward文件的语法与local aliases(5)的语法结构在其他方面都一样。

本地所有的(catch-all)地址
当local(8)传递代理发现邮件接收者不存在时,正常情况下,邮件会退给发送者。有时,postfix管理者希望吧这些接收者不存在的邮件传递到另外一个邮件服务器。基于这样的目的,你能通过luser_relay参数指定一个专用的邮件地址来接收这些邮件。

另外一种办法:通过指定fallback_transport参数,把接收者不存在的邮件委派给其他传输。更详细的内容参见local(8)传输代理文档。

注意:如果你使用了luser_relay功能以便接收非unix帐户的邮件,那么必须指定:
/etc/postfix/main.cf:
    local_recipient_maps =

(即:空值)在main.cf文件中,否则,postfix的smtp服务器将用“本地接收者表中的未知用户”来拒绝非unix账户的邮件。更详细的内容参见LOCAL_RECIPIENT_README.

luser_relay能指定一个地址。它受$name的影响。
示例:
$user@other.host
没有扩展地址的username,后缀为@other.host。例如:把发送给username+foo的邮件发给username@other.host

$local@other.host
例如:把发送给username+foo的邮件发送给username+foo@other.host

sysadmin+$user
例如:把发送给username+foo的邮件发送给sysadmin+username

sysadmin+$local
例如:把发送给username+foo的邮件发送给sysadmin+useranme+foo

调试地址控制功能
postfix 2.1及以后的版本会产生基于调试目的的邮件传递报告。这些报告不仅显示地址重写,别名扩展/转发后的发送者/接收者地址,而且这些报告也显示传递到邮箱的信息,非postfix命令的传递信息,应答远程smtp服务器的信息等等。

postfix产生两种邮件传输调试报告:

•what-if:报告发生了什么,但是没有真正的传递邮件。这种运作模式要求:
$ /usr/sbin/sendmail -bv address...
邮件传递状态报告将被发送到登陆帐户的邮箱中。

•what happened:传递邮件,报告成功,失败与否,包括远程smtp服务器的应答。这种运作模式要求:
$ /usr/sbin/sendmail -v address...
邮件传递状态报告将被发送到登陆帐户的邮箱中。

这些报告包括postfix传输代理产生的信息。因为它们以守护进程的身份运行,不直接的与用户交互,结果是以邮件的形式发送给测试消息的发送者。这些报告的格式实际上与未送达报告的格式是一样的。

例如:下面是用"sendmail -bv postfix-users@postfix.org"产生的传递报告。这个报告的第一部分包含了人工可读的信息。在这种情况下,邮件将通过mail.cloud9.net传递,smtp服务器将回复一个"250 ok"。其他的报告可能显示了到mailbox的传递,或者到feipostfix命令的传递。

Content-Description: Notification
Content-Type: text/plain

This is the mail system at host spike.porcupine.org.

Enclosed is the mail delivery report that you requested.

                        The mail system

<postfix-users@postfix.org>: delivery via mail.cloud9.net[168.100.1.4]: 250 2.1.5 Ok

报告的第二部分是机器可读的形式,包括了下面的信息:

•信封发送者的地址(wietse@porcupine.org)
•信封接收者的地址(postfix-users@postfix.org)。如果接收者的地址被postfix改变,那么postfix也包含原始的接收者地址。
•传递状态

一些信息取决于postfix的版本。下面的示例适用于postfix 2.3及以后的版本。

Content-Description: Delivery report
Content-Type: message/delivery-status

Reporting-MTA: dns; spike.porcupine.org
X-Postfix-Queue-ID: 84863BC0E5
X-Postfix-Sender: rfc822; wietse@porcupine.org
Arrival-Date: Sun, 26 Nov 2006 17:01:01 -0500 (EST)

Final-Recipient: rfc822; postfix-users@postfix.org
Action: deliverable
Status: 2.1.5
Remote-MTA: dns; mail.cloud9.net
Diagnostic-Code: smtp; 250 2.1.5 Ok

报告的第三部分包含了postfix传递的信息,包括:form:,to:,信息头,你能在这儿看到地址重写对邮件传递的影响。用"sendmail -bv"提交的邮件没有正文,所有下面的例子中没有任何内容。

Content-Description: Message
Content-Type: message/rfc822

Received: by spike.porcupine.org (Postfix, from userid 1001)
        id 84863BC0E5; Sun, 26 Nov 2006 17:01:01 -0500 (EST)
Subject: probe
To: postfix-users@postfix.org
Message-Id: <20061126220101.84863BC0E5@spike.porcupine.org>
Date: Sun, 26 Nov 2006 17:01:01 -0500 (EST)
From: wietse@porcupine.org (Wietse Venema)