Introducing Postfixer

"Have you got anything without spam?"

We recently heard from a client concerned that a few of his customers weren't receiving automated emails. His email account was filling with hundreds of bounce notifications, customers were getting frustrated, and there was nothing he could do about it.

Inspection of the Postfix logs on his and several other servers revealed a systematic problem. When these apps were first deployed, most emails were successfully delivered to customers (although a few might have ended up in the Spam/Bulk folder). Over the years, however, the spam problem has only gotten worse; and email clients, ISPs, and mail services aggressively terminate suspect messages with extreme prejudice.

Enter Postfixer

Most of us aren't Postfix gurus, let alone email experts. Postfixer helps with the following error-prone tasks:

  • Configuring Postfix properly
  • Setting up the necessary DNS entries
  • Testing everything to ensure your emails make it to the Inbox

Postfixer is a standalone utility powered by Capistrano. Git clone it, drop in a config file, and say goodbye to the Spam folder.

$ git clone https://github.com/sumbach/postfixer.git
$ cd postfixer
$ cp config-hostname.example.com.yml config-errbit.thinkrelevance.com.yml

Here's a sample config:

#
# config-errbit.thinkrelevance.com.yml
#
canonical_hostname: errbit.thinkrelevance.com
additional_hostnames:
  - utility.thinkrelevance.com
email_domains:
  - thinkrelevance.com
forwarding_address: utility-errors@thinkrelevance.com
envelope_sender: bounces@thinkrelevance.com
application_user: deploy

sudo_user: deploy
address: errbit.thinkrelevance.com

Let's break it down:

canonical_hostname: Fully-qualified domain name (FQDN) for your application server

additional_hostnames: Any additional hostnames that this server is known by (may be empty)

email_domains: All domains for which this server should be able to send email

forwarding_address: Email address for local messages (such as errors from cron jobs)

  • I typically use "errors@" followed by the first entry from email_domains.
  • The domain (the part following the @ sign) MUST be included in email_domains.
  • Email addressed to local user accounts (root, nobody, etc) will be forwarded to this address.

envelope_sender: SMTP envelope sender (where bounce messages end up)

  • As with forwarding_address, the domain (the part following the @ sign) MUST be included in email_domains.

application_user: Local user account under which your application runs

  • Emails addressed to this user's account (for example, error messages from cron jobs) will be sent to forwarding_address.
  • Outgoing email from this user's account (messages sent by ActionMailer without an explicit From address or sent using the /usr/sbin/sendmail binary, for example) will be rewritten to change the From address to the forwarding_address set above.

sudo_user: Local user account with root sudo permissions

  • Postfixer will SSH into the server using this account. Might be the same as application_user.

address: FQDN or IP address used to SSH into this server

Once you've updated your Postfixer config, a number of cap tasks are at your disposal:

$ export CONFIG=errbit.thinkrelevance.com   # specifies which .yml file Postfixer uses
$ cap email:send_test_email                 # send a test message
$ cap email:check_dns                       # print a report of your existing DNS entries

The send_test_email task uses the very helpful port25 verifier. If you're like me, the response to the test email looks something like this:

==========================================================
Summary of Results
==========================================================
SPF check:          softfail      (BAD)
DomainKeys check:   neutral       (BAD)
DKIM check:         neutral       (BAD)
Sender-ID check:    softfail      (BAD)
SpamAssassin check: ham           (GOOD)

Yuck! Let's fix that up.

$ cap email:install_packages                # install DKIM-milter (and Postfix, if not installed)
$ cap email:backup_config                   # archive your current config files
$ cap email:generate_config                 # generate new config file based on the .yml
$ cap email:install_config                  # copy new config files to the server
$ cap email:restart                         # restart Postfix and DKIM-milter
$ cap email:print_dns                       # generate a report of the DNS changes you should make

Based on the output from print_dns, you should add/update your DNS configuration. If you're using Slicehost for DNS, log in and edit DNS entries via SliceManager. Otherwise, check with your registrar (Namecheap, GoDaddy, etc).

(Note: print_dns assumes you're using Google Apps. If you're using your ISP's or your own incoming mail server, replace "include:_spf.google.com" in the SPF records with something appropriate to your setup.)

Once you've updated your Postfix config and DNS entries, give cap email:send_test_email another try.

==========================================================
Summary of Results
==========================================================
SPF check:          pass          (GOOD)
DomainKeys check:   neutral       (BAD)
DKIM check:         pass          (GOOD)
Sender-ID check:    pass          (GOOD)
SpamAssassin check: ham           (GOOD)

Ahhhhh, much better.

Caveats

Postfixer does not (yet) check if your server is on a blacklist

  • I recommend the awesome DNSBL Lookup tool from mxtoolbox.com
  • If your server is on a blacklist, you'll need to request to be removed (the process should be available on the blacklist provider's web site)

Postfixer does not check for general DNS issues

Posfixer does not (yet) support Yahoo DomainKeys (as seen in the port25 verifier results above)

Thanks

Huge thanks to Relevance for giving me Fridays to make these kinds of contributions.

Thanks also to Shay Frendt for being my first guinea pig and for his new feature ideas.

Thanks to Mike Bailey for deprec, from which I got the idea to generate config files locally (using ERB templates) and deploy via Capistrano.

I'd like to contribute

That sounds great; I'd like you to contribute, too! Postfixer is available on Github and distributed under the MIT license.