Fighting spam, aren’t we all… To stem the tide of unwanted junk mail look no further than the built-in access restrictions in Postfix.
If you find yourself the postmaster-in-chief, the last think you want is having to rely on your users to weed through spam and navigate around phishing mails without fail. In fact, spam is a major source of security breaches that involve using social engineering to allow hackers to get a foot in the door.
Why mail services fail at filtering spam (some even fail miserably)
Unsolicited mail is a problem that every organization must fix for itself, one way or the other. Even those of us who are paying for an enterprise-class spam detection service aren’t always happy with the results. The same goes for anti-spam services that set honeybee traps all over the Internet to collect junk trying to come up with effective filters. Your mileage will vary in either case. Are their traps attracting the same spammers that are flooding your users’ inboxes? Judging upon the efficiency of software tools and anti-spam services in real-world scenarios, the answer is: not so much. On top of it, mail services such as Gmail for Business or AWS Workmail have to tailor their services to all of their customers’ industries, including such spam hotspots as car insurance, home improvement, and health care. Good luck with that.
So how do you go about fixing the problem in your organization when you find yourself in charge of it? You could set up spam filtering software that uses heuristics such as Apache Spam Assassin, but it will come at the cost of a performance penalty and with somewhat questionable benefits of ongoing maintenance.
Postfix supports Sendmail version 8 Milter protocol and thus Sendmail milters (mail filters). Milter.org, once the world’s most comprehensive catalog of Milters, has been shut down some time ago, and Sendmail, Inc. is now Proofpoint, Inc, following an acquisition. Proofpoint Inc. continues to provide milters in its standard open source distribution of Sendmail. Milters can be used to fight spam, scan attachments for viruses, and sign messages. Even so, the future of milters is anything but clear. Unless you wanted to implement Sendmail milters in Postfix, your best bet when trying to stem the flood of spam is using Postfix’ built-in functionality: smtpd restrictions, header and body checks, and external rbl/rhsbl databases. These tools are more than adequate for most use case scenarios. Your other option is using the likes of Spam Assassin.
To a degree, you could also rely on the built-in access restrictions in the mail client of your users’ choice. To that end, you can extract email addresses of known spammers from your mail clients’ log files or from discarded junk mail, then use them to build rules for handling incoming messages.
Here is how to do it in Postfix.
A multi-step approach to spam filtering
Postfix allows you to implement multi-layered spam filtering: during the connection negotiation with SMTP clients using smtpd restrictions, then when accepting incoming mail by scanning the headers and the body of each message for clues. Postfix provides the following functionlity right off the bat:
- smtpd access restrictions are defined using Postfix access maps; they come into play during the exchange between the SMTP server (your host) and the SMTP client that’s sending an incoming message to it:
- smtpd client access restrictions block the SMTP client based on its host name or IP address
- smtpd HELO access restrictions block malformed or inappropriate HELO (this method is not recommended in light of the widespread misconfiguration of legitimate SMTP clients)
- smtpd sender restrictions block sender’s address (MAIL FROM)
- smtpd recipient restrictions block recipient (RCPT TO)
- external spam-fighting services:
- RBL lists (MAIL FROM)
- RHSBL lists (RCPT TO)
- content versification of incoming messages (which passed smtpd access restrictions checks):
- header checks
- body checks
Tip: Quickly look up Postfix configuration parameters
In order to quickly read out your current configuration, use the Postfix configuration utility at the command prompt:
if possible, with the parameter you want to read out the value of, for example:
In order to look up the defaults, add the -d flag.
The basics of smtpd access restrictions in Postfix
Postfix can enforce several types of smtpd access restrictions (set in main.cf), most notably:
- smtpd client restrictions: restrictions based on the address of the originating mail client (client IP and/or host name)
- smtpd HELO restrictions: restrictions based on the contents of HELO
- smtpd sender restrictions: restrictions based on the email address of the sender of the message (MAIL FROM:)
- smtpd recipient restrictions: restrictions based on the email address of the message recipient (RCPT TO:).
Wrapping all restrictions inside the smtpd_recipient_restrictions directive gives you one meaningful advantage: the ability to log all relevant information about each incoming connection that may help you to spot bugs in your configuration and/or further refine your setup.
In order to set helo restrictions, it is advisable to enforce that a helo be sent for each message (in main.cf):
smtpd_helo_required = yes
You can specify optional smtpd_restriction_classes for groups of access restrictions.
Once you know how it works, configuring spam defenses and setting your access restrictions in Postfix is rather easy.
Setting smtpd access restrictions in Postfix
Postfix applies restrictions in the order they are specified; the first restriction that matches “wins”.
Step 1. Set smtpd_recipient_restrictions
In main.cf, set smtpd_recipient_restrictions as follows (continue long lines by starting the next line with a whitespace character):
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_invalid_hostname, reject_unauth_pipelining, reject_non_fqdn_sender, reject_unknown_sender_domain, reject_non_fqdn_recipient, reject_unknown_recipient_domain, ### make sure the files referenced on the following four lines exist check_client_access hash:/etc/postfix/access_maps/access_client, check_helo_access hash:/etc/postfix/access_maps/access_helo, check_sender_access regexp:/etc/postfix/access_maps/regex_access_sender, check_recipient_access hash:/etc/postfix/access_maps/access_recipient, ### from time to time, anti-spam services may stop working; check the logs regularly to stay on top of things! reject_rhsbl_client blackhole.securitysage.com, reject_rhsbl_sender blackhole.securitysage.com, reject_rbl_client relays.ordb.org, reject_rbl_client zen.spamhaus.org, reject_rbl_client b.bararacudacentral.org, reject_rbl_client blackholes.easynet.nl, reject_rbl_client cbl.abuseat.org, reject_rbl_client proxies.blackholes.wirehub.net, reject_rbl_client bl.spamcop.net, reject_rbl_client sbl.spamhaus.org, reject_rbl_client ubl.unsubscore.com, permit
By placing the RBL (Real-time, IP Based Blacklist) and RHSBL (Real-time, Domain Based Blacklist) checks at the end of the list of restrictions you minimize costly external DNS checks to those instances of mail that managed to pass all the other spam checks.
Postfix allows you to define your own access restrictions in a number of ways, including using regular expressions. In order to confirm which methods are supported by your version, you can inquire at the prompt using:
Step 2. Create access restrictions files that you referenced in main.cf
Create your access restrictions files as referenced in main.cf:
/etc/postfix/access_maps/access_client /etc/postfix/access_maps/access_helo /etc/postfix/access_maps/regex_access_sender /etc/postfix/access_maps/access_recipient
Step 3. Edit your access restrictions files
Edit your access restrictions files in a text editor and populate them with rules. Follow the directions set in the /etc/postfix/access file.
Be vigilant of the type of restriction you are applying in each case. For example, to block a domain (domainname.tld) with its subdomains (anything.domainname.tld) in the check_client_access table, enter this in your /etc/postfix/access_maps/access_client file:
.spammingdomain.tld REJECT Get lost.
In this case, REJECT is the directive, “Get lost.” is an optional message (could be a numerical error code as well). Instead of REJECT, here are some alternatives:
- you could DISCARD the message to avoid responding to it (which ties up resources and may turn you into a spammer yourself if your bounces hit an innocent victim),
- hold the message in the queue for the lifetime of the queue (HOLD optional comment),
- add a header (PREPEND headername: headervalue on a separate line preceding the header which triggered the event) or
- redirect spam to one and only one recipient REDIRECT email@example.com).
For how these and other directives work, refer to the manual page for access:
A legitimate email addresses rarely includes a hostname, but the exact opposite is usually true for spam. When you block a hostname in your access restrictions, the spammers can still get to you at virtually no expense by modifying the hostname. To turn the tables and make spamming of your systems uneconomical, you need to make their domains obsolete (so they can’t spam you without getting into expenses). Arguably the most efficient, sure-fire way of doing this involves regular expressions.
For some types of restrictions, you may want to use regex-based evaluation to account for variations in user names, subject lines, and the like. For example, you could block the most frequently abused top lever domains by using these directives:
/\.xyz$/ REJECT /\.top$/ REJECT /\.date$/ REJECT /\.science$/ REJECT /\.faith$/ REJECT
in your check_client_access and check_sender_access parameters.
Other prime candidates for this type of a block are the top level domains .pro, webcam, .work, and .download. This is not a solution for prime top-level domains such as .com, however. That’s where your past logs come in handy (read about how to extract spammers’ identifying information from logs and junk messages). For more detail on how to set up anti-spam filters using regular expressions, read this post (includes ready-to-use RegEx expressions).
Step 4. Run postmap on your access restrictions files (if they are being referenced via hash)
When you are done setting up the restrictions, run postmap on the files that are referenced with the parameters check_client_access, check_helo_access, check_sender_access, check_recipient_access using hash:
This is not necessary for files referenced via regexp: (a reload will suffice) and won’t help with pcre: (in this case, it is advisable to restart postfix).
Step 5. Restart Postfix
service postfix restart
to apply changes to its main configuration file. When you change only files referenced via regexp:, all you need to do is reload Postfix:
service postfix reload
Step 6. Test your filter pattern matching
As always, test your configuration to ensure that it works the way you intended. For files using regular expressions and referenced via regexp:
postmap -q 'teststring' regexp:/etc/postfix/access_maps/regex_access_sender
Step 7. Correct and update
Whenever you update access restriction files referenced via hash, run postmap on them. For files referenced via regexp:, simply reload postfix and you should be good to go.
Watch your back (keep an eye on the logs!)
You can view what’s going on in the main log file by using the command:
tail -f /var/log/maillog
Also, keep an eye on the mail in the system’s postmaster account (it may be routed to root’s mail directory, so check there too).
Tip: One way to get acquainted with various anti-spam services is MxToolbox, a testing suite which sports quite an impressive array of useful diagnostics tricks up its sleeve. Run a blacklist check on a domain and see what happens.