=^.^=

Remote Controlled Netfilter with ClearOS API

karma

In my last post I shared a scriptlet that could be used to remote block access to your network with apache, sudo and iptables. This script suffers from the major flaw that appended iptables rules are read last and anywhere a universal ACCEPT rule preceded the script's additions they would be ignored. Another major drawback is in the rules disappearing if the firewall is reloaded, the host is rebooted and so on. Fortunately ClearOS has an easy to use API that lets you directly manipulate its firewall properties the same way as webconfig. This script doesn't require sudo rules or apache to be running. It DOES require ClearOS, and this is how to install it:

The SSL certificate webconfig provides will probably cause problems, so call the script like this if you use wget:

wget -O - 'https://192.168.8.1:81/rcleartables.php?action=deny&name=$name&ip=$ip' --no-check-certificate >/dev/null 2>&1

Note that this script requires a name variable, it should be a unique identifier containing letters and numbers (no spaces). I keep the name and other data associated with the blocks on the client end of things so the blocks can be removed by a button that executes the script with action=remove and also take care of cleaning stale blocks by way of recorded  timestamps. How you choose to extend the functionality is up to you.

<?php
/*
           # Remote Controlled iptables ClearOS API
           # June 2010 http://foxpa.ws
           # WTFPL v.2 http://foxpa.ws/wtfpl/

/// DOCUMENTATION

DANGER: Improperly configured, this script could be used by an attacker to
        block legitimate traffic.

This script adds or removes a name/IP pair pssed to it through the GET
variables "ip", "name" and "action" to or from the ClearOS Incoming Block
firewall ruleset. Valid action values are deny, and remove.
The script will exit with a 0 on error or a 1 upon successful execution.
Place the script in /var/webconfig/htdocs and chown it to webconfig.

To block an IP, one would GET request it thus:
https://address:81/rcleartables.php?action=block&ip=222.222.222.222&name=
On a successful block you would receive HTTP headers and a single 1 in the
body, or a 0 if the block was unsuccessful.

$whitelist is an array of IP addresses that should never be blocked
$allowed_clients should be an array of IP addresses allowed to have access to
this script. leave it blank to allow any host (not recommended).
$shared_secret is an optional key that can be passed to the script as an MD5
hash via GET var "key" to authenticate your application. Blank to disable.
$log_path should be the path to the specific file you would like to log actions
to. Blank to disable logging. Remember to update your log rotater's config.
*/

// CONFIGURATION
$whitelist = array();
$allowed_clients = array('');
$shared_secret = '';
$log_path = '/var/log/riptables.log';

// FUNCTIONS
function log_action($line)
{
	global $log_path, $remote_addr;
	if(!empty($log_path))
	{
		$fh = fopen($log_path, 'a');
		$date = date("Y-m-d H:i:s", time());
		fwrite($fh, "$date $remote_addr - $line\n");
		fclose($fh);
	}
}

// SANITY CHECKING
if(empty($_GET['ip']))
{
	log_action("IP not specified");
	die('0');
}
if($_GET['action'] == 'deny' and empty($_GET['name']))
{
	log_action('Rule name not specified');
	die('0');
}
$ip = $_GET['ip'];
$name = $_GET['name'];
$remote_addr = $_SERVER['REMOTE_ADDR'];
$octets = explode('.', $ip);
foreach($octets as $octet)
{
	if($octet > 255 or $octet < 0)
	{
		log_action("Invalid IP Address $ip");
		die('0');
	}
}
$ip = escapeshellcmd($ip);
if(!empty($shared_secret) and $_GET['key'] != md5($shared_secret))
{
	log_action("DANGER Invalid shared secret. Remember to encrypt your key variable with MD5.");
	die('0');
}
if(!empty($allowed_clients[0]))
{
	$valid = false;
	foreach($allowed_clients as $allowed)
	{
		if($allowed = $remote_addr)
			$valid = true;
	}
	if(!$valid)
	{
		log_action("DANGER Client is not in \$allowed_hosts array. This could be a sign of exposure.");
		die('0');
	}
}

// THE BRAINS
require_once("/var/webconfig/api/FirewallIncoming.class.php");
$fw = new FirewallIncoming();

if($_GET['action'] == 'deny')
{
	$fw->AddBlockHost($name, $ip);
	$fw->Restart();
	log_action("$ip was blocked");
	print('1');
}
elseif($_GET['action'] == 'remove')
{
	$fw->DeleteBlockHost($ip);
	$fw->Restart();
	log_action("$ip was removed");
	print('1');
}
else
{
	log_action('Invalid action parameter.');
	die('0');
}

?>
<?php
/*
# Remote Controlled iptables ClearOS API
# June 2010 http://foxpa.ws
# WTFPL v.2 http://foxpa.ws/wtfpl/

/// DOCUMENTATION

DANGER: Improperly configured, this script could be used by an attacker to
block legitimate traffic.

This script adds or removes a name/IP pair pssed to it through the GET
variables "ip", "name" and "action" to or from the ClearOS Incoming Block
firewall ruleset. Valid action values are block, and remove.
The script will exit with a 0 on error or a 1 upon successful execution.
Place the script in /var/webconfig/htdocs and chown it to webconfig.

To block an IP, one would GET request it thus:
https://address:81/rcleartables.php?action=block&ip=222.222.222.222&name=
On a successful block you would receive HTTP headers and a single 1 in the
body, or a 0 if the block was unsuccessful.

$whitelist is an array of IP addresses that should never be blocked
$allowed_clients should be an array of IP addresses allowed to have access to
this script. leave it blank to allow any host (not recommended).
$shared_secret is an optional key that can be passed to the script as an MD5
hash via GET var "key" to authenticate your application. Blank to disable.
$log_path should be the path to the specific file you would like to log actions
to. Blank to disable logging. Remember to update your log rotater's config.
*/

// CONFIGURATION
$whitelist = array();
$allowed_clients = array('');
$shared_secret = '';
$log_path = '/var/log/riptables.log';

// FUNCTIONS
function log_action($line)
{
global $log_path, $remote_addr;
if(!empty($log_path))
{
$fh = fopen($log_path, 'a');
$date = date("Y-m-d H:i:s", time());
fwrite($fh, "$date $remote_addr - $line\n");
fclose($fh);
}
}

// SANITY CHECKING
if(empty($_GET['ip']))
{
log_action("IP not specified");
die('0');
}
if($_GET['action'] == 'block' and empty($_GET['name']))
{
log_action('Rule name not specified');
die('0');
}
$ip = $_GET['ip'];
$name = $_GET['name'];
$remote_addr = $_SERVER['REMOTE_ADDR'];
$octets = explode('.', $ip);
foreach($octets as $octet)
{
if($octet > 255 or $octet < 0)
{
log_action("Invalid IP Address $ip");
die('0');
}
}
$ip = escapeshellcmd($ip);
if(!empty($shared_secret) and $_GET['key'] != md5($shared_secret))
{
log_action("DANGER Invalid shared secret. Remember to encrypt your key variable with MD5.");
die('0');
}
if(!empty($allowed_clients[0]))
{
$valid = false;
foreach($allowed_clients as $allowed)
{
if($allowed = $remote_addr)
$valid = true;
}
if(!$valid)
{
log_action("DANGER Client is not in \$allowed_hosts array. This could be a sign of exposure.");
die('0');
}
}

// THE BRAINS
require_once("/var/webconfig/api/FirewallIncoming.class.php");
$fw = new FirewallIncoming();

if($_GET['action'] == 'deny')
{
$fw->AddBlockHost($name, $ip);
$fw->Restart();
log_action("$ip was blocked");
print('1');
}
elseif($_GET['action'] == 'remove')
{
$fw->DeleteBlockHost($ip);
$fw->Restart();
log_action("$ip was removed");
print('1');
}
else
{
log_action('Invalid action parameter.');
die('0');
}

?>

Comments

There are no comments for this item.