A place to make php web form simply works

Using Plesk Qmail Server as Spam Bot for 6 Years – Spamdyke Is the Solution to Fix It

The Problem – Qmail server being used as Spam Bot for a long time, 6 years actually

One client of mine told me that his email server was very slow sometime. I found the reason that because his email server was being used by spammers as a spam bot for a long time – 6 years actually. There were tons of spam messages being sent out in every minute during the last 6 years (from 2004 to 2010). The server is a dedicated server rented from an ISP in Vancouver, Canada. The server is using Plesk control panel (7.1.1 RedHat 7.3 71040727.08) and Qmail server.

If you happened to know such Plesk Qmail server like my client’s one, you can setup your Outlook or Thunderbird email client. Then use that machine as spam machine as easy as 123:

  • Step 1 : setup a fake email account as sender, like what...@domain-in-that-machine.com
  • Step 2 : setup a faker email account as receiver, like no-s...@domain-in-that-machine.com
  • Step 3 : CC or BCC email addresses for spamming

The trick is to use any valid domains in that email server.


The Solution – Use Spamdyke to Stop Spamming Relay

The Spamdyke is a brilliant idea. As spamdyke.org said it’s a drop-in connection-time spam filter for qmail AND no need to re-compile the qmail server. After suffering some pains on the configuration of spamdyke, the Spamdyke and qmail work amazingly good togther.

Mainly, I use two methods of spamdyke to fight with spammers:

  1. smtp authentication – only allows authorized users to send out emails. This will stop spammers to use email address like m...@mydomain.com as a sender to send out spam messages through the qmail server. However, use smtp authentication method ALONE is not enough. If we use it alone, we can’t receive any emails from outside (Yahoo, Gmail, etc…). That’s reason the method 2) is used.
  2. the recipients whitelist – only receive emails for accounts in the white list. This will allow all email accounts within qmail server receiving emails from outside. Because the email accounts is constantly updated by the Plesk admin panel. I have to write a script as a cronjob to update this recipients whitelist based on Plesk’s email accounts.

Here is the spamdyke configuration file:

filter-level=require-auth
smtp-auth-level=ondemand-encrypted
smtp-auth-command=/var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bin/cmd5checkpw /var/qmail/bin/true
log-level=verbose
#full-log-dir=/usr/local/psa/var/log/spamdyke
local-domains-file=/var/qmail/control/rcpthosts
max-recipients=20
idle-timeout-secs=60
graylist-level=only
graylist-dir=/var/qmail/spamdyke/greylist
graylist-min-secs=300
graylist-max-secs=1814400
sender-blacklist-file=/var/qmail/spamdyke/blacklist_senders
recipient-blacklist-file=/var/qmail/spamdyke/blacklist_recipients
recipient-whitelist-file=/var/qmail/spamdyke/whitelist_recipients
ip-blacklist-file=/var/qmail/spamdyke/blacklist_ip
rdns-whitelist-file=/var/qmail/spamdyke/whitelist_rdns
ip-whitelist-file=/var/qmail/spamdyke/whitelist_ip
reject-empty-rdns
reject-unresolvable-rdns
greeting-delay-secs=2
reject-missing-sender-mx

tls-level=smtp
tls-certificate-file=/var/qmail/control/servercert.pem
local-domains-file=/var/qmail/control/rcpthosts

Here is the Plesk qmail service for xinetd. Drop in spamdyke before the qmail_smtpd:

service smtp
{
        socket_type     = stream
        protocol        = tcp
        wait            = no
        disable         = no
        #user            = root
        user            = qmaild
        instances       = UNLIMITED
        server          = /var/qmail/bin/tcp-env
        #server_args     = /usr/sbin/rblsmtpd  -r sbl.spamhaus.org /var/qmail/bin/relaylock /var/qmail/bin/qmail-smtpd /var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bi
n/cmd5checkpw /var/qmail/bin/true
        server_args     = -Rt0 /usr/local/bin/spamdyke -f /etc/spamdyke.conf  /var/qmail/bin/relaylock /var/qmail/bin/qmail-smtpd /var/qmail/bin/smtp_auth /var/qmail/bin/true /v
ar/qmail/bin/cmd5checkpw /var/qmail/bin/true
}

Here is the php cronjob script to update the spamdyke recipients whitelist:

<?php
/**
 * May 5, 2010
 *
 * email: phpfmg
 *  ^.^ at ^.^
 * gmail.com
 *
 * Spamdyke will use filter-level=require-auth to force smtp authentication.
 * However, use it ALONE will stop us receiving emails from 3rd party email servers, like Yahoo, Gmail, etc...
 * The idea is to use whitelist_recipients in Spamdyke (even it said it's BAD idea).
 * Combining  whitelist_recipients and filter-level=require-auth is to stop spamming (sending and receiveing)
 * email addresses with random name. For example, notE...@yourdomain.com.
 *
 * Create whitelist_recipients for Spamdyke.
 *
 *  */

$GLOBALS['qmail_mailnames_dir'] = '/var/qmail/mailnames';
$GLOBALS['spamdyke_whitelist_file'] = '/var/qmail/spamdyke/whitelist_recipients';

update_whitelist();

function update_whitelist(){
    $dir = $GLOBALS['qmail_mailnames_dir'];
    if( !is_dir($dir) )
        return ;

    $list = array();
    if ($dh = opendir($dir)) {
        while (($file = readdir($dh)) !== false) {
            $fullpath = $dir . "/" . $file;
            if( $file == '.' || $file == '..' || !is_dir($fullpath) )
                continue; 

            $s = get_email_accounts($fullpath);
            if( !empty($s) ) $list[] = $s ;
        };
        closedir($dh);
    };

    return strToFile( join("\n",$list), $GLOBALS['spamdyke_whitelist_file'] );
}

function get_email_accounts( $domain_path ){
    $dir = $domain_path;
    $domain = trim(basename($dir));
    $accounts = array();
    if ($dh = opendir($dir)) {
        while (($file = readdir($dh)) !== false) {
            $fullpath = $dir . "/" . $file;
            if( $file == '.' || $file == '..' || !is_dir($fullpath) )
                continue; 

            $accounts[] = trim($file) . "@" . $domain;
        };
        closedir($dh);
    };

    return join("\n",$accounts);
}

function strToFile( $str = '', $file, $mode='w' )
{
	$h = fopen( $file, $mode ) ;
	if( $h ){
		fwrite($h, $str);
        fclose($h);
		return true ;
	};
	return false;
}

?>

The story is end here.

If you want to know more about the spamkdy, please continue reading.

The problems and the fixed when installing Spamdyke for Plesk Qmail

The Configuration Tests is a very good tool to scan its configuration and look for common configuration mistakes. The command I used is following:

/usr/local/bin/spamdyke -f /etc/spamdyke.conf \
--config-test \
--config-test-smtpauth-username myusername \
--config-test-smtpauth-password xxxxxxxx \
--access-file /etc/tcp.smtp \
/var/qmail/bin/relaylock \
/var/qmail/bin/qmail-smtpd

The output with Error detected:

05/03/2010 13:40:33 LOG OUTPUT
Testing configuration...
WARNING: Running tests as superuser root(0), group root(0). These test results may not be valid if the mail server runs as another user.
SUCCESS: spamdyke binary (/usr/sbin/spamdyke) is not owned by root and/or is not marked setuid.
INFO: Running command to test capabilities: /var/qmail/bin/relaylock
DEBUG(exec_command_argv()@exec.c:480): executing command: /var/qmail/bin/relaylock
SUCCESS: /var/qmail/bin/relaylock appears to offer TLS support but spamdyke will intercept and decrypt the TLS traffic so all of its filters can operate.
SUCCESS: /var/qmail/bin/relaylock appears to offer SMTP AUTH support. spamdyke will observe any authentication and trust its response. spamdyke will offer authentication if /var/qmail/bin/relaylock does not.
INFO(access-file): Testing file read: /etc/tcp.smtp
SUCCESS(access-file): Opened for reading: /etc/tcp.smtp
INFO(config-file): Testing file read: /etc/spamdyke.conf
SUCCESS(config-file): Opened for reading: /etc/spamdyke.conf
INFO(full-log-dir): Testing directory for writing: /usr/local/psa/var/log/spamdyke
SUCCESS(full-log-dir): Created and deleted file in directory: /usr/local/psa/var/log/spamdyke/spamdyke-test_1272919233_30663
INFO(ip-blacklist-file): Testing file read: /var/qmail/spamdyke/blacklist_ip
SUCCESS(ip-blacklist-file): Opened for reading: /var/qmail/spamdyke/blacklist_ip
INFO(ip-whitelist-file): Testing file read: /var/qmail/spamdyke/whitelist_ip
SUCCESS(ip-whitelist-file): Opened for reading: /var/qmail/spamdyke/whitelist_ip
INFO(local-domains-file): Testing file read: /var/qmail/control/rcpthosts
SUCCESS(local-domains-file): Opened for reading: /var/qmail/control/rcpthosts
INFO(rdns-whitelist-file): Testing file read: /var/qmail/spamdyke/whitelist_rdns
SUCCESS(rdns-whitelist-file): Opened for reading: /var/qmail/spamdyke/whitelist_rdns
INFO(recipient-blacklist-file): Testing file read: /var/qmail/spamdyke/blacklist_recipients
SUCCESS(recipient-blacklist-file): Opened for reading: /var/qmail/spamdyke/blacklist_recipients
INFO(sender-blacklist-file): Testing file read: /var/qmail/spamdyke/blacklist_senders
SUCCESS(sender-blacklist-file): Opened for reading: /var/qmail/spamdyke/blacklist_senders
INFO(sender-whitelist-file): Testing file read: /var/qmail/spamdyke/whitelist_senders
SUCCESS(sender-whitelist-file): Opened for reading: /var/qmail/spamdyke/whitelist_senders
INFO(smtp-auth-level): Examining authentication command: /var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bin/cmd5checkpw /var/qmail/bin/true
SUCCESS(smtp-auth-level): File is executable: /var/qmail/bin/smtp_auth
INFO(smtp-auth-level): Running authentication command with unencrypted input: /var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bin/cmd5checkpw /var/qmail/bin/true
DEBUG(exec_checkpassword_argv()@exec.c:108): executing SMTP AUTH command /var/qmail/bin/smtp_auth for user: myusername
ERROR: authentication misuse (no input given or no additional command path given, e.g. /bin/true): myusername
ERROR(smtp-auth-level): Authentication failed with unencrypted input: /var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bin/cmd5checkpw /var/qmail/bin/true
INFO(smtp-auth-level): Running authentication command with encrypted input: /var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bin/cmd5checkpw /var/qmail/bin/true
DEBUG(exec_checkpassword_argv()@exec.c:108): executing SMTP AUTH command /var/qmail/bin/smtp_auth for user: myusername
ERROR: authentication misuse (no input given or no additional command path given, e.g. /bin/true): myusername
ERROR(smtp-auth-level): Authentication failed with encrypted input: /var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bin/cmd5checkpw /var/qmail/bin/true
INFO: No authentication commands support encrypted input; change the value of "smtp-auth-level" to "ondemand" or "always" instead of "ondemand-encrypted"
INFO(tls-certificate-file): Testing TLS by initializing SSL/TLS library with certificate and key
SUCCESS(tls-certificate-file): Opened for reading: /var/qmail/control/servercert.pem
SUCCESS(tls-certificate-file): Certificate and key loaded; SSL/TLS library successfully initialized
ERROR: Tests complete. Errors detected.

Then I found I have to add the –run-as-user qmaild parameter :

/usr/local/bin/spamdyke -f /etc/spamdyke.conf \
--config-test \
--config-test-smtpauth-username raymond \
--config-test-smtpauth-password spiderman2 \
--access-file /etc/tcp.smtp \
--run-as-user qmaild \
/var/qmail/bin/relaylock \
/var/qmail/bin/qmail-smtpd

This time has no Error detected:

05/03/2010 13:45:27 LOG OUTPUT
Testing configuration...
SUCCESS: Running tests as user qmaild(2020), group root(0).
SUCCESS: spamdyke binary (/usr/sbin/spamdyke) is not owned by root and/or is not marked setuid.
INFO: Running command to test capabilities: /var/qmail/bin/relaylock
DEBUG(exec_command_argv()@exec.c:480): executing command: /var/qmail/bin/relaylock
SUCCESS: /var/qmail/bin/relaylock does not appear to offer TLS support. spamdyke will offer, intercept and decrypt TLS traffic.
SUCCESS: /var/qmail/bin/relaylock does not appear to offer SMTP AUTH support. spamdyke will offer and process authentication.
INFO(access-file): Testing file read: /etc/tcp.smtp
SUCCESS(access-file): Opened for reading: /etc/tcp.smtp
INFO(config-file): Testing file read: /etc/spamdyke.conf
SUCCESS(config-file): Opened for reading: /etc/spamdyke.conf
INFO(full-log-dir): Testing directory for writing: /usr/local/psa/var/log/spamdyke
SUCCESS(full-log-dir): Created and deleted file in directory: /usr/local/psa/var/log/spamdyke/spamdyke-test_1272919527_31221
INFO(ip-blacklist-file): Testing file read: /var/qmail/spamdyke/blacklist_ip
SUCCESS(ip-blacklist-file): Opened for reading: /var/qmail/spamdyke/blacklist_ip
INFO(ip-whitelist-file): Testing file read: /var/qmail/spamdyke/whitelist_ip
SUCCESS(ip-whitelist-file): Opened for reading: /var/qmail/spamdyke/whitelist_ip
INFO(local-domains-file): Testing file read: /var/qmail/control/rcpthosts
SUCCESS(local-domains-file): Opened for reading: /var/qmail/control/rcpthosts
INFO(rdns-whitelist-file): Testing file read: /var/qmail/spamdyke/whitelist_rdns
SUCCESS(rdns-whitelist-file): Opened for reading: /var/qmail/spamdyke/whitelist_rdns
INFO(recipient-blacklist-file): Testing file read: /var/qmail/spamdyke/blacklist_recipients
SUCCESS(recipient-blacklist-file): Opened for reading: /var/qmail/spamdyke/blacklist_recipients
INFO(sender-blacklist-file): Testing file read: /var/qmail/spamdyke/blacklist_senders
SUCCESS(sender-blacklist-file): Opened for reading: /var/qmail/spamdyke/blacklist_senders
INFO(sender-whitelist-file): Testing file read: /var/qmail/spamdyke/whitelist_senders
SUCCESS(sender-whitelist-file): Opened for reading: /var/qmail/spamdyke/whitelist_senders
INFO(smtp-auth-level): Examining authentication command: /var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bin/cmd5checkpw /var/qmail/bin/true
SUCCESS(smtp-auth-level): File is executable: /var/qmail/bin/smtp_auth
INFO(smtp-auth-level): Running authentication command with unencrypted input: /var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bin/cmd5checkpw /var/qmail/bin/true
DEBUG(exec_checkpassword_argv()@exec.c:108): executing SMTP AUTH command /var/qmail/bin/smtp_auth for user: myusername
DEBUG(exec_checkpassword_argv()@exec.c:201): authentication successful: myusername
SUCCESS(smtp-auth-level): Authentication succeeded with unencrypted input: /var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bin/cmd5checkpw /var/qmail/bin/true
INFO(smtp-auth-level): Running authentication command with encrypted input: /var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bin/cmd5checkpw /var/qmail/bin/true
DEBUG(exec_checkpassword_argv()@exec.c:108): executing SMTP AUTH command /var/qmail/bin/smtp_auth for user: myusername
ERROR: authentication failure (bad username/password, vchkpw uses this to indicate SMTP access is not allowed): myusername
ERROR(smtp-auth-level): Authentication failed with encrypted input: /var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bin/cmd5checkpw /var/qmail/bin/true
INFO: No authentication commands support encrypted input; change the value of "smtp-auth-level" to "ondemand" or "always" instead of "ondemand-encrypted"
INFO(tls-certificate-file): Testing TLS by initializing SSL/TLS library with certificate and key
SUCCESS(tls-certificate-file): Opened for reading: /var/qmail/control/servercert.pem
SUCCESS(tls-certificate-file): Certificate and key loaded; SSL/TLS library successfully initialized
SUCCESS: Tests complete. No errors detected.

I was happy about the test result. No errors detected. Good. So I restarted xinetd with following command:

Killall –HUP xinetd

And then test the spamdyke with Outlook. However, I got error “Authentication failed: myusername”.

DEBUG(exec_checkpassword_argv()@exec.c:108): executing SMTP AUTH command /var/qmail/bin/smtp_auth for user: myusername
ERROR: authentication misuse (no input given or no additional command path given, e.g. /bin/true): myusername

05/04/2010 11:30:01 - Authentication failed: myusername

05/04/2010 11:30:01 FROM SPAMDYKE TO REMOTE: 37 bytes
535 Refused. Authentication failed.

05/04/2010 11:30:01 CLOSED

I tried telnet to do smtp commands manually without any problems. I used base64 to encode my username and password. Then use the following telnet commands:

> telnet mymailserver.com 25
auth login
334 VXNlcm5hbWU6
bXl1c2VybmFtZQ==
334 UGFzc3dvcmQ6
bXlwYXNzd29yZA==
235 go ahead

As you can see, I passed the Spamdyke SMTP authentication. Why my outlook failed with the same username and password? Finally, I found the problem can be solved on the xinetd smtp script. I have to change the user from root to qmaild. I don’t know why, but it works. If you can explain to me, I will appreciate your input.

Here is final /etc/xinetd/smtp_psa script:

service smtp
{
        socket_type     = stream
        protocol        = tcp
        wait            = no
        disable         = no
        #user            = root
        user            = qmaild
        instances       = UNLIMITED
        server          = /var/qmail/bin/tcp-env
        #server_args     = /usr/sbin/rblsmtpd  -r sbl.spamhaus.org /var/qmail/bin/relaylock /var/qmail/bin/qmail-smtpd /var/qmail/bin/smtp_auth /var/qmail/bin/true /var/qmail/bi
n/cmd5checkpw /var/qmail/bin/true
        server_args     = -Rt0 /usr/local/bin/spamdyke -f /etc/spamdyke.conf  /var/qmail/bin/relaylock /var/qmail/bin/qmail-smtpd /var/qmail/bin/smtp_auth /var/qmail/bin/true /v
ar/qmail/bin/cmd5checkpw /var/qmail/bin/true
}

From the maillog file, there were 15585 DENIED_AUTH_REQUIRED message were logged in one day. Each DENIED_AUTH_REQUIRED message means one spamming message attempted. So in the last six years, there were over 34 millions (15585 * 6 * 365 = 34,131,150) spam messages had been sent out by this email server.

Thankfully, the happy ending is the Qmail server is no longer be used as spam bot.

One Response to “Using Plesk Qmail Server as Spam Bot for 6 Years – Spamdyke Is the Solution to Fix It”

  1. Calcyfer says:

    The PHP cronjob script doesn’t check for aliases. I’ve edited it, so that it now will. Updated file contents are below – hope they help you. =)

    $group) {
    $match = in_array($domain, $group);
    if($match) {
    $domainid = $id;
    continue;
    }
    }

    if($domainid) {
    $aliases = $GLOBALS['aliases'][$domainid];
    } else {
    $aliases = array($domain);
    }
    return $aliases;
    }

    function strToFile( $str = ”, $file, $mode=’w’ )
    {
    $h = fopen( $file, $mode ) ;
    if( $h ){
    fwrite($h, $str);
    fclose($h);
    return true ;
    };
    return false;
    }

    ?>