PHP Classes

File: dashboard/api/index.php

Recommend this page to a friend!
  Packages of Moises Espindola   Sentinela   dashboard/api/index.php   Download  
File: dashboard/api/index.php
Role: Application script
Content type: text/plain
Description: Application script
Class: Sentinela
Linux protection tools with Web dashboard panel
Author: By
Last change:
Date: 6 days ago
Size: 13,736 bytes
 

Contents

Class file image Download
<?php /** * Sentinela ? Intelligent Linux Security Framework * API REST para el dashboard * * Endpoints: * GET /api/?action=status - Estado del servidor y firewall * GET /api/?action=stats - Estadísticas de ataques * GET /api/?action=banned - Lista de IPs bloqueadas * GET /api/?action=attacks - Últimos ataques detectados * GET /api/?action=top_ips - Top IPs atacantes * GET /api/?action=top_countries - Top países atacantes * GET /api/?action=geoip&ip=X - GeoIP de una IP * POST /api/?action=unban&ip=X - Desbloquear IP * POST /api/?action=ban&ip=X - Bloquear IP */ header('Content-Type: application/json'); header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: GET, POST, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type'); if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; } // Configuración de rutas $logDir = '/var/log/sentinela'; $installDir = '/usr/local/sentinela'; $configFile = '/etc/sentinela/config.conf'; if (!is_dir($logDir)) { $logDir = '/root/sentinela/logs'; } if (!is_dir($installDir)) { $installDir = '/root/sentinela'; } // Cargar configuración function getConfig() { global $configFile; $config = []; if (file_exists($configFile)) { $lines = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); foreach ($lines as $line) { $line = trim($line); if (strpos($line, '#') === 0 || strpos($line, '=') === false) continue; list($key, $value) = explode('=', $line, 2); $key = trim($key); $value = trim($value, "\"' \t\r\n"); $config[$key] = $value; } } return $config; } // Leer archivos de log function readLogFile($path, $lines = 100) { if (!file_exists($path)) return []; $content = file_get_contents($path); if (empty($content)) return []; $allLines = explode("\n", trim($content)); return array_slice($allLines, -$lines); } // Parsear línea de log de ataque function parseAttackLine($line) { // Formato: [FECHA] [ATTACK] TIPO|IP|REASON|URI|UA $parts = explode('] [ATTACK] ', $line); if (count($parts) < 2) return null; $timestamp = trim($parts[0], '[]'); $data = $parts[1]; $fields = explode('|', $data); return [ 'timestamp' => $timestamp, 'type' => $fields[0] ?? 'unknown', 'ip' => $fields[1] ?? '0.0.0.0', 'reason' => $fields[2] ?? '', 'uri' => $fields[3] ?? '', 'ua' => $fields[4] ?? '', 'service' => $fields[5] ?? 'web' ]; } // Parsear línea de log de banned function parseBannedLine($line) { // Formato: [FECHA] [BAN] IP blocked (razón, timeout: Xs) $parts = explode('] [BAN] ', $line); if (count($parts) < 2) return null; $timestamp = trim($parts[0], '[]'); $data = $parts[1]; preg_match('/^(\S+) blocked \((.*), timeout: (\d+)s\)/', $data, $m); if (count($m) >= 4) { return [ 'timestamp' => $timestamp, 'ip' => $m[1], 'reason' => $m[2], 'timeout' => (int)$m[3] ]; } preg_match('/^(\S+) blocked \((.*)\)/', $data, $m); if (count($m) >= 3) { return [ 'timestamp' => $timestamp, 'ip' => $m[1], 'reason' => $m[2], 'timeout' => 0 ]; } return null; } // Obtener IPs bloqueadas desde ipset function getBannedIPs() { $output = shell_exec('sudo ipset list SENTINELA_BLACKLIST 2>/dev/null'); if (empty($output)) return []; $ips = []; $lines = explode("\n", $output); $inList = false; foreach ($lines as $line) { if (preg_match('/^\d/', $line)) { $parts = preg_split('/\s+/', trim($line)); if (count($parts) >= 2) { $ips[] = [ 'ip' => $parts[0], 'timeout' => (int)$parts[1] ]; } } } return $ips; } // Obtener stats del sistema function getSystemStats() { // CPU $cpu = shell_exec("top -bn1 | grep 'Cpu(s)' | awk '{print $2}'"); $cpu = trim($cpu ?? '0'); // RAM $memInfo = file_get_contents('/proc/meminfo'); preg_match('/MemTotal:\s+(\d+)/', $memInfo, $mt); preg_match('/MemAvailable:\s+(\d+)/', $memInfo, $ma); $memTotal = (int)($mt[1] ?? 0); $memAvail = (int)($ma[1] ?? 0); $memUsed = $memTotal - $memAvail; $memPercent = $memTotal > 0 ? round(($memUsed / $memTotal) * 100, 1) : 0; // Disk $disk = shell_exec("df -h / | tail -1 | awk '{print $5}'"); $disk = trim($disk ?? '0%'); // Load $load = sys_getloadavg(); // Uptime $uptime = file_get_contents('/proc/uptime'); $uptimeSeconds = (int)($uptime ? explode(' ', $uptime)[0] : 0); $uptimeDays = floor($uptimeSeconds / 86400); // Conexiones $connections = shell_exec("ss -tn state established 2>/dev/null | tail -n +2 | wc -l"); $connections = trim($connections ?? '0'); // Puertos escucha $listening = shell_exec("ss -tln 2>/dev/null | tail -n +2 | wc -l"); $listening = trim($listening ?? '0'); return [ 'cpu' => $cpu, 'mem_total' => $memTotal, 'mem_used' => $memUsed, 'mem_percent' => $memPercent, 'disk' => $disk, 'load_1' => $load[0] ?? 0, 'load_5' => $load[1] ?? 0, 'load_15' => $load[2] ?? 0, 'uptime_days' => $uptimeDays, 'connections' => (int)$connections, 'listening' => (int)$listening, 'hostname' => gethostname(), 'kernel' => php_uname('r') ]; } // Obtener estado del firewall function getFirewallStatus() { $chains = []; $chainInput = shell_exec('sudo iptables -L SENTINELA_INPUT -n 2>/dev/null | tail -n +3 | wc -l'); $chainNew = shell_exec('sudo iptables -L SENTINELA_NEW -n 2>/dev/null | tail -n +3 | wc -l'); $chains['SENTINELA_INPUT'] = (int)trim($chainInput ?? '0'); $chains['SENTINELA_NEW'] = (int)trim($chainNew ?? '0'); $enabled = ($chains['SENTINELA_INPUT'] > 0); // Ipsets $ipsets = []; $setNames = ['SENTINELA_TRUSTED', 'SENTINELA_BLACKLIST', 'SENTINELA_DDOS', 'SENTINELA_REPUTATION', 'SENTINELA_HONEY']; foreach ($setNames as $name) { $output = shell_exec("sudo ipset list {$name} 2>/dev/null | grep -cE '^[0-9]'"); $ipsets[$name] = (int)trim($output ?? '0'); } return [ 'enabled' => $enabled, 'chains' => $chains, 'ipsets' => $ipsets ]; } // Obtener ataques recientes function getRecentAttacks($limit = 50) { global $logDir; $lines = readLogFile("{$logDir}/attacks.log", $limit); $attacks = []; foreach ($lines as $line) { $parsed = parseAttackLine($line); if ($parsed) $attacks[] = $parsed; } return array_reverse($attacks); } // Obtener IPs bloqueadas (desde log + ipset) function getBannedLog($limit = 50) { global $logDir; $lines = readLogFile("{$logDir}/banned.log", $limit); $banned = []; foreach ($lines as $line) { $parsed = parseBannedLine($line); if ($parsed) $banned[] = $parsed; } return array_reverse($banned); } // Contar tipos de ataque function countAttackTypes($attacks) { $counts = []; foreach ($attacks as $a) { $type = $a['type'] ?? 'unknown'; if (!isset($counts[$type])) $counts[$type] = 0; $counts[$type]++; } arsort($counts); return $counts; } // Contar IPs function countTopIPs($attacks, $limit = 10) { $counts = []; foreach ($attacks as $a) { $ip = $a['ip'] ?? '0.0.0.0'; if (!isset($counts[$ip])) $counts[$ip] = 0; $counts[$ip]++; } arsort($counts); return array_slice($counts, 0, $limit); } // Contar países (simulado desde archivo cache o API) function countTopCountries($attacks, $limit = 10) { $counts = []; foreach ($attacks as $a) { $ip = $a['ip'] ?? '0.0.0.0'; $country = getCountryFromCache($ip); if (!isset($counts[$country])) $counts[$country] = 0; $counts[$country]++; } arsort($counts); return array_slice($counts, 0, $limit); } // Cache de GeoIP function getCountryFromCache($ip) { $cacheFile = '/tmp/sentinela_geoip_cache.json'; $cache = []; if (file_exists($cacheFile)) { $cache = json_decode(file_get_contents($cacheFile), true) ?? []; } if (isset($cache[$ip])) return $cache[$ip]; // Intentar con geoiplookup $country = '??'; $output = shell_exec("geoiplookup {$ip} 2>/dev/null | grep -oP 'Country:\s+\K\w+'"); if ($output) { $country = trim($output); } else { // API externa $json = @file_get_contents("http://ip-api.com/json/{$ip}"); if ($json) { $data = json_decode($json, true); $country = $data['countryCode'] ?? '??'; usleep(100000); // Rate limit: 45 req/min } } $cache[$ip] = $country; if (count($cache) > 1000) $cache = array_slice($cache, -500, 500, true); file_put_contents($cacheFile, json_encode($cache)); return $country; } // ========================================================================= // Manejo de acciones // ========================================================================= $action = $_GET['action'] ?? $_POST['action'] ?? 'status'; switch ($action) { case 'status': $system = getSystemStats(); $firewall = getFirewallStatus(); $banned = getBannedIPs(); $attacks = getRecentAttacks(10); $attackTypes = countAttackTypes(getRecentAttacks(500)); echo json_encode([ 'success' => true, 'system' => $system, 'firewall' => $firewall, 'banned_count' => count($banned), 'recent_attacks' => $attacks, 'attack_types' => $attackTypes ]); break; case 'stats': $attacks = getRecentAttacks(1000); $banned = getBannedIPs(); $attackTypes = countAttackTypes($attacks); $topIPs = countTopIPs($attacks, 15); $topCountries = countTopCountries($attacks, 15); echo json_encode([ 'success' => true, 'total_attacks' => count($attacks), 'total_banned' => count($banned), 'attack_types' => $attackTypes, 'top_ips' => $topIPs, 'top_countries' => $topCountries ]); break; case 'banned': $banned = getBannedIPs(); $bannedLog = getBannedLog(100); echo json_encode([ 'success' => true, 'banned' => $banned, 'banned_log' => $bannedLog, 'total' => count($banned) ]); break; case 'attacks': $attacks = getRecentAttacks(200); echo json_encode([ 'success' => true, 'attacks' => $attacks, 'total' => count($attacks) ]); break; case 'top_ips': $attacks = getRecentAttacks(1000); $topIPs = countTopIPs($attacks, 20); echo json_encode([ 'success' => true, 'top_ips' => $topIPs ]); break; case 'top_countries': $attacks = getRecentAttacks(1000); $topCountries = countTopCountries($attacks, 20); echo json_encode([ 'success' => true, 'top_countries' => $topCountries ]); break; case 'geoip': $ip = $_GET['ip'] ?? ''; if (empty($ip) || !filter_var($ip, FILTER_VALIDATE_IP)) { echo json_encode(['success' => false, 'error' => 'IP inválida']); exit; } $country = getCountryFromCache($ip); $asn = shell_exec("mmdblookup --file /usr/share/GeoIP/GeoLite2-ASN.mmdb --ip {$ip} autonomous_system_number 2>/dev/null | grep -oP '\d+' | head -1"); $org = shell_exec("mmdblookup --file /usr/share/GeoIP/GeoLite2-ASN.mmdb --ip {$ip} autonomous_system_organization 2>/dev/null | grep -oP '\"[^\"]+\"' | head -1 | tr -d '\"'"); echo json_encode([ 'success' => true, 'ip' => $ip, 'country' => $country ?: '??', 'asn' => $asn ? 'AS' . trim($asn) : 'N/A', 'org' => $org ?: 'N/A' ]); break; case 'unban': $ip = $_POST['ip'] ?? $_GET['ip'] ?? ''; if (empty($ip) || !filter_var($ip, FILTER_VALIDATE_IP)) { echo json_encode(['success' => false, 'error' => 'IP inválida']); exit; } $output = shell_exec("sudo ipset del SENTINELA_BLACKLIST {$ip} 2>&1"); echo json_encode([ 'success' => true, 'message' => "IP {$ip} desbloqueada", 'output' => $output ]); break; case 'ban': $ip = $_POST['ip'] ?? $_GET['ip'] ?? ''; if (empty($ip) || !filter_var($ip, FILTER_VALIDATE_IP)) { echo json_encode(['success' => false, 'error' => 'IP inválida']); exit; } $output = shell_exec("sudo ipset add SENTINELA_BLACKLIST {$ip} timeout 600 2>&1"); echo json_encode([ 'success' => true, 'message' => "IP {$ip} bloqueada", 'output' => $output ]); break; default: echo json_encode([ 'success' => false, 'error' => 'Acción no válida', 'available_actions' => [ 'status', 'stats', 'banned', 'attacks', 'top_ips', 'top_countries', 'geoip', 'unban', 'ban' ] ]); }