To help reduce the load on my Postfix mail servers, I rely on Postscreen (which is built in to Postfix) to help weed out bot spam and misconfigured mailers. And to reduce the load on Postscreen, one of my tricks is to whitelist the outbound IP addresses of a couple of the larger webmail providers, such as Google’s Gmail and Microsoft’s Hotmail (and Outlook.com). This is not a “full” whitelist of a message from those mailers, however. I only whitelist connections from those mailers to get past Postscreen, which is merely the first line of defense on my mail systems. All incoming messages still get inspected for spam, viruses, and other unwanted stuff. By creating these whitelists, I’m simply telling Postscreen to save some time (and resources) on inbound connections from an IP address that’s known to belong to Google or Microsoft. I’d gladly include Yahoo as well, but they make the process of finding a true list of their outbound IPs near impossible.
So for now, I just do Google and Microsoft. (This project started out with just Google and Microsoft, but has expanded into more).
Postscreen Whitelisting Needs
Of course, the IP addresses used by large webmailers are subject to change at their whim, so I needed an automated process that I could run weekly that would do the following:
- Query the webmailer directly to “ask” them for a current list of their outbound IP addresses.
- Collect the responses and create a list whitelist of the webmailer’s CIDR ranges and/or IP addresses.
- Reload the Postfix configuration to pick up any changes.
The initial idea for Postwhite was based on script from Mike Miller that does steps 1 and 2 above for Gmail. I tweaked the script in a very minor way to allow it to reload the configuration when it was done, then I created a weekly cron job that would store the output in the Postfix directory. It worked pretty well, except that when I wanted to add support for Hotmail or Outlook.com, I needed another script, which had to be tweaked to deal with the differences between Google and Microsoft’s approaches to SPF records (spoiler alert: Google’s is much simpler).
The original version of this article went into more detail about the previous scripts I used, but not long after publishing this post, I received a comment from the author of SPF-Tools, who suggested that his SPF querying script might simplify things for me. He was right, so I re-wrote my scripts in a new script called Postwhite that uses the despf.sh script from SPF-Tools to do the initial queries and sub-queries to discover all the IP addresses used by a number of mailers (which you can toggle within the script), then format the results in a single whitelist for Postscreen.
You can find Postwhite on Git-Hub.
Postwhite’s README explains how to use it, but what it does is simple: you flag which mailers’ IPs you want to include in your whitelist, then let Postwhite do its thing. It will query all the mailers you selected, build a single whitelist, sort the whitelist numerically, remove any duplicates, save the whitelist in your Postfix directory, and reload Postfix.
All you have to do at that point is tell Postfix about your new whitelist.
Configuring Postfix to Use Postwhite’s Whitelist Files with Postscreen
By default, the postwhite script creates a file called postscreen_spf_whitelist.cidr in your /etc/postfix directory.
Add the new whitelist line to the postscreen_access_list directive in your Postfix’s main.cf file:
postscreen_access_list = permit_mynetworks, ... cidr:/etc/postfix/postscreen_spf_whitelist.cidr, ...
I also recommend creating a cron job to refresh your whitelist with updated queries every week:
@weekly /usr/local/bin/postwhite.sh > /dev/null 2>&1 #Update Postscreen Whitelists
Once you’ve told Postfix about your whitelist and executed the postwhite.sh script (which includes a postfix reload), you’re done, right?
Sigh. If only it were that simple.
Microsoft Is Publishing Invalid IP Ranges in their SPF Record
The first time I ran Postwhite, I got the following warnings in my maillog:
Nov 23 11:30:05 mailhost postfix/postscreen: warning: cidr map /etc/postfix/msft_whitelist.cidr, line 36: non-null host address bits in "126.96.36.199/30", perhaps you should use "188.8.131.52/30" instead: skipping this rule Nov 23 11:30:05 mailhost postfix/postscreen: warning: cidr map /etc/postfix/msft_whitelist.cidr, line 40: non-null host address bits in "184.108.40.206/26", perhaps you should use "220.127.116.11/26" instead: skipping this rule Nov 23 11:30:05 mailhost postfix/postscreen: warning: cidr map /etc/postfix/msft_whitelist.cidr, line 41: non-null host address bits in "18.104.22.168/26", perhaps you should use "22.214.171.124/26" instead: skipping this rule
I ran the first IP range through a CIDR validator, and sure enough, I got “The network address in the 126.96.36.199/30 CIDR block is not valid.” Same thing with 188.8.131.52/26 (which actually appears twice in the SPF record, and therefore twice in the whitelist).
After some checking, I discovered that both of the invalid IP ranges showed up when querying _spf-ssg-b.microsoft.com:
# dig _spf-ssg-b.microsoft.com txt | grep spf1 _spf-ssg-b.microsoft.com. 494 IN TXT "v=spf1 ip4:184.108.40.206/30 ip4:220.127.116.11/26 ip4:18.104.22.168/27 ip4:22.214.171.124/27 ip4:126.96.36.199/26 ip4:188.8.131.52/26 ip4:184.108.40.206/29 ip4:220.127.116.11/27 ip4:18.104.22.168/27 ip4:22.214.171.124/28 ~all"
That’s not a “bug” with Postwhite. It’s doing what it’s supposed to do: take the IP ranges stated by the mailer and include them in a whitelist. So I included some additional logic that validates CIDR ranges, then gives you the option to either remove, fix, or keep any invalid CIDRs in your whitelist.
I’ve also tried figuring out how to contact the Microsoft Postmaster to inform them of their mistake, but I can’t find any way to notify them. I’ll try tweeting a link to this article to them once I publish it, and hope they come around (I’ll update here if they do).
Check Your Postfix Log Files to See Your Whitelisting
With your whitelist now active, you can monitor your mail log (on a RedHat system, i use tail -f /var/log/maillog) to verify that your Postscreen whitelists are working. You should see something like this when a whitelisted system connects:
Nov 25 09:08:05 mailhost postfix/postscreen: CONNECT from [126.96.36.199]:38891 to [xxx.xxx.xxx.xxx]:25 Nov 25 09:08:05 mailhostpostfix/postscreen: WHITELISTED [188.8.131.52]:38891
Because that IP matches the whitelist for a Google mail server, Postscreen doesn’t waste any more time trying to figure out if it’s a valid sender, and immediately passes the connection off to Postfix’s smtpd process:
Nov 25 09:08:05 mailhostpostfix/smtpd: connect from mail-ig0-f171.google.com[184.108.40.206] Nov 25 09:08:06 mailhostpostfix/smtpd: Anonymous TLS connection established from mail-ig0-f171.google.com[220.127.116.11]: TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits) Nov 25 09:08:06 titanium postfix/smtpd: B3BAC2E07EF: client=mail-ig0-f171.google.com[18.104.22.168]
Congratulations! Your Postscreen whitelist is working properly.
As always, I welcome your questions, comments, and feedback below!