后缀多个 smtpd_sender_login_maps

scr*_*nie 2 postfix

我正在使用带有假期功能的 postfixadmin。support@mail-server.cc 是我用来通过 smtp auth 从不同服务发送电子邮件的常规邮件帐户 - 在这种情况下,在假期.pl 文件中用于发送外出消息用户发件人地址。一切都好,除非我启用 smtpd_sender_login_maps。

根据 postfix 文档,smtpd_sender_login_maps 可以有多个查找表。我正在尝试使用两个,mysql 和一个散列的 db 文件,其中我使用了 support@mail-server.cc 但使用了不同的 FROM 地址。他们每个人都在工作,但是当我指定他们两个时,只有第一个表在工作。

因此,当首先进行 mysql 查询时,只有来自 mysql db 的别名可以用作 FROM 地址:

smtpd_sender_login_maps     = 
   mysql:/etc/postfix/virtual_alias.cf,
   hash:/etc/postfix/allowed_sender_aliases.cf
Run Code Online (Sandbox Code Playgroud)

并且假期自动回复失败:

Jan 10 00:34:01 mx1 postfix/submission/smtpd[16856]: NOQUEUE: reject: RCPT from mx1.mail-server.cc[195.88.238.11]: 553 5.7.1 <alex@mail-server.cc>: Sender address rejected: not owned by user support@mail-server.cc; from=<alex@mail-server.cc> to=<alex5000@gmx.net> proto=ESMTP helo=<localhost.localdomain>
Run Code Online (Sandbox Code Playgroud)

当我首先指定散列的 db 文件时,假期自动回复正在工作,但用户不再能够使用他的帐户或别名发送电子邮件:

smtpd_sender_login_maps = 
   hash:/etc/postfix/allowed_sender_aliases.cf,
   mysql:/etc/postfix/virtual_alias.cf
Run Code Online (Sandbox Code Playgroud)

日志条目:

Jan 10 00:49:40 mx1 postfix/submission/smtpd[26589]: NOQUEUE: reject: RCPT from unknown[192.168.200.100]: 553 5.7.1 <alex@mail-server.cc>: Sender address rejected: not owned by user alex@mail-server.cc; from=<alex@mail-server.cc> to=<alex5000@gmx.net> proto=ESMTP helo=<[192.168.200.100]>
Run Code Online (Sandbox Code Playgroud)

/etc/postfix/allowed_sender_aliases.cf 的内容:

@mail-server.cc         support@mail-server.cc
Run Code Online (Sandbox Code Playgroud)

/etc/postfix/virtual_alias.cf 的内容:

# alias mapping
hosts = 127.0.0.1
user = vmail
password = xxxxx
dbname = mail
query = SELECT goto FROM alias WHERE address = '%s' AND active = '1'
Run Code Online (Sandbox Code Playgroud)

有什么问题还是真的只使用了第一个表?奇怪的是,当使用哈希表时,用户不能再发送电子邮件了。

Nic*_*ing 5

经过一番折腾,我想我找到了答案。Postfix 在搜索时定义了一个“匹配”sender_login_maps作为任何成功的查找,但不考虑该查找的结果

tl; dr:如果您打算使用 中所有映射的结果的联合smtpd_sender_login_maps,而不是仅采用第一个后续查找结果,则必须以某种方式组合这些映射(例如,通过 SQLUNIONunionmap)。

从本质上讲,这是一个令人惊讶的简单问题,由 Postfix 在进行映射查找时的短路行为引起。系好安全带,这是一个很长的答案......


背景

来自关于发件人登录地图的 Postfix 文档:

smtpd_sender_login_maps(默认值:空)...

指定零个或多个“type:name”查找表,用空格或逗号分隔。
将按指定的顺序搜索表,直到找到匹配项。

http://www.postfix.org/postconf.5.html#smtpd_sender_login_maps

使用的目的smtpd_sender_login_maps是验证是否允许当前经过身份验证的 SMTP 用户从其邮件中的给定 FROM 地址发送。这是一项强大的功能,可以根据经过身份验证的 SMTP 用户的身份来限制客户端可以发送的地址。

smtpd_sender_login_maps 有很多用例,例如:

  • 阻止用户以其他人的身份登录和发送消息
    > Bob 可以发送为bob@example.com,但不能发送为alice@example.com

  • 允许多个用户从他们都不拥有的单个共享地址发送
    > Bob 和 Alice 都可以发送marketing@example.com

  • 允许单个用户从他们拥有的多个地址
    发送 > Alice 可以同时发送alice@example.com和发送alice.smith@example.com

  • 允许管理员从多个地址发送消息(甚至为他人所有)
    > Tracy,IT 管理员可以作为任何人发送.*@example.com

  • 还有更多...(请记住,可以通过 TCP 套接字、SQL 查询、正则表达式等进行查找)


示例场景

我们将特别调查这个用例:

  • 允许管理员从多个地址发送消息(甚至为他人所有)
    > Tracy,IT 管理员可以作为任何人发送.*@example.com

想象一下我们在邮件服务器上运行 Postfix 和 MySQL 的设置。有 3 个用户存储在数据库中,他们可以通过带有 postfix 的 SMTP 进行身份验证:

  • alice@example.com: 只能从alice@example.com,发送alice.smith@example.com
  • bob@example.com: 只能从bob@example.com,发送bob.jones@example.com
  • admin@example.com: 应该可以从任何 .*@example.com地址发送

普通用户只能从他们的主要地址和他们的别名发送,但我们希望允许管理员能够无限制地伪装成任何发件人。在现实生活中,当用户需要.*@example.com仅使用一次 SMTP 登录就能够从多个地址发送信息时(例如,如果地址是动态生成的或属于其他用户),这种类型的设置非常有用。

正常的做法是使用smtpd_sender_login_maps这种设置来实现。

示例配置

/etc/postfix/main.cf

...

smtpd_sender_login_maps = 
    mysql:/etc/postfix/sender_logins.cf,
    pcre:/etc/postfix/sender_overrides.cf

...
Run Code Online (Sandbox Code Playgroud)

首先检查 MySQL,然后仅当 mysql 查找返回 0 结果时才检查 pcre。


/etc/postfix/sender_logins.cf( mysql):

hosts = 127.0.0.1
user = postfix
password = yourDatabasePasswordHere
dbname = mail
query = SELECT email FROM users WHERE email='%s'
Run Code Online (Sandbox Code Playgroud)

此映射检查数据库并返回给定 FROM addr 的普通 SMTP 用户,例如:
alice@example.com -> alice@example.com
bob.jones@example.com -> bob@example.com


/etc/postfix/sender_overrides.cf( pcre):

/.*@example.com/     admin@example.com
Run Code Online (Sandbox Code Playgroud)

此映射匹配所有 @example.comFROM 地址并返回管理员 SMTP 用户,例如:
.*@example.com -> admin@example.com


问题

用户期望的行为:

  1. Postfix 在第一个sender_login_mapsdb 中查找 FROM addr
  2. 它为 FROM addr 找到匹配的条目
  3. 返回的 SMTP 用户 != 登录用户,所以我们尝试下一个映射 db
  4. Postfix 在下一个sender_login_mapsdb 中查找 FROM addr
  5. 它为 FROM addr 找到匹配的条目
  6. SMTP 用户 == 登录用户
  7. Postfix 发送消息成功

实际发生的情况:

  1. Postfix 在第一个sender_login_mapsdb 中查找 FROM addr
  2. 它为 FROM addr 找到匹配的条目,因此查找过程停止
  3. 返回的 SMTP 用户 != 登录用户
  4. Postfix 拒绝邮件

解释

问题在于 Postfix 不会检查两个映射并合并结果,而是在遇到返回任何 SMTP 用户的第一个匹配查找时停止查找过程。

如果返回的 SMTP 用户与当前经过身份验证的用户不匹配,它不会继续在下一个数据库中查找地址,它会立即查找DENY

任何两个共享密钥的映射都可能发生相同的查找冲突,例如两个 mysql 数据库mysql:...,mysql:...,不一定只是pcrewith/.*//.*@example.com/@example.com someuser@example.com第一个映射中的任何完全匹配或通配符匹配都将优先,并完全阻止查询第二个映射。

场景 1:alice@example.com尝试从alice@example.com

? 这工作正常,发送电子邮件是因为地址按预期与发件人匹配。

  1. alice@example.com 通过 SMTP 登录以从 alice@example.com
  2. alice@example.comsmtpd_sender_login_maps返回中查找alice@example.com
  3. 发送成功,alice@example.com== SMTP 认证用户alice@example.com

场景 2:admin@example.com尝试从alice@example.com

? 这不起作用,电子邮件被拒绝,因为 SMTP 身份验证用户admin@example.com与第一个查找结果不匹配alice@example.com

  1. admin@example.com 通过 SMTP 登录以从 alice@example.com
  2. alice@example.comsmtpd_sender_login_maps返回中查找alice@example.com
  3. 发送失败,alice@example.com!= SMTP 认证用户admin@example.com
    postfix/smtpd[16645]: NOQUEUE: reject: RCPT from webmail.mailserver[192.168.1.5]: 553 5.7.1 <alice@example.com>: Sender address rejected: not owned by user admin@example.com; from=<alice@example.com> to=<bob@example.com> proto=ESMTP helo=<mail.example.com>

如果我们翻转映射的顺序会怎样?

smtpd_sender_login_maps = 
    pcre:...,        # moving the pcre mapping above mysql makes it worse
    mysql:...
Run Code Online (Sandbox Code Playgroud)

翻转映射的顺序,以便在 mysql 无法解决问题之前检查 pcre 文件。它甚至使情况变得更糟,因为包罗万象.*@example.com将掩盖 MySQL 中的所有真实用户,并阻止除admin@example.com发送电子邮件之外的任何用户。

当它alice@example.com在 pcre 文件中查找时,它admin@example.com作为唯一允许的用户返回,并在检查 mysql 数据库之前失败。


解决方案

A. 使smtpd_sender_login_maps不相交

如果映射中的键不包含任何重叠,则顺序无关紧要,任何与第一个数据库不匹配的查找都将按预期继续检查后续的。

列表中较早的映射不能有任何“catchall”或通配符键,否则它们将匹配所有内容并掩盖后面映射的结果。

B. 在 SQL 中加入多个查找结果 UNION

如果您使用mysql表类型进行smtpd_sender_login_maps映射,那么您可以在执行地址查找时控制 SQL 查询运行,并且您可以在 SQL 级别加入多个映射。

假设您的所有映射都可以在同一个 MySQL 数据库中访问,您可以使用如下UNION语句在 SQL 级别连接多个地址查找的结果:

main.cf

smtpd_sender_login_maps = mysql:/etc/postfix/sender_logins.cf
Run Code Online (Sandbox Code Playgroud)

sender_logins.cf

hosts = 127.0.0.1
user = postfix
password = yourDatabasePasswordHere
dbname = mail
query = SELECT email
            FROM users
            WHERE email='%s'
        UNION SELECT destination
            FROM aliases
            WHERE source='%s'
        UNION SELECT email
            FROM users
            WHERE wildcard_sending=1
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我们将在 SQL 中设置admin@example.com为 have wildcard_sending=1,然后它将与每个查找结果以及普通用户和别名匹配一起返回,例如

alice.smith@example.com    -> alice@example.com,admin@example.com
bob@example.com            -> bob@example.com,admin@example.com
Run Code Online (Sandbox Code Playgroud)

C.unionmap用于组合多个映射

如果您使用的是 Postfix 3.0 或更高版本,您或许可以尝试使用新unionmap功能,该功能会一次查找所有映射并将结果连接在一起。

smtpd_sender_login_maps = unionmap:{
        mysql:/etc/postfix/sender_logins.cf,
        pcre:/etc/postfix/sender_overrides.cf }
Run Code Online (Sandbox Code Playgroud)

使用此设置,mysql 结果将与 pcre 查找结果连接,例如

alice.smith@example.com    -> alice@example.com,admin@example.com
bob@example.com            -> bob@example.com,admin@example.com
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅http://www.postfix.org/DATABASE_README.html#types unionmap


来源

因此,这里的“匹配”一词是最狭义的:只是匹配的地址,而不是匹配的(地址、登录名)对。此外,当找到匹配的地址时,查找链会立即停止,并做出二元拒绝/接受决定。