Grant Google Access to SMTP on cPanel/WHM Centos Server without advertising SMTP Auth?

1.1k views Asked by At

A non-stop wave of distributed smtp auth attacks on my server prompted me to ban non specified IPs from connecting to smtp on my server and sending mail though it. Very effective. (instructions: http://sysadmintips.in/advanced/csf/exim)

However I now cannot use Google Mail (Gmail) to 'Send Mail As' for new accounts without either enabling two-factor authentication (which is a pain as I'm setting this up remotely for my clients) or switching smtp auth back on on my server.

My other option would be to white-list Google Mail's IP addresses.

Google searching discovered this way to retrieve the current Google IP ranges using something along these lines (which I've copied from this page: https://support.google.com/a/answer/60764?hl=en):

nslookup -q=TXT _spf.google.com 8.8.8.8

This returns a list of the domains included in Google's SPF record, such as: _netblocks.google.com, _netblocks2.google.com, _netblocks3.google.com

Now look up the DNS records associated with those domains, one at a time, like so:

nslookup -q=TXT _netblocks.google.com 8.8.8.8
nslookup -q=TXT _netblocks2.google.com 8.8.8.8
nslookup -q=TXT _netblocks3.google.com 8.8.8.8

The results of these commands contain the current range of addresses.

Can I use the output of these to generate useful content for /etc/csf/csf.smtpauth ?

I can code something to do this in PHP and run it as a cron task as root, but what format is acceptable? Does csf.smtpauth accept IP range declarations? Does it cope OK with IPV6 IPs?

After any change I'll also need to force a restart of csf and lfd automatically so the new IPs are in use. Is that possible from PHP running as root?

Thanks!

1

There are 1 answers

0
Claud On BEST ANSWER

Solved.

I've coded up the following PHP which queries Google's SPF records and then, only if required, will replace the existing SMTP Auth block with a new one. It then creates a file which is used as a flag for a bash script to re-start the firewall.

Note that /etc/csf/csf.smtpauth accepts IPV4 and IPV6 addresses and CIDR address ranges.

// Grab current Google SPF IPs...
$dns = dns_get_record('_spf.google.com', DNS_TXT);
if (!$dns)
{
    echo "FAILED TO RETRIEVE DNS RECORD<br />\n";
    exit;
}

// The variable in which to store the results
$ranges = array();

// Of interest in particular to us is...
$val = $dns[0]['txt'];

preg_match_all("/include:[^\s]+\s/", $val, $matches);

if (sizeof($matches[0]) <= 0)
{
    echo "BAD DATA RECEIVED OR FAILED TO DECODE DATA<br />\n";
    exit;
}

foreach ($matches[0] as $match)
{
    $match = trim($match);
    $domain = trim(preg_replace("/include\:/", "", $match));

    // Now do it all again for this domain to get the IP range
    $dns = dns_get_record($domain, DNS_TXT);

    if (!$dns)
    {
        echo "DNS LOOKUP FAILURE AT PASS 2<br />\n";
        exit;
    }

    $val = $dns[0]['txt'];
    preg_match_all("/ip\d:[^\s]+\s/", $val, $ips);

    if (sizeof($ips[0])<=0)
    {
        // At time of writing this is entirely possible as _netblocks3.google.com
        // currently holds NO IP ranges
    }
    else
    {
        foreach ($ips[0] as $ip)
        {
            $ip = trim($ip);
            if ($ip <> '')
            {
                $ip = preg_replace("/ip\d\:/", "", $ip);
                $ranges[] = $ip;
            }
        }
    }
}

// To be here means we made it without a major problem. Form the new IP range for
// the smtp auth file (/etc/csf/csf.smtpauth) and compare with the existing. Update only if there has
// been a change. Also update only if there are at least N ranges found.
// When I wrote this there were 11 IPV4 ranges and 6 IPV6 ranges so setting 
// low limit to 10
$limit = 10;
$filename  = '/etc/csf/csf.smtpauth';

if (sizeof($ranges) < $limit)
{
    echo "NOT UPDATING RANGES, TOO FEW DISCOVERED, PROBLEM?";
    exit;
}

$filerange = "# GOOGLE SPF RESULTS START\n";
$filerange .= join("\n", $ranges);
$filerange .= "\n# GOOGLE SPF RESULTS END";

// Read in existing conf file 
$econf = file_get_contents($filename);
if (sizeof($econf)<=0)
{
    echo "FAILED TO READ $filename<br />\n";
    exit;
}

// Extract the block
if (!preg_match("/\# GOOGLE SPF RESULTS START.+\# GOOGLE SPF RESULTS END/s", $econf, $matches))
{
    echo "FAILED TO FIND EXISTING BLOCK. CORRUPT AUTH FILE?<br />\n";
    exit;
}

if ($filerange == $matches[0])
{
    // IT'S THE SAME DO NOT UPDATE IT!;
    exit;
}

// Replace the block entirely
$econf = preg_replace("/\# GOOGLE SPF RESULTS START.+\# GOOGLE SPF RESULTS END/s", $filerange, $econf);

// Write out the new file contents
file_put_contents($filename, $econf);

// Trigger a CSF/LFD restart by creating trigger file.
touch("restartcsflfd"); 

Then create a CRON task to run this shell script shortly after and each time the above is run:

#!/bin/bash
if [ -f /path-to-file/restartcsflfd ];
then
    csf -r
    /etc/init.d/lfd restart
    rm -f restartcsflfd
    echo "RE-STARTED CSF and LFD"
fi