You all have persevered through quite a bit to get to this point: we have a functional and secure e-mail server that does a good job at ignoring or dumping off spam before it hits your inbox. We've got all the right pieces in place to ensure that the mail we send gets delivered; we've got OpenDKIM operational, and we've got DNS properly configured (including reverse lookups!).
We could stop here and declare success. After all, you can plug your mail account name and password into your mail app or your smartphone and send and receive e-mail. You can easily add new accounts. Even better, you can easily add aliases, so creating "firstname.lastname@example.org" and using it at a skeevy website that requires e-mail registration and then deleting the alias takes only a few seconds.
But are there any extra steps we can take to increase security? What about one-time passwords or two-factor authentication? What about bolting on a webmail front end so we can access our e-mail from a browser? What about push notifications or calendaring? What about letting users set and change their own e-mail passwords? What else can we bolt onto our server, and what other configuration paths might we take to do things differently?
We're going to answer all of these questions in this final installment.
Postscreen and additional filtering
The Postfix configuration we set up in parts 1-3 is pretty aggressive. We're refusing to communicate with other e-mail servers that don't do things exactly right; further, if you've implemented the Sieve filter rules I laid out in part 2, we're also discarding mail without properly formed message IDs. However, we haven't specifically implemented DNS-based black hole list checking. We're going to do that now.
DNS black holes lists—which you'll see referred to as DNSRBLs, for "DNS real-time black hole lists"—are lists maintained by various entities, some companies, and some individuals, filled with IP addresses or ranges of IP addresses from which the list maintainer believes spam has originated. The various RBLs all have their own criteria for listing an address, and many of them are notoriously difficult to get off of once you're listed. Much of the work we did in parts 2 and 3 was so that our own servers would behave properly and not end up on one or more of these blacklists.
SpamAssassin (which we configured in part 3) already checks many DNSRBLs as part of its default configuration and scores e-mails appropriately if the sender address is on one or more of them, but we're going to go a step further here and have Postfix reject messages entirely based on the lists. We'll do it by turning on a Postfix feature called postscreen.
Postscreen actually does a whole bunch of other stuff, too, all with the intent of separating legitimate mail senders from spammers. Out of the box with very minimal effort, postscreen implements a number of checks on incoming e-mail to make sure the remote servers are configured in a normal, non-spammy way; it also offers deeper checks, but we're going to leave those alone, because they can delay receipt of e-mail (or deny it all together).
Postscreen as a whole involves a number of additional Postfix-spawned
processes working on incoming connections before the sptmd process
accepts that connection. This means that to get postscreen working, we
need to first make changes in
master.cf, because that
configuration file controls how the Postfix master process wrangles all
of its separate daemons and utilities. Open up
master.cf; at the beginning of the file, you should see this:
We want to comment out the initial
smtp line, and then un-comment all four of the commented-out lines, so that we wind up with this:
If those lines are missing, then you'll want to add them in near the top of
These lines open the appropriate local ports and create the appropriate
unix sockets for the postscreen process, which will involve TLS
connection handling, DNS lookups, and remote blacklist checking.
After this, we also need to make some additions to
to tell Postfix to actually use postscreen, as well as which remote DNS
blacklists we want to check and which senders will be excluded from
postscreen checking. So, open up
main.cf for editing and add the following at the bottom:
The first two parameters implement the screening part of postscreen's
functionality, telling it that we want to enforce non-spammy connection
behavior (and not accept connections from clients that try to
short-circuit the process to deliver spam faster) and to turn on DNS
blacklist checking. The next parameter excludes your
variable contents from postscreen checking (which will keep LAN clients
from getting postscreen'd). Finally, the last parameter adds three
DNSRBLs to postscreen's check list.
You can specify as many or as few RBLs as you'd like; there are lots to choose from. Additionally, you can use the
parameter to control how many lists a sender has to be on in order to
get postscreen to reject the e-mail; if you want to get really fancy,
you can also assign different weights to your blocklists so that being
on one "counts" more than being on another. The Postfix documentation has more info on how to set this up.
Restart Postfix with
sudo service postfix restart to
make your changes live. And that's it—postscreen is now active. If you
want to watch it in action, send yourself an e-mail from an external
account and watch your
One more security tip: rate limiting with iptables
Postfix will automatically rate-limit connection attempts—that's what the postfix anvil process is for. However, rather than modify more default configuration, we can lean on a much more powerful and flexible tool to do our rate limiting: iptables.
Iptables lets you set rules that will affect the behavior of the operating system's kernel firewall. It's got an extraordinarily flexible (and extraordinarily complex) level of configurability, and so rather than give an exhaustive iptables tutorial, we're going to very quickly run through how to use it to drop connections from spammy spammers who spam too hard.
Note that when implementing iptables here on top of postscreen, it is a lot like wearing a belt with suspenders: this might be a bit of overkill. However, if you're in a situation where you can't (or don't want to) implement postscreen or another rate-limiting solution, iptables can easily fit the bill. It's very, very good at what it does.
What it's not, though, is friendly to inexperienced users. The first thing we need to do is install a quick program to automatically save your running iptables configuration at shutdown and re-apply it at startup (iptables doesn't do this by itself). So, run the following command:
As part of the quick
iptables-persistent setup, you'll be asked if you want to save your current set of iptables rules; you probably don't have any, but say yes anyway.
Next, navigate to
/etc/iptables. This is where
iptables-persistent keeps the saved copies of your firewall rules. There should be a file named
rules.v4 in the directory, containing your IPv4-specific firewall rules (there's probably also a
rules.v6 file, which we can ignore).
rules.v4 file should have very little in it. If you haven't explicitly defined any iptables rules, it ought to look something like this:
If it doesn't, and instead contains one or more rules, then you should probably skip this section unless you know exactly what the existing rules are for and why they're there. If you're comfortable with iptables' syntax, you can graft our configuration onto your existing rules, but it will take a bit of rejiggering; if you're not sure exactly how to do it already, you should stop and move on to the next section.
If your rules are indeed empty, with only three "accept" chains defined, then we can proceed. Delete the file's contents and replace it with the following:
Be sure to modify "192.168.0.0/24" to point to your LAN subnet (or just to your own server's IP address if you're on EC2 or other shared hosting). We're creating a new "LOG_AND_DROP" chain, and directing iptables to keep track of incoming connections on TCP ports 993, 587, 465, and 25. If a client hits those ports more than 20 times in 10 seconds, iptables will firewall off that client and drop all traffic from it until it stops sending.
The 20 connections and 10 seconds limits can be adjusted to suit your tastes, though counting more connections might require modifying some kernel parameters.
After you've saved and exited out of
rules.v4, you can make the rules active with the
iptables-restore command, like this:
To see a list of active iptables rules, you can run
Now, on to webmail!