Update Event Guard version 2.0

- Use the service class
- Use object interfaces
- Add support for nftables
This commit is contained in:
markjcrane
2026-01-29 05:31:02 -07:00
parent 78b1ebc47d
commit 2c448c7bfb
9 changed files with 965 additions and 932 deletions

View File

@@ -1,11 +1,11 @@
<?php
//application details
$apps[$x]['name'] = 'Event Guard Logs';
$apps[$x]['name'] = 'Event Guard';
$apps[$x]['uuid'] = 'c5b86612-1514-40cb-8e2c-3f01a8f6f637';
$apps[$x]['category'] = '';
$apps[$x]['subcategory'] = '';
$apps[$x]['version'] = '';
$apps[$x]['category'] = 'Switch';
$apps[$x]['subcategory'] = 'Security';
$apps[$x]['version'] = '2.0';
$apps[$x]['license'] = 'Mozilla Public License 1.1';
$apps[$x]['url'] = 'http://www.fusionpbx.com';
$apps[$x]['description']['en-us'] = '';

View File

@@ -83,14 +83,14 @@ class event_guard {
* @return void
*/
public function __construct(array $setting_array = []) {
//set domain and user UUIDs
// Set domain and user UUIDs
$this->domain_uuid = $setting_array['domain_uuid'] ?? $_SESSION['domain_uuid'] ?? '';
$this->user_uuid = $setting_array['user_uuid'] ?? $_SESSION['user_uuid'] ?? '';
//set objects
// Set the objects
$this->database = $setting_array['database'] ?? database::new();
//assign the variables
// Assign the variables
$this->name = 'event_guard_log';
$this->table = 'event_guard_logs';
$this->toggle_field = '';
@@ -110,11 +110,11 @@ class event_guard {
public function delete($records) {
if (permission_exists($this->name . '_delete')) {
//add multi-lingual support
// Add multi-lingual support
$language = new text;
$text = $language->get();
//validate the token
// Validate the token
$token = new token;
if (!$token->validate($_SERVER['PHP_SELF'])) {
message::add($text['message-invalid_token'], 'negative');
@@ -122,27 +122,27 @@ class event_guard {
exit;
}
//delete multiple records
// Delete multiple records
if (is_array($records) && @sizeof($records) != 0) {
//build the delete array
// Build the delete array
$x = 0;
foreach ($records as $record) {
//add to the array
// Add to the array
if ($record['checked'] == 'true' && is_uuid($record['event_guard_log_uuid'])) {
$array[$this->table][$x]['event_guard_log_uuid'] = $record['event_guard_log_uuid'];
}
//increment the id
// Increment the id
$x++;
}
//delete the checked rows
// Delete the checked rows
if (is_array($array) && @sizeof($array) != 0) {
//execute delete
// Execute delete
$this->database->delete($array);
unset($array);
//set message
// Set the message
message::add($text['message-delete']);
}
unset($records);
@@ -160,11 +160,11 @@ class event_guard {
public function unblock($records) {
if (permission_exists($this->name . '_unblock')) {
//add multi-lingual support
// Add multi-lingual support
$language = new text;
$text = $language->get();
//validate the token
// Validate the token
$token = new token;
if (!$token->validate($_SERVER['PHP_SELF'])) {
message::add($text['message-invalid_token'], 'negative');
@@ -172,7 +172,7 @@ class event_guard {
exit;
}
//delete multiple records
// Delete multiple records
if (is_array($records) && @sizeof($records) != 0) {
//build the delete array
$x = 0;
@@ -187,23 +187,23 @@ class event_guard {
$x++;
}
//delete the checked rows
// Delete the checked rows
if (is_array($array) && @sizeof($array) != 0) {
//execute delete
// Execute delete
$this->database->save($array);
unset($array);
//initialize the settings object
// Initialize the settings object
$setting = new settings(["category" => 'switch']);
//send unblock event
$cmd = "sendevent CUSTOM\n";
$cmd .= "Event-Name: CUSTOM\n";
$cmd .= "Event-Subclass: event_guard:unblock\n";
$esl = event_socket::create();
// Send unblock event
$cmd = "sendevent CUSTOM\n";
$cmd .= "Event-Name: CUSTOM\n";
$cmd .= "Event-Subclass: event_guard:unblock\n";
$esl = event_socket::create();
$switch_result = event_socket::command($cmd);
//set message
// Set the message
message::add($text['message-delete']);
}
unset($records);
@@ -223,11 +223,11 @@ class event_guard {
public function toggle($records) {
if (permission_exists($this->name . '_edit')) {
//add multi-lingual support
// Add multi-lingual support
$language = new text;
$text = $language->get();
//validate the token
// Validate the token
$token = new token;
if (!$token->validate($_SERVER['PHP_SELF'])) {
message::add($text['message-invalid_token'], 'negative');
@@ -235,9 +235,9 @@ class event_guard {
exit;
}
//toggle the checked records
// Toggle the checked records
if (is_array($records) && @sizeof($records) != 0) {
//get current toggle state
// Get current toggle state
foreach ($records as $record) {
if (!empty($record['checked']) && $record['checked'] == 'true' && is_uuid($record['event_guard_log_uuid'])) {
$uuids[] = "'" . $record['event_guard_log_uuid'] . "'";
@@ -255,25 +255,24 @@ class event_guard {
unset($sql, $parameters, $rows, $row);
}
//build update array
// Build update array
$x = 0;
foreach ($states as $uuid => $state) {
//create the array
// Create the array
$array[$this->table][$x][$this->name . '_uuid'] = $uuid;
$array[$this->table][$x][$this->toggle_field] = $state == $this->toggle_values[0] ? $this->toggle_values[1] : $this->toggle_values[0];
//increment the id
// Increment the id
$x++;
}
//save the changes
// Save the changes
if (is_array($array) && @sizeof($array) != 0) {
//save the array
// Save the array
$this->database->save($array);
unset($array);
//set message
// Set the message
message::add($text['message-toggle']);
}
unset($records, $states);
@@ -293,11 +292,11 @@ class event_guard {
public function copy($records) {
if (permission_exists($this->name . '_add')) {
//add multi-lingual support
// Add multi-lingual support
$language = new text;
$text = $language->get();
//validate the token
// Validate the token
$token = new token;
if (!$token->validate($_SERVER['PHP_SELF'])) {
message::add($text['message-invalid_token'], 'negative');
@@ -305,17 +304,17 @@ class event_guard {
exit;
}
//copy the checked records
// Copy the checked records
if (is_array($records) && @sizeof($records) != 0) {
//get checked records
// Get checked records
foreach ($records as $record) {
if (!empty($record['checked']) && $record['checked'] == 'true' && is_uuid($record['event_guard_log_uuid'])) {
$uuids[] = "'" . $record['event_guard_log_uuid'] . "'";
}
}
//create the array from existing data
// Create the array from existing data
if (is_array($uuids) && @sizeof($uuids) != 0) {
$sql = "select * from v_" . $this->table . " ";
$sql .= "where event_guard_log_uuid in (" . implode(', ', $uuids) . ") ";
@@ -323,7 +322,7 @@ class event_guard {
if (is_array($rows) && @sizeof($rows) != 0) {
$x = 0;
foreach ($rows as $row) {
//convert boolean values to a string
// Convert boolean values to a string
foreach ($row as $key => $value) {
if (gettype($value) == 'boolean') {
$value = $value ? 'true' : 'false';
@@ -331,32 +330,30 @@ class event_guard {
}
}
//copy data
// Copy data
$array[$this->table][$x] = $row;
//add copy to the description
// Add copy to the description
$array[$this->table][$x]['event_guard_log_uuid'] = uuid();
//increment the id
// Increment the id
$x++;
}
}
unset($sql, $parameters, $rows, $row);
}
//save the changes and set the message
// Save the changes and set the message
if (is_array($array) && @sizeof($array) != 0) {
//save the array
// Save the array
$this->database->save($array);
unset($array);
//set message
// Set the message
message::add($text['message-copy']);
}
unset($records);
}
}
}
}

View File

@@ -0,0 +1,128 @@
<?php
/**
* event_guard_iptables class
*
*/
class event_guard_iptables implements event_guard_interface {
/**
* database object
* @var database
*/
private $database;
/**
* settings object
* @var settings
*/
private $settings;
/**
* firewall_path string
* @var string
*/
private $firewall_path;
/**
* called when the object is created
*/
public function __construct(settings $settings) {
// Save the settings object
$this->settings = $settings;
// Set the database object from the settings object
$this->database = $settings->database();
// Set firewall path
$this->firewall_path = trim(shell_exec('command -v iptables'));
// Create a chain array
$chains[] = 'sip-auth-ip';
$chains[] = 'sip-auth-fail';
foreach ($chains as $chain) {
shell_exec($this->firewall_path.' --new ' . $chain . ' >/dev/null 2>&1 &');
shell_exec($this->firewall_path.' -I INPUT -j '.$chain . ' >/dev/null 2>&1 &');
}
}
/**
* Execute a block command for iptables
*
* @param string $ip_address The IP address to block
* @param string $filter The filter name for nftables, iptables or pf
* @param array $event The event data containing 'to-user' and 'to-host'
*
* @return boolean True if the block command was executed successfully, false otherwise
*/
public function block_add(string $ip_address, string $filter) : bool {
// Invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
// Run the block command for iptables
// Example: iptables -I INPUT -s 127.0.0.1 -j DROP
$command = $this->firewall_path.' -I '.$filter.' -s '.$ip_address.' -j DROP';
$result = shell_exec($command);
if (!empty($result)) {
return false;
}
// Return success
return true;
}
/**
* Unblock a specified IP address from a firewall.
*
* @param string $ip_address The IP address to unblock.
* @param string $filter The filter name used in the firewall configuration.
*
* @return bool True if the IP address was successfully unblocked, false otherwise.
*/
public function block_delete(string $ip_address, string $filter) : bool {
// Invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
// Unblock the address
$command = $this->firewall_path.' -L '.$filter.' -n --line-numbers | grep "'.$ip_address.' " | cut -d " " -f1';
$line_number = trim(shell_exec($command));
echo "\n". $command . " line ".__line__."\n";
if (is_numeric($line_number)) {
//$result = shell_exec('iptables -D INPUT '.$line_number);
$command = $this->firewall_path.' -D '.$filter.' '.$line_number;
$result = shell_exec($command);
if (!empty($result)) {
return false;
}
echo "Unblock address ".$ip_address ." line ".$line_number." command ".$command." result ".$result."\n";
}
// Return success
return true;
}
/**
* Check if an IP address is blocked in the configured firewall.
*
* @param string $ip_address The IP address to check
*
* @return bool True if the address is blocked, False otherwise
*/
public function block_exists(string $ip_address, string $filter) : bool {
// Determine whether to return true or false
// Check to see if the address is blocked
$command = $this->firewall_path.' -L -n --line-numbers | grep '.$ip_address;
$result = shell_exec($command);
if (!empty($result) && strlen($result) > 3) {
return true;
}
// Return the result
return false;
}
}

View File

@@ -0,0 +1,129 @@
<?php
/**
* event_guard_nftables class
*
*/
class event_guard_nftables implements event_guard_interface {
/**
* database object
* @var database
*/
private $database;
/**
* settings object
* @var settings
*/
private $settings;
/**
* firewall_path string
* @var string
*/
private $firewall_path;
/**
* called when the object is created
*/
public function __construct(settings $settings) {
// Save the settings object
$this->settings = $settings;
// Set the database object from the settings object
$this->database = $settings->database();
// Set firewall path
$this->firewall_path = trim(shell_exec('command -v nft'));
// Create a chain array
$chains[] = 'sip-auth-ip';
$chains[] = 'sip-auth-fail';
foreach ($chains as $chain) {
shell_exec($this->firewall_path.' add chain inet filter ' . $chain . ' { type filter hook input priority -50 \; }'); // >/dev/null 2>&1 &');
}
}
/**
* Execute a block command for iptables
*
* @param string $ip_address The IP address to block
* @param string $filter The filter name for nftables, iptables or pf
* @param array $event The event data containing 'to-user' and 'to-host'
*
* @return boolean True if the block command was executed successfully, false otherwise
*/
public function block_add(string $ip_address, string $filter) : bool {
// Invalid IP address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
// Run the block command for nftables
// Example: nft add element inet filter sip-auth-ip { 192.168.1.100 }
$command = $this->firewall_path.' add rule inet filter '.$filter.' ip saddr '.$ip_address.' counter drop';
$result = shell_exec($command);
if (!empty($result) && strlen($result) > 3) {
return false;
}
// Return success
return true;
}
/**
* Unblock a specified IP address from a firewall.
*
* @param string $ip_address The IP address to unblock.
* @param string $filter The filter name used in the firewall configuration.
*
* @return bool True if the IP address was successfully unblocked, false otherwise.
*/
public function block_delete(string $ip_address, string $filter) : bool {
// Invalid IP address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
// Command used to get the handle
$command = $this->firewall_path.' -a list chain inet filter '.$filter.' | grep '.$ip_address;
echo $command."\n";
$result = trim(shell_exec($command));
$rows = explode("\n", $result);
// Unblock the address
foreach ($rows as $row) {
$handle = trim(explode("#", $row)[1] ?? '');
$command = $this->firewall_path.' delete rule inet filter '.$filter.' '.$handle;
echo $command."\n";
$result = shell_exec($command);
}
if (!empty($result)) {
return false;
}
// Return success
return true;
}
/**
* Check if an IP address is blocked in the configured firewall.
*
* @param string $ip_address The IP address to check
*
* @return bool True if the address is blocked, False otherwise
*/
public function block_exists(string $ip_address, string $filter) : bool {
// Determine whether to return true or false
// Check to see if the address is blocked
$command = $this->firewall_path.' list chain inet filter input | grep "ip saddr '.$ip_address.'"';
$result = shell_exec($command);
if (!empty($result) && strlen($result) > 3) {
return true;
}
// Return failed
return false;
}
}

View File

@@ -0,0 +1,114 @@
<?php
/**
* event_guard_pf class
*
*/
class event_guard_pf implements event_guard_interface {
/**
* database object
* @var database
*/
private $database;
/**
* settings object
* @var settings
*/
private $settings;
/**
* firewall_path string
* @var string
*/
private $firewall_path;
/**
* called when the object is created
*/
public function __construct(settings $settings) {
// Save the settings object
$this->settings = $settings;
// Set the database object from the settings object
$this->database = $settings->database();
// Set firewall path
$this->firewall_path = trim(shell_exec('command -v pfctl'));
}
/**
* Execute a block command for iptables
*
* @param string $ip_address The IP address to block
* @param string $filter The filter name for nftables, iptables or pf
* @param array $event The event data containing 'to-user' and 'to-host'
*
* @return boolean True if the block command was executed successfully, false otherwise
*/
public function block_add(string $ip_address, string $filter) : bool {
// Invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
// Run the block command for pf
// Example: pfctl -t sip-auth-ip -T add 127.0.0.5
$command = $this->firewall_path.' -t '.$filter.' -T add '.$ip_address;
$result = shell_exec($command);
if (!empty($result)) {
return false;
}
// Return success
return true;
}
/**
* Unblock a specified IP address from a firewall.
*
* @param string $ip_address The IP address to unblock.
* @param string $filter The filter name used in the firewall configuration.
*
* @return bool True if the IP address was successfully unblocked, false otherwise.
*/
public function block_delete(string $ip_address, string $filter) : bool {
// Invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
// Unblock the address
// Example: pfctl -t sip-auth-ip -T delete 127.0.0.5
$command = $this->firewall_path.' -t '.$filter.' -T delete '.$ip_address;
$result = shell_exec($command);
if (!empty($result)) {
return false;
}
// Return success
return true;
}
/**
* Check if an IP address is blocked in the configured firewall.
*
* @param string $ip_address The IP address to check
*
* @return bool True if the address is blocked, False otherwise
*/
public function block_exists(string $ip_address, string $filter) : bool {
// Determine whether to return true or false
// Check to see if the address is blocked
$command = $this->firewall_path.' -t ".$filter." -Ts | grep '.$ip_address;
$result = shell_exec($command);
if (!empty($result) && strlen($result) > 3) {
return true;
}
// Return the result
return false;
}
}

View File

@@ -0,0 +1,517 @@
<?php
/**
* Description goes here for event_guard service
*/
class event_guard_service extends service {
/**
* database object
* @var database
*/
private $database;
/**
* settings object
* @var settings
*/
private $settings;
/**
* hostname variable
* @var string
*/
private $hostname;
/**
* firewall object
* @var event_guard_interface
*/
private $firewall;
/**
* socket object
* @var event_socket
*/
private $socket;
/**
* Reloads settings from database, config file and websocket server.
*
* @return void
*/
protected function reload_settings(): void {
// Re-read the config file to get any possible changes
parent::$config->read();
// Connect to the database
$this->database = new database(['config' => parent::$config]);
// Get the settings using global defaults
$this->settings = new settings(['database' => $this->database]);
// Set the php operating system
$php_os = strtolower(PHP_OS);
// Set the firewall name
if ($php_os == 'freebsd') {
$firewall_name = $this->settings->get('system','firewall_name', 'pf');
}
if ($php_os == 'linux') {
$firewall_name = $this->settings->get('system','firewall_name', 'nftables');
}
if (empty($firewall_name)) {
throw new Exception("No firewall name specified in settings");
}
// Get the settings using global defaults
$class_name = 'event_guard_'.$firewall_name;
$this->firewall = new $class_name($this->settings);
if (!($this->firewall instanceof event_guard_interface)) {
throw new Exception("Must be an event_guard_interface firewall");
}
// Get the hostname
$this->hostname = gethostname();
// Connect to event socket
$this->socket = new event_socket;
if ($this->socket->connect()) {
// Loop through the switch events
$cmd = "event json ALL";
$result = $this->socket->request($cmd);
$this->debug('subscribe to ALL events '. print_r($result, true));
// Filter for specific events
$cmd = "filter Event-Name CUSTOM";
$result = $this->socket->request($cmd);
$this->debug('subscribe to CUSTOM events '. print_r($result, true));
}
else {
$this->warning('Unable to connect to event socket');
}
}
public function run(): int {
// Reload the settings
$this->reload_settings();
// Service work is handled here
while ($this->running) {
// Initialize the array for switch events
$json_array = [];
// Make sure the database connection is available
while (!$this->database->is_connected()) {
// Connect to the database
$this->database->connect();
// Reload settings after connection to the database
$this->settings = new settings(['database' => $this->database]);
// Sleep for a moment
sleep(1);
}
// Reconnect to event socket
if (!$this->socket->connected()) {
$this->warning('Not connected to even socket');
if ($this->socket->connect()) {
$cmd = "event json ALL";
$result = $this->socket->request($cmd);
$this->debug('subscribe to ALL events '. print_r($result, true));
$cmd = "filter Event-Name CUSTOM";
$result = $this->socket->request($cmd);
$this->debug('subscribe to CUSTOM events '. print_r($result, true));
$this->info('Re-connected to event socket');
}
else {
// Unable to connect to event socket
$this->warning('Unable to connect to event socket');
// Sleep and then attempt to reconnect
sleep(1);
continue;
}
}
// Read the socket
$json_response = $this->socket->read_event();
// Decode the response
if (isset($json_response) && $json_response != '') {
$json_array = json_decode($json_response['$'], true);
unset($json_response);
}
// Debug the event array
$this->debug('Event array '. print_r($json_array, true));
// Registration failed - block IP address unless they are registered
if (is_array($json_array) && $json_array['Event-Subclass'] == 'sofia::register_failure') {
//not registered so block the address
if (!$this->allow_access($json_array['network-ip'])) {
$this->block_add($json_array['network-ip'], 'sip-auth-fail', $json_array);
}
}
// Sendevent CUSTOM event_guard:unblock
if (is_array($json_array) && $json_array['Event-Subclass'] == 'event_guard:unblock') {
//check the database for pending requests
$sql = "select event_guard_log_uuid, log_date, filter, ip_address, extension, user_agent ";
$sql .= "from v_event_guard_logs ";
$sql .= "where log_status = 'pending' ";
$sql .= "and hostname = :hostname ";
//$this->debug($sql." ".$this->hostname);
$parameters['hostname'] = $this->hostname;
$event_guard_logs = $this->database->select($sql, $parameters, 'all');
if (is_array($event_guard_logs)) {
$x = 0;
foreach($event_guard_logs as $row) {
//unblock the ip address
$this->block_delete($row['ip_address'], $row['filter']);
//debug info
$this->info("unblocked: [ip_address: ".$row['ip_address'].", filter: ".$row['filter'].", to-user: ".$row['extension'].", to-host: ".$row['hostname'].", line: ".__line__);
//log the blocked ip address to the database
$array['event_guard_logs'][$x]['event_guard_log_uuid'] = $row['event_guard_log_uuid'];
$array['event_guard_logs'][$x]['log_date'] = 'now()';
$array['event_guard_logs'][$x]['log_status'] = 'unblocked';
$x++;
}
if (is_array($array)) {
$p = permissions::new();
$p->add('event_guard_log_edit', 'temp');
$this->database->save($array, false);
$p->delete('event_guard_log_edit', 'temp');
unset($array);
}
}
}
// Registration to the IP address
if (is_array($json_array) && $json_array['Event-Subclass'] == 'sofia::pre_register') {
if (isset($json_array['to-host'])) {
$is_valid_ip = filter_var($json_array['to-host'], FILTER_VALIDATE_IP);
if ($is_valid_ip) {
//if not registered block the address
if (!$this->allow_access($json_array['network-ip'])) {
$this->block_add($json_array['network-ip'], 'sip-auth-ip', $json_array);
}
//debug info
$this->debug("network-ip ".$json_array['network-ip'].", to-host ".$json_array['to-host']);
}
}
}
// Debug information
//if (($json_array['Event-Subclass'] == 'sofia::register_failure' || $json_array['Event-Subclass'] == 'sofia::pre_register')) {
//echo "\n";
//print_r($json_array);
//echo "event_name: ".$json_array['Event-Name']."\n";
//echo "event_type: ".$json_array['event_type']."\n";
//echo "event_subclass: ".$json_array['Event-Subclass']."\n";
//echo "status: ".$json_array['status']."\n";
//echo "network_ip: ".$json_array['network-ip']."\n";
//echo "channel_state: ".$json_array['Channel-State']."\n";
//echo "channel_call_state: ".$json_array['Channel-Call-State']."\n";
//echo "call_direction: ".$json_array['Call-Direction']."\n";
//echo "channel_call_uuid: ".$json_array['Channel-Call-UUID']."\n";
//echo "answer_state: ".$json_array['Answer-State']."\n";
//echo "hangup_cause: ".$json_array['Hangup-Cause']."\n";
//echo "to-host: $json_array['to-host']\n";
//echo "\n";
//}
// Sleep for 100 ms
usleep(100000);
}
return 0;
}
protected static function display_version(): void {
echo "1.1\n";
}
protected static function set_command_options() {
}
/**
* Execute a block command for nftables, iptables or pf based on the firewall type.
*
* @param string $ip_address The IP address to block
* @param string $filter The filter name for nftables, iptables or pf
* @param array $event The event data containing 'to-user' and 'to-host'
*
* @return boolean True if the block command was executed successfully, false otherwise
*/
public function block_add(string $ip_address, string $filter, array $event) : bool {
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//block the IP address
$result = $this->firewall->block_add($ip_address, $filter);
if ($result) {
//log the blocked ip address to the log
$this->warning("blocked: [ip_address: ".$ip_address.", filter: ".$filter.", to-user: ".$event['to-user'].", to-host: ".$event['to-host'].", line: ".__line__."]");
//log the blocked ip address to the database
$array = [];
$array['event_guard_logs'][0]['event_guard_log_uuid'] = uuid();
$array['event_guard_logs'][0]['hostname'] = gethostname();
$array['event_guard_logs'][0]['log_date'] = 'now()';
$array['event_guard_logs'][0]['filter'] = $filter;
$array['event_guard_logs'][0]['ip_address'] = $ip_address;
$array['event_guard_logs'][0]['extension'] = $event['to-user'].'@'.$event['to-host'];
$array['event_guard_logs'][0]['user_agent'] = $event['user-agent'];
$array['event_guard_logs'][0]['log_status'] = 'blocked';
$p = permissions::new();
$p->add('event_guard_log_add', 'temp');
$this->database->save($array, false);
$p->delete('event_guard_log_add', 'temp');
//send debug information to the console
$this->info("blocked address " . $ip_address . ", line " . __line__);
}
//return the result
return $result;
}
public function block_delete(string $ip_address, string $filter) : bool {
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//unblock the IP address
$result = $this->firewall->block_delete($ip_address, $filter);
if ($result) {
//send debug information to the console
$this->info("Unblock address " . $ip_address . ", line " . __line__);
}
//return the result
return $result;
}
public function block_exists(string $ip_address, string $filter) : bool {
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//check if the address is blocked
$result = $this->firewall->block_exists($ip_address, $filter);
//send debug information to the console
$this->debug("Address Exists " . $ip_address . ", line " . __line__);
//return the result
return $result;
}
/**
* Determine if access is allowed for a given IP address.
*
* This method checks the IP address is inside the cache, user logs, event guard logs, access controls,
* and registration to determine if access should be allowed. If the IP address is found
* in the access control list, user logs with result success, or valid registrations
* is found then the address is automatically allowed.
*
* @param string $ip_address The IP address to check for access.
*
* @return boolean True if access is allowed, false otherwise.
*/
private function allow_access($ip_address) {
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//check the cache to see if the address is allowed
$cache = new cache;
if ($cache->get("switch:allowed:".$ip_address) === 'true') {
//debug info
$this->debug("address: ".$ip_address." allowed by: cache");
//return boolean true
return true;
}
//allow access for addresses with authentication status success
if ($this->allow_user_log_success($ip_address)) {
//save address to the cache as allowed
$cache->set("switch:allowed:".$ip_address, 'true');
//debug info
$this->debug("address: ".$ip_address." allowed by: user logs");
//return boolean true
return true;
}
//allow access for addresses that have been unblocked
/*
if (event_guard_log_allowed($ip_address)) {
//save address to the cache as allowed
$cache->set("switch:allowed:".$ip_address, 'true');
//debug info
$this->debug("address: ".$ip_address." allowed by: unblocked");
//return boolean true
return true;
}
*/
//allow access if the cidr address is allowed
if ($this->allow_access_control($ip_address)) {
//save address to the cache as allowed
$cache->set("switch:allowed:".$ip_address, 'true');
//debug info
$this->debug("address: ".$ip_address." allowed by: access controls");
//return boolean true
return true;
}
//allow if there is a registration from the same IP address
if ($this->allow_registered($ip_address)) {
//save address to the cache as allowed
$cache->set("switch:allowed:".$ip_address, 'true');
//debug info
$this->debug("address: ".$ip_address." allowed by: registration");
//return boolean true
return true;
}
//return
return false;
}
/**
* Checks if the given IP address is authorized by any access control node.
*
* @param string $ip_address The IP address to check for authorization.
*
* @return bool True if the IP address is authorized, false otherwise.
*/
private function allow_access_control($ip_address) {
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//get the access control allowed nodes
$sql = "select access_control_node_uuid, access_control_uuid, node_cidr, node_description ";
$sql .= "from v_access_control_nodes ";
$sql .= "where node_type = 'allow' ";
$sql .= "and length(node_cidr) > 0 ";
$parameters = null;
$allowed_nodes = $this->database->select($sql, $parameters, 'all');
//default authorized to false
$allowed = false;
//use the ip address to get the authorized nodes
if (is_array($allowed_nodes)) {
foreach($allowed_nodes as $row) {
if (check_cidr($row['node_cidr'], $ip_address)) {
//debug info
// print_r($row);
// $this->debug("Authorized: ".$ip_address);
//set the allowed to true
$allowed = true;
//exit the loop
break;
}
}
}
//return
return $allowed;
}
/**
* Determines if a user's IP address is allowed based on their login history.
*
* @param string $ip_address The IP address to check for access.
*
* @return bool True if the IP address is allowed, false otherwise.
*/
private function allow_user_log_success($ip_address) {
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//check to see if the address was authenticated successfully
$sql = "select count(user_log_uuid) ";
$sql .= "from v_user_logs ";
$sql .= "where remote_address = :remote_address ";
$sql .= "and result = 'success' ";
$sql .= "and timestamp > NOW() - INTERVAL '8 days' ";
$parameters['remote_address'] = $ip_address;
$user_log_count = $this->database->select($sql, $parameters, 'column');
//debug info
$this->debug("address ".$ip_address." count ".$user_log_count);
//default authorized to false
$allowed = false;
//use the ip address to get the authorized nodes
if (!empty($user_log_count) && $user_log_count > 0) {
$allowed = true;
}
//return
return $allowed;
}
/**
* Checks if the given IP address is registered on the network.
*
* @param string $ip_address The IP address to check for registration.
*
* @return bool True if the IP address is registered, false otherwise.
*/
private function allow_registered($ip_address) {
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
$registered = false;
$command = "fs_cli -x 'show registrations as json' ";
$result = shell_exec($command);
$array = json_decode($result, true);
if (is_array($array['rows'])) {
foreach ($array['rows'] as $row) {
if ($row['network_ip'] == $ip_address) {
$registered = true;
}
}
}
//return registered boolean
return $registered;
}
}

View File

@@ -0,0 +1,11 @@
<?php
/**
* event_guard_interface class
*
*/
interface event_guard_interface {
public function block_add(string $ip_address, string $filter) : bool;
public function block_delete(string $ip_address, string $filter) : bool;
public function block_exists(string $ip_address, string $filter) : bool;
}

View File

@@ -16,6 +16,9 @@ StartLimitIntervalSec=0
[Service]
WorkingDirectory=/var/www/fusionpbx
ExecStart=/usr/bin/php /var/www/fusionpbx/app/event_guard/resources/service/event_guard.php
RuntimeDirectory=fusionpbx
RuntimeDirectoryMode=0755
RuntimeDirectoryPreserve=yes
User=root
Group=root
TimeoutSec=55s
@@ -23,3 +26,4 @@ Restart=always
[Install]
WantedBy=multi-user.target

View File

@@ -1,884 +1,17 @@
<?php
/*
Copyright (C) 2022-2024 Mark J Crane <markjcrane@fusionpbx.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
require_once dirname(__DIR__, 4) . '/resources/require.php';
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
try {
// Create the service
$service = event_guard_service::create();
//check the permission
if (defined('STDIN')) {
//includes files
require_once dirname(__DIR__, 4) . "/resources/require.php";
}
else {
//only allow running this from command line
exit;
}
// Exit using the status run method returns
exit($service->run());
} catch (Throwable $ex) {
// Show the details of the error
echo "Error occurred in " . $ex->getFile() . ' (' . $ex->getLine() . '):' . $ex->getMessage();
//increase limits
set_time_limit(0);
ini_set('max_execution_time', 0);
ini_set('memory_limit', '256M');
//save the arguments to variables
$script_name = $argv[0];
if (!empty($argv[1])) {
parse_str($argv[1], $_GET);
}
//set the variables
if (!empty($_GET['hostname'])) {
$hostname = urldecode($_GET['hostname']);
}
$debug = false;
if (!empty($_GET['debug'])) {
if (is_numeric($_GET['debug'])) {
$debug_level = $_GET['debug'];
}
$debug = true;
}
//get the hostname
if (empty($hostname)) {
$hostname = gethostname();
}
//set the php operating system
$php_os = strtolower(PHP_OS);
//define the firewall command
if ($php_os == 'freebsd') {
$firewall_name = 'pf';
if (file_exists('/sbin/pfctl')) {
$firewall_path = '/sbin';
}
}
if ($php_os == 'linux') {
$firewall_name = 'iptables';
//find the firewall path
if (file_exists('/usr/sbin/iptables')) {
$firewall_path = '/usr/sbin';
}
if (empty($firewall_path) && file_exists('/sbin/iptables')) {
$firewall_path = '/sbin';
}
}
//check if the firewall command was found
if (empty($firewall_path)) {
echo $firewall_name." command not found\n";
exit;
}
//add pf tables into your pf.conf file
//if ($firewall_name == 'pf') {
// table <sip-auth-ip> persist
// table <sip-auth-fail> persist
// block in quick from <sip-auth-ip>
// block in quick from <sip-auth-fail>
//}
//add the iptables chains
if ($firewall_name == 'iptables') {
//create a chain array
$chains[] = 'sip-auth-ip';
$chains[] = 'sip-auth-fail';
//loop through the chains
if (is_array($chains)) {
foreach ($chains as $chain) {
iptables_chain_add($chain);
}
}
}
//define the process id file
$pid_file = "/var/run/fusionpbx/".basename($argv[0], ".php") .".pid";
echo "pid_file: ".$pid_file."\n";
//function to check if the process exists
/**
* Checks if a process exists.
*
* @param string $file Path to the file containing the process ID (PID)
*
* @return bool True if the process exists, false otherwise
*/
function process_exists($file) {
//set the default exists to false
$exists = false;
//check to see if the process is running
if (file_exists($file)) {
$pid = file_get_contents($file);
if (function_exists('posix_getsid')) {
if (posix_getsid($pid) === false) {
//process is not running
$exists = false;
}
else {
//process is running
$exists = true;
}
}
else {
if (file_exists('/proc/'.$pid)) {
//process is running
$exists = true;
}
else {
//process is not running
$exists = false;
}
}
}
//return the result
return $exists;
}
//check to see if the process exists
$pid_exists = process_exists($pid_file);
//prevent the process running more than once
if ($pid_exists) {
echo "Cannot lock pid file {$pid_file}\n";
exit;
}
else {
echo "pid file exists\n";
}
//create the process id file if the process doesn't exist
if (!$pid_exists) {
//remove the old pid file
if (file_exists($pid_file)) {
unlink($pid_file);
}
//show the details to the user
if (isset($debug) && $debug == true) {
echo "\n";
echo "Service: ".basename( $argv[0], ".php")."\n";
echo "Process ID: ".getmypid()."\n";
echo "PID File: ".$pid_file."\n";
}
//save the pid file
file_put_contents($pid_file, getmypid());
}
//connect to the database
$database = database::new();
//test a specific address
//$ip_address = '10.7.0.253';
//$result = access_allowed($ip_address);
//connect to event socket
$socket = new event_socket;
if (!$socket->connect()) {
echo "Unable to connect to event socket\n";
}
//preset values
//$interval_seconds = 30;
//$previous_time = time() - $interval_seconds;
//loop through the switch events
$cmd = "event json ALL";
$result = $socket->request($cmd);
if ($debug) { print_r($result); }
//filter for specific events
$cmd = "filter Event-Name CUSTOM";
$result = $socket->request($cmd);
if ($debug) { print_r($result); }
while (true) {
//check pending unblock requests
/*
if ((time() - $previous_time) > $interval_seconds) {
//debug info
if ($debug) {
echo "time difference: ". (time() - $previous_time)."\n";
}
//update the time
$previous_time = time();
}
*/
//reconnect to event socket
if (!$socket->connected()) {
//echo "Not connected to even socket\n";
if ($socket->connect()) {
$cmd = "event json ALL";
$result = $socket->request($cmd);
if ($debug) { print_r($result); }
$cmd = "filter Event-Name CUSTOM";
$result = $socket->request($cmd);
if ($debug) { print_r($result); }
echo "Re-connected to event socket\n";
}
else {
//unable to connect to event socket
echo "Unable to connect to event socket\n";
//sleep and then attempt to reconnect
sleep(1);
continue;
}
}
//read the socket
$json_response = $socket->read_event();
//decode the response
if (isset($json_response) && $json_response != '') {
$json_array = json_decode($json_response['$'], true);
unset($json_response);
}
//debug info
//if ($debug) {
// print_r($json_array);
//}
//registration failed - block IP address unless they are registered
if (is_array($json_array) && $json_array['Event-Subclass'] == 'sofia::register_failure') {
//not registered so block the address
if (!access_allowed($json_array['network-ip'])) {
block($json_array['network-ip'], 'sip-auth-fail', $json_array);
}
}
//sendevent CUSTOM event_guard:unblock
if (is_array($json_array) && $json_array['Event-Subclass'] == 'event_guard:unblock') {
//check the database for pending requests
$sql = "select event_guard_log_uuid, log_date, filter, ip_address, extension, user_agent ";
$sql .= "from v_event_guard_logs ";
$sql .= "where log_status = 'pending' ";
$sql .= "and hostname = :hostname ";
//if ($debug) { echo $sql." ".$hostname."\n"; }
$parameters['hostname'] = $hostname;
$event_guard_logs = $database->select($sql, $parameters, 'all');
if (is_array($event_guard_logs)) {
foreach($event_guard_logs as $row) {
//unblock the ip address
unblock($row['ip_address'], $row['filter']);
//log the blocked ip address to the syslog
openlog("fusionpbx", LOG_PID | LOG_PERROR, LOG_AUTH);
syslog(LOG_WARNING, "fusionpbx: unblocked: [ip_address: ".$row['ip_address'].", filter: ".$row['filter'].", to-user: ".$row['extension'].", to-host: ".$row['hostname'].", line: ".__line__."]");
closelog();
//debug info
if ($debug) {
echo "unblocked: [ip_address: ".$row['ip_address'].", filter: ".$row['filter'].", to-user: ".$row['extension'].", to-host: ".$row['hostname'].", line: ".__line__."]\n";
}
//log the blocked ip address to the database
$array['event_guard_logs'][$x]['event_guard_log_uuid'] = $row['event_guard_log_uuid'];
$array['event_guard_logs'][$x]['log_date'] = 'now()';
$array['event_guard_logs'][$x]['log_status'] = 'unblocked';
$x++;
}
if (is_array($array)) {
$p = permissions::new();
$p->add('event_guard_log_edit', 'temp');
$database->app_name = 'event guard';
$database->app_uuid = 'c5b86612-1514-40cb-8e2c-3f01a8f6f637';
$database->save($array, false);
//$message = $database->message;
$p->delete('event_guard_log_edit', 'temp');
unset($array);
}
}
}
//registration to the IP address
if (is_array($json_array) && $json_array['Event-Subclass'] == 'sofia::pre_register') {
if (isset($json_array['to-host'])) {
$is_valid_ip = filter_var($json_array['to-host'], FILTER_VALIDATE_IP);
if ($is_valid_ip) {
//if not registered block the address
if (!access_allowed($json_array['network-ip'])) {
block($json_array['network-ip'], 'sip-auth-ip', $json_array);
}
//debug info
if ($debug) {
echo "network-ip ".$json_array['network-ip']."\n";
echo "to-host ".$json_array['to-host']."\n";
echo "\n";
}
}
}
}
//debug information
//if ($debug && ($json_array['Event-Subclass'] == 'sofia::register_failure' || $json_array['Event-Subclass'] == 'sofia::pre_register')) {
//echo "\n";
//print_r($json_array);
//echo "event_name: ".$json_array['Event-Name']."\n";
//echo "event_type: ".$json_array['event_type']."\n";
//echo "event_subclass: ".$json_array['Event-Subclass']."\n";
//echo "status: ".$json_array['status']."\n";
//echo "network_ip: ".$json_array['network-ip']."\n";
//echo "channel_state: ".$json_array['Channel-State']."\n";
//echo "channel_call_state: ".$json_array['Channel-Call-State']."\n";
//echo "call_direction: ".$json_array['Call-Direction']."\n";
//echo "channel_call_uuid: ".$json_array['Channel-Call-UUID']."\n";
//echo "answer_state: ".$json_array['Answer-State']."\n";
//echo "hangup_cause: ".$json_array['Hangup-Cause']."\n";
//echo "to-host: $json_array['to-host']\n";
//echo "\n";
//}
//unset the array
if (is_array($json_array)) {
unset($json_array);
}
//debug info
if ($debug && $debug_level == '2') {
//current memory
$memory_usage = memory_get_usage();
//peak memory
$memory_peak = memory_get_peak_usage();
echo "\n";
echo 'Current memory: ' . round($memory_usage / 1024) . " KB\n";
echo 'Peak memory: ' . round($memory_peak / 1024) . " KB\n\n";
echo "\n";
}
}
//run command and capture standard output
/**
* Execute a shell command and capture its output.
*
* @param string $command The shell command to execute
*
* @return string The output of the executed command
*/
function shell($command) {
ob_start();
$result = system($command);
ob_get_clean();
return $result;
}
//block an ip address
/**
* Execute a block command for iptables or pf based on the firewall type.
*
* @param string $ip_address The IP address to block
* @param string $filter The filter name for iptables or pf
* @param array $event The event data containing 'to-user' and 'to-host'
*
* @return boolean True if the block command was executed successfully, false otherwise
*/
function block($ip_address, $filter, $event) {
//define the global variables
global $database, $debug, $firewall_path, $firewall_name;
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//run the block command for iptables
if ($firewall_name == 'iptables') {
//example: iptables -I INPUT -s 127.0.0.1 -j DROP
$command = $firewall_path.'/./iptables -I '.$filter.' -s '.$ip_address.' -j DROP';
$result = shell($command);
}
//run the block command for pf
if ($firewall_name == 'pf') {
//example: pfctl -t sip-auth-ip -T add 127.0.0.5
$command = $firewall_path.'/pfctl -t '.$filter.' -T add '.$ip_address;
$result = shell($command);
}
//log the blocked ip address to the syslog
openlog("fusionpbx", LOG_PID | LOG_PERROR, LOG_AUTH);
syslog(LOG_WARNING, "fusionpbx: blocked: [ip_address: ".$ip_address.", filter: ".$filter.", to-user: ".$event['to-user'].", to-host: ".$event['to-host'].", line: ".__line__."]");
closelog();
//log the blocked ip address to the database
$array = [];
$array['event_guard_logs'][0]['event_guard_log_uuid'] = uuid();
$array['event_guard_logs'][0]['hostname'] = gethostname();
$array['event_guard_logs'][0]['log_date'] = 'now()';
$array['event_guard_logs'][0]['filter'] = $filter;
$array['event_guard_logs'][0]['ip_address'] = $ip_address;
$array['event_guard_logs'][0]['extension'] = $event['to-user'].'@'.$event['to-host'];
$array['event_guard_logs'][0]['user_agent'] = $event['user-agent'];
$array['event_guard_logs'][0]['log_status'] = 'blocked';
$p = permissions::new();
$p->add('event_guard_log_add', 'temp');
$database->app_name = 'event guard';
$database->app_uuid = 'c5b86612-1514-40cb-8e2c-3f01a8f6f637';
$database->save($array, false);
$p->delete('event_guard_log_add', 'temp');
//send debug information to the console
if ($debug) {
echo "blocked address ".$ip_address .", line ".__line__."\n";
}
}
//unblock the ip address
/**
* Unblock a specified IP address from a firewall.
*
* @param string $ip_address The IP address to unblock.
* @param string $filter The filter name used in the firewall configuration.
*
* @return bool True if the IP address was successfully unblocked, false otherwise.
*/
function unblock($ip_address, $filter) {
//define the global variables
global $debug, $firewall_path, $firewall_name;
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//unblock the address
if ($firewall_name == 'iptables') {
$command = $firewall_path.'/./iptables -L '.$filter.' -n --line-numbers | grep "'.$ip_address.' " | cut -d " " -f1';
$line_number = trim(shell($command));
echo "\n". $command . " line ".__line__."\n";
if (is_numeric($line_number)) {
//$result = shell('iptables -D INPUT '.$line_number);
$command = $firewall_path.'/./iptables -D '.$filter.' '.$line_number;
$result = shell($command);
echo "Unblock address ".$ip_address ." line ".$line_number." command ".$command." result ".$result."\n";
}
}
//unblock the address
if ($firewall_name == 'pf') {
//example: pfctl -t sip-auth-ip -T delete 127.0.0.5
$command = $firewall_path.'/pfctl -t '.$filter.' -T delete '.$ip_address;
$result = shell($command);
}
//send debug information to the console
if ($debug) {
echo "Unblock address ".$ip_address ."\n";
}
}
//is the ip address blocked
/**
* Check if an IP address is blocked in the configured firewall.
*
* @param string $ip_address The IP address to check
*
* @return bool True if the address is blocked, False otherwise
*/
function is_blocked($ip_address) {
//define the global variables
global $firewall_path, $firewall_name;
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//determine whether to return true or false
if ($firewall_name == 'iptables') {
//check to see if the address is blocked
$command = $firewall_path.'/./iptables -L -n --line-numbers | grep '.$ip_address;
$result = shell($command);
if (!empty($result) && strlen($result) > 3) {
return true;
}
}
elseif ($firewall_name == 'pf') {
//check to see if the address is blocked
$command = $firewall_path.'/pfctl -t ".$filter." -Ts | grep '.$ip_address;
$result = shell($command);
if (!empty($result) && strlen($result) > 3) {
return true;
}
}
else {
return false;
}
}
//determine if the IP address has been allowed by the access control list node cidr
/**
* Determine if access is allowed for a given IP address.
*
* This method checks the cache, user logs, event guard logs, access controls,
* and registration to determine if access should be granted. If no valid reason
* is found to deny access, it will be automatically allowed.
*
* @param string $ip_address The IP address to check for access.
*
* @return boolean True if access is allowed, false otherwise.
*/
function access_allowed($ip_address) {
//define global variables
global $debug;
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//check the cache to see if the address is allowed
$cache = new cache;
if ($cache->get("switch:allowed:".$ip_address) === 'true') {
//debug info
if ($debug) {
echo "address: ".$ip_address." allowed by: cache\n";
}
//return boolean true
return true;
}
//allow access for addresses with authentication status success
if (user_log_allowed($ip_address)) {
//save address to the cache as allowed
$cache->set("switch:allowed:".$ip_address, 'true');
//debug info
if ($debug) {
echo "address: ".$ip_address." allowed by: user logs\n";
}
//return boolean true
return true;
}
//allow access for addresses that have been unblocked
/*
if (event_guard_log_allowed($ip_address)) {
//save address to the cache as allowed
$cache->set("switch:allowed:".$ip_address, 'true');
//debug info
if ($debug) {
echo "address: ".$ip_address." allowed by: unblocked\n";
}
//return boolean true
return true;
}
*/
//allow access if the cidr address is allowed
if (access_control_allowed($ip_address)) {
//save address to the cache as allowed
$cache->set("switch:allowed:".$ip_address, 'true');
//debug info
if ($debug) {
echo "address: ".$ip_address." allowed by: access controls\n";
}
//return boolean true
return true;
}
//auto allow if there is a registration from the same IP address
if (is_registered($ip_address)) {
//save address to the cache as allowed
$cache->set("switch:allowed:".$ip_address, 'true');
//debug info
if ($debug) {
echo "address: ".$ip_address." allowed by: registration\n";
}
//return boolean true
return true;
}
//return
return false;
}
//is the ip address registered
/**
* Checks if the given IP address is registered on the network.
*
* @param string $ip_address The IP address to check for registration.
*
* @return bool True if the IP address is registered, false otherwise.
*/
function is_registered($ip_address) {
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
$registered = false;
$command = "fs_cli -x 'show registrations as json' ";
$result = shell($command);
$array = json_decode($result, true);
if (is_array($array['rows'])) {
foreach ($array['rows'] as $row) {
if ($row['network_ip'] == $ip_address) {
$registered = true;
}
}
}
//return registered boolean
return $registered;
}
//determine if the IP address has been allowed by the access control list node cidr
/**
* Checks if the given IP address is authorized by any access control node.
*
* @param string $ip_address The IP address to check for authorization.
*
* @return bool True if the IP address is authorized, false otherwise.
*/
function access_control_allowed($ip_address) {
global $database;
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//get the access control allowed nodes
$sql = "select access_control_node_uuid, access_control_uuid, node_cidr, node_description ";
$sql .= "from v_access_control_nodes ";
$sql .= "where node_type = 'allow' ";
$sql .= "and length(node_cidr) > 0 ";
$parameters = null;
$allowed_nodes = $database->select($sql, $parameters, 'all');
//default authorized to false
$allowed = false;
//use the ip address to get the authorized nodes
if (is_array($allowed_nodes)) {
foreach($allowed_nodes as $row) {
if (check_cidr($row['node_cidr'], $ip_address)) {
//debug info
//if ($debug) {
// print_r($row);
// echo $ip_address."\n";
//}
//set the allowed to true
$allowed = true;
//exit the loop
break;
}
}
}
//return
return $allowed;
}
//determine if the IP address has been allowed by a successful authentication
/**
* Determines if a user's IP address is allowed based on their login history.
*
* @param string $ip_address The IP address to check for access.
*
* @return bool True if the IP address is allowed, false otherwise.
*/
function user_log_allowed($ip_address) {
global $database, $debug;
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//check to see if the address was authenticated successfully
$sql = "select count(user_log_uuid) ";
$sql .= "from v_user_logs ";
$sql .= "where remote_address = :remote_address ";
$sql .= "and result = 'success' ";
$sql .= "and timestamp > NOW() - INTERVAL '8 days' ";
$parameters['remote_address'] = $ip_address;
$user_log_count = $database->select($sql, $parameters, 'column');
//debug info
if ($debug) {
echo "address ".$ip_address." count ".$user_log_count."\n";
}
//default authorized to false
$allowed = false;
//use the ip address to get the authorized nodes
if (!empty($user_log_count) && $user_log_count > 0) {
$allowed = true;
}
//return
return $allowed;
}
//determine if the IP address has been unblocked in the event guard log
/**
* Determines if an IP address is allowed based on event guard logs.
*
* @param string $ip_address The IP address to check for access.
*
* @return bool True if the IP address is allowed, false otherwise.
*/
function event_guard_log_allowed($ip_address) {
global $database, $debug;
//invalid ip address
if (!filter_var($ip_address, FILTER_VALIDATE_IP)) {
return false;
}
//get the access control allowed nodes
$sql = "select count(event_guard_log_uuid) ";
$sql .= "from v_event_guard_logs ";
$sql .= "where ip_address = :ip_address ";
$sql .= "and log_status = 'unblocked' ";
$parameters['ip_address'] = $ip_address;
$user_log_count = $database->select($sql, $parameters, 'column');
//debug info
if ($debug) {
echo "address ".$ip_address." count ".$user_log_count."\n";
}
//default authorized to false
$allowed = false;
//use the ip address to get the authorized nodes
if ($user_log_count > 0) {
$allowed = true;
}
//return
return $allowed;
}
//check if the iptables chain exists
/**
* Determines if a pf table exists in the firewall configuration.
*
* @param string $table The name of the pf table to check for existence.
*
* @return bool True if the pf table exists, false otherwise.
*/
function pf_table_exists($table) {
//define the global variables
global $firewall_path, $firewall_name;
//build the command to check if the pf table exists
$command = $firewall_path."/./pfctl -t ".$table." -T show | grep error";
//if ($debug) { echo $command."\n"; }
$response = shell($command);
if (!empty($response)) {
return true;
}
else {
return false;
}
}
//add IP table chains
/**
* Adds a new IPtables chain and inserts it into the INPUT table.
*
* @param string $chain The name of the IPtables chain to add.
*
* @return bool True if the chain was successfully added, false otherwise.
*/
function iptables_chain_add($chain) {
//define the global variables
global $firewall_path;
//if the chain exists return true
if (iptables_chain_exists($chain)) {
echo "IPtables ".$chain." chain already exists\n";
return true;
}
//log info to the console
echo "Add iptables ".$chain." chain\n";
//add the chain
system($firewall_path.'/./iptables --new '.$chain);
system($firewall_path.'/./iptables -I INPUT -j '.$chain);
//check if the chain exists
if (iptables_chain_exists($chain)) {
return true;
}
else {
sleep(1);
iptables_chain_add($chain);
}
}
//check if the iptables chain exists
/**
* Determines if a specified iptables chain exists.
*
* @param string $chain The name of the iptables chain to check for existence.
*
* @return bool True if the iptables chain exists, false otherwise.
*/
function iptables_chain_exists($chain) {
//define the global variables
global $firewall_path;
//build the command to check if the iptables chain exists
$command = $firewall_path."/./iptables --list INPUT --numeric | grep ".$chain." | awk '{print \$1}' | sed ':a;N;\$!ba;s/\\n/,/g' ";
//if ($debug) { echo $command."\n"; }
$response = shell($command);
if (in_array($chain, explode(",", $response))) {
return true;
}
else {
return false;
}
}
// Exit with error code
exit($ex->getCode());
}