feat: integrate network analysis feature in firewall view with detailed connection data display
This commit is contained in:
@@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Paymenter\Extensions\Servers\Proxmox\Models\Server;
|
||||
use Paymenter\Extensions\Servers\Proxmox\Proxmox;
|
||||
use Paymenter\Extensions\Servers\Proxmox\Services\CoreRouterClient;
|
||||
|
||||
class Firewall extends Component
|
||||
{
|
||||
@@ -1566,6 +1567,165 @@ class Firewall extends Component
|
||||
return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false;
|
||||
}
|
||||
|
||||
// ==================== NETWORK ANALYSIS INTEGRATION ====================
|
||||
|
||||
public function getNetworkAnalysisForLog(
|
||||
string $sourceIP,
|
||||
string $destIP,
|
||||
string $sourcePort,
|
||||
string $destPort,
|
||||
string $protocol
|
||||
): array {
|
||||
try {
|
||||
// Check if Core Router is configured
|
||||
$coreRouterEnabled = \Illuminate\Support\Facades\Config::get('corerouter.enabled', false);
|
||||
if (!$coreRouterEnabled) {
|
||||
return [
|
||||
'connections' => [],
|
||||
'error' => 'Core Router is not configured'
|
||||
];
|
||||
}
|
||||
|
||||
// Get network analysis from Statistics component logic
|
||||
$client = \Illuminate\Support\Facades\App::make(CoreRouterClient::class);
|
||||
|
||||
// Fetch all firewall states
|
||||
$allStates = $client->listStates();
|
||||
|
||||
// Filter states matching the log entry
|
||||
$matchingConnections = [];
|
||||
|
||||
foreach ($allStates as $state) {
|
||||
$rawSource = (string)($state['source'] ?? '');
|
||||
$rawDestination = (string)($state['destination'] ?? '');
|
||||
|
||||
// Extract IPs from the state
|
||||
$stateSourceIP = $this->extractIpFromField($rawSource);
|
||||
$stateDestIP = $this->extractIpFromField($rawDestination);
|
||||
|
||||
// Extract ports
|
||||
$stateSourcePort = null;
|
||||
$stateDestPort = null;
|
||||
|
||||
if (preg_match('/\[(\d+)\]$/', $rawSource, $matches)) {
|
||||
$stateSourcePort = (int)$matches[1];
|
||||
} elseif (preg_match('/:(\d+)$/', $rawSource, $matches) && filter_var(explode(':', $rawSource)[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
$stateSourcePort = (int)$matches[1];
|
||||
}
|
||||
|
||||
if (preg_match('/\[(\d+)\]$/', $rawDestination, $matches)) {
|
||||
$stateDestPort = (int)$matches[1];
|
||||
} elseif (preg_match('/:(\d+)$/', $rawDestination, $matches) && filter_var(explode(':', $rawDestination)[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
$stateDestPort = (int)$matches[1];
|
||||
}
|
||||
|
||||
$stateProtocol = strtoupper($state['protocol'] ?? '');
|
||||
|
||||
// Check if this state matches the log entry
|
||||
// Match can be source->dest or dest->source (bidirectional)
|
||||
$sourceMatch = (
|
||||
strtolower(trim($stateSourceIP)) === strtolower(trim($sourceIP)) &&
|
||||
strtolower(trim($stateDestIP)) === strtolower(trim($destIP))
|
||||
);
|
||||
|
||||
$reverseMatch = (
|
||||
strtolower(trim($stateSourceIP)) === strtolower(trim($destIP)) &&
|
||||
strtolower(trim($stateDestIP)) === strtolower(trim($sourceIP))
|
||||
);
|
||||
|
||||
// Check protocol match
|
||||
$protocolMatch = empty($protocol) ||
|
||||
strtoupper(trim($protocol)) === $stateProtocol;
|
||||
|
||||
// Check port match (if specified in log)
|
||||
$portMatch = true;
|
||||
if (!empty($destPort) && $destPort !== '-') {
|
||||
$portMatch = ($stateDestPort == $destPort) || ($stateSourcePort == $destPort);
|
||||
}
|
||||
|
||||
if (($sourceMatch || $reverseMatch) && $protocolMatch && $portMatch) {
|
||||
$bytes = (int)($state['bytes_total'] ?? $state['bytes'] ?? 0);
|
||||
$packets = (int)($state['packets_total'] ?? $state['packets'] ?? 0);
|
||||
$stateName = strtoupper($state['state'] ?? 'UNKNOWN');
|
||||
$interface = strtoupper($state['interface'] ?? 'N/A');
|
||||
$direction = strtolower($state['direction'] ?? '') === 'in' ? 'Inbound' : 'Outbound';
|
||||
|
||||
$matchingConnections[] = [
|
||||
'protocol' => $stateProtocol,
|
||||
'source' => $stateSourceIP,
|
||||
'destination' => $stateDestIP,
|
||||
'source_port' => $stateSourcePort,
|
||||
'destination_port' => $stateDestPort,
|
||||
'raw_source' => $rawSource,
|
||||
'raw_destination' => $rawDestination,
|
||||
'bytes' => $bytes,
|
||||
'bytes_human' => $this->formatBytes($bytes),
|
||||
'packets' => $packets,
|
||||
'state' => $stateName,
|
||||
'interface' => $interface,
|
||||
'direction' => $direction,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by bytes (highest first)
|
||||
usort($matchingConnections, fn($a, $b) => $b['bytes'] <=> $a['bytes']);
|
||||
|
||||
return [
|
||||
'connections' => $matchingConnections,
|
||||
'error' => null
|
||||
];
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Failed to fetch network analysis for log entry', [
|
||||
'error' => $e->getMessage(),
|
||||
'source' => $sourceIP,
|
||||
'dest' => $destIP
|
||||
]);
|
||||
|
||||
return [
|
||||
'connections' => [],
|
||||
'error' => 'Failed to fetch network data: ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
protected function extractIpFromField(string $field): string
|
||||
{
|
||||
if (empty($field)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$field = trim($field);
|
||||
|
||||
// Handle IPv4 "IP:port" format FIRST
|
||||
if (preg_match('/^(\d+\.\d+\.\d+\.\d+):(\d+)$/', $field, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
// Handle "IP[port]" format
|
||||
if (preg_match('/^([^\[\]]+)(?:\[[^\]]+\])?$/', $field, $matches)) {
|
||||
$ip = trim($matches[1]);
|
||||
$ip = rtrim($ip, ':[]');
|
||||
return $ip;
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
protected function formatBytes(int $bytes): string
|
||||
{
|
||||
if ($bytes === 0) {
|
||||
return '0 B';
|
||||
}
|
||||
|
||||
$units = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];
|
||||
$power = floor(log($bytes, 1024));
|
||||
$power = min($power, count($units) - 1);
|
||||
|
||||
return round($bytes / pow(1024, $power), 2) . ' ' . $units[$power];
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('proxmox::firewall')->layoutData([
|
||||
|
||||
Reference in New Issue
Block a user