Scudamore Consulting Banner





Building a Mail Server on CentOS with MailScanner, Squirrelmail, Mailwatch, spamassassin & ClamAV

What is MailScanner

MailScanner is a free anti-virus and anti-spam filter. It serves as a wrapper for the MTA of your choice (Sendmail, Postfix, etc), spamassassin, a variety of other anti-spam tools, as well as a multitude of anti-virus scanners. 

Why CentOS

CentOS is a mature, robust distribution which includes cutting edge, but stable versions of the many open source projects that are bundled with it. This is very useful as production mail servers often require frequent security audits such as the PCI or Payment Card Industry compliance scans. The rpm update structure and the yum update mechanism make it very easy to keep a system current and patched for security compliance.  I usually rebuild the production mail servers I am responsible for at least every 18 months.

Why CentOS, because CentOS is powerful, cutting edge and easy to maintain. 


Hardware Recommendations For a Commercial-Grade Mail Gateway

For a lab environment or a small business, you can get away with a Pentium 4 class machine with 1GB of RAM and IDE or SATA drives.

Installing CentOS

Installing MailScanner and Related Tools

            perl Makefile.PL
            make
            make test
            make install


Customizing MailScanner.conf

MailScanner.conf is the primary configuration file for MailScanner. It is extremely large, so it can be very daunting the first few times you attempt to customize it for your environment. I have included a list of the key options that I use for most environments. Most of these options are required for the proper operation of Mailwatch. 

Quarantine User = root
Quarantine Group = apache
Virus Scanners = clamav
Allow Password-Protected Archives = yes
Change Iframe Tags, Form Tags, Script Tags, Web Bugs and Object Codebase Tags from disarm  to yes. This allows incoming HTML mail to work properly.
Quarantine Whole Message = yes
Always Include SpamAssassin Report = yes
Change Sign Clean Messages = no
Change Deliver Cleaned Messages = no
Spam List =  spamhaus-ZEN
Is Definitely Not Spam = &SQLWhitelist
Is Definitely Spam = &SQLBlacklist
Definite Spam Is High Scoring = yes
Required SpamAssassin Score from 6  to 5
Rebuild Bayes Every = 86400
Spam Actions = store
High Scoring Spam Actions = store
Always Looked Up Last = &MailWatchLogging


Configure Services

MailWatch for MailScanner

Mailwatch is a php/mysql app which provides real-time operational views as well as historical reporting for a MailScanner server.

         short_open_tag = On
         safe_mode = Off
         register_globals = Off
         magic_quotes_gpc = On
         magic_quotes_runtime = Off
         session.auto_start = 0

            GRANT ALL ON mailscanner.* TO mailwatch@localhost IDENTIFIED BY 'yourpassword';
            GRANT FILE ON *.* TO mailwatch@localhost IDENTIFIED BY 'yourpassword';
            FLUSH PRIVILEGES;

            INSERT INTO users VALUES ('joe',md5('password'),'Joe Bloe','A',0,0,0,0,'');

            chown -R root:apache /var/www/html/mailscanner/
            chmod -R ug+rwx /var/www/html/mailscanner/
            cp /var/www/html/mailscanner/conf.php.example /var/www/html/mailscanner/conf.php
            bayes_path /etc/MailScanner/bayes/bayes
            bayes_file_mode 0770             mkdir /etc/MailScanner/bayes
            chown root:apache /etc/MailScanner/bayes
            chmod g+rws /etc/MailScanner/bayes

            vim /usr/lib/MailScanner/MailScanner/CustomFunctions/MailWatch.pm

Find:
MailScanner::Log::InfoLog("$$message{id}: Logged to MailWatch SQL");

and change to
MailScanner::Log::DebugLog("$$message{id}: Logged to MailWatch SQL");


Find:
MailScanner::Log::InfoLog("Logging message $msg{id} to SQL");

and change to
MailScanner::Log::DebugLog("Logging message $msg{id} to SQL");







Find:
case default:

and change to
default:




Find:
#!/usr/bin/php -qn

change to
#!/usr/bin/php -q

Find:
dbquery("DELETE LOW_PRIORITY FROM maillog WHERE timestamp < (now() - INTERVAL 60 DAY)");

change to:
dbquery("DELETE LOW_PRIORITY FROM maillog WHERE date < (now() - INTERVAL 60 DAY)");


mv /opt/installs/mailwatch/tools/mailq.php /usr/local/bin
mv /opt/installs/mailwatch/tools/db_clean.php /usr/local/bin
mv /opt/installs/mailwatch/tools/quarantine_maint.php /usr/local/bin
mv /opt/installs/mailwatch/tools/quarantine_report.php /usr/local/bin
mv /opt/installs/mailwatch/tools/sendmail_relay.php /usr/sbin

chown root:root /usr/local/bin/mailq.php
chown root:root /usr/local/bin/db_clean.php
chown root:root /usr/local/bin/quarantine_maint.php
chown root:root /usr/local/bin/quarantine_report.php
chown root:root /usr/sbin/sendmail_relay.php
Copy the following code into the file:

MAILTO=admin
SHELL=/bin/sh
* * * * * root /usr/local/bin/mailq.php
0 0 * * * root /usr/local/bin/quarantine_maint.php --clean
0 0 * * * root /usr/local/bin/quarantine_report.php
0 0 * * * root /usr/local/bin/db_clean.php

Always Looked Up Last = &MailWatchLogging
Detailed Spam Report = yes
Include Scores In SpamAssassin Report = yes
Quarantine Whole Message = yes
Quarantine Whole Message As Queue Files = no
Quarantine User = root
Quarantine Group = apache
Quarantine Permissions = 0660
Quarantine Dir = /var/spool/MailScanner/quarantine
Is Definitely Not Spam = &SQLWhitelist
Is Definitely Spam = &SQLBlacklist

         $disabled = 1;

mv /opt/installs/mailwatch/tools/sendmail_relay.init /etc/rc.d/init.d
chown root:root /etc/rc.d/init.d/sendmail_relay.init
ln -s /etc/rc.d/init.d/sendmail_relay.init /etc/rc.d/init.d/mailwatch
/sbin/chkconfig --add mailwatch


Configure and Harden Apache

This documentation assumes running multiple SSL-enabled sites using name-based hosting. If you have the IP space to provide a separate IP Alias for each site, you can use IP-based hosting and use the default https port of 443 for all sites.

    ServerTokens Prod

    Find the Global instance of Options located under <Directory "/var/www/html">
    Remove File Indexes by changing Options Indexes FollowSymLinks to Options FollowSymLinks

    Insert FileEtag None just prior to the line reading --> DirectoryIndex index.html

    Change the default setting of ServerSignature On to ServerSignature Off
   
    Append to the end of the configuration file:

    NameVirtualHost *:80
    NameVirtualHost *:4500
    Include sites/*.conf

<VirtualHost *:4500>
ServerAdmin srvadmin@myorg.com
DocumentRoot /var/www/html/mailscanner/
ServerName mailwatch.myorg.com
ServerAlias mailwatch
ErrorLog logs/mailwatch.myorg.com-error_log
CustomLog logs/mailwatch.myorg.com-access_log common
SSLEngine on
SSLCertificateFile /etc/pki/tls/certs/mail1.crt
SSLCertificateKeyFile /etc/pki/tls/certs/mail.key
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)$
RewriteRule .* - [F]
</IfModule>
<Directory /var/www/html/mailscanner/>
php_admin_flag safe_mode 0
Options -Indexes
AllowOverride All
</Directory>

</VirtualHost>


<VirtualHost *:80>
        ServerAdmin srvadmin@myorg.com
        DocumentRoot /var/www/html/mailscanner/
        ServerName mailwatch.myorg.com
        ServerAlias mailwatch
        Redirect permanent /    https://mailwatch.myorg.com:4500
        <IfModule mod_rewrite.c>
        RewriteEngine on
        RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)$
        RewriteRule .* - [F]
        </IfModule>
</VirtualHost>



Create a Private CA and Create a Self-Signed Certificate

    create a private CA:
    /etc/pki/tls/misc/CA -newca
    copy openssl.cnf to MyOrgCA.cnf

    create a certificate:
    cd /etc/pki/tls/certs
    openssl genrsa -out certname.key 1024
   
    create a signing request:
    openssl req -new -key certname.key -out certname.csr

    Sign the Request:
    openssl ca -config ../MyOrgCA.cnf -policy policy_anything -out certname.crt -infiles     certname.csr

    Move the key file to the appropriate directory:
    mv /etc/pki/tls/certs/mail.key /etc/pki/tls/private/



Install Additional perl modules from CPAN

Execute perl -MCPAN -e shell from command line. Fill out the form for the first-time configuration of CPAN. If there are problems with the LWP sessions , then your firewall is not set up correctly to proxy passive ftp. Once you get a cpan> prompt use the install command to download and install Net::Ident and IO::Socket::SSL.


Configuring Sendmail

These are the options that I like to use when configuring sendmail for most situations. It is worth the time to research all of these individual options on Sendmail's website, http://sendmail.org
    Add define(`confBIND_OPTS',`WorkAroundBrokenAAAA') to the end of the define
    section right after the confAUTH_OPTIONS definition.

    uncomment the TRUST_AUTH_MECH line and add GSSAPI so it reads like this:
    TRUST_AUTH_MECH(`EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
   
    uncomment the confAUTH_MECHANISMS line right below TRUST_AUTH_MECH.

    It should read like this:
    define(`confAUTH_MECHANISMS', `EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl

    uncomment the CACERT_PATH,CACERT,SERVER_CERT and SERVER_KEY definitions
    and modify the key names and paths to look like this:
   
    define(`confCACERT_PATH',`/etc/pki/tls/certs')dnl
    define(`confCACERT',`/etc/pki/tls/certs/ca-bundle.crt')dnl
    define(`confSERVER_CERT',`/etc/pki/tls/certs/mail.crt')dnl
    define(`confSERVER_KEY',`/etc/pki/tls/private/mail.key')dnl

    Add the following generics lines right above the FEATURE(redirect) line:
    FEATURE(`genericstable', `hash -o /etc/mail/genericstable')dnl
    GENERICS_DOMAIN_FILE(`/etc/mail/generics-domains')dnl

    Comment out the always_add_domain feature by placing dnl at the beginning of the line.

    Remove the address section from  the DAEMON_OPTIONS line. This allows smtp to receive mail from the other IP addresses assigned to the box, not just from the loopback address.

    The modified line should look like this:
    DAEMON_OPTIONS(`Port=smtp, Name=MTA')dnl

    Uncomment the following line to enable MX-based relaying:
    FEATURE(`relay_based_on_MX')dnl

    Change the LOCAL_DOMAIN definition to your domain. e.g. myorg.com.

    Add the following to the very end of the file. This tightens the security on encrypted connections:

    LOCAL_CONFIG
    O CipherList=ALL:!ADH:!NULL:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:!LOW:+SSLv3:+TLSv1:!SSLv2:!EXP:!eNULL:!aNULL


            If it does not you will need to recompile sendmail. http://sial.org/howto/sendmail/cipherlist/ is a great resource for additional information.

            foo.net        smtp:[mail1.foo.net]
            bar.com      smtp:[mail1.foo.net]

Where is My Sendmail Vacation Program?!?

The redhat/CentOS rpm installation of sendmail does not install the sendmail vacation program. I have never heard a good explanation for this rationale. Nevertheless, for those of us building production mail servers, the vacation program is necessary. Here is how to get it.

    cd /usr/src/redhat/SOURCES/
    tar xvzf sendmail.xxx.tgz
    cd sendmail.xxxxx/vacation
    make
    make install

Configuring Squirrelmail

Squirrelmail is a web-based email program which allows your users to browse their mail while it is still on the server. Squirrelmail communicates with the mail server via IMAP. Since the Squirrelmail server is running on the mail server, there are no security issues with passing IMAP through the firewall. Additionally, through the addition of a few plug-ins, users can securely change their local user/email password and set a vacation message. There are many other plug-ins available for Squirrelmail so it can be customized to suit your needs. See their web page for more information. http://www.squirrelmail.org


 service poppassd
        {
        disable = no
        socket_type             = stream
        wait                    = no
        user                    = root
        server                  = /usr/sbin/poppassd
        log_on_success  += HOST DURATION
        log_on_failure  += HOST
        }




#%PAM-1.0
        auth       required     /lib/security/pam_unix.so shadow nullok
        account    required     /lib/security/pam_unix.so
        password   required     /lib/security/pam_cracklib.so retry=3
        password   required     /lib/security/pam_unix.so use_authtok nullok



            BINDIR = /usr/local/sbin

           
Then, make and make install

            cd ../
            cp config.php.sample config.php
            vim config.php
   

    $vacation_backend = 'suid';   
    $mail_vacation_binary = '/usr/local/sbin/squirrelmail_vacation_proxy';
    $vacation_path = '/usr/bin/vacation';






<VirtualHost *:4501>
        ServerAdmin srvadmin@myorg.com
        DocumentRoot /usr/share/squirrelmail
        ServerName webmail.myorg.com
        ServerAlias webmail
        ErrorLog logs/webmail.myorg.com-error_log
        CustomLog logs/webmail.myorg.com-access_log common
        SSLEngine on
        SSLCertificateFile /etc/pki/tls/certs/mail.crt
        SSLCertificateKeyFile /etc/pki/tls/private/mail.key
        <IfModule mod_rewrite.c>
        RewriteEngine on
        RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)$
        RewriteRule .* - [F]
        </IfModule>
</VirtualHost>

<VirtualHost *:80>
    ServerAdmin sysadmin@myorg.com
    ServerName webmail.myorg.com
    ServerAlias webmail
    Redirect permanent /        https://webmail.myorg.com:4501
        <IfModule mod_rewrite.c>
        RewriteEngine on
        RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)$
        RewriteRule .* - [F]
        </IfModule>
</VirtualHost>