I'm using postfixadmin with included vacation feature. [email protected] is a regular mail-account i'm using to send e-Mails from different services via smtp auth - in this case in the vacation.pl file for to send the out-of-office message with the users FROM address. Everything is okay unless i enable smtpd_sender_login_maps.
As per postfix documentation the smtpd_sender_login_maps could have more than one lookup tables. I'm trying to use two, mysql and a hashed db file with additional ones where i use the [email protected] but a different FROM address. Each of them are working but when i specify both of them only the first table is working.
So, when having the mysql query first only the aliases from the mysql db can be used as FROM address:
smtpd_sender_login_maps =
mysql:/etc/postfix/virtual_alias.cf,
hash:/etc/postfix/allowed_sender_aliases.cf
and the vacation autoresponder is failing:
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>
When i specify the hashed db file first then the vacation autoresponder is working but the user is no longer able to send e-mails with his account or aliases:
smtpd_sender_login_maps =
hash:/etc/postfix/allowed_sender_aliases.cf,
mysql:/etc/postfix/virtual_alias.cf
log entry:
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]>
content of /etc/postfix/allowed_sender_aliases.cf:
@mail-server.cc [email protected]
content of /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'
Is there something wrong or is it really that only the first table is used? Strange is that the user is not able to send e-mail's anymore when using the hash table.
After much head-scratching, I think I found the answer. Postfix defines a "match" when searching
sender_login_maps
as any lookup that succeeds, but the result of that lookup is not considered.tl; dr
: If you intend to use a union of the results from all mappings insmtpd_sender_login_maps
, instead of just taking the first succeeding lookup result, the mappings must be combined somehow (e.g. via SQLUNION
orunionmap
).At its core, it's a surprisingly simple problem caused by Postfix's short-circuiting behavior when doing mapping lookups. Buckle up for the ride, this is a long answer...
Background
From the Postfix documentation on sender login maps:
http://www.postfix.org/postconf.5.html#smtpd_sender_login_maps
The purpose of using
smtpd_sender_login_maps
is to validate whether the currently authenticated SMTP user is allowed to send from a given FROM address in their message. It's a powerful feature to restrict the addresses a client can send from based on who the authenticated SMTP user is.smtpd_sender_login_maps
has many use cases, e.g.:Prevent a user from logging in and sending a message as someone else
> Bob can send as
[email protected]
, but can't send as[email protected]
Allow multiple users to send from a single shared address neither of them own
> Bob and Alice can both send as
[email protected]
Allow a single user to send from multiple addresses they own
> Alice can send as both
[email protected]
and[email protected]
Allow an admin to send messages from many addresses (even owned by others)
> Tracy, the IT admin can send as anyone
.*@example.com
And more... (remember, lookups can be via TCP socket, SQL query, regex, etc.)
Example Scenario
We're going to investigate this use-case in particular:
Imagine a setup where we have Postfix and MySQL running on a mailserver. There are 3 users stored in the DB who can authenticate via SMTP with postfix:
[email protected]
: can only send from[email protected]
,[email protected]
[email protected]
: can only send from[email protected]
,[email protected]
[email protected]
: should be able to send from any.*@example.com
addressRegular users can only send from their main address and their aliases, but we want to allow the admin to be able to masquerade as any sender without restrictions. In real life, this type of setup is useful whenever a user needs to be able to send from many addresses
.*@example.com
using only one SMTP login (e.g. if the addresses are dynamically-generated or belong to other users).The normal approach is to use
smtpd_sender_login_maps
to achieve this setup.The problem
The behavior the user expects:
sender_login_maps
dbsender_login_maps
dbWhat actually happens:
sender_login_maps
dbExplanation
The issue is that Postfix doesn't check both mappings and merge the results, instead it stops the lookup process the moment it encounters the first matching lookup returning any SMTP user.
If the returned SMTP user doesn't match the currently authed user, it won't proceed to lookup the address in the next database, it'll just immediately
DENY
.The same lookup conflict can happen with any two mappings that share keys, e.g. two mysql databases
mysql:...,mysql:...
, not necessarily just apcre
with/.*/
or/.*@example.com/
. Any exact match or wildcard match like@example.com [email protected]
in the first mapping will take precedence and prevent the second mapping from being queried at all.Scenario 1:
[email protected]
tries to send from[email protected]
✅ This works fine, the email is sent because the address matches the sender as expected.
[email protected]
logs in via SMTP to send a msg from[email protected]
[email protected]
lookup insmtpd_sender_login_maps
returns[email protected]
[email protected]
== SMTP authed user[email protected]
Scenario 2:
[email protected]
tries to send from[email protected]
❌ This does not work, the email is rejected because the SMTP authed user
[email protected]
doesn't match the first lookup result[email protected]
.[email protected]
logs in via SMTP to send a msg from[email protected]
[email protected]
lookup insmtpd_sender_login_maps
returns[email protected]
[email protected]
!= SMTP authed user[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>
What if we flip the order of the mappings?
Flipping the order of the mappings so that the pcre file is checked before mysql won't fix the problem. It even makes it worse because the catchall
.*@example.com
will overshadow all the real users in MySQL and prevent any user other than[email protected]
from sending email.When it looks up
[email protected]
in the pcre file, it returns[email protected]
as the only allowed user and fails before it ever checks the mysql database.The solutions
A. Make the
smtpd_sender_login_maps
disjointIf the mappings don't contain any overlap in keys, then the order doesn't matter, and any lookup that doesn't match the first db will proceed to check the subsequent ones as expected.
Mappings earlier in the list cannot have any "catchall" or wildcard keys or they will match everything and overshadow results from later mappings.
B. Join multiple lookup results in SQL with
UNION
If you're using the
mysql
table type for yoursmtpd_sender_login_maps
mappings, then you can control the SQL query run when doing an address lookup and you may be able to join multiple mappings at the SQL level.Assuming all your mappings are accessible in the same MySQL database, you can concatenante the results of multiple address lookups at the SQL level using a
UNION
statement like so:main.cf
:sender_logins.cf
:In this example we would set
[email protected]
to havewildcard_sending=1
in SQL, and then it would be returned with every lookup result along with the normal user and alias matches, e.g.C. Use
unionmap
to combine multiple mappingsIf you're using Postfix 3.0 or above, you might be able to try using the new
unionmap
feature, which performs a lookup to all the mappings at once and concatenates the results together.With this setup, the mysql results will be concatenated with the pcre lookup results, e.g.
See http://www.postfix.org/DATABASE_README.html#types unionmap for more info.
Sources
(I highly recommend reading this entire mailing list thread for more context)