Mailu: Allow specific users to send email from any address

Created on 5 Aug 2019  ·  33Comments  ·  Source: Mailu/Mailu

Before switching to Mailu I had postfix set up so that my admin user [email protected] could impersonate / send email as any other user.

This was extremely useful for sending email programmatically from multiple sources, e.g. from a web app that needs to send email from *@mydomain.dev and several other domains like *@admin.mydomain.dev/*@someotherdomain.dev.

I suspect I can add something into overrides/postfix.conf to get this behavior back, but I'm not sure how to do it since I only want a specific user to be able to impersonate others, not all users. Ideally there would be a UI page to configure the permitted sender addresses for each user, with sqlite-style wildcard support.

smtpd_client_restrictions =
  permit_mynetworks,
  check_sender_access ${podop}senderaccess,
  reject_non_fqdn_sender,
  reject_unknown_sender_domain,
  reject_unknown_recipient_domain,
  reject_unverified_recipient,
  permit

smtpd_relay_restrictions =
  permit_mynetworks,
  permit_sasl_authenticated,
  reject_unauth_destination

For now, given I'm the only one using the server and I need this behavior back ASAP, can I just remove the check_sender_access ${podop}senderaccess, line? Or is there a better long-term fix?

I also cant just use RELAYNETS with some whitelisted IPs becuase the servers doing the sending have dynamic IP addresses that often change, I need it to be whitelisted on a per-user basis not a per-sender-ip basis.

Useful links:

backlog typenhancement

Most helpful comment

Hi There,

The Mailu-Project is currently in a bit of a bind! We are short on man-power, and we need to judge if it is possible for us to put in some work on this issue.

To help with that, we are currently trying to find out which issues are actively keeping users from using Mailu, which issues have someone who want to work on them — and which issues may be less important. These a less important ones could be discarded for the time being, until the project is in a more stable and regular state once again.

In order for us to better assess this, it would be helpful if you could put a reaction on this post (use the :smiley: icon to the top-right).

  • 👍️ if you need this to be able to use Mailu. Ideally, you’d also be able to test this on your installation, and provide feedback …
  • 🎉 if you find it a nice bonus, but no deal-breaker
  • 🚀 if you want to work on it yourself!
    We want to keep this voting open for 2 weeks from now, so please help out!

All 33 comments

Using relaynets is probably better advised, since removing sender access will also allow illegitimate impersonation from anywhere, even external scammers.

Then, depending on your setup you could use aliases: a user can impersonate any alias that points to its address. Otherwise we will need to implement this while it is not entirely trivial: sender access verifications do not pass the authenticated user to the background.

Wait but isn't it all authenticated with the SMTP credentials? How would a spammer be able to send from anywhere if they don't have SMTP credentials for the server?

All registered user accounts on this server are trusted, it's just me and a couple other people, so there's no chance of users that have SMTP credentials / registered accounts being spammers. I just want to authorize a single SMTP user/pass to be able to send from %@mydomain.com (or even better, %@% without domain validation).

I tried the alias setup you suggested, the problem is that the alias for %@mydomain.com that allows it to send from any address conflicts with other accounts and becomes a catch-all for received mail, I just want to allow arbitrary sending without changing the received mail routing.

I also cant just use RELAYNETS with some whitelisted IPs becuase the servers doing the sending have dynamic IP addresses that often change.

Incoming mail using is not authenticated: SMTP only supports authenticating local users, remote users aren't. And sender access is the set of rules that makes sure that remote servers cannot impersonate your local users, among other things.

Also, the catch all alias is probably the simplest way to go. It should not conflict with any other alias or local user since it should have the lowest priority. It will just act as a default catch all.

Right, I know incoming mail isn't authenticated, I meant for outgoing mail.

makes sure that remote servers cannot impersonate your local users, among other things.

This is exactly what I'm trying to accomplish, I need my servers to be able to send from *@allmydomains.com using one set of SMTP credentials.

I already tried the catchall, it didn't work, all relayed emails got rejected due to sender addresses not being owned by the SMTP user.

I figured it out using overrides. For anyone else trying to do this, here's the full config I ended up with. This config will allow a specific authenticated mailu/smtp user [email protected] to send mail from *@*, and as an example I added a slightly more limited [email protected] user that can only send from any *@otherdomain.com address.

data/overrides/postfix.cf:
Beware, this removes most restrictions on allowed sending addresses (do not use with mailservers that have open registration, only use if all your users are trusted / never send spam)

smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination
smtpd_sender_login_maps = unionmap:{socketmap:unix:/tmp/podop.socket:senderlogin, pcre:/overrides/sender_logins}
sender_dependent_relayhost_maps = texthash:/overrides/relay_hosts
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_password_maps = texthash:/overrides/sasl_passwd

Note: this used to say pcre:/overrides/sender_logins, socketmap:unix:/tmp/podop.socket:senderlogin but was fixed later.

texthash: is an alternative to hash: used so you don't have to run postmap every time you update them. There's little point in generating hashmaps for 3 line files, and the standard postfix setup already expects these to be stored in root-readable cleartext form next to the hashes anyway. You can also use pcre: instead of texthash: if you want to use regex to match addresses.

data/overrides/sender_logins:
Authorize only the [email protected] smtp user to send from any address on any domain
Authorize the [email protected] user to send from any @otherdomain.com address

/.*/                      [email protected]
/.^*@otherdomain.com/     [email protected]

After setting this up I verified that it works without needing to configure a catchall alias for [email protected], and I also verified that the server is not acting as an open relay using a few different mailserver security scanning tools. The major change is that this config no longer restricts sender addresses as thoroughly (e.g. by checking that they're valid public FQDNs that resolve back to the correct ip and are owned by the correct user).

Assuming your users are trusted, the rest of the public-facing security model is relatively unimpacted, the server still requires proper sasl authentication and users not configured in sender_logins are still restricted to only sending from addresses they own. For users in sender_logins, sender address validation is totally unrestricted, they can even send from invalid addresses like root@localhost or root@12345 (but that's what I was trying to do all along).

(correct me if any of this is wrong though, I probably missed something along the way)


An unrelated optional thing that I ended up needing was the ability to relay some sending addresses through different smtp servers/providers.

You don't need this to use the above config, but you can add it if some of your sending address domains should be sent through a different server. (e.g. if you prefer to use a 3rd party like Mailgun or if spf/dmarc/dkim/mx records only allow sending from a specific server).

data/overrides/relay_hosts:
Relay all email sent from *@yetanotherdomain.com through Mailgun instead, relay *@example.com through mail.example.com, etc.

@yetanotherdomain.com     [smtp.mailgun.org]:587
@somegoogleappsdomain.com [smtp.gmail.com]:587
@example.com              [mail.example.com]:25

data/overrides/sasl_passwd:
credentials to use for remote smtp servers

[mail.example.com]:25 [email protected]:passwordhere
[smtp.mailgun.org]:587 [email protected]:mailgunpasswordhere
[smtp.gmail.com]:587 [email protected]:passwordhere

This should probably done at the admin service because it delivers the list of allowed "from" addresses to postfix.

I would also love to see this feature, as this is a key requirement of working with Owncloud/Nextcloud (the Owncloud server needs to be able to log in to Mailu and send email on behalf of many Mailu users).

@pirate I tried your solution and was not able to get it to work. Firstly I put these in the overrides folder (not under /data/overrides, which did not work at all).

When I make your above changes, I am able to send from my admin email as any other email on the server, however, I cannot send as a "normal" user anymore, sending as themself. I get this error:

smtp_1 | Feb 06 22:30:47 mail2 postfix/smtpd[194]: NOQUEUE: reject: RCPT from mailu_webmail_1.mailu_default[xxx.xxx.xxx.xxx]: 553 5.7.1 <[email protected]>: Sender address rejected: not owned by user [email protected]; from=<[email protected]> to=<[email protected]> proto=ESMTP helo=<domain.com>

where [email protected] is a standard user, sending mail as themself.

I am not using the optional configs in your suggestions; perhaps some lines need to be removed from overrides/postfix.cf? I'm not overly familiar with the overrides file, so I didn't want to blindly start trying changes...

Hello!

I'm struggleing as well. I love to setup one or two users that are able to send as every user, but all other users should send email using their own email adress. When I add the settings in override, I can achieve that I can send emails from my website through PHP using a different sender adress, but when I use the integrated rainloop, I cannot send mails. The error is this:

postfix/smtpd[131]: NOQUEUE: reject: RCPT from mailu_webmail_1.mailu_default[192.168.203.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=<test.example.de>

@pirate, I guess, I'm facing the same issue as @fracture-point does.

Any ideas?

Best regards

Jens

Strange... can you post your exact config used in overrides/*?

Sure. Here are my files:

postfix.cf:
$ cat postfix.cf
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination
smtpd_sender_login_maps = pcre:/overrides/sender_logins, socketmap:unix:/tmp/podop.socket:senderlogin
sender_dependent_relayhost_maps = texthash:/overrides/relay_hosts
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_password_maps = texthash:/overrides/sasl_passwd

sender_logins:
$ cat sender_logins
/.*/ [email protected]

The files relay_hosts and sasl_passwd are empty, because I only want to have this single mail address "[email protected]" to be able to send mails from every address wehile all other should only be able to send through their own address.

Thanks and best regards

Jens

Actually I just realized my server has this issue too!

postfix/smtpd[16645]: NOQUEUE: reject: RCPT from mailu_webmail_1.mailu_default[192.168.203.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=<mail.zervice.io>

I'll investigate and report back. I didn't notice until now because I had only been using the RoundCube UI to send as the admin user.


Edit 1: Could be related to this issue: https://serverfault.com/questions/948362/postfix-multiple-smtpd-sender-login-maps


Edit 2: Aha! I looks like this is the reason:

Looking back at the documentation: "Tables will be searched in the specified 
order until a match is found".

Word "match" is thus to be taken here in the narrowest sense: just a matching 
address, not a matching (address, login name) pair. Moreover, when a matching 
address is found, the lookup chain is immediately halted with a binary 
reject/accept decision.

https://www.mail-archive.com/[email protected]/msg00677.html

So the PCRE line /.*/ [email protected] in the overrides/sender_logins matches all addresses and overshadows the podop db lookup.


Edit 3: Yeah that's definitely the cause, see: https://serverfault.com/questions/948362/postfix-multiple-smtpd-sender-login-maps/1005041#1005041

So I think the solution is probably to reverse the order of the sender map lookups:

overrides/postfix.cf:

smtpd_sender_login_maps = socketmap:unix:/tmp/podop.socket:senderlogin, pcre:/overrides/sender_logins

Edit 4: nvm, reversing the order doesn't seem to help, because if you impersonate another user that actually exists, the first db lookup will return their username, and it wont match yours causing the send to fail. It seems the only way may be to allow everyone to masquerade as each-other with no restrictions?

smtpd_sender_restrictions = reject_unauthenticated_sender_login_mismatch

The alternative is to edit the Mailu source that handles the socketmap:unix:/tmp/podop.socket:senderlogin sender login lookup:

@internal.route("/postfix/sender/login/<path:sender>")
def postfix_sender_login(sender):
+    # SMTP usernames of users that are allowed to send from *any* FROM address, even addresses they dont own
+    WILDCARD_SENDERS = [username.strip() for username in os.environ.get('WILDCARD_SENDER', '').split(',')]
+    # e.g. [email protected]
+    #      [email protected],[email protected]

    localpart, domain_name = models.Email.resolve_domain(sender)
    if localpart is None:
-        return flask.abort(404)
+        return flask.jsonify(",".join(WILDCARD_SENDERS)) if WILDCARD_SENDERS else flask.abort(404)

    destination = models.Email.resolve_destination(localpart, domain_name, True)
-    return flask.jsonify(",".join(destination)) if destination else flask.abort(404)
+    return flask.jsonify(",".join([*destination, *WILDCARD_SENDERS]) if destination or WILDCARD_SENDERS else flask.abort(404)

https://github.com/Mailu/Mailu/blob/master/core/admin/mailu/internal/views/postfix.py#L43


Edit 5: There may be another way with unionmap, stay tuned!

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 in smtpd_sender_login_maps, instead of just taking the first succeeding lookup result, the mappings must be combined somehow (e.g. via SQL UNION or unionmap).

The behavior the user expects:

  1. Postfix looks up the FROM addr in the first sender_login_maps db
  2. it finds a matching entry for the FROM addr
  3. the returned SMTP user != logged in user, so we try the next mapping db
  4. Postfix looks up the FROM addr in the next sender_login_maps db
  5. it finds a matching entry for the FROM addr
  6. the SMTP user == logged in user
  7. Postfix sends the message succesfully

What actually happens:

  1. Postfix looks up the FROM addr in the first sender_login_maps db
  2. it finds a matching entry for the FROM addr, so the lookup process stops
  3. the returned SMTP user != logged in user
  4. Postfix rejects the message

MySQL is checked first, then the pcre is checked only if the mysql lookup returned 0 results.

Read the full explanation on my blog: https://docs.monadical.com/s/using-multiple-smtpd-sender-login-maps-in-postfix or on ServerFault: https://serverfault.com/a/1005041/214836

So the actual solution for the Mailu overrides is a one-line fix (@fracture-point @jheinitz):

overrides/postfix.cf:

smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination
-smtpd_sender_login_maps = pcre:/overrides/sender_logins, socketmap:unix:/tmp/podop.socket:senderlogin
+smtpd_sender_login_maps = unionmap:{socketmap:unix:/tmp/podop.socket:senderlogin, pcre:/overrides/sender_logins}
sender_dependent_relayhost_maps = texthash:/overrides/relay_hosts
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_password_maps = texthash:/overrides/sasl_passwd

I just deployed it on my Mailu server and confirmed it works :smile::

  • regular users are able to send from their own address
  • regular users are able to send from their aliases
  • users with a wildcard/catchall/regex defined in overrides/sender_logins are able to send as all the addresses they match
  • non override/admin users are not allowed to send from any address, so it's not an open relay

Give that fix a shot and let me know if you're still having any issues.

Moin pirate.

Thanks for the detailed explanation. I just deployed the change on my server and I can confirm that it works perfectly. Sometimes it is worth to dig a little bit deeper into the documentation.

Again, thank you so much for solving this issue.

Best regards

Jens

Thank you so much!!
I have used your instructions to configure Odoo v13 to works with mailu. And works great!

Sorry for grabbing this up again from the dead …
… but @pirate — your works seems really helpful to a lot of users out there. Currently, I don’t see we have the resources to implement this as a full-blown feature (which would need UI, database, integration into podop, postfix, etc), especially given the initial concerns given by @kaiyou (that i share).
Your workaround on the other hand is lean and easy to understand.

Would you care to add this to the documentation? That way, other users can more easily profit from this ….

Thanks a bunch regardless of what you’ll decide ^_^.

That aside … how do you reckon we should go forward with this ticket? Leave it open as a reminder that this is a potential future feature (with bigger implementation), or close it as there’s a known and feasible workaround?

Thanks! I'm fine with adding it to a docs file or wiki somewhere, no point building UI and db, etc. unless there's bigger demand.
One question though, can you or @kaiyou clarify this part:

Using relaynets is probably better advised, since removing sender access will also allow illegitimate impersonation from anywhere, even external scammers

Based on what I've learned so far, this is not actually the case, but I could be wrong.

So far on my server it seems like smtpd clients are only able to send arbitrary mail if they are the (Mailu password authenticated) admin user. Regular mailu users (and external clients) remain restricted to sending from only addresses they own. I'm definitely curious, because I don't want spoofed sender relaying happening on my servers, and because I want to make sure the docs are accurate with non-harmful/secure defaults in the examples. If either you or @kaiyou has more context on this statement I'm all ears.

Wonderful, thanks @pirate , that did the trick. Here's what I've added to my deployment script, which would be run during the install/migration:

cat > /mailu/overrides/postfix.cf <<'endmsg'
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination
smtpd_sender_login_maps = unionmap:{socketmap:unix:/tmp/podop.socket:senderlogin, pcre:/overrides/sender_logins}
sender_dependent_relayhost_maps = texthash:/overrides/relay_hosts
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_password_maps = texthash:/overrides/sasl_passwd
endmsg

cat >  /mailu/overrides/sender_logins <<'endmsg'
/.*/                      [email protected]
endmsg

touch /mailu/overrides/relay_hosts
touch /mailu/overrides/sasl_passwd

Thanks for this issue, but unfortunately I still get the following error:
reject: RCPT from unknown[10.0.0.2]: 553 5.7.1 <no-reply@xxx>: Sender address rejected: not owned by user admin@xxx

Does anyone know what could be the problem?

Can you post your verbatim configs (with passwords and hostnames redacted) @rabauss?

settings are like @fracture-point mentioned:

//overrides/postfix.cf
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, defer_unauth_destination
smtpd_sender_login_maps = unionmap:{socketmap:unix:/tmp/podop.socket:senderlogin, pcre:/overrides/sender_logins}
sender_dependent_relayhost_maps = texthash:/overrides/relay_hosts
smtp_sasl_auth_enable = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_password_maps = texthash:/overrides/sasl_passwd
//overrides/sender_logins
/.*/                   admin@xxx

empty files overrides/sasl_passwd + overrides/relay_hosts

EDIT:
I have to say, that I tried to send from no-reply@yyy - different domain!
So I'm not sure if the same domain would work, I would have to test it again.
But I can't find any errors in my settings for different domain :-( - maybe the file permissions on the docker host?

I'd also really like to be able to use this, and relaynets aren't what I need - I need something which can use some admin credentials, from any network, and send a notification from *@mydomain.com. Especially for people without a static IP, relaynets aren't useful.

Is this planned to be added to the gui, or is it still an edit in a config file?

This might sound odd, but upon further investigation it would appear that only the first line in /overrides/sender_logins is getting parsed? If I have two lines, the account on the 2nd line gets a "Sender address rejected" when sending as a different account. 1st line works fine, and if I switch the order, the 1st line continues to be observed properly and the 2nd line is not. 🤔Not sure if there is a syntax here around spacings/tabs, etc. but I've tried both, and I've also tried with/without a blank line at the end of the file.

Can you post your sender_logins file (redacted) @fracture-point, it may be due to overlapping regexes? You could also try creating a whole other pcre file and adding it to the unionmap: {...} list.

Remeber, postfix stops the halts lookup the moment the address matches, so you cannot have two .* regexes in the same file or one will overshadow the other, you must add another unionnmap entry if you want to have multiple wildcard lines.

Okay so maybe that's the problem - I have sender_logins currently configured with two "cross-domain admins" as such:

/.*/                      [email protected]
/.*/                      [email protected]

If this is improper, how might I accomplish having 2 such accounts?

Yeah that won't work, the first regex will match everything and the second one will never be checked. Create a 2nd file containing only the 2nd line and add it to the unionmap list.

It's an unfortunate result of the "short-cirtuiting" implementation that postfix uses, and is why I wrote a whole blog post about how counter-intuitive it is haha. Unfortunately I think I made it too long so no one reads it, but that's my own bad writing's fault. (this behavior actually happens across many postfix config options that use this short-circuiting lookup implementation, not just sender_logins, which is why I felt a whole 3-page post was justified)

A little bit clunky, but that works! Thank you @pirate.

Oh I guess, that was my problem as well - thanks @pirate :-/

Hi There,

The Mailu-Project is currently in a bit of a bind! We are short on man-power, and we need to judge if it is possible for us to put in some work on this issue.

To help with that, we are currently trying to find out which issues are actively keeping users from using Mailu, which issues have someone who want to work on them — and which issues may be less important. These a less important ones could be discarded for the time being, until the project is in a more stable and regular state once again.

In order for us to better assess this, it would be helpful if you could put a reaction on this post (use the :smiley: icon to the top-right).

  • 👍️ if you need this to be able to use Mailu. Ideally, you’d also be able to test this on your installation, and provide feedback …
  • 🎉 if you find it a nice bonus, but no deal-breaker
  • 🚀 if you want to work on it yourself!
    We want to keep this voting open for 2 weeks from now, so please help out!

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Hello,
I did it by an other simplest way.
The restriction is here : reject_authenticated_sender_login_mismatch

# Internal SMTP service                                                                                                                                                                       
10025     inet  n       -       n       -       -       smtpd                                                                                                                                 
  -o smtpd_sasl_auth_enable=yes                                                                                                                                                               
  -o smtpd_client_restrictions=reject_unlisted_sender,**reject_authenticated_sender_login_mismatch**,permit                                                                                       
  -o smtpd_reject_unlisted_recipient=no                                                                                                                                                       
  -o cleanup_service_name=outclean

So I did a quick overrides/postfix.master

10025/inet=10025      inet  n       -       n       -       -       smtpd -o smtpd_sasl_auth_enable=yes -o smtpd_client_restrictions=reject_unlisted_sender,permit -o smtpd_reject_unlisted_recipient=no -o cleanup_service_name=outclean

My 2 cents
Cyril

Unless I'm mistaken, I believe this opens up your server to allow any auth'd user to send from any address @clopnis. This is less granular (and potentially more dangerous) than the other solution, which only allows specific senders to send from specific patterns (which could be *@*, but don't have to be, e.g. you could only allow sending from bot-*@example.com).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

alizowghi picture alizowghi  ·  3Comments

elektro-wolle picture elektro-wolle  ·  3Comments

githtz picture githtz  ·  4Comments

gizocz picture gizocz  ·  4Comments

Diman0 picture Diman0  ·  3Comments