We have a setup where postfix sends mails via Amazon SES relay. All is working fine except email forwards.

While this topic has already been discuessed at least here and here, there are still some points which I can't wrap my head around.

The problem is that Amazon SES won't send emails, where From: is not verified. So when an internal address wants to forward to an external and the sender is external as well, the mail will not get sent.

To solve this, we currently use the following config in main.cf

header_checks = regexp:/etc/postfix/first_header_checks
smtp_header_checks = regexp:/etc/postfix/second_header_checks
sender_canonical_maps = regexp:/etc/postfix/sender_canonical
sender_canonical_classes = envelope_sender
smtpd_data_restrictions = check_sender_access pcre:/etc/postfix/sender_access

With first_header_checks

/^From:(\s)?(.*)/i PREPEND X-Original-From: $2
/^To:(\s)?(.*)$/i PREPEND X-Original-To: $2


/^From:(.*)/i REPLACE From: <no-reply@verified-domain.com>


/.*/    user@verified-domain.com


/(.*)/  prepend Reply-To: <$1>

This works great for incoming mail. user@external.com sends the mail to me@verified-domain.com and it gets forwarded to new@another-external.com

Reply-To: <user@external.com>
X-Original-To: <me@verified-domain.com>
To: new@another-external.com
From: <no-reply@verified-domain.com>
X-Original-From: <user@external.com>

The problem, this also happens for outgoing mail from the server. Say me@verified-domain.com sends a mail, the from gets rewritten to no-reply and a Reply-To will be set. This I want to fix. The mail headers should only be rewritten for incoming mail that will be forwarded.

I have tried using regular expressions like !/^From:(\s)?(.*@verified-domain\.com)/ but so far with no luck.


Postfix 2.1 and above supports conditional operators within regex and pcre tables. In your case second_header_checks using these conditionals should look like this:

if !/^From:(.*)@verified-domain.com/i
/^From:(.*)/i REPLACE From: <no-reply@verified-domain.com>

You can test your lookup tables without actually sending anything like this:

peter@mail:~peter $ cat msgheaders
From: <peter@external.com>
To: new@another-external.com

peter@mail:~peter $ postmap -hmq - regexp:/etc/postfix/second_header_checks < msgheaders
From: <peter@external.com>     REPLACE From: <no-reply@verified-domain.com>

peter@mail:~peter $ cat msgheaders-1
From: <peter@verified-domain.com>
To: new@another-external.com

peter@mail:~peter $ postmap -hmq - regexp:/etc/postfix/second_header_checks < msgheaders-1
peter@mail:~peter $ 
  • Problem with the negative regex is, that all email not from this person or domain will be replaced. I actually have multiple scenarios where I need to apply this - all the same postfix on the same machine. Which is why I want to match the to: and according to that change from: – kero Feb 24 '17 at 15:45
  • Well, smtp_header_checks only kick in if your Postfix is sending something out, that is, acting as SMTP client, so masking out your own domains with one regex (using alternation regex) should solve your problem. Postfix header_checks work one header line at a time feeding it through the maps, so if you need complex logic you should consider using Procmail or Dovecot Sieve.. – Peter Zhabin Feb 24 '17 at 16:10
  • I THINK you can add the extra steps by looping mail over multiple transports, but that sounds terribad...you could also look for a custom header if x-skipcheck then continue. – Jacob Evans Feb 27 '17 at 3:06
  • @PeterZhabin Sorry if I am not making myself clear. The problem is that - as far as I know - I need to make an if to == x and change from then. As you said, header_checks uses only one line at a time, so it cannot be used for this. I have no experience yet with either Procmail or Dovecot Sieve – kero Feb 27 '17 at 15:50

I was attempting to do something very similar to this w/ a different email service, heres how I solved it (after getting much help from this thread):

  1. Set up a transport map that sets the next hop to a locally running smtp daemon for your external destination example: somebody@external.com smtp:localhost:9999
  2. Add a new locally running smtp daemon to master.cf, apply the header checks, canonical maps, & canonical_classes to only this daemon
  3. use a content_filter on the locally running daemon to point to the mx server of the external dest. Mine looked like this -o content_filter=smtp:[mx.somedomain.com]:25
  4. You might need to modify your smtpd_recipient_restrictions on the locally running daemon only to permit it to act as a relay. For example: -o smtpd_recipient_restrictions=permit_mynetworks,defer (This might not be ideal, dbl check security policy before pushing live)

Good luck yo, if you need any more details i can try and help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.