This was originally published on, August 29, 2010. “Simple” is a relative term; this how-to is intended for readers with a reasonable working knowledge of Linux.


I can’t afford a really pricy third-party spam filtering option.  GFI, Symantec, even Microsoft offer up some pretty robust solutions.  They are pricy though, and I don’t see why I should bother fighting that particular funding war when there are some easy solutions available for free.  In my particular environment, I run an Exchange 2010 server front-ended by a CentOS box running Sendmail, SpamAssassin, ClamAV and a few others.

The first and most important thing is to of course go get the latest and greatest CentOS.  As of the time of this write-up that would be CentOS 5.5.  Toss it in a virtual machine and install it with nothing but the bare bones.  In my case, I gave it two interfaces; one directly externally accessible, and the other on my local LAN.  (I trust iptables to keep the baddies out as much as I do any other firewall, so I see little reason to hide the spam server behind a separate firewall and port forward.)  Let’s get to the build.

0) Set up your IP addressing according to your own internal schema.  Pointing the spamserver at your internal DNS (probably your domain controller) saves you having to build extensive hosts files on the spam server.  (It will be talking to your active directory, so using your AD’s DNS is a good plan.)

1) Enable the RPMforge repo.  ( I use this for the simple reason that they have a tendency to keep ClamAV significantly more up-to-date than RedHat (and thus CentOS) do.  If you don’t use RPMforge, eventually ClamAV will get so out of date it will refuse to download new definitions.  Save yourself the aggravation; use RPMforge.  (I tend to wget the latest rpm, then “yum install [rpm name] –nogpgpcheck”.  This is because CentOS doesn’t natively have RPMforge’s key available, and RPMforge keeps changing the location on their site where they store the rpm installer for the key…)

2) Install the necessary software: yum install procmail sendmail sendmail-cf sendmail-milter clam* spamass* pyzor perl-Razor-Agent

3) Download and install Webmin: RPMs are available, and certainly work well enough.

4) Disable SELinux and allow ports 10000 and 25 through the firewall, as this is what centos works on.  You can usually do this from the command line via system-config-securitylevel on a base CentOS install.  Don’t forget to restart the system after disabling SELinux!  I know that there are ways around disabling SELinux, but frankly I’m too lazy to futz with the thing.  (At some point in the future I will figure out how to get SpamAssassin and ClamAV working with SELinux enabled.)

5) Create a user called Sendmail in your Active Directory under to OU “users.”

6) Save the password for this user in a file on the spam server.  I used /etc/mail/ldap.secret

7) Log into Webmin, and under servers go to “Sendmail Mail Server.”

The following is what we are going to need to modify to get Sendmail to use ClamAV and SpamAssassin.  It will also be set up to talk to your domain controller in order to look up users when a server attempts to deliver mail.  In this way the Sendmail server will be able to reject recipients who don’t exist in your organization.  (Thus avoiding a truckload of NDRs from your exchange server.)

1) Under Webmin -> Servers -> Sendmail Mail Server -> Domain Routing (mailertable)

The mailertable tells Sendmail where to send e-mail it receives for a given domain.  In the example below, and are being redirected to  To achieve this, click on “manually edit /etc/mail/mailertable.”  Update it to suit your configuration.

Mailertable example:

2) Under Webmin -> Servers -> Sendmail Mail Server -> Spam control (access)

This file contains a list of servers allowed to use your spam server as a relay.  While e-mail relays are generally a very bad plan, in this case they are an excellent way to scan all your outbound company e-mail.  Enter the internal IP address of your exchange server (and any other e-mail sending systems) in your organizations here.  You can then configure them to treat your spam server as a “smart host,” thus providing antiviral and antispam scanning for all outbound e-mail traffic.  To achieve this, click on “manually edit /etc/mail/access.”  Update it to suit your configuration.

Acceslist example: RELAY RELAY

3) Under Webmin -> Servers -> Sendmail Mail Server -> Relay Domains
Enter a list (separated by carriage returns) of all domains that you will be handling internally and which you wish to pass through this spam server.

Relay Domains example:

4) Under Webmin -> Servers -> Sendmail Mail Server -> Sendmail M4 Configuration

This is the heart of configuring Sendmail.  Most of the default configuration provided by CentOS 5.5 is good, but we need to add a few goodies to get it working the way we want it.

The first and most important thing is the setting LOCAL_DOMAIN(`’).  There is a big push right now by e-mail administrators the world over to require reverse DNS.  The long story short is that the hostname of your spam server (as your incoming and outgoing mail point) absolutely must match the reverse DNS of the IP address assigned to it.  That reverse DNS also needs to contain the word “mail.”  So the hostname of your spamserver should be something akin to, and the reverse DNS on your external IP address provided you by your ISP should also read

In this vein, it is a good idea to set LOCAL_DOMAIN(`’) to LOCAL_DOMAIN(`’).  This means your spamserver would always accept mail for “” without forwarding it to your exchange server (an odd requirement that some e-mail administrators have begun to put into place.)  It still allows you to forward mail bound for internally.
Keep an out for this command: DAEMON_OPTIONS(`Port=smtp,Addr=, Name=MTA’).  Toss a dnl #  in front of it if you want your sendmail to listen on any addresses other than!

I also tend to dnl # out EXPOSED_USER(`root’) and FEATURE(`accept_unresolvable_domains’) for sanity reasons.

The rest of the commands I won’t go into too much detail on; if you are really curious there is plenty of documentation available online as to their specific functions.  If you are reading this page, I trust you are capable of spotting where in the configuration you should be changing “” and “company.local” style commands to suit your configuration.

define(`LUSER_RELAY’,`error:5.1.1:”550 User unknown”‘)dnl
INPUT_MAIL_FILTER(`clamav-milter’, `S=/var/clamav/clmilter.socket, T=S:4m;R:4m’)dnl
INPUT_MAIL_FILTER(`spamassassin’, `S=:/var/run/spamass.sock, F=,T=C:15m;S:4m;R:4m;E:10m’)dnl
define(`confINPUT_MAIL_FILTERS’, `clamav-milter,spamassassin’)dnl
FEATURE(`ldap_routing’,, `ldap -1 -T<TMPF> -v mail -k proxyAddresses=SMTP:%0′, `bounce’)dnl
define(`confLDAP_DEFAULT_SPEC’,`-h “” -d “CN=sendmail,CN=Users,DC=company,DC=local” -M simple -P /etc/mail/ldap-secret -b “DC=company,DC=local”‘)dnl

Once you have finished this, go save and rebuild the Sendmail configuration.  It’s a good plan to restart Sendmail at this point to see if it blows up.  Remember that Sendmail is really grouchy if you have an extra carriage return, or forget a ` or a ‘.

For SpamAssassin configuration, first go to Webmin -> Servers -> SpamAssassin Mail Filter -> Setup Procmail For SpamAssassin and enable SpamAssassin.

Next stop is Webmin -> Servers -> SpamAssassin Mail Filter and modify to your heart’s desire.  I generally change the setting “Prepend text to Subject: header” to read [SPAM ASSASSIN DETECTED SPAM].  This then allows me to set either an Outlook rule or an Exchange -> Hub Transport -> Transport rule.

In the case of a local Outlook rule each client must be individually configured to deal with the [SPAM ASSASSIN DETECTED SPAM] in the subject line of “spam” e-mails.  (I usually have them directed to the “Junk-Email” folder.)

In the case of an Exchange -> Hub Transport -> Transport rule, I usually set exchange to assign anything with [SPAM ASSASSIN DETECTED SPAM] in the subject line to a Spam Confidence Level (SCL) of 7.  If  you want to enable SCL junk filtering and set your own SCL levels, you will need some Exchange PowerShell commands.  Google can tell you more. is a good article to read as well.

Set-ContentFilterConfig -SCLDeleteEnabled $true -SCLDeleteThreshold 9
Set-ContentFilterConfig -SCLRejectEnabled $true -SCLRejectThreshold 8
Set-OrganizationConfig -SCLJunkEnabled $true -SCLJunkThreshold 7

Go to Exchange -> Hub Transport -> Anti-Spam -> Content Filtering.  Enable it, and uncheck any boxes except “Delete Messages that have an SCL greater than or equal to.”  The rationale behind this is that the SpamAssassin server is doing all the heavy filtering.  If you allow Exchange to reject mails, you are going to end up with a mess of rejection NDRs that will pile up and go nowhere.  Similarly, under Exchange -> Hub Transport -> Remote Domains -> Default (*) I really recommend disabling non-delivery reports.  There is a growing trend amongst email administrators to not accept mail from domains that send NDRs, as NDRs are being used by spammers as a vector to get spam into people’s e-mail boxes.

run freshclam and sa-update from the command line to get ClamAV and SpamAssassin updated to the latest definitions.

Go into Webmin -> System -> Bootup and Shutdown.  Make sure important things like ClamAV-Milter, SpamAssassin and Sendmail are all set to start on boot (and are currently running.)

That’s it!  If you’ve done it right, then you should now have a CentOS box capable of receiving e-mail from the internet, scanning it for viruses and Spam, and forwarding it on to your exchange server.  The exchange server itself can be configured with junk-filtering properties, adding a second layer of protection.  (Though in truth I’ve not needed it: SpamAssassin does the job just fine, and better than Exchange’s native capabilities.)