Replit-Commit-Author: Agent Replit-Commit-Session-Id: 7a657272-55ba-4a79-9a2e-f1ed9bc7a528 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 1c71ce6e-1a3e-4f53-bb5d-77cdd22b8ea3
756 lines
32 KiB
Python
756 lines
32 KiB
Python
#!/usr/bin/env python3
|
||
|
||
import paramiko
|
||
import mysql.connector
|
||
from mysql.connector import Error
|
||
from datetime import datetime, timedelta
|
||
import logging
|
||
import re
|
||
from dotenv import load_dotenv
|
||
import os
|
||
|
||
# Carica le variabili d'ambiente
|
||
load_dotenv()
|
||
|
||
# Configurazione del logging
|
||
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
|
||
|
||
# Configurazione del database
|
||
db_config = {
|
||
'user': os.getenv('DB_USER'),
|
||
'password': os.getenv('DB_PASSWORD'),
|
||
'host': os.getenv('DB_HOST'),
|
||
'database': os.getenv('DB_NAME'),
|
||
}
|
||
|
||
def connect_to_database():
|
||
try:
|
||
logging.info('🔗 Connessione al database...')
|
||
db = mysql.connector.connect(**db_config)
|
||
logging.info('✅ Connessione al database riuscita.')
|
||
return db
|
||
except Error as e:
|
||
logging.error(f'❌ Errore di connessione al database: {e}')
|
||
exit(1)
|
||
|
||
def get_routers(cursor):
|
||
try:
|
||
logging.info('📡 Recupero dell\'elenco dei router dal database...')
|
||
cursor.execute("SELECT * FROM routers")
|
||
routers = cursor.fetchall()
|
||
if not routers:
|
||
logging.warning('⚠️ Nessun router trovato nel database.')
|
||
exit(0)
|
||
logging.info(f'✅ Trovati {len(routers)} router nel database.')
|
||
return routers
|
||
except Error as e:
|
||
logging.error(f'❌ Errore nel recupero dei router: {e}')
|
||
exit(1)
|
||
|
||
def connect_to_router(router_host, ssh_username, ssh_password):
|
||
try:
|
||
logging.info(f'🔐 Connessione al router {router_host} via SSH...')
|
||
ssh = paramiko.SSHClient()
|
||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||
ssh.connect(router_host, username=ssh_username, password=ssh_password, timeout=10)
|
||
logging.info(f'✅ Connessione SSH al router {router_host} riuscita.')
|
||
return ssh
|
||
except paramiko.ssh_exception.AuthenticationException as e:
|
||
logging.error(f'🔒 Autenticazione fallita per il router {router_host}: {e}')
|
||
except paramiko.ssh_exception.SSHException as e:
|
||
logging.error(f'📡 Errore SSH con il router {router_host}: {e}')
|
||
except Exception as e:
|
||
logging.error(f'❌ Errore nella connessione al router {router_host}: {e}')
|
||
return None
|
||
|
||
def get_address_list(ssh, list_name):
|
||
try:
|
||
command = f"/ip firewall address-list print where list={list_name}"
|
||
logging.info(f'⚙️ Esecuzione del comando sul router: {command}')
|
||
stdin, stdout, stderr = ssh.exec_command(command)
|
||
output = stdout.read().decode('utf-8', errors='ignore')
|
||
error_output = stderr.read().decode('utf-8', errors='ignore')
|
||
|
||
if error_output:
|
||
logging.error(f'❌ Errore durante l\'esecuzione del comando: {error_output}')
|
||
return []
|
||
|
||
logging.debug(f'📋 Output del comando "{command}":\n{output}')
|
||
return parse_address_list_output(output)
|
||
except Exception as e:
|
||
logging.error(f'❌ Errore nella get_address_list per {list_name}: {e}')
|
||
return []
|
||
|
||
def add_ip_to_list(ssh, ip_address, list_name, timeout=None):
|
||
"""
|
||
🆕 Aggiunge un IP alla address-list del router MikroTik
|
||
🕐 Supporta timeout automatico per ddos_detect_v03 e whitelist (60 minuti)
|
||
"""
|
||
try:
|
||
# 🆕 Timeout automatico per ddos_detect_v03 E whitelist
|
||
if list_name == 'ddos_detect_v03':
|
||
timeout = '1h' # 60 minuti per IP DDoS detection
|
||
logging.info(f'⏰ Applicando timeout 60min per {ip_address} in {list_name}')
|
||
elif list_name == 'whitelist':
|
||
timeout = '1h' # 🆕 60 minuti anche per whitelist (sicurezza)
|
||
logging.info(f'⏰ Applicando timeout 60min per {ip_address} in whitelist (sicurezza)')
|
||
|
||
# Costruzione comando con o senza timeout
|
||
if timeout:
|
||
command = f'/ip firewall address-list add list={list_name} address={ip_address} timeout={timeout} comment="Added by DDoS System - {datetime.now().strftime("%Y-%m-%d %H:%M")} - Timeout: {timeout}"'
|
||
else:
|
||
command = f'/ip firewall address-list add list={list_name} address={ip_address} comment="Added by DDoS System - {datetime.now().strftime("%Y-%m-%d %H:%M")}"'
|
||
|
||
logging.info(f'➕ Aggiungendo {ip_address} alla lista {list_name}{"" if not timeout else f" con timeout {timeout}"}...')
|
||
|
||
stdin, stdout, stderr = ssh.exec_command(command)
|
||
error_output = stderr.read().decode('utf-8', errors='ignore')
|
||
|
||
if error_output and 'already exists' not in error_output.lower():
|
||
logging.error(f'❌ Errore aggiungendo {ip_address} a {list_name}: {error_output}')
|
||
return False
|
||
elif 'already exists' in error_output.lower():
|
||
logging.debug(f'ℹ️ IP {ip_address} già presente in {list_name}')
|
||
return True
|
||
|
||
if timeout:
|
||
logging.info(f'✅ IP {ip_address} aggiunto alla lista {list_name} con timeout {timeout}')
|
||
else:
|
||
logging.info(f'✅ IP {ip_address} aggiunto alla lista {list_name}')
|
||
return True
|
||
|
||
except Exception as e:
|
||
logging.error(f'❌ Errore nella funzione add_ip_to_list: {e}')
|
||
return False
|
||
|
||
def parse_address_list_output(output):
|
||
ips = []
|
||
lines = output.strip().split('\n')
|
||
current_comment = ''
|
||
|
||
for line in lines:
|
||
line = line.strip()
|
||
logging.debug(f'📝 Linea analizzata: {line}')
|
||
|
||
# Salta le linee vuote
|
||
if not line:
|
||
continue
|
||
|
||
# Salta le linee non pertinenti
|
||
if line.startswith('Flags:') or line.startswith('Columns:') or line.startswith('#'):
|
||
continue
|
||
|
||
# Se la linea inizia con ';;;', è un commento
|
||
if line.startswith(';;;'):
|
||
current_comment = line[4:].strip()
|
||
continue
|
||
|
||
# Se la linea inizia con un numero, è un entry
|
||
if re.match(r'^\d', line):
|
||
parts = line.split()
|
||
idx = 0
|
||
|
||
if len(parts) < 3: # Controllo minimo campi richiesti
|
||
continue
|
||
|
||
index = parts[idx]
|
||
idx += 1
|
||
|
||
# Controlla se il prossimo elemento è un flag
|
||
flags = ''
|
||
if idx < len(parts) and re.match(r'^[A-Z]+$', parts[idx]):
|
||
flags = parts[idx]
|
||
idx += 1
|
||
|
||
# Nome della lista
|
||
if idx < len(parts):
|
||
list_name = parts[idx]
|
||
idx += 1
|
||
else:
|
||
list_name = ''
|
||
|
||
# Indirizzo IP
|
||
if idx < len(parts):
|
||
ip_address = parts[idx]
|
||
idx += 1
|
||
else:
|
||
ip_address = ''
|
||
|
||
# Creazione dell'entry solo se abbiamo dati validi
|
||
if list_name and ip_address:
|
||
entry = {
|
||
'list_name': list_name,
|
||
'ip_address': ip_address,
|
||
'comment': current_comment
|
||
}
|
||
ips.append(entry)
|
||
logging.debug(f'✅ Aggiunta entry: {entry}')
|
||
|
||
# Reset del commento corrente
|
||
current_comment = ''
|
||
else:
|
||
# Linea non riconosciuta
|
||
logging.warning(f'⚠️ Linea non riconosciuta: {line}')
|
||
|
||
logging.debug(f'📊 IP estratti: {len(ips)} entries')
|
||
return ips
|
||
|
||
def insert_ips_into_database(cursor, db, router_id, ips):
|
||
if not ips:
|
||
logging.info(f'ℹ️ Nessun IP da inserire per router ID {router_id}')
|
||
return
|
||
|
||
inserted_count = 0
|
||
updated_count = 0
|
||
|
||
for ip in ips:
|
||
try:
|
||
cursor.execute("""
|
||
INSERT INTO router_data (router_id, list_name, ip_address, comment)
|
||
VALUES (%s, %s, %s, %s)
|
||
ON DUPLICATE KEY UPDATE comment=%s, retrieved_at=%s
|
||
""", (router_id, ip['list_name'], ip['ip_address'], ip['comment'], ip['comment'], datetime.now()))
|
||
|
||
if cursor.rowcount == 1:
|
||
inserted_count += 1
|
||
elif cursor.rowcount == 2:
|
||
updated_count += 1
|
||
|
||
db.commit()
|
||
logging.debug(f'💾 Processato IP {ip["ip_address"]} nella lista {ip["list_name"]} per router ID {router_id}')
|
||
|
||
except mysql.connector.Error as err:
|
||
logging.error(f"❌ Errore nell'inserimento di {ip['ip_address']}: {err}")
|
||
|
||
logging.info(f'📊 Router ID {router_id}: {inserted_count} IP inseriti, {updated_count} IP aggiornati')
|
||
|
||
def cleanup_old_ips(cursor, db):
|
||
"""
|
||
🆕 Rimuove gli IP più vecchi delle soglie specificate
|
||
📊 AGGIORNATO: ddos_detect_v03 da ip_list, ddos2/ddos3-attackers da router_data
|
||
"""
|
||
cleanup_stats = {}
|
||
|
||
try:
|
||
logging.info('🧹 === PULIZIA DATABASE ===')
|
||
|
||
# 🆕 Pulizia tabella ip_list (solo ddos_detect_v03)
|
||
logging.info('📋 Pulizia tabella ip_list...')
|
||
list_name = 'ddos_detect_v03'
|
||
days = 7
|
||
|
||
logging.info(f'🧹 Pulizia lista {list_name} da ip_list (>{days} giorni)...')
|
||
|
||
# Prima conta quanti record verranno eliminati
|
||
cursor.execute("""
|
||
SELECT COUNT(*) as count FROM ip_list
|
||
WHERE list_name = %s
|
||
AND retrieved_at < DATE_SUB(NOW(), INTERVAL %s DAY)
|
||
""", (list_name, days))
|
||
|
||
count_result = cursor.fetchone()
|
||
records_to_delete = count_result['count'] if count_result else 0
|
||
|
||
if records_to_delete == 0:
|
||
logging.info(f'ℹ️ Nessun record da eliminare per {list_name} in ip_list')
|
||
cleanup_stats[f'{list_name}_ip_list'] = 0
|
||
else:
|
||
# Esegui la pulizia
|
||
cursor.execute("""
|
||
DELETE FROM ip_list
|
||
WHERE list_name = %s
|
||
AND retrieved_at < DATE_SUB(NOW(), INTERVAL %s DAY)
|
||
""", (list_name, days))
|
||
|
||
deleted_count = cursor.rowcount
|
||
cleanup_stats[f'{list_name}_ip_list'] = deleted_count
|
||
logging.info(f'✅ ip_list {list_name}: eliminati {deleted_count} record più vecchi di {days} giorni')
|
||
|
||
# 🆕 Pulizia tabella router_data (ddos2-attackers e ddos3-attackers)
|
||
logging.info('🎯 Pulizia tabella router_data...')
|
||
router_data_policies = [
|
||
('ddos2-attackers', 15), # Lista attackers livello 2
|
||
('ddos3-attackers', 20) # Lista attackers livello 3
|
||
]
|
||
|
||
for list_name, days in router_data_policies:
|
||
logging.info(f'🧹 Pulizia lista {list_name} da router_data (>{days} giorni)...')
|
||
|
||
# Prima conta quanti record verranno eliminati
|
||
cursor.execute("""
|
||
SELECT COUNT(*) as count FROM router_data
|
||
WHERE list_name = %s
|
||
AND retrieved_at < DATE_SUB(NOW(), INTERVAL %s DAY)
|
||
""", (list_name, days))
|
||
|
||
count_result = cursor.fetchone()
|
||
records_to_delete = count_result['count'] if count_result else 0
|
||
|
||
if records_to_delete == 0:
|
||
logging.info(f'ℹ️ Nessun record da eliminare per {list_name} in router_data')
|
||
cleanup_stats[f'{list_name}_router_data'] = 0
|
||
else:
|
||
# Esegui la pulizia
|
||
cursor.execute("""
|
||
DELETE FROM router_data
|
||
WHERE list_name = %s
|
||
AND retrieved_at < DATE_SUB(NOW(), INTERVAL %s DAY)
|
||
""", (list_name, days))
|
||
|
||
deleted_count = cursor.rowcount
|
||
cleanup_stats[f'{list_name}_router_data'] = deleted_count
|
||
logging.info(f'✅ router_data {list_name}: eliminati {deleted_count} record più vecchi di {days} giorni')
|
||
|
||
db.commit()
|
||
|
||
# Statistiche finali
|
||
total_deleted = sum(cleanup_stats.values())
|
||
logging.info(f'🎯 Pulizia completata: {total_deleted} record totali eliminati')
|
||
|
||
for key, count in cleanup_stats.items():
|
||
if count > 0:
|
||
logging.info(f' └─ {key}: {count} record eliminati')
|
||
|
||
except Error as e:
|
||
logging.error(f"❌ Errore durante la pulizia degli IP vecchi: {e}")
|
||
db.rollback()
|
||
|
||
def check_expired_ips_on_router(ssh, router_host):
|
||
"""
|
||
🆕 Verifica IP scaduti sul router e fornisce statistiche
|
||
📊 AGGIORNATO: liste corrette senza ddos_ia
|
||
"""
|
||
try:
|
||
logging.info(f'🕐 Verifica IP scaduti sul router {router_host}...')
|
||
|
||
# 🆕 Lista delle liste da controllare (senza ddos_ia)
|
||
lists_to_check = ['ddos_detect_v03', 'ddos2-attackers', 'ddos3-attackers', 'whitelist']
|
||
|
||
total_active_ips = 0
|
||
timeout_stats = {}
|
||
|
||
for list_name in lists_to_check:
|
||
current_ips = get_address_list(ssh, list_name)
|
||
count = len([ip for ip in current_ips if 'ip_address' in ip])
|
||
total_active_ips += count
|
||
timeout_stats[list_name] = count
|
||
|
||
if count > 0:
|
||
logging.info(f'📊 Lista {list_name}: {count} IP attivi')
|
||
|
||
logging.info(f'🎯 Router {router_host}: {total_active_ips} IP totali attivi')
|
||
|
||
# Log specifico per ddos_detect_v03 con timeout
|
||
if timeout_stats.get('ddos_detect_v03', 0) > 0:
|
||
logging.info(f'⏰ Lista ddos_detect_v03: {timeout_stats["ddos_detect_v03"]} IP con timeout 60min attivi')
|
||
|
||
return timeout_stats
|
||
|
||
except Exception as e:
|
||
logging.error(f'❌ Errore verifica IP scaduti su router {router_host}: {e}')
|
||
return {}
|
||
|
||
def check_global_whitelist_conflicts(cursor, db):
|
||
"""
|
||
🆕 Controlla e risolve conflitti tra whitelistGlobale e ddos_detect_v03
|
||
"""
|
||
try:
|
||
logging.info('🛡️ === CONTROLLO WHITELIST GLOBALE ===')
|
||
|
||
# Trova IP che sono sia in whitelist globale che in ddos_detect_v03
|
||
cursor.execute("""
|
||
SELECT DISTINCT w.ip_address, w.comment, w.reason
|
||
FROM whitelistGlobale w
|
||
INNER JOIN ip_list i ON w.ip_address = i.ip_address
|
||
WHERE w.active = 1
|
||
AND i.list_name = 'ddos_detect_v03'
|
||
""")
|
||
|
||
conflicts = cursor.fetchall()
|
||
|
||
if not conflicts:
|
||
logging.info('✅ Nessun conflitto trovato tra whitelist globale e ddos_detect_v03')
|
||
return
|
||
|
||
logging.info(f'⚠️ Trovati {len(conflicts)} IP in conflitto da risolvere')
|
||
|
||
resolved_count = 0
|
||
for conflict in conflicts:
|
||
ip_address = conflict['ip_address']
|
||
comment = conflict['comment'] or 'Whitelist globale'
|
||
reason = conflict['reason'] or 'Risoluzione automatica conflitto'
|
||
|
||
# Rimuovi da ddos_detect_v03
|
||
cursor.execute("""
|
||
DELETE FROM ip_list
|
||
WHERE ip_address = %s AND list_name = 'ddos_detect_v03'
|
||
""", (ip_address,))
|
||
|
||
if cursor.rowcount > 0:
|
||
resolved_count += 1
|
||
logging.info(f'✅ Rimosso {ip_address} da ddos_detect_v03 (motivo: {reason})')
|
||
|
||
db.commit()
|
||
logging.info(f'🎯 Risolti {resolved_count} conflitti whitelist vs blacklist')
|
||
|
||
except Error as e:
|
||
logging.error(f"❌ Errore controllo whitelist globale: {e}")
|
||
db.rollback()
|
||
|
||
def sync_global_whitelist_to_routers(cursor, db, ssh, router_host):
|
||
"""
|
||
🆕 Sincronizza whitelistGlobale con tutti i router
|
||
🕐 AGGIORNATO: applica timeout 60min anche alla whitelist per sicurezza
|
||
"""
|
||
try:
|
||
logging.info(f'🌐 Sincronizzazione whitelist globale per router {router_host}...')
|
||
|
||
# Recupera IP attivi dalla whitelist globale
|
||
cursor.execute("""
|
||
SELECT ip_address, comment, reason, last_sync
|
||
FROM whitelistGlobale
|
||
WHERE active = 1
|
||
ORDER BY created_at DESC
|
||
""")
|
||
|
||
global_whitelist = cursor.fetchall()
|
||
|
||
if not global_whitelist:
|
||
logging.info(f'ℹ️ Nessun IP nella whitelist globale')
|
||
return 0, 0
|
||
|
||
logging.info(f'📋 Trovati {len(global_whitelist)} IP nella whitelist globale')
|
||
|
||
# Ottieni whitelist corrente dal router
|
||
current_whitelist = get_address_list(ssh, 'whitelist')
|
||
current_whitelist_set = {entry['ip_address'] for entry in current_whitelist if 'ip_address' in entry}
|
||
|
||
added_count = 0
|
||
updated_count = 0
|
||
timeout_applied = 0
|
||
|
||
for entry in global_whitelist:
|
||
ip_address = entry['ip_address']
|
||
comment = entry['comment'] or 'Whitelist globale'
|
||
reason = entry['reason'] or 'IP fidato globale'
|
||
|
||
if ip_address not in current_whitelist_set:
|
||
# 🆕 Aggiungi alla whitelist del router CON TIMEOUT 60min
|
||
if add_ip_to_list(ssh, ip_address, 'whitelist'): # Timeout automatico applicato
|
||
added_count += 1
|
||
timeout_applied += 1 # 🆕 Conta timeout whitelist
|
||
|
||
# Aggiorna last_sync
|
||
cursor.execute("""
|
||
UPDATE whitelistGlobale
|
||
SET last_sync = NOW()
|
||
WHERE ip_address = %s
|
||
""", (ip_address,))
|
||
updated_count += 1
|
||
|
||
logging.info(f'✅ IP {ip_address} aggiunto alla whitelist router {router_host} con timeout 60min')
|
||
else:
|
||
logging.debug(f'➖ IP {ip_address} già presente nella whitelist router {router_host}')
|
||
|
||
if updated_count > 0:
|
||
db.commit()
|
||
|
||
logging.info(f'🌐 Whitelist globale router {router_host}: {added_count} IP aggiunti, {timeout_applied} timeout applicati')
|
||
return added_count, timeout_applied
|
||
|
||
except Error as e:
|
||
logging.error(f"❌ Errore sincronizzazione whitelist globale router {router_host}: {e}")
|
||
return 0, 0
|
||
|
||
def sync_ip_list_to_routers(cursor, db, ssh, router_id, router_host):
|
||
"""
|
||
🆕 Sincronizza SOLO ddos_detect_v03 dalla tabella ip_list con il router
|
||
📊 AGGIORNATO: controlla whitelist globale prima di bloccare IP
|
||
"""
|
||
try:
|
||
# 🆕 Query ottimizzata per SOLO ddos_detect_v03 ESCLUDENDO whitelist globale
|
||
logging.info(f'📊 Recupero IP ddos_detect_v03 (esclusi whitelist globale) per router {router_host}...')
|
||
|
||
cursor.execute("""
|
||
SELECT i.list_name, i.ip_address, i.retrieved_at
|
||
FROM ip_list i
|
||
LEFT JOIN whitelistGlobale w ON i.ip_address = w.ip_address AND w.active = 1
|
||
WHERE i.list_name = 'ddos_detect_v03'
|
||
AND i.retrieved_at > DATE_SUB(NOW(), INTERVAL 3 DAY)
|
||
AND w.ip_address IS NULL -- 🆕 Escludi IP in whitelist globale
|
||
ORDER BY i.retrieved_at DESC
|
||
LIMIT 3000
|
||
""")
|
||
|
||
ip_list_entries = cursor.fetchall()
|
||
|
||
if not ip_list_entries:
|
||
logging.info(f'ℹ️ Nessun IP ddos_detect_v03 da bloccare (whitelist globale applicata)')
|
||
return
|
||
|
||
logging.info(f'📋 Trovati {len(ip_list_entries)} IP ddos_detect_v03 da sincronizzare (dopo filtro whitelist)')
|
||
|
||
# Ottieni la lista corrente dal router
|
||
current_ips = get_address_list(ssh, 'ddos_detect_v03')
|
||
current_ips_set = {entry['ip_address'] for entry in current_ips if 'ip_address' in entry}
|
||
|
||
# 🆕 Ottieni anche whitelist corrente per doppio controllo
|
||
current_whitelist = get_address_list(ssh, 'whitelist')
|
||
current_whitelist_set = {entry['ip_address'] for entry in current_whitelist if 'ip_address' in entry}
|
||
|
||
added_count = 0
|
||
timeout_applied = 0
|
||
skipped_whitelist = 0
|
||
|
||
# Processa solo ddos_detect_v03
|
||
for entry in ip_list_entries:
|
||
ip_address = entry['ip_address']
|
||
|
||
# 🆕 Doppio controllo: non bloccare IP in whitelist router
|
||
if ip_address in current_whitelist_set:
|
||
skipped_whitelist += 1
|
||
logging.debug(f'🛡️ IP {ip_address} saltato - presente in whitelist router')
|
||
continue
|
||
|
||
if ip_address not in current_ips_set:
|
||
if add_ip_to_list(ssh, ip_address, 'ddos_detect_v03'): # Timeout automatico applicato
|
||
added_count += 1
|
||
timeout_applied += 1
|
||
else:
|
||
logging.warning(f'⚠️ Fallita aggiunta {ip_address} a ddos_detect_v03')
|
||
else:
|
||
logging.debug(f'➖ IP {ip_address} già presente in ddos_detect_v03')
|
||
|
||
# Statistiche
|
||
logging.info(f'✅ Lista ddos_detect_v03: aggiunti {added_count} nuovi IP')
|
||
logging.info(f'⏰ Timeout 60min applicati: {timeout_applied} IP in ddos_detect_v03')
|
||
if skipped_whitelist > 0:
|
||
logging.info(f'🛡️ IP saltati per whitelist: {skipped_whitelist}')
|
||
logging.info(f'🎯 Sincronizzazione ddos_detect_v03 completata su router {router_host}')
|
||
|
||
except Error as e:
|
||
logging.error(f"❌ Errore durante la sincronizzazione ddos_detect_v03 con router {router_host}: {e}")
|
||
|
||
def sync_router_data_to_routers(cursor, db, ssh, router_id, router_host):
|
||
"""
|
||
🆕 Sincronizza ddos2-attackers, ddos3-attackers e whitelist dalla tabella router_data
|
||
📊 AGGIORNATO: gestione specifica per router e whitelist CON TIMEOUT
|
||
"""
|
||
try:
|
||
logging.info(f'🎯 Sincronizzazione dati router_data per router {router_host} (ID: {router_id})...')
|
||
|
||
# 🆕 Query per ddos2-attackers e ddos3-attackers specifici del router
|
||
cursor.execute("""
|
||
SELECT list_name, ip_address, retrieved_at, whitelist, added_to_router
|
||
FROM router_data
|
||
WHERE router_id = %s
|
||
AND list_name IN ('ddos2-attackers', 'ddos3-attackers')
|
||
AND retrieved_at > DATE_SUB(NOW(), INTERVAL 7 DAY)
|
||
ORDER BY retrieved_at DESC
|
||
LIMIT 2000
|
||
""", (router_id,))
|
||
|
||
router_entries = cursor.fetchall()
|
||
|
||
total_added = 0
|
||
whitelist_timeout_applied = 0
|
||
|
||
if not router_entries:
|
||
logging.info(f'ℹ️ Nessun dato ddos2/ddos3-attackers recente per router {router_host}')
|
||
else:
|
||
logging.info(f'📋 Trovati {len(router_entries)} record ddos2/ddos3-attackers per router {router_host}')
|
||
|
||
# Raggruppa per lista
|
||
lists_data = {}
|
||
whitelist_ips = []
|
||
|
||
for entry in router_entries:
|
||
list_name = entry['list_name']
|
||
ip_address = entry['ip_address']
|
||
is_whitelist = entry['whitelist'] == 1
|
||
|
||
if is_whitelist:
|
||
whitelist_ips.append(entry)
|
||
else:
|
||
if list_name not in lists_data:
|
||
lists_data[list_name] = []
|
||
lists_data[list_name].append(ip_address)
|
||
|
||
# Processa liste ddos2/ddos3-attackers (SENZA timeout)
|
||
for list_name, ip_addresses in lists_data.items():
|
||
logging.info(f'🔄 Sincronizzazione lista {list_name} ({len(ip_addresses)} IP) per router {router_host}...')
|
||
|
||
# Ottieni lista corrente dal router
|
||
current_ips = get_address_list(ssh, list_name)
|
||
current_ips_set = {entry['ip_address'] for entry in current_ips if 'ip_address' in entry}
|
||
|
||
added_count = 0
|
||
for ip_address in ip_addresses:
|
||
if ip_address not in current_ips_set:
|
||
if add_ip_to_list(ssh, ip_address, list_name): # Nessun timeout per queste liste
|
||
added_count += 1
|
||
total_added += 1
|
||
|
||
if added_count > 0:
|
||
logging.info(f'✅ Lista {list_name}: aggiunti {added_count} IP per router {router_host}')
|
||
|
||
# 🆕 Gestione whitelist specifica CON TIMEOUT
|
||
if whitelist_ips:
|
||
logging.info(f'🛡️ Elaborazione {len(whitelist_ips)} IP per whitelist router {router_host} (CON TIMEOUT)...')
|
||
whitelist_added = 0
|
||
|
||
# Ottieni whitelist corrente
|
||
current_whitelist = get_address_list(ssh, 'whitelist')
|
||
current_whitelist_set = {entry['ip_address'] for entry in current_whitelist if 'ip_address' in entry}
|
||
|
||
for entry in whitelist_ips:
|
||
ip_address = entry['ip_address']
|
||
|
||
if ip_address not in current_whitelist_set:
|
||
if add_ip_to_list(ssh, ip_address, 'whitelist'): # 🆕 CON TIMEOUT 60min
|
||
# Aggiorna il database: marca come aggiunto al router
|
||
cursor.execute("""
|
||
UPDATE router_data
|
||
SET added_to_router = 1
|
||
WHERE ip_address = %s AND router_id = %s AND whitelist = 1
|
||
""", (ip_address, router_id))
|
||
db.commit()
|
||
whitelist_added += 1
|
||
whitelist_timeout_applied += 1 # 🆕 Conta timeout whitelist
|
||
logging.info(f'✅ IP {ip_address} aggiunto alla whitelist router {router_host} con timeout 60min')
|
||
|
||
logging.info(f'🛡️ Whitelist router {router_host}: aggiunti {whitelist_added} IP con timeout')
|
||
|
||
logging.info(f'🎯 Sincronizzazione router_data completata: {total_added} IP ddos2/ddos3-attackers aggiunti')
|
||
|
||
# 🆕 Gestione IP whitelist non ancora aggiunti (query separata per performance)
|
||
cursor.execute("""
|
||
SELECT ip_address FROM router_data
|
||
WHERE whitelist = 1 AND router_id = %s AND added_to_router = 0
|
||
AND retrieved_at > DATE_SUB(NOW(), INTERVAL 7 DAY)
|
||
""", (router_id,))
|
||
|
||
pending_whitelist = cursor.fetchall()
|
||
|
||
if pending_whitelist:
|
||
logging.info(f'🛡️ Trovati {len(pending_whitelist)} IP whitelist pending per router {router_host}...')
|
||
|
||
current_whitelist = get_address_list(ssh, 'whitelist')
|
||
current_whitelist_set = {entry['ip_address'] for entry in current_whitelist if 'ip_address' in entry}
|
||
|
||
added_whitelist = 0
|
||
for ip_entry in pending_whitelist:
|
||
ip_address = ip_entry['ip_address']
|
||
|
||
if ip_address not in current_whitelist_set:
|
||
if add_ip_to_list(ssh, ip_address, 'whitelist'): # 🆕 CON TIMEOUT 60min
|
||
cursor.execute("""
|
||
UPDATE router_data
|
||
SET added_to_router = 1
|
||
WHERE ip_address = %s AND router_id = %s AND whitelist = 1
|
||
""", (ip_address, router_id))
|
||
db.commit()
|
||
added_whitelist += 1
|
||
whitelist_timeout_applied += 1 # 🆕 Conta timeout
|
||
|
||
if added_whitelist > 0:
|
||
logging.info(f'🛡️ Whitelist pending router {router_host}: aggiunti {added_whitelist} IP con timeout')
|
||
|
||
return whitelist_timeout_applied
|
||
|
||
except Error as e:
|
||
logging.error(f"❌ Errore durante la sincronizzazione router_data per router {router_host}: {e}")
|
||
return 0
|
||
|
||
def main():
|
||
logging.info('🚀 === AVVIO SISTEMA CONTROLLO ROUTER MIKROTIK ===')
|
||
|
||
db = connect_to_database()
|
||
cursor = db.cursor(dictionary=True)
|
||
|
||
try:
|
||
# 🆕 FASE 0: Controllo whitelist globale e risoluzione conflitti
|
||
logging.info('🛡️ === FASE 0: CONTROLLO WHITELIST GLOBALE ===')
|
||
check_global_whitelist_conflicts(cursor, db)
|
||
|
||
# 🆕 Pulizia IP vecchi (ora gestisce correttamente le tabelle separate)
|
||
logging.info('🧹 === FASE 1: PULIZIA IP OBSOLETI ===')
|
||
cleanup_old_ips(cursor, db)
|
||
|
||
# Recupera i router dal database
|
||
logging.info('📡 === FASE 2: RECUPERO ROUTER ===')
|
||
routers = get_routers(cursor)
|
||
|
||
logging.info('🔄 === FASE 3: SINCRONIZZAZIONE ROUTER ===')
|
||
successful_syncs = 0
|
||
failed_syncs = 0
|
||
total_ddos_timeouts = 0
|
||
total_whitelist_timeouts = 0
|
||
|
||
for router in routers:
|
||
router_id = router['id']
|
||
router_host = router['ip_address']
|
||
ssh_username = router['ssh_username']
|
||
ssh_password = router['ssh_password']
|
||
|
||
logging.info(f'🎯 Elaborazione router {router_host} (ID: {router_id})...')
|
||
|
||
ssh = connect_to_router(router_host, ssh_username, ssh_password)
|
||
if ssh is None:
|
||
failed_syncs += 1
|
||
logging.error(f'❌ Saltato router {router_host} - connessione fallita')
|
||
continue
|
||
|
||
try:
|
||
# 🆕 Verifica IP scaduti prima della sincronizzazione
|
||
current_stats = check_expired_ips_on_router(ssh, router_host)
|
||
|
||
# 🆕 0. Sincronizza whitelist globale (PRIMA di tutto) CON TIMEOUT
|
||
global_added, global_timeouts = sync_global_whitelist_to_routers(cursor, db, ssh, router_host)
|
||
total_whitelist_timeouts += global_timeouts
|
||
|
||
# 1. Sincronizza ddos_detect_v03 da ip_list (con filtro whitelist globale)
|
||
sync_ip_list_to_routers(cursor, db, ssh, router_id, router_host)
|
||
|
||
# 2. Sincronizza ddos2-attackers, ddos3-attackers e whitelist da router_data
|
||
router_whitelist_timeouts = sync_router_data_to_routers(cursor, db, ssh, router_id, router_host)
|
||
total_whitelist_timeouts += router_whitelist_timeouts
|
||
|
||
# 🆕 Verifica finale per statistiche timeout
|
||
final_stats = check_expired_ips_on_router(ssh, router_host)
|
||
ddos_v03_count = final_stats.get('ddos_detect_v03', 0)
|
||
if ddos_v03_count > 0:
|
||
total_ddos_timeouts += ddos_v03_count
|
||
|
||
successful_syncs += 1
|
||
logging.info(f'✅ Router {router_host} sincronizzato con successo')
|
||
|
||
except Exception as e:
|
||
failed_syncs += 1
|
||
logging.error(f'❌ Errore durante elaborazione router {router_host}: {e}')
|
||
finally:
|
||
ssh.close()
|
||
logging.info(f'🔐 Connessione SSH al router {router_host} chiusa.')
|
||
|
||
# Statistiche finali
|
||
logging.info('📊 === STATISTICHE FINALI ===')
|
||
logging.info(f'✅ Router sincronizzati con successo: {successful_syncs}')
|
||
logging.info(f'❌ Router falliti: {failed_syncs}')
|
||
logging.info(f'📡 Router totali processati: {len(routers)}')
|
||
logging.info(f'⏰ IP ddos_detect_v03 con timeout 60min attivi: {total_ddos_timeouts}')
|
||
logging.info(f'🛡️ IP whitelist con timeout 60min applicati: {total_whitelist_timeouts}')
|
||
logging.info('🎯 Gestione automatica whitelist tramite campo router_data.whitelist=1')
|
||
logging.info('🛡️ Protezione automatica whitelist globale applicata')
|
||
logging.info('⏰ NUOVO: Timeout 60min applicato anche alla whitelist per maggiore sicurezza')
|
||
|
||
except Exception as e:
|
||
logging.error(f'❌ Errore critico nel main: {e}')
|
||
finally:
|
||
cursor.close()
|
||
db.close()
|
||
logging.info('🔗 Connessione al database chiusa.')
|
||
logging.info('🏁 === FINE ESECUZIONE SISTEMA CONTROLLO ROUTER ===')
|
||
|
||
if __name__ == '__main__':
|
||
main()
|