Friday, June 8, 2012

Local postfix as relay to Amazon SES

Introduction

Alright, this is a quick guide for the impatient but otherwise experienced linux admin/hacker/hobbyist. Some past postfix experiences might be advantageous for general understanding and troubleshoooting.

Why would I want a local postfix and relay to another smtp anyway? Simple: When my application code needs to send an e-mail, there is an SMTP server ready to accept the e-mail from me. It will then take care of everything else like re-delivery, dealing with being grey-listed and many other things. Also, if connectivity to the SES SMTP happens to be interrupted it's no big deal because here too, the local postfix will handle re-sending for me. Nice, huh?

The documentation for setting up postfix as an SMTP relay to Amazon SES is correct but seems incomplete and I had to hunt a few bits of extra information so below is my complete config that I use on my EC2 instances and my development notebook.

Configuration (EC2)

So let's make this quick, here are the configs:

main.cf

This is just condensed and stripped to the bare minimum. We only acceppt connections from the localhost, this ensures we don't relay e-mail from any party.

##
## default config (condensed, no coments)
##
queue_directory   = /var/spool/postfix
command_directory = /usr/sbin
daemon_directory  = /usr/libexec/postfix
mail_owner        = postfix
inet_interfaces   = localhost
mydestination     = $myhostname, localhost.$mydomain, localhost
unknown_local_recipient_reject_code = 550
alias_maps        = hash:/etc/aliases
alias_database    = hash:/etc/aliases
debug_peer_level  = 2
sendmail_path     = /usr/sbin/sendmail.postfix
newaliases_path   = /usr/bin/newaliases.postfix
mailq_path        = /usr/bin/mailq.postfix
setgid_group      = postdrop
html_directory    = no
manpage_directory = /usr/share/man
sample_directory  = /usr/share/doc/postfix-2.3.3/samples
readme_directory  = /usr/share/doc/postfix-2.3.3/README_FILES

##
## some extras
##
inet_interfaces    = loopback-only
masquerade_domains = $mydomain

##
## use amazon ses via smtp with starttls
##
relayhost                      = email-smtp.us-east-1.amazonaws.com:25
smtp_sasl_auth_enable          = yes
smtp_sasl_security_options     = noanonymous
smtp_sasl_tls_security_options = noanonymous
smtp_sasl_password_maps        = hash:/etc/postfix/sasl_passwd
smtp_use_tls                   = yes
smtp_tls_security_level        = encrypt
smtp_tls_note_starttls_offer   = yes
smtp_tls_CAfile                = /etc/ssl/certs/ca-bundle.crt

master.cf

The only change to the default config is to comment out the fallback line just below smtp and the rest of the master.cf is unchanged

smtp      unix  -       -       n       -       -       smtp -v
# When relaying mail as backup MX, disable fallback_relay to avoid MX loops
relay     unix  -       -       n       -       -       smtp
#       -o fallback_relay=
#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5

Configuration (Development)

When applying the configuration to my development machine I've had to copy the ca-bundle.crt file. So for my development machine the last line of the main.cf file looks like this:

smtp_tls_CAfile                = /etc/ssl/certs/amazon-aws-ca-bundle.crt

Also, the path daemon_directory is different on my notebook. You can figure out the correct path by checking where your package manager installs all the postfix binaries, that's your daemon_directory. (or just look at the distro's config before overwriting it)

daemon_directory = /usr/lib/postfix

And that's all that's different between my EC2 and development hosts.

Amazon SES Credentials

Amazon requires sending systems to authenticate against their SMTP, so you need to first create an SES user and password in the AWS SES Console. Those will be generated by Amazon for you and you have 1 (one) opportunity to see/download those credentials. Put them in the file /etc/postfix/sasl_passwd as you can see here:

email-smtp.us-east-1.amazonaws.com:25 <user>:<password>
ses-smtp-prod-335357831.us-east-1.elb.amazonaws.com:25 <user>:<password>

Then run "postmap hash:/etc/postfix/sasl_passwd" and remove the file.

Verify your sender address

Amazon SES only allows you to send from a verified address. So head on over to the AWS Console and add a sender address, wait for the E-Mail and verify the sender address.

Note: If you're in sandbox mode, the recipient must also be a verified address, otherwise SES will bounce the e-mail!! For test/dev this is usually good enough, if you need to be able to send e-mails anywhere then it's time to request production access in the AWS Console.

Sending some test e-mails

This was my biggest pitfall when setting SES up. Sendmail must be invoked in a special way so it behaves nicely and the mail will actually be sent. The first line is the shell command, and the rest is typed into the console.

root@remote:/tmp$ sendmail -t -f verifiedsender@example.com
To: recipient@example.com
Subject: Testing postfix relay via Amazon SES
Hey there, this is a test E-Mail!!
.

Note the line with a single dot and nothing else on it. This tells sendmail the body of the e-mail has ended and the e-mail will be sent.

And that's it already. If you know what you're doing it'll take you 10mins tops to set this up and get it running. If you're doing it for the first time like I did it's more like 2-3hrs with troubleshooting. Anyway, I hope this will save some folks some time so they can concentrate on more important matters!

Troubleshooting

If you want some debugging Information from postfix, you can append "-v" on the smtp line in the master.cf and restart postfix. The postfix log is /var/log/maillog on most systems. You need to read the log carefully but it'll tell you everything you need to find most if not all problems.

3 comments:

  1. I would like to add one more step I needed to work through.

    On a new install of postfix, you need to generate the aliases.db with "newaliases". It wouldn't work for me until I set the default MTA to be postfix, using "alternatives --config mta".

    ReplyDelete
    Replies
    1. What distro are you on? I recently set up another pc with kubuntu and didn't have to run newaliases myself. And postfix apparently being the only/default mta here, I didn't run into the second issue either (or forgot about it).

      Thanks for the heads-up

      Delete
  2. We've been noticing a strange issue. Our Postfix server has:

    mydestination = xyz.abc.com, localhost.localdomain, localhost in the configuration file.

    When Postfix relays an email say to efg@hij.com via SES, SES returns the email as Recipient address rejected: User unknown in local recipient table. Does postfix try and send the efg@hij.com email to xyz.abc.com? How can we fix this?

    ReplyDelete