Skip to content

Selectively forwarding email with Postfix

You may not always want to send every email though Sendamatic. Perhaps you only want to initially forward a small percentage of mail to test the service, or you may need to handle certain types of messages via different forwarders.

You can do this using Postfix header_checks.

Configure your forwarders

In these examples we'll only create one forwarder, Sendamatic. However, you can add more forwarders in the same fashion. Postfix will continue to directly deliver any mail not specifically matched by the header_checks patterns.

Create the credential files

Add the following line to /etc/postfix/sasl_passwd

[]:587 <credential ID>:<credential password>
# Create the postfix hashmap
sudo postmap hash:/etc/postfix/sasl_passwd

# Secure the credential files, only allow root to ready them
sudo chown root:root /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
sudo chmod 0600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db

Configure Postfix

sudo postconf -e "header_checks = regexp:/etc/postfix/header_checks" \
"smtp_sasl_auth_enable = yes" \
"smtp_sasl_security_options = noanonymous" \
"smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd" \
"smtp_use_tls = yes"

# CA certificates for Debian / Ubuntu
sudo postconf -e "smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt"

# CA certificates for Redhat / CentOS
sudo postconf -e "smtp_tls_CAfile = /etc/ssl/certs/ca-bundle.crt"

Add regex patterns

In /etc/postfix/header_checks, add regular expressions to match headers to forwarders.

These patterns may also match within the body of an email, so bear that in mind if you can't trust the email contents

Routing based on email subject

Here Postfix forwards only password reset and invoice emails through Sendamatic.

/^[sS]ubject:.*Password reset.*$/ FILTER smtp:[]:587
/^[sS]ubject:.*Invoice.*$/ FILTER smtp:[]:587

Routing a percentage of email

If you can configure the sending application to add a header a percentage of the time, that can then drive the Postfix routing.
import smtplib
import random
from email.message import EmailMessage

msg = EmailMessage()
msg.set_content('Hello world')
msg['Subject'] = 'Test message'
msg['From'] = '[email protected]'
msg['To'] = '[email protected]'

# Let Postfix forward percentage_chance % of email
percentage_chance = 0.05

if random.random() < percentage_chance:
    msg['X-Forwarder'] = 'sendamatic'

s = smtplib.SMTP('postfix-host')
/^X-Forwarder: sendamatic$/ FILTER smtp:[]:587

Reload Postfix

Finally, reload the Postfix configuration to make your changes take effect

sudo postfix reload

Verifying the changes

Swaks is a great tool for testing SMTP. Quickly adding headers is easy:

swaks --header 'X-Forwarder: sendamatic' -f '[email protected]' -t '[email protected]' --server 'postfix-host'

In the postfix logs, you can check the relay value to verify the routing works as expected.

# Tail the postfix SMTP logs on systemd machines
journalctl -f SYSLOG_IDENTIFIER='postfix/smtp'

Jan 08 15:33:51 postfix-host postfix/smtp[2641]: 123AB4C: to=<>,[]:587, delay=0.44, delays=0.05/0/0.25/0.14, dsn=2.0.0, status=sent...