我正在使用包含休假功能的 postfixadmin。 [电子邮件保护]是一个常规邮件帐户,我使用它通过 smtp 身份验证从不同的服务发送电子邮件 - 在本例中,在 vacation.pl 文件中,使用用户发件人地址发送外出消息。除非我启用 smtpd_sender_login_maps,否则一切都正常。
根据 postfix 文档,smtpd_sender_login_maps 可以有多个查找表。我尝试使用两个,mysql 和一个散列数据库文件,以及额外的文件,其中我使用[电子邮件保护]但 FROM 地址不同。每个表都在工作,但当我同时指定它们两个时,只有第一个表在工作。
因此,当首先进行 mysql 查询时,只有来自 mysql db 的别名可以用作 FROM 地址:
smtpd_sender_login_maps =
mysql:/etc/postfix/virtual_alias.cf,
hash:/etc/postfix/allowed_sender_aliases.cf
休假自动回复失败:
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 <[email protected]>: Sender address rejected: not owned by user [email protected]; from=<[email protected]> to=<[email protected]> proto=ESMTP helo=<localhost.localdomain>
当我首先指定散列数据库文件时,休假自动回复程序正在运行,但用户不再能够使用其帐户或别名发送电子邮件:
smtpd_sender_login_maps =
hash:/etc/postfix/allowed_sender_aliases.cf,
mysql:/etc/postfix/virtual_alias.cf
日志条目:
Jan 10 00:49:40 mx1 postfix/submission/smtpd[26589]: NOQUEUE: reject: RCPT from unknown[192.168.200.100]: 553 5.7.1 <[email protected]>: Sender address rejected: not owned by user [email protected]; from=<[email protected]> to=<[email protected]> proto=ESMTP helo=<[192.168.200.100]>
/etc/postfix/allowed_sender_aliases.cf 的内容:
@mail-server.cc [email protected]
/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'
是不是出了什么问题,还是真的只使用了第一个表?奇怪的是,使用哈希表时用户无法再发送电子邮件。
答案1
经过一番思考,我想我找到了答案。Postfix 将搜索时的“匹配”定义sender_login_maps
为任何成功的查找,但结果不考虑该查找。
tl; dr
:如果您打算使用联盟所有映射的结果smtpd_sender_login_maps
,而不是仅仅取第一的后续查找结果,映射必须以某种方式组合(例如通过 SQLUNION
或unionmap
)。
从本质上讲,这是一个非常简单的问题,由 Postfix 在进行映射查找时的短路行为引起。系好安全带,这是一个很长的答案……
背景
来自 Postfix 文档中有关发件人登录映射的内容:
smtpd_sender_login_maps(默认:空)...
指定零个或多个“type:name”查找表,以空格或逗号分隔。
将按照指定的顺序搜索表,直到找到匹配项。
http://www.postfix.org/postconf.5.html#smtpd_sender_login_maps
使用它的目的smtpd_sender_login_maps
是验证当前已验证的 SMTP 用户是否被允许从其邮件中指定的发件人地址发送邮件。根据已验证的 SMTP 用户的身份来限制客户端可以发送邮件的地址是一项非常强大的功能。
smtpd_sender_login_maps
有很多用例,例如:
防止用户以他人身份登录并发送消息
> Bob 可以发送为[email protected]
,但不能发送为[email protected]
允许多个用户从他们都不拥有的单个共享地址发送
> Bob 和 Alice 都可以发送[email protected]
允许单个用户从其拥有的多个地址发送
> Alice 可以同时[email protected]
发送[email protected]
允许管理员从多个地址发送消息(甚至其他人拥有的地址)
> Tracy,IT 管理员可以以任何人的身份发送.*@example.com
还有更多...(请记住,查找可以通过 TCP 套接字、SQL 查询、正则表达式等进行。)
示例场景
我们将特别调查这个用例:
- 允许管理员从多个地址发送消息(甚至其他人拥有的地址)
> Tracy,IT 管理员可以以任何人的身份发送.*@example.com
想象一下我们在邮件服务器上运行 Postfix 和 MySQL 的设置。数据库中存储了 3 个用户,他们可以通过 Postfix 的 SMTP 进行身份验证:
[email protected]
:只能从 发送[email protected]
,[email protected]
[email protected]
:只能从 发送[email protected]
,[email protected]
[email protected]
:应该能够从任何.*@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 ...
首先检查 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'
此映射检查数据库并返回给定 FROM 地址的正常 SMTP 用户,例如:
[email protected] -> [email protected]
[email protected] -> [email protected]
/etc/postfix/sender_overrides.cf
(pcre
):/.*@example.com/ [email protected]
此映射匹配全部
@example.com
FROM addrs 并返回管理员 SMTP 用户,例如:
.*@example.com -> [email protected]
问题
用户期望的行为:
sender_login_maps
Postfix 在第一个数据库中查找 FROM addr- 它找到与 FROM addr 匹配的条目
- 返回的 SMTP 用户 != 登录用户,因此我们尝试下一个映射数据库
sender_login_maps
Postfix 在下一个数据库中查找 FROM addr- 它找到与 FROM addr 匹配的条目
- SMTP 用户 == 登录用户
- Postfix 发送邮件成功
实际情况:
sender_login_maps
Postfix 在第一个数据库中查找 FROM addr- 它找到与 FROM 地址匹配的条目,所以查找过程停止
- 返回的 SMTP 用户 != 登录用户
- Postfix 拒绝该消息
解释
问题是 Postfix 不会检查两个映射并合并结果,而是在遇到以下情况时立即停止查找过程:第一的匹配查找返回任何 SMTP 用户。
如果返回的 SMTP 用户与当前授权的用户不匹配,它将不会继续在下一个数据库中查找地址,而是立即执行DENY
。
任何两个共享密钥的映射都可能发生相同的查找冲突,例如两个 mysql 数据库mysql:...,mysql:...
,不一定只是一个pcre
带有/.*/
或 的/.*@example.com/
映射。任何精确匹配或通配符匹配(如第一个映射中)都将优先,并完全阻止查询第二个映射。@example.com [email protected]
场景 1:[email protected]
尝试从[email protected]
✅ 这可以正常工作,电子邮件已发送,因为地址与发件人匹配,符合预期。
[email protected]
通过 SMTP 登录以发送消息[email protected]
[email protected]
查找smtpd_sender_login_maps
回报[email protected]
- 发送成功,
[email protected]
== SMTP 认证用户[email protected]
场景 2:[email protected]
尝试从[email protected]
❌ 这不起作用,电子邮件被拒绝,因为 SMTP 授权用户与[email protected]
第一个查找结果不匹配[email protected]
。
[email protected]
通过 SMTP 登录以发送消息[email protected]
[email protected]
查找smtpd_sender_login_maps
回报[email protected]
- 发送失败,
[email protected]
!= SMTP 授权用户[email protected]
postfix/smtpd[16645]: NOQUEUE: reject: RCPT from webmail.mailserver[192.168.1.5]: 553 5.7.1 <[email protected]>: Sender address rejected: not owned by user [email protected]; from=<[email protected]> to=<[email protected]> proto=ESMTP helo=<mail.example.com>
如果我们翻转映射的顺序会怎么样?
smtpd_sender_login_maps =
pcre:..., # moving the pcre mapping above mysql makes it worse
mysql:...
翻转映射顺序,以便在 mysql 之前检查 pcre 文件,这并不能解决问题。它甚至会使情况变得更糟,因为 catchall会遮蔽 MySQL 中的所有真实用户,并阻止除发送电子邮件.*@example.com
之外的任何用户。[email protected]
[email protected]
当它在 pcre 文件中查找时,它会[email protected]
作为唯一允许的用户返回,并且在检查 mysql 数据库之前失败。
解决方案
A. 使smtpd_sender_login_maps
分离
如果映射中不包含任何键重叠,则顺序并不重要,并且任何与第一个数据库不匹配的查找都会按预期继续检查后续数据库。
列表中较早的映射不能具有任何“catchall”或通配符键,否则它们将匹配所有内容并掩盖后续映射的结果。
B. 使用 SQL 连接多个查找结果UNION
如果您使用mysql
表类型进行smtpd_sender_login_maps
映射,那么您可以在进行地址查找时控制运行的 SQL 查询,并且您可能能够在 SQL 级别连接多个映射。
UNION
假设所有映射都可以在同一个 MySQL 数据库中访问,则可以使用如下语句在 SQL 级别连接多个地址查找的结果:
main.cf
:
smtpd_sender_login_maps = mysql:/etc/postfix/sender_logins.cf
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
在此示例中,我们将在 SQL 中设置[email protected]
为wildcard_sending=1
,然后它将与每个查找结果以及常规用户和别名匹配一起返回,例如
[email protected] -> [email protected],[email protected]
[email protected] -> [email protected],[email protected]
C. 使用unionmap
组合多个映射
如果您使用的是 Postfix 3.0 或更高版本,您可能可以尝试使用新unionmap
功能,它可以一次查找所有映射并将结果连接在一起。
smtpd_sender_login_maps = unionmap:{
mysql:/etc/postfix/sender_logins.cf,
pcre:/etc/postfix/sender_overrides.cf }
通过此设置,mysql 结果将与 pcre 查找结果连接在一起,例如
[email protected] -> [email protected],[email protected]
[email protected] -> [email protected],[email protected]
看http://www.postfix.org/DATABASE_README.html#types 联合地图了解更多信息。
来源
因此,此处的“匹配”一词应以最狭义理解:仅指匹配的地址,而不是匹配的(地址、登录名)对。此外,当找到匹配的地址时,查找链会立即停止,并进行二进制拒绝/接受决策。
- https://www.mail-archive.com/[电子邮件保护]/msg00677.html
(我强烈建议阅读整个邮件列表以了解更多背景信息) - http://www.postfix.org/postconf.5.html#smtpd_sender_login_maps
- http://www.postfix.org/SMTPD_ACCESS_README.html#lists
- http://www.postfix.org/DATABASE_README.html#types
- http://www.postfix.org/access.5.html
- https://unix.stackexchange.com/questions/294300/postfix-prevent-users-from-changing-the-real-e-mail-address
- https://groups.google.com/forum/#!topic/mailing.postfix.users/X7Wj_nSEyKI
- https://workaround.org/ispmail/jessie/relaying-smtp-authentication