Fix: harmonize Firewall and Fail2ban status, decouple strict mode and fix installer jail generation
This commit is contained in:
1 parent
8bbeb9caae
commit
8b57981b1b
6 files changed
+69
-121
No files matched your search
+17
-48
@@ -183,7 +183,7 @@ setfacl -R -d -m u:www-data:rwx,g:asterisk:rwx /etc/asterisk 2>/dev/null || true
|
||||
# Droits sudoers pour l'interface web (www-data)
|
||||
cat << 'EOF' > /etc/sudoers.d/nexus-pbx
|
||||
# Privilèges spécifiques NexusPBX pour l'utilisateur Web www-data
|
||||
www-data ALL=(ALL) NOPASSWD: /usr/sbin/asterisk, /usr/sbin/nginx, /usr/bin/systemctl, /usr/bin/fail2ban-client, /var/www/nexus-pbx/scripts/nexus-firewall, /usr/bin/certbot, /usr/sbin/nft, /usr/sbin/iptables
|
||||
www-data ALL=(ALL) NOPASSWD: /usr/sbin/asterisk, /usr/sbin/nginx, /usr/bin/systemctl, /usr/bin/fail2ban-client, /bin/fail2ban-client, /var/www/nexus-pbx/scripts/nexus-firewall, /usr/bin/certbot, /usr/sbin/nft, /usr/sbin/iptables
|
||||
EOF
|
||||
chmod 0440 /etc/sudoers.d/nexus-pbx
|
||||
|
||||
@@ -355,18 +355,7 @@ $st = $db->query("SELECT admin_https_port, certbot_strict_port80 FROM settings W
|
||||
App\FirewallManager::applyNginxConfig((int)($st["admin_https_port"] ?? 8443), (int)($st["certbot_strict_port80"] ?? 0));
|
||||
'
|
||||
|
||||
# Filtre Fail2ban Asterisk & Nexus-Web
|
||||
cat << 'EOF' > /etc/fail2ban/filter.d/asterisk.conf
|
||||
[INCLUDES]
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
failregex = ^(%(__prefix_line)s|\[\]\s*)*(?:NOTICE|SECURITY)\[\d+\]:? (?:\S+: )?(?:Registration from |Call from |Invalid password for |Sending fake auth challenge to |No matching endpoint found for )?'<HOST>'(?::\d+)?
|
||||
^(%(__prefix_line)s|\[\]\s*)*SECURITY\[\d+\]:? (?:\S+: )?SecurityEvent="(?:FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword|AuthFailed)"(?:.*?)RemoteAddress="IPV[46]/(?:UDP|TCP|TLS)/<HOST>/\d+"
|
||||
ignoreregex =
|
||||
EOF
|
||||
chmod 0644 /etc/fail2ban/filter.d/asterisk.conf
|
||||
|
||||
# Filtre Fail2ban pour l'interface Nexus-Web
|
||||
cat << 'EOF' > /etc/fail2ban/filter.d/nexus-web.conf
|
||||
[Definition]
|
||||
failregex = ^.*\[NEXUS-WEB-AUTH-FAIL\]:\s*<HOST>.*$
|
||||
@@ -374,41 +363,21 @@ ignoreregex =
|
||||
EOF
|
||||
chmod 0644 /etc/fail2ban/filter.d/nexus-web.conf
|
||||
|
||||
# Jails Fail2ban
|
||||
cat << 'EOF' > /etc/fail2ban/jail.local
|
||||
[DEFAULT]
|
||||
bantime = 3600
|
||||
findtime = 600
|
||||
maxretry = 3
|
||||
usedns = no
|
||||
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
|
||||
|
||||
[asterisk]
|
||||
enabled = true
|
||||
port = 5060,5061
|
||||
protocol = udp
|
||||
filter = asterisk
|
||||
logpath = /var/log/asterisk/messages
|
||||
action = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
||||
maxretry = 3
|
||||
bantime = 3600
|
||||
|
||||
[nginx-http-auth]
|
||||
enabled = true
|
||||
port = http,https,8443
|
||||
logpath = /var/log/nginx/*error.log
|
||||
maxretry = 5
|
||||
bantime = 3600
|
||||
|
||||
[nexus-web]
|
||||
enabled = true
|
||||
port = http,https,8443
|
||||
filter = nexus-web
|
||||
logpath = /var/log/nexus_security.log
|
||||
maxretry = 3
|
||||
findtime = 600
|
||||
bantime = 3600
|
||||
EOF
|
||||
# Génération automatique et centralisée de /etc/fail2ban/jail.local (Source unique FirewallManager)
|
||||
php -r '
|
||||
require_once "/var/www/nexus-pbx/src/Database.php";
|
||||
require_once "/var/www/nexus-pbx/src/FirewallManager.php";
|
||||
$st = App\FirewallManager::getSettings();
|
||||
App\FirewallManager::generateJailLocal(
|
||||
$st["fail2ban_whitelist_ips"],
|
||||
(int)$st["fail2ban_sip_maxretry"],
|
||||
(int)$st["fail2ban_sip_findtime"],
|
||||
(int)$st["fail2ban_sip_bantime"],
|
||||
(int)$st["fail2ban_web_maxretry"],
|
||||
(int)$st["fail2ban_web_bantime"],
|
||||
(int)$st["admin_https_port"]
|
||||
);
|
||||
'
|
||||
|
||||
chown root:asterisk /etc/fail2ban/jail.local
|
||||
chmod 0664 /etc/fail2ban/jail.local
|
||||
|
||||
@@ -417,7 +417,7 @@ function renderDashboardMetrics() {
|
||||
const fwStatus = document.getElementById('dash-fw-status');
|
||||
const fwDetail = document.getElementById('dash-fw-detail');
|
||||
|
||||
const isFwActive = Boolean(sec.firewall_enabled || sec.strict_port80) && Boolean(sec.fail2ban_running);
|
||||
const isFwActive = Boolean(sec.firewall_enabled);
|
||||
if (isFwActive) {
|
||||
if (fwDot) fwDot.className = 'w-2 h-2 rounded-full bg-emerald-400 shadow-sm shadow-emerald-500/50';
|
||||
if (fwStatus) {
|
||||
@@ -431,7 +431,7 @@ function renderDashboardMetrics() {
|
||||
fwStatus.textContent = 'Inactif';
|
||||
fwStatus.className = 'text-sm font-extrabold text-slate-400 mt-1';
|
||||
}
|
||||
if (fwDetail) fwDetail.textContent = !sec.fail2ban_running ? 'Moteur Fail2ban arrêté' : 'Tout le trafic autorisé';
|
||||
if (fwDetail) fwDetail.textContent = 'Tout le trafic autorisé';
|
||||
}
|
||||
|
||||
const f2bDot = document.getElementById('dash-f2b-dot');
|
||||
@@ -445,7 +445,7 @@ function renderDashboardMetrics() {
|
||||
f2bStatus.className = 'text-sm font-extrabold text-emerald-400 mt-1';
|
||||
}
|
||||
const activeJailCount = Object.keys(sec.jails || {}).length;
|
||||
if (f2bDetail) f2bDetail.textContent = `${activeJailCount} prison(s) active(s)`;
|
||||
if (f2bDetail) f2bDetail.textContent = activeJailCount > 0 ? `${activeJailCount} prison(s) active(s)` : 'Surveillance active';
|
||||
} else {
|
||||
if (f2bDot) f2bDot.className = 'w-2 h-2 rounded-full bg-rose-500';
|
||||
if (f2bStatus) {
|
||||
@@ -3797,7 +3797,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (btn) btn.disabled = true;
|
||||
|
||||
const payload = {
|
||||
firewall_enabled: f.elements['firewall_enabled']?.checked ? 1 : (f.elements['certbot_strict_port80']?.checked ? 1 : 0),
|
||||
firewall_enabled: f.elements['firewall_enabled']?.checked ? 1 : 0,
|
||||
admin_https_port: Number(f.elements['admin_https_port']?.value || 8443),
|
||||
certbot_strict_port80: f.elements['certbot_strict_port80']?.checked ? 1 : 0,
|
||||
fail2ban_sip_maxretry: Number(f.elements['fail2ban_sip_maxretry']?.value || 3),
|
||||
@@ -4776,7 +4776,7 @@ function renderFirewall() {
|
||||
form.elements['certbot_strict_port80'].checked = Number(s.certbot_strict_port80) === 1;
|
||||
}
|
||||
if (form.elements['firewall_enabled']) {
|
||||
form.elements['firewall_enabled'].checked = (Number(s.firewall_enabled) === 1 || Number(s.certbot_strict_port80) === 1);
|
||||
form.elements['firewall_enabled'].checked = Number(s.firewall_enabled) === 1;
|
||||
}
|
||||
if (form.elements['fail2ban_sip_maxretry']) {
|
||||
form.elements['fail2ban_sip_maxretry'].value = s.fail2ban_sip_maxretry || 3;
|
||||
|
||||
+4
-4
@@ -635,11 +635,11 @@ $currentUser = Auth::getUsername();
|
||||
<!-- Statut Pare-feu -->
|
||||
<div class="p-3 rounded-xl bg-anthracite-950 border border-anthracite-700">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-[10px] text-slate-400 font-bold uppercase">Pare-feu Strict</span>
|
||||
<span id="dash-fw-dot" class="w-2 h-2 rounded-full bg-slate-500"></span>
|
||||
<span class="text-[10px] text-slate-400 font-bold uppercase">Filtrage & Pare-feu</span>
|
||||
<span id="dash-fw-dot" class="w-2 h-2 rounded-full bg-emerald-400 shadow-sm shadow-emerald-500/50"></span>
|
||||
</div>
|
||||
<div id="dash-fw-status" class="text-sm font-extrabold text-white mt-1">Inactif</div>
|
||||
<div id="dash-fw-detail" class="text-[10px] text-slate-400 truncate mt-0.5">Filtrage strict non actif</div>
|
||||
<div id="dash-fw-status" class="text-sm font-extrabold text-emerald-400 mt-1">Actif</div>
|
||||
<div id="dash-fw-detail" class="text-[10px] text-slate-400 truncate mt-0.5">Port HTTPS : 8443</div>
|
||||
</div>
|
||||
|
||||
<!-- Statut Fail2ban -->
|
||||
|
||||
+17
-47
@@ -28,18 +28,7 @@ chmod 0664 /var/log/asterisk/messages /var/log/asterisk/security.log 2>/dev/null
|
||||
chown www-data:asterisk /var/log/nexus_security.log 2>/dev/null || true
|
||||
chmod 0664 /var/log/nexus_security.log 2>/dev/null || true
|
||||
|
||||
echo "-> Filtres Fail2ban Asterisk & Nexus-Web..."
|
||||
cat << 'EOF' > /etc/fail2ban/filter.d/asterisk.conf
|
||||
[INCLUDES]
|
||||
before = common.conf
|
||||
|
||||
[Definition]
|
||||
failregex = ^(%(__prefix_line)s|\[\]\s*)*(?:NOTICE|SECURITY)\[\d+\]:? (?:\S+: )?(?:Registration from |Call from |Invalid password for |Sending fake auth challenge to |No matching endpoint found for )?'<HOST>'(?::\d+)?
|
||||
^(%(__prefix_line)s|\[\]\s*)*SECURITY\[\d+\]:? (?:\S+: )?SecurityEvent="(?:FailedACL|InvalidAccountID|ChallengeResponseFailed|InvalidPassword|AuthFailed)"(?:.*?)RemoteAddress="IPV[46]/(?:UDP|TCP|TLS)/<HOST>/\d+"
|
||||
ignoreregex =
|
||||
EOF
|
||||
chmod 0644 /etc/fail2ban/filter.d/asterisk.conf
|
||||
|
||||
echo "-> Filtres Fail2ban Nexus-Web..."
|
||||
cat << 'EOF' > /etc/fail2ban/filter.d/nexus-web.conf
|
||||
[Definition]
|
||||
failregex = ^.*\[NEXUS-WEB-AUTH-FAIL\]:\s*<HOST>.*$
|
||||
@@ -47,40 +36,21 @@ ignoreregex =
|
||||
EOF
|
||||
chmod 0644 /etc/fail2ban/filter.d/nexus-web.conf
|
||||
|
||||
cat << 'EOF' > /etc/fail2ban/jail.local
|
||||
[DEFAULT]
|
||||
bantime = 3600
|
||||
findtime = 600
|
||||
maxretry = 3
|
||||
usedns = no
|
||||
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
|
||||
|
||||
[asterisk]
|
||||
enabled = true
|
||||
port = 5060,5061
|
||||
protocol = udp
|
||||
filter = asterisk
|
||||
logpath = /var/log/asterisk/messages
|
||||
action = %(banaction)s[name=%(__name__)s, bantime="%(bantime)s", port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
|
||||
maxretry = 3
|
||||
bantime = 3600
|
||||
|
||||
[nginx-http-auth]
|
||||
enabled = true
|
||||
port = http,https,8443
|
||||
logpath = /var/log/nginx/*error.log
|
||||
maxretry = 5
|
||||
bantime = 3600
|
||||
|
||||
[nexus-web]
|
||||
enabled = true
|
||||
port = http,https,8443
|
||||
filter = nexus-web
|
||||
logpath = /var/log/nexus_security.log
|
||||
maxretry = 3
|
||||
findtime = 600
|
||||
bantime = 3600
|
||||
EOF
|
||||
echo "-> Génération et application de /etc/fail2ban/jail.local..."
|
||||
php -r '
|
||||
require_once "/var/www/nexus-pbx/src/Database.php";
|
||||
require_once "/var/www/nexus-pbx/src/FirewallManager.php";
|
||||
$st = App\FirewallManager::getSettings();
|
||||
App\FirewallManager::generateJailLocal(
|
||||
$st["fail2ban_whitelist_ips"],
|
||||
(int)$st["fail2ban_sip_maxretry"],
|
||||
(int)$st["fail2ban_sip_findtime"],
|
||||
(int)$st["fail2ban_sip_bantime"],
|
||||
(int)$st["fail2ban_web_maxretry"],
|
||||
(int)$st["fail2ban_web_bantime"],
|
||||
(int)$st["admin_https_port"]
|
||||
);
|
||||
' 2>/dev/null || true
|
||||
|
||||
echo "-> Droits applicatifs et ACL POSIX (/etc/asterisk et /var/www/nexus-pbx)..."
|
||||
usermod -aG asterisk www-data 2>/dev/null || true
|
||||
@@ -112,7 +82,7 @@ chmod 0664 /etc/fail2ban/jail.local /etc/nginx/sites-available/nexus-pbx 2>/dev/
|
||||
setfacl -m u:www-data:rw /etc/fail2ban/jail.local /etc/nginx/sites-available/nexus-pbx 2>/dev/null || true
|
||||
|
||||
cat << 'EOF' > /etc/sudoers.d/nexus-pbx
|
||||
www-data ALL=(ALL) NOPASSWD: /usr/sbin/asterisk, /usr/sbin/nginx, /usr/bin/systemctl, /usr/bin/fail2ban-client, /var/www/nexus-pbx/scripts/nexus-firewall, /usr/bin/certbot, /usr/sbin/nft, /usr/sbin/iptables
|
||||
www-data ALL=(ALL) NOPASSWD: /usr/sbin/asterisk, /usr/sbin/nginx, /usr/bin/systemctl, /usr/bin/fail2ban-client, /bin/fail2ban-client, /var/www/nexus-pbx/scripts/nexus-firewall, /usr/bin/certbot, /usr/sbin/nft, /usr/sbin/iptables
|
||||
EOF
|
||||
chmod 0440 /etc/sudoers.d/nexus-pbx
|
||||
|
||||
|
||||
+23
-14
@@ -73,16 +73,22 @@ class FirewallManager
|
||||
|
||||
$settings = self::getSettings();
|
||||
|
||||
// 1. Détection service Fail2ban (avec timeout strict de 2s anti-blocage)
|
||||
exec('timeout 2 sudo /usr/bin/fail2ban-client ping 2>&1', $pingOut, $pingCode);
|
||||
$isRunning = ($pingCode === 0 && str_contains(implode(' ', $pingOut), 'Server replied: pong'));
|
||||
// 1. Détection service Fail2ban (Statut Systemd + vérification socket ping)
|
||||
$sysActive = trim((string)@shell_exec('systemctl is-active fail2ban 2>/dev/null')) === 'active';
|
||||
$pingCode = -1;
|
||||
$pingOut = [];
|
||||
if ($sysActive) {
|
||||
exec('timeout 2 sudo -n /usr/bin/fail2ban-client ping 2>&1', $pingOut, $pingCode);
|
||||
}
|
||||
$pingOk = ($pingCode === 0 && str_contains(implode(' ', $pingOut), 'Server replied: pong'));
|
||||
$isRunning = $sysActive || $pingOk;
|
||||
|
||||
$jails = [];
|
||||
$bannedList = [];
|
||||
$totalBannedCount = 0;
|
||||
|
||||
if ($isRunning) {
|
||||
exec('timeout 2 sudo /usr/bin/fail2ban-client status 2>&1', $statOut);
|
||||
if ($isRunning && $pingOk) {
|
||||
exec('timeout 2 sudo -n /usr/bin/fail2ban-client status 2>&1', $statOut);
|
||||
$statStr = implode("\n", $statOut);
|
||||
|
||||
// Extraction des prisons actives
|
||||
@@ -138,7 +144,7 @@ class FirewallManager
|
||||
$unbannedFrom = [];
|
||||
|
||||
if ($jail !== '' && $jail !== 'all') {
|
||||
$cmd = "timeout 2 sudo /usr/bin/fail2ban-client set " . escapeshellarg($jail) . " unbanip " . escapeshellarg($ip) . " 2>&1";
|
||||
$cmd = "timeout 2 sudo -n /usr/bin/fail2ban-client set " . escapeshellarg($jail) . " unbanip " . escapeshellarg($ip) . " 2>&1";
|
||||
exec($cmd, $out, $code);
|
||||
if ($code === 0) {
|
||||
$unbannedFrom[] = $jail;
|
||||
@@ -146,7 +152,7 @@ class FirewallManager
|
||||
} else {
|
||||
// Débannir de toutes les prisons connues
|
||||
foreach (['asterisk', 'nexus-web', 'sshd'] as $j) {
|
||||
$cmd = "timeout 2 sudo /usr/bin/fail2ban-client set " . escapeshellarg($j) . " unbanip " . escapeshellarg($ip) . " 2>&1";
|
||||
$cmd = "timeout 2 sudo -n /usr/bin/fail2ban-client set " . escapeshellarg($j) . " unbanip " . escapeshellarg($ip) . " 2>&1";
|
||||
exec($cmd, $out, $code);
|
||||
if ($code === 0) {
|
||||
$unbannedFrom[] = $j;
|
||||
@@ -175,7 +181,7 @@ class FirewallManager
|
||||
}
|
||||
|
||||
$jail = trim($jail) ?: 'asterisk';
|
||||
$cmd = "timeout 2 sudo /usr/bin/fail2ban-client set " . escapeshellarg($jail) . " banip " . escapeshellarg($ip) . " 2>&1";
|
||||
$cmd = "timeout 2 sudo -n /usr/bin/fail2ban-client set " . escapeshellarg($jail) . " banip " . escapeshellarg($ip) . " 2>&1";
|
||||
exec($cmd, $out, $code);
|
||||
@unlink('/tmp/nexus_fw_status_cache.json');
|
||||
|
||||
@@ -195,7 +201,7 @@ class FirewallManager
|
||||
$db = Database::getConnection();
|
||||
|
||||
$strictPort80 = !empty($data['certbot_strict_port80']) ? 1 : 0;
|
||||
$firewallEnabled = (!empty($data['firewall_enabled']) || $strictPort80 === 1) ? 1 : 0;
|
||||
$firewallEnabled = isset($data['firewall_enabled']) ? (!empty($data['firewall_enabled']) ? 1 : 0) : 1;
|
||||
$adminPort = max(1, min(65535, (int)($data['admin_https_port'] ?? 8443)));
|
||||
$sipMaxRetry = max(1, min(20, (int)($data['fail2ban_sip_maxretry'] ?? 3)));
|
||||
$sipFindTime = max(60, min(86400, (int)($data['fail2ban_sip_findtime'] ?? 600)));
|
||||
@@ -253,8 +259,11 @@ class FirewallManager
|
||||
$adminPort
|
||||
);
|
||||
|
||||
// 3. Rechargement de Fail2ban (timeout 3s)
|
||||
exec('timeout 3 sudo /usr/bin/fail2ban-client reload 2>&1', $f2bOut, $f2bCode);
|
||||
// 3. Rechargement / Redémarrage propre de Fail2ban
|
||||
exec('timeout 3 sudo -n /usr/bin/fail2ban-client reload 2>&1', $f2bOut, $f2bCode);
|
||||
if ($f2bCode !== 0) {
|
||||
exec('timeout 5 sudo -n /usr/bin/systemctl restart fail2ban 2>&1');
|
||||
}
|
||||
|
||||
// 4. Mise à jour de la configuration Nginx
|
||||
self::applyNginxConfig($adminPort, $strictPort80);
|
||||
@@ -482,18 +491,18 @@ EOF;
|
||||
@chmod(self::NGINX_CONF_PATH, 0664);
|
||||
|
||||
// Test de syntaxe
|
||||
exec('sudo /usr/sbin/nginx -t 2>&1', $tOut, $tCode);
|
||||
exec('sudo -n /usr/sbin/nginx -t 2>&1', $tOut, $tCode);
|
||||
if ($tCode !== 0) {
|
||||
throw new RuntimeException("Erreur de syntaxe Nginx : " . implode("\n", $tOut));
|
||||
}
|
||||
|
||||
// Rechargement Nginx sans coupure
|
||||
exec('sudo /usr/bin/systemctl reload nginx 2>&1');
|
||||
exec('sudo -n /usr/bin/systemctl reload nginx 2>&1');
|
||||
}
|
||||
|
||||
private static function getJailDetails(string $jail): array
|
||||
{
|
||||
exec('timeout 2 sudo /usr/bin/fail2ban-client status ' . escapeshellarg($jail) . ' 2>&1', $out);
|
||||
exec('timeout 2 sudo -n /usr/bin/fail2ban-client status ' . escapeshellarg($jail) . ' 2>&1', $out);
|
||||
$str = implode("\n", $out);
|
||||
|
||||
$currentlyFailed = 0;
|
||||
|
||||
@@ -135,10 +135,10 @@ class SystemMetrics
|
||||
$nginxActive = ($lines[1] ?? '') === 'active';
|
||||
$phpActive = ($lines[2] ?? '') === 'active';
|
||||
$f2bActive = ($lines[3] ?? '') === 'active' || !empty($fwStatus['is_running']);
|
||||
$isF2bRunning = !empty($fwStatus['is_running']) && (($lines[3] ?? '') === 'active');
|
||||
$fwEnabled = !empty($fwStatus['settings']['firewall_enabled']) && $isF2bRunning;
|
||||
$fwEnabled = !empty($fwStatus['settings']['firewall_enabled']) && $nginxActive;
|
||||
|
||||
$adminPort = $fwStatus['settings']['admin_https_port'] ?? 8443;
|
||||
$isStrictPort80 = !empty($fwStatus['settings']['certbot_strict_port80']);
|
||||
|
||||
return [
|
||||
[
|
||||
@@ -180,7 +180,7 @@ class SystemMetrics
|
||||
[
|
||||
'id' => 'firewall',
|
||||
'name' => 'Pare-feu & Filtrage',
|
||||
'desc' => $fwEnabled ? 'Règles actives & Isolation 80' : 'Inactif (Tout autoriser)',
|
||||
'desc' => $fwEnabled ? ($isStrictPort80 ? 'Mode Strict (Port 80 Isolé)' : 'Filtrage Nginx & Ports Actif') : 'Inactif (Tout autoriser)',
|
||||
'port' => 'Filtrage IP & Nginx',
|
||||
'active' => $fwEnabled,
|
||||
'status_text' => $fwEnabled ? 'Filtrage Actif' : 'Inactif',
|
||||
|
||||
Reference in new issue
Block a user