Remote Controlled Netfilter with ClearOS API
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:
- wget http://foxpa.ws/dist/rcleartables.php.bz2
- bunzip2 rcleartables.php.bz2
- mv rcleartables.php /var/webconfig/htdocs
- chown webconfig: /var/webconfig/htdocs/rcleartables.php
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'); } ?>
/*
# 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.