Skip to content

Instantly share code, notes, and snippets.

@iredmail
Last active November 18, 2022 04:57
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iredmail/fda90a4fd0fc4453f6a2c6c18638e047 to your computer and use it in GitHub Desktop.
Save iredmail/fda90a4fd0fc4453f6a2c6c18638e047 to your computer and use it in GitHub Desktop.
Split mail delivery

Split mail delivery

####################################################################
## THIS IS A DRAFT DOCUMENT.                                      ##
## DO NOT APPLY IT ON PRODUCTION SERVER WITHOUT TESTING.          ##
####################################################################

Summary

If you're hosting same mail domain on 2 servers, e.g. Google G-suite and iRedMail server, you can follow this tutorial to update iRedMail server settings to relay non-existing addresses (under the hosted mail domain) to G-suite. For example, mailbox xyz@domain.com exists on Gsuite but NOT on iRedMail, with this turotial, you're able to relay email sent to xyz@ to Gsuite.

WARNING

  • Although it's been tested locally and working, but it's not yet tested in production, so it may have some issue that i'm not aware yet. Please try it with a testing machine first, and report any issue you found in iRedMail forum: https://forum.iredmail.org/
  • If we implement this feature in iRedMail someday, the new SQL column mentioned in the document (domain.split_transport) MAY be different. You need to track the change by check the detailed release notes.

Add required new SQL column domain.split_transport

USE vmail;
ALTER TABLE domain ADD COLUMN split_transport VARCHAR(255) NOT NULL DEFAULT '';

Update Postfix configuration

  • With updated transport_maps, Postfix will query transport in order:
    • per-user transport
    • per-mailing-list transport
    • if no per-user or per-mailing-list transport, query transport for split deliver if the domain is configured to relay email which is sent to local domain but the recipient address doesn't exist locally.
    • if none of above 3 matches, query per-domain transport
  • With updated virtual_mailbox_maps, if split delivery is enabled, return a value so that Postfix considers the address exists locally.

Update Postfix main.cf

Update file /etc/postfix/main.cf, add 2 lines:

transport_maps =
    proxy:mysql:/etc/postfix/mysql/transport_maps_user.cf
    proxy:mysql:/etc/postfix/mysql/transport_maps_maillist.cf
    proxy:mysql:/etc/postfix/mysql/transport_maps_split.cf      # <- Add this line
    proxy:mysql:/etc/postfix/mysql/transport_maps_domain.cf

virtual_mailbox_maps =
    proxy:mysql:/etc/postfix/mysql/virtual_mailbox_maps.cf
    proxy:mysql:/etc/postfix/mysql/virtual_mailbox_split.cf     # <- Add this line

Create required files

###########################################
# WARNING
#
# Please replace the sample password by the real one which is used
# in other files under `/etc/postfix/mysql/*.cf`.
###########################################
  • /etc/postfix/mysql/transport_maps_split.cf
hosts       = 127.0.0.1:3306
user        = vmail
password    = 030b94feae9eab379a0a3c2cecd9775b
dbname      = vmail
query       = SELECT split_transport FROM domain WHERE domain='%d' AND backupmx=0 AND split_transport<>'' AND active=1
  • /etc/postfix/mysql/virtual_mailbox_split.cf
hosts       = 127.0.0.1:3306
user        = vmail
password    = 030b94feae9eab379a0a3c2cecd9775b
dbname      = vmail
query       = SELECT split_transport FROM domain WHERE NOT EXISTS (SELECT address FROM forwardings WHERE address='%s' AND active=1 LIMIT 1) AND domain='%d' AND backupmx=0 AND split_transport<>'' AND active=1

Make sure these 2 new files are owned by root:postfix with permission 0640, and restart Postfix service:

cd /etc/postfix/mysql/
chown root:postfix *split*.cf
chmod 0640 *split*.cf
service postfix restart

Update iRedAPD config file

iRedAPD plugin reject_sender_login_mismatch will reject emails relayed from another server if the sender domain is hosted on iRedMail, so you need to either disable this plugin, or tell iRedAPD to accept the "forged" emails by adding this setting in /opt/iredapd/settings.py:

ALLOWED_FORGED_SENDERS = ['domain.com']

Restarting iredapd service is required after the change.

Sample usage

Mailbox xyz@domain.com exists on Gsuite but NOT on iRedMail, to relay email sent to xyz@ to Gsuite, please set the relay address with SQL command like below:

USE vmail;
UPDATE domain SET split_transport='smtp:ASPMX.L.GOOGLE.COM:25' WHERE domain='domain.com';

Value of split_transport must be a valid Postfix transport. FYI: http://www.postfix.org/transport.5.html

Known issues

  • If either server receives a message sent to a non-existing address, the message will be relayed again and again between 2 servers until one of them decides to end the loop. It's safe to ignore because it's expected (address doesn't exist), but yes it's annoying.
@ugleiton
Copy link

ugleiton commented May 31, 2021

When I used your query in this file /etc/postfix/mysql/transport_maps_split.cf the email never arrived in iredadmin.

Changed the query to:

SELECT split_transport FROM domain WHERE domain='%d' AND NOT EXISTS (select username from mailbox where username='%s') AND backupmx=0 AND split_transport<>'' AND active=1

And then the emails arrived as expected.

tive que fazer o mesmo para receber os emails no iredadmin.

@erwin
Copy link

erwin commented Nov 18, 2022

Re: Known Issues

If either server receives a message sent to a non-existing address, the message will be relayed again and again between 2 servers until one of them decides to end the loop. It's safe to ignore because it's expected (address doesn't exist), but yes it's annoying.

I have been using split delivery for years with CommuniGate, and just researching how to do it with iredmail.

To prevent your server and it's logs being spammed with the undeliverable address loop, I use a rule that says:

If "Any Route" is "SMTP(aspmx.l.google.com)*"
and
If "Header Field" is "Received: from *.google.com*"
Then
Action "Write to the Log" "Loop Prevention - Discarded Message"
Then
Action "Discard"
Then
Action "Stop Processing"

Another simple way to prevent the loop for unknown addresses that I also used was:

  • Set a header, for example X-Server-Loop-Prevention: my-domain on all outbound messages
  • If you get a message with that header, you must have sent it, therefore it's a loop and safe to discard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment