Fighting SPAM with Postfix
SPAM is a huge problem these days, but there are some easy steps you can take that will drastically reduce the amount of junk mail you receive, if you are using the Postfix mail server.
Why Postfix as a SPAM fighter?
I've been using the techniques in this article for years, both on my own
personal systems and at various companies where I've worked as a systems
administrator. It really is amazing how easily you can get rid of most of
the junk without having to run a SPAM filter of any kind.
Some junk will still get through, even with these defences in place, but I
promise you that the volume will be greatly reduced.
Why should we use Postfix to fight SPAM? Why not just install a SPAM filter
like SpamAssassin or DSpam and let those programs
deal with the problem?
When dealing with SPAM it makes sense to reject the junk as early as
possibly and Postfix (being a SMTP server) is about as early as you can get
(if we ignore rejecting incomming connections by IP in a firewall or
similar). This saves server resources, which can be a big deal on busy
servers. It also makes sense to have multiple layers of defence; Postfix may
catch some stuff that your other filters won't, thus reducing the total
amount of crap that passes through the whole chain. Besides, making Postfix
reject obvious junk is easy (as I'll show you below), so there's really no
reason not to do it.
Three setups to (hopefully) fit all needs.
I'm going to describe 3 different Postfix setups. They all aim to reduce the
amount of SPAM you receive, but they differ in how agressive they are.
I'll refer to the 3 setups as 'Soft', 'Medium' & 'Hard'. The difference
being both how much SPAM they will reject ('Soft' less, 'Hard' more), but
also how much legitimate mail they are likely to reject ('Soft' less, 'Hard'
more).
Which of the 3 setups is suitable for you depends entirely on your tolerance
for unsolicited bulk email (SPAM), your willingness to accept the loss of
legitimate email and what users you are handling mail for. I can only
describe what works for me and try to give you an idea of how the various
settings will impact you, but only you can make the decision as to what is
the proper setup in your case.
I define the 3 different setups like this:
- Hard
- This is the setup that will reject the most SPAM. It also carries the highest risk of rejecting legitimate email. I can only recommend it if you are only managing your own personal mail, have a very low tolerance for SPAM and don't care about the odd lost valid email. It's pretty agressive (but effective).
- Medium
- This setup attempts to be a reasonable compromise between 'Hard' and 'Soft'. It tries to accomodate the most common misconfigurations of valid mail servers while still rejecting most outright bogus mails and senders. It's a good starting point and the setup I recommend you start out with and then tweak to your personal preference. It will reject the odd valid email, but in my oppinion it rejects more SPAM (junk) than HAM (valid mails) at a ratio that should be acceptable to most users.
- Soft
- This is the setup you should use if you only want to reject mails that are very obviously bogus and you want a very low risk of ever rejecting a valid mail. It will catch some SPAM, but it will also let a lot through. But sometimes, like when you are administering a mail server used by lots of people, that's the the best you can do without pissing off your users - and a little less SPAM is better than nothing.
I will describe the 3 setups in the order 'Hard', 'Medium', 'Soft'. Detailed comments are provided to describe the various options used.
For each setup I also divide the config into two Sections; the 'General'
Section and the 'Anti SPAM' section.
The difference between the two sections is simply that the options I've put in
'General' are the ones that don't usually have an influence on the amount of
SPAM you receive (although they might in some cases) and the ones in 'Anti
SPAM' are the onces that are strictly targeted at fighting off junk mail.
Postfix has many more options than the ones I describe below. I've chosen to
limit myself to the options I've personally found useful and the options
that are supported by the version of Postfix that I currently run on my home
system (version 2.5.5). I leave it as an exercise for the reader to make use
of options available in newer Postfix versions as well as options I've
chosen to omit in this document.
Please note that the options described below do not make up complete main.cf
files for Postfix, I've only included the SPAM relevant options, so you'll
still need to setup things like relay access control, local delivery
options, etc, on your own.
The 'Hard' setup - for hardcore SPAM haters
This setup tries to reject any mail that is just a tiny bit suspect as well as mail from senders listed in high quality blacklists. This setup is very strict and since quite a few (valid) mail servers out there are improperly configured it will reject mail from senders that are not sending junk but just happen to be using a mailserver setup by a clueless admin (and it will, in fact, also reject mail from correctly configured setups that just happen to not follow a few common practices but are still actually fully standards compliant). Be warned; you will get complaints from people who are unable to send you mail if you use this setup (but you'll also get very little SPAM). You should not use this setup if not all your users are OK with it and if losing some valid mail once in a while does not concern you.
Here are the options (including descriptions - some very short as the details can be found in the Postfix documentation) that I have in my main.cf file to enable the 'Hard' setup.
### General settings ### soft_bounce = no # We do not want clients to retry if we reject # them once. This makes us reject mail with a # permanent error so the client should not retry. # You may want to set this to 'yes' for a short # while during testing, but do remember to set it # back to 'no' when you are done. unknown_local_recipient_reject_code = 550 # If someone tries to deliver mail to a recipient # we know nothing about, then reject the mail # outright. # If you would like the sender to retry later you # can use code 450 (temporary error) instead of 550 # (permanent error). in_flow_delay = 5s # If we are being flooded with mails, make new # clients wait a bit. # This is not really targeted at SPAM bots, but more # at keeping the server alive during a heavy # onslaught, but it seems that (at least some) SPAM # bots are quite impatient and making them wait for # 5 seconds discourages some of them. smtpd_banner = $myhostname ESMTP 8BIT-OK NO UCE NO UBE $mail_name # Strictly speaking this has nothing to do with # SPAM, but I thought I'd mention it anyway. # It's a good idea to make sure that Postfix # displays a banner (some clients get confused if # you don't), but there's no reason to show your # Postfix version (SPAM bots may use it to take # advantage of known bugs). # The 'ESMTP' is there to let clients know that we # support ESMTP and '8BIT-OK' it there to please some # versions of Sendmail that check for that rather # than ESMTP. The two strings 'NO UCE' and 'NO UBE' # are there since there has been proposed # legislation in the US making it illegal to send # Unsolicited Commercial/Bulk Email through servers # displaying those strings in their banner. I doubt # it will ever stop a single SPAM, but if it does # it's worth it having the strings there - they do # no harm. ### Anti SPAM settings ### header_checks = regexp:/etc/postfix/header_checks # This line in itself doesn't reject any SPAM. It # just sets us up with a file that we can put # regular expressions into to match against mail # headers and then reject or allow mail based on # whether or not there's a match. # This can be very effective when you receive a lot # of mail from the same sender or with similar # Subject: lines. A few rules along the lines of # this goes a long way in that file: # # /^Received:(.*)hispeedmailer\.com/ REJECT Mass-mailer. Bye bye spammer... # /^Subject:.*=\?(big5|euc-kr|gb2312|ks_c_5601-1987)\?/ REJECT No foreign character sets, please. # /^Subject:(.*)v[i1][a@4]+g*r[a@4]+/ REJECT No pills needed, thank you. strict_rfc821_envelopes = yes # If the sender can't even submit mail with a proper # RFC821 envelope then no thank you, I don't want # any of the mail from there. That's pretty basic # and everyone should know how to specify sender and # recipient correctly (although the sad fact is that # a few commercial apps do not - well, that's just # too bad). smtpd_helo_required = yes # Ok, it's strictly not required that a client sends # HELO or EHLO, but most do and those that do not # are usually junk mail bots, so making it a # requirement usually does not discard valid email # but it does sometimes rejects junk. This is not # something you should enable unless you can accept # a few incorrect rejects, but it does stop a lot of # crap, so I personally enable it. smtpd_helo_restrictions = # This specifies the list of requirements we place # on what the client sends as part of HELO/EHLO. permit_mynetworks, # If the client is from our own net, allow it, # regardless of what it submits. reject_invalid_hostname, # Although it is, strictly speaking, not correct to # require a valid hostname in HELO/EHLO, we do it # anyway since most SPAM bots don't send one and # most (although not all) valid mail servers do. # Be warned though, that this does reject a fair bit # of valid mail servers. reject_non_fqdn_hostname, # The only requirement is really that the client # sends something unique, but most send a FQDN and # making that a requirement discards more junk mail # than most other settings (and unfortunately also a # fair bit of valid email, but that cannot always be # helped - this is the 'Hard' config after all). reject_unknown_hostname, # There is no requirement in the standards that the # client must submit a hostname that can be looked # up in DNS, but most do and it's pretty common to # require that, so it doesnt reject too much valid # mail - but it sure does kill a lot of junk, since # most SPAM bots don't send valid hostnames. check_helo_access hash:/etc/postfix/helo_checks # A simple database of stuff to check against what # the client sends in HELO/EHLO. # It's of fairly limited use, but there are a few # things you can put in there that makes sense, like # this: # localhost REJECT You are not me smtpd_sender_restrictions = # This lists requirements we place on the sender # address. reject_non_fqdn_sender, # If the address in the 'Mail from' command is not a # fully qualified domain name, then we do not want # the email - we most likely won't be able to reply # and it's most likely fake anyway. reject_unknown_sender_domain # If the client submits mail with a from adress that # we can't look up in DNS then we don't want the # mail (at the very least we won't have a way to # reply and it's probably forged anyway). # The domain might be valid, we may just not be able # to look it up from our location at the moment, so # this may reject valid email, but that's very rare. smtpd_recipient_restrictions = # These are the restrictions we place on the # recepient address. reject_non_fqdn_recipient, # The recipient address must be a fully qualified # domain name. reject_unknown_recipient_domain, # If the recipient address has no DNS A or MX # record, reject the mail. it should be easy for you # to ensure that all domains you receive mail for # meet this requirement. permit_mynetworks, # If the client IP address matches our network, # permit the request (unless it was already # rejected by a prior rule). reject_unauth_destination, # Unless the recipient matches our list of relay # domains (or a sub domain) and contains no sender # specified routing or we are the final destination; # reject the mail. smtpd_client_restrictions = # These are the restrictions we place on the what # client machines are allowed to deliver mail to us. permit_mynetworks, # Allow clients from our own network. reject_unknown_client, # This rejects all mails from clients where we # cannot do a reverse DNS lookup on the clients IP # address. # Most spammers fail this requirement. Unfortunately # so do a large amount of valid mail servers also. It # drops an enormous amount of bad hosts, but you # won't get much valid email either - certainly not # from any servers with dynamic IP addresses since # those rarely have valid PTR records. reject_rbl_client cbl.abuseat.org, reject_rbl_client list.dsbl.org, reject_rbl_client sbl.spamhaus.org, reject_rbl_client pbl.spamhaus.org # The above 4 blacklists are trustworthy and of high # quality (in my humble opinion), and if the # client's IP address is listed in one of them, then # we don't want the mail. smtpd_data_restrictions = reject_unauth_pipelining # This rejects clients that try to use the # 'pipelining' extension without first checking if # we support that. Well behaved mail servers will # check first - spammers usually don't.
The 'Medium' setup - for those who hate SPAM but still want valid email
This is my recommended setup. It tries to be a good compromise between rejecting SPAM and receiving HAM. It will reject most obvious spammers and will rarely reject valid mail (although you can't avoid losing a little). It tries not to reject senders that are using correctly configured mail servers.
Here are the options (including descriptions - some very short as the details can be found in the Postfix documentation) that I have in my main.cf file to enable the 'Medium' setup.
### General settings ### soft_bounce = no # We do not want clients to retry if we reject # them once. This makes us reject mail with a # permanent error so the client should not retry. # You may want to set this to 'yes' for a short # while during testing, but do remember to set it # back to 'no' when you are done. unknown_local_recipient_reject_code = 550 # If someone tries to deliver mail to a recipient # we know nothing about, then reject the mail # outright. # If you would like the sender to retry later you # can use code 450 (temporary error) instead of 550 # (permanent error) - that's a good idea to do for a # while when you are testing your setup. in_flow_delay = 4s # If we are being flooded with mails, make new # clients wait a bit. # This is not really targeted at SPAM bots, but more # at keeping the server alive during a heavy # onslaught, but it seems that (at least some) SPAM # bots are quite impatient and making them wait for # 4 seconds discourages some of them. smtpd_banner = $myhostname ESMTP 8BIT-OK NO UCE NO UBE $mail_name # Strictly speaking this has nothing to do with # SPAM, but I thought I'd mention it anyway. # It's a good idea to make sure that Postfix # displays a banner (some clients get confused if # you don't), but there's no reason to show your # Postfix version (SPAM bots may use it to take # advantage of known bugs). # The 'ESMTP' is there to let clients know that we # support ESMTP and '8BIT-OK' it there to please some # versions of Sendmail that check for that rather # than ESMTP. The two strings 'NO UCE' and 'NO UBE' # are there since there has been proposed # legislation in the US making it illegal to send # Unsolicited Commercial/Bulk Email through servers # displaying those strings in their banner. I doubt # it will ever stop a single SPAM, but if it does # it's worth it having the strings there - they do # no harm. ### Anti SPAM settings ### header_checks = regexp:/etc/postfix/header_checks # This line in itself doesn't reject any SPAM. It # just sets us up with a file that we can put # regular expressions into to match against mail # headers and then reject or allow mail based on # whether or not there's a match. # This can be very effective when you receive a lot # of mail from the same sender or with similar # Subject: lines. A few rules along the lines of # this goes a long way in that file: # # /^Received:(.*)hispeedmailer\.com/ REJECT Mass-mailer. Bye bye spammer... # /^Subject:.*=\?(big5|euc-kr|gb2312|ks_c_5601-1987)\?/ REJECT No foreign character sets, please. # /^Subject:(.*)v[i1][a@4]+g*r[a@4]+/ REJECT No pills needed, thank you. strict_rfc821_envelopes = yes # If the sender can't even submit mail with a proper # RFC821 envelope then no thank you, I don't want # any of the mail from there. That's pretty basic # and everyone should know how to specify sender and # recipient correctly (although the sad fact is that # a few commercial apps do not - well, that's just # too bad). smtpd_helo_required = no # Sending HELO is not a requirement, and sending # EHLO really only is for negotiating protocol # extensions, so although almost everyone send it we # don't want to require it. smtpd_helo_restrictions = # This specifies the list of requirements we place # on what the client sends as part of HELO/EHLO (if # they do send something). For this setup we don't # specify anything here. smtpd_sender_restrictions = # This lists requirements we place on the sender # address. reject_non_fqdn_sender, # If the address in the 'Mail from' command is not a # fully qualified domain name, then we do not want # the email - we most likely won't be able to reply # and it's most likely fake anyway. reject_unknown_sender_domain # If the client submits mail with a from adress that # we can't look up in DNS then we don't want the # mail (at the very least we won't have a way to # reply and it's probably forged anyway). # The domain might be valid, we may just not be able # to look it up from our location at the moment, so # this may reject valid email, but that's very rare. smtpd_recipient_restrictions = # These are the restrictions we place on the # recepient address. reject_non_fqdn_recipient, # The recipient address must be a fully qualified # domain name. reject_unknown_recipient_domain, # If the recipient address has no DNS A or MX # record, reject the mail. it should be easy for you # to ensure that all domains you receive mail for # meet this requirement. permit_mynetworks, # If the client IP address matches our network, # permit the request (unless it was already # rejected by a prior rule). reject_unauth_destination, # Unless the recipient matches our list of relay # domains (or a sub domain) and contains no sender # specified routing or we are the final destination; # reject the mail. smtpd_client_restrictions = # These are the restrictions we place on the what # client machines are allowed to deliver mail to us. permit_mynetworks, # Allow clients from our own network. reject_rbl_client cbl.abuseat.org, reject_rbl_client list.dsbl.org, reject_rbl_client sbl.spamhaus.org, reject_rbl_client pbl.spamhaus.org, # The above 4 blacklists are trustworthy and of high # quality (in my humble opinion), and if the # client's IP address is listed in one of them, then # we don't want the mail. smtpd_data_restrictions = reject_unauth_pipelining # This rejects clients that try to use the # 'pipelining' extension without first checking # whether we support that. Well behaved mail servers # will check first - spammers usually don't.
The 'Soft' setup - for those who cannot tolerate lost mail at all
This setup is very paranoid about not rejecting valid mail. This naturally cuts down on its effectiveness against SPAM, but some people have a very low tolerance for lost valid mail but still want to reject the most obviously bogus crap.
Here are the options (including descriptions - some very short as the details can be found in the Postfix documentation) that I have in my main.cf file to enable the 'Soft' setup.
### General settings ### soft_bounce = no # We do not want clients to retry if we reject # them once. This makes us reject mail with a # permanent error so the client should not retry. # You may want to set this to 'yes' for a short # while during testing, but do remember to set it # back to 'no' when you are done. unknown_local_recipient_reject_code = 550 # If someone tries to deliver mail to a recipient # we know nothing about, then reject the mail # outright. # If you would like the sender to retry later you # can use code 450 (temporary error) instead of 550 # (permanent error) - that's a good idea to do for a # while when you are testing your setup. in_flow_delay = 3s # If we are being flooded with mails, make new # clients wait a bit. # This is not really targeted at SPAM bots, but more # at keeping the server alive during a heavy # onslaught, but it seems that (at least some) SPAM # bots are quite impatient and making them wait for # 3 seconds discourages some of them. smtpd_banner = $myhostname ESMTP 8BIT-OK NO UCE NO UBE $mail_name # Strictly speaking this has nothing to do with # SPAM, but I thought I'd mention it anyway. # It's a good idea to make sure that Postfix # displays a banner (some clients get confused if # you don't), but there's no reason to show your # Postfix version (SPAM bots may use it to take # advantage of known bugs). # The 'ESMTP' is there to let clients know that we # support ESMTP and '8BIT-OK' it there to please some # versions of Sendmail that check for that rather # than ESMTP. The two strings 'NO UCE' and 'NO UBE' # are there since there has been proposed # legislation in the US making it illegal to send # Unsolicited Commercial/Bulk Email through servers # displaying those strings in their banner. I doubt # it will ever stop a single SPAM, but if it does # it's worth it having the strings there - they do # no harm. ### Anti SPAM settings ### header_checks = regexp:/etc/postfix/header_checks # This line in itself doesn't reject any SPAM. It # just sets us up with a file that we can put # regular expressions into to match against mail # headers and then reject or allow mail based on # whether or not there's a match. # This can be very effective when you receive a lot # of mail from the same sender or with similar # Subject: lines. A few rules along the lines of # this goes a long way in that file: # # /^Received:(.*)hispeedmailer\.com/ REJECT Mass-mailer. Bye bye spammer... # /^Subject:.*=\?(big5|euc-kr|gb2312|ks_c_5601-1987)\?/ REJECT No foreign character sets, please. # /^Subject:(.*)v[i1][a@4]+g*r[a@4]+/ REJECT No pills needed, thank you. strict_rfc821_envelopes = no # If the sender does not send mail with a proper RFC # 821 envelope, then we'll still accept it. There's # a lot of poorly written software out there and we # can't risk losing a valid mail just because it's # being delivered by badly written software. smtpd_helo_required = no # Sending HELO is not a requirement, and sending # EHLO really only is for negotiating protocol # extensions, so although almost everyone send it we # don't want to require it. smtpd_helo_restrictions = # This specifies the list of requirements we place # on what the client sends as part of HELO/EHLO (if # they do send something). For this setup we don't # specify anything here. smtpd_sender_restrictions = # This lists requirements we place on the sender # address. reject_non_fqdn_sender, # If the address in the 'Mail from' command is not a # fully qualified domain name, then we do not want # the email - we most likely won't be able to reply # and it's most likely fake anyway. smtpd_recipient_restrictions = # These are the restrictions we place on the # recepient address. reject_non_fqdn_recipient, # The recipient address must be a fully qualified # domain name. reject_unknown_recipient_domain, # If the recipient address has no DNS A or MX # record, reject the mail. it should be easy for you # to ensure that all domains you receive mail for # meet this requirement. permit_mynetworks, # If the client IP address matches our network, # permit the request (unless it was already # rejected by a prior rule). reject_unauth_destination, # Unless the recipient matches our list of relay # domains (or a sub domain) and contains no sender # specified routing or we are the final destination; # reject the mail. smtpd_client_restrictions = # These are the restrictions we place on the what # client machines are allowed to deliver mail to us. permit_mynetworks, # Allow clients from our own network. reject_rbl_client cbl.abuseat.org, reject_rbl_client list.dsbl.org, reject_rbl_client sbl.spamhaus.org, reject_rbl_client pbl.spamhaus.org, # The above 4 blacklists are trustworthy and of high # quality (in my humble opinion), and if the # client's IP address is listed in one of them, then # we don't want the mail. smtpd_data_restrictions = reject_unauth_pipelining # This rejects clients that try to use the # 'pipelining' extension without first checking # whether we support that. Well behaved mail servers # will check first - spammers usually don't.
That's all for now. I hope this article will help you fight off more SPAM at
the front door. You can still add SPAM filters, Virus checkers, Graylisting
daemons (I can recommend Postgrey)
and the like, but by rejecting more mail with Postfix, the other stages of
your defence will get less work and all in all you'll let less junk through.
Good luck and happy SPAM fighting.