Files
jabali-panel/app/Console/Commands/CheckFail2banAlerts.php
2026-01-24 19:36:46 +02:00

82 lines
2.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Console\Commands;
use App\Services\AdminNotificationService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
class CheckFail2banAlerts extends Command
{
protected $signature = 'jabali:check-fail2ban';
protected $description = 'Check fail2ban logs for recent bans and send notifications';
public function handle(): int
{
$this->info('Checking fail2ban for recent bans...');
$logFile = '/var/log/fail2ban.log';
if (!file_exists($logFile)) {
$this->info('Fail2ban log not found.');
return Command::SUCCESS;
}
// Get last check position
$lastPosition = (int) Cache::get('fail2ban_check_position', 0);
$currentSize = filesize($logFile);
// If log was rotated, start from beginning
if ($currentSize < $lastPosition) {
$lastPosition = 0;
}
$handle = fopen($logFile, 'r');
if (!$handle) {
$this->error('Cannot open fail2ban log.');
return Command::FAILURE;
}
fseek($handle, $lastPosition);
$banCount = 0;
$bans = [];
while (($line = fgets($handle)) !== false) {
// Match fail2ban ban entries
// Format: 2024-01-15 10:30:45,123 fail2ban.actions [12345]: NOTICE [sshd] Ban 192.168.1.100
if (preg_match('/\[([^\]]+)\]\s+Ban\s+(\S+)/', $line, $matches)) {
$service = $matches[1];
$ip = $matches[2];
$key = "{$service}:{$ip}";
if (!isset($bans[$key])) {
$bans[$key] = [
'service' => $service,
'ip' => $ip,
'count' => 0,
];
}
$bans[$key]['count']++;
$banCount++;
}
}
$newPosition = ftell($handle);
fclose($handle);
// Save new position
Cache::put('fail2ban_check_position', $newPosition, now()->addDays(7));
// Send notifications for each unique IP/service combination
foreach ($bans as $ban) {
$this->warn("Ban detected: {$ban['ip']} on {$ban['service']} ({$ban['count']} times)");
AdminNotificationService::loginFailure($ban['ip'], $ban['service'], $ban['count']);
}
$this->info("Fail2ban check complete. {$banCount} ban(s) found.");
return Command::SUCCESS;
}
}