FAQ

Questa è la guida all'installazione, alla configurazione e alla manutenzione di Arcivéo Monitor. Le sezioni sono raggruppate: panoramica generale, distribuzione del pannello, integrazione degli strumenti di sicurezza, moduli integrati e diagnostica. I comandi possono essere copiati con il pulsante a destra.

Da dove iniziare

01. Installazione del pannello — scelga il metodo

L'installazione del pannello è descritta in pagine passo-passo dedicate. Scelga il metodo:

Se non è sicuro, scelga quella automatica. Questa guida resta il riferimento unico per SSL, strumenti, cron e diagnostica — le pagine di installazione rimandano alle sue sezioni senza duplicare nulla.

Panoramica

02. Cos'è Arcivéo Monitor

Arcivéo Monitor è il pannello di sicurezza del server. Raccoglie i dati dagli strumenti installati (Fail2ban, UFW, Lynis, ModSecurity, AIDE, ClamAV, Auditd, CrowdSec, Suricata, Falco e altri) e li mostra in un'interfaccia unica con dashboard, mappa degli attacchi e pagine dettagliate per ogni strumento.

Il monitor non è uno strumento di protezione attivo: non blocca gli attacchi da solo. Il suo compito è aggregare le informazioni dagli strumenti già in funzione e presentarle in modo chiaro.

03. Come funziona il monitor sul server

Il monitor funziona solo in locale: deve essere installato sullo stesso server che monitora. Non usa né SSH né API remote.

Tutti i comandi (fail2ban-client, ufw status, ipset list ecc.) vengono eseguiti dalla dashboard con l'utente del web server (di solito www-data, sui pannelli di hosting l'account del sito) con un set ristretto di permessi sudo, limitati a specifiche utility, senza accesso root generale. I risultati vengono analizzati e mostrati nel browser.

Per più server installi il monitor separatamente su ciascuno, con un dominio univoco.

04. Come viene calcolato il Punteggio di sicurezza

Il punteggio parte dal massimo e diminuisce per ogni problema rilevato:

  • UFW non attivo — −30
  • Fail2ban non in esecuzione (nessun jail attivo) — −25
  • Nessuna chiave WebAuthn — −15
  • Lynis hardening index < 60 — −20; 60–79 — −10
  • IPset ipsum non caricato — −10
  • Minacce rilevate da ClamAV — −20
  • Modifiche ai file rilevate da AIDE — −15
  • SSL scaduto — −30, scade tra <14 giorni — −15, <30 giorni — −5
  • CrowdSec installato ma non in esecuzione — −5
  • Suricata installata ma non in esecuzione — −5
  • DBMS/cache (MySQL, PostgreSQL, Redis…) accessibili dall'esterno — −10
  • Accesso root via SSH consentito (PermitRootLogin yes) — −20
  • Aggiornamenti di sicurezza in attesa di installazione — −5

Risultato: 80+ = Protetto, 60–79 = Attenzione, <60 = A rischio.

Le penalità per ClamAV, AIDE, CrowdSec e Suricata si applicano solo se lo strumento è installato. Lynis e AIDE senza database inizializzato vengono mostrati come «nessun dato» e non riducono il punteggio. Il numero di attacchi di oggi è indicato nella dashboard, ma non influisce sul Punteggio di sicurezza.

Impostazioni e licenza

05. WebAuthn — autenticazione a due fattori

WebAuthn — standard di autenticazione senza password tramite chiave hardware. Supporta YubiKey, Touch ID, Face ID, Windows Hello, Passkey.

Dopo l'accesso con password, il sistema richiede una conferma tramite la chiave registrata. Anche se la password viene compromessa, senza la chiave fisica o la biometria non è possibile accedere.

Per la configurazione apra Chiavi WebAuthn nel menu laterale e prema «Registra chiave». Registri subito due chiavi: se l'unica chiave va persa o si rompe, l'accesso al pannello con essa non sarà possibile.

WebAuthn funziona solo tramite HTTPS. Su una connessione HTTP la registrazione e l'accesso con chiave non sono disponibili.

06. Notifiche: Telegram ed Email

Il pannello può inviare il report di sicurezza su Telegram e via email (con un pulsante o pianificato). Si configura nella sezione «Impostazioni».

Telegram. Servono un token del bot e un chat id:

  1. Su Telegram scriva a @BotFather/newbot → riceverà un token del tipo 123456:ABC....
  2. Invii un messaggio qualsiasi al suo nuovo bot (così potrà risponderle).
  3. Ricavi il suo chat id: scriva al bot @userinfobot, oppure apra https://api.telegram.org/bot<TOKEN>/getUpdates e trovi "chat":{"id":...}.
  4. Inserisca token e chat id in «Impostazioni» → Telegram, poi prema «Salva e invia test».

Email. Due metodi a scelta in «Impostazioni» → Email:

  • SMTP — host, porta (465/SSL o 587/TLS), login e password della sua casella di posta;
  • Resend — API moderna: indichi la chiave API (re_...) e un dominio mittente verificato.
Il pulsante «Invia test» verifica subito il canale. La pianificazione del report automatico avviene tramite cron (sezione «Tutte le attività cron»): esso attiva l'invio, mentre i canali vengono presi dalle impostazioni.

Stato del report: «ATTENZIONE» o «OK». L'intestazione diventa «ATTENZIONE» solo in caso di un problema reale o di un'azione in sospeso: minaccia rilevata da ClamAV, modifiche ai file in AIDE, eventi critici di Falco (Emergency/Alert/Critical nelle ultime 24 h), servizio caduto in Monit, riavvio richiesto, SSL in scadenza (≤14 giorni) o aggiornamenti di sicurezza in attesa. Il rumore di fondo — tentativi SSH dei bot, IP bannati da fail2ban, avvisi Suricata, avvertimenti Lynis e richieste già respinte da ModSecurity — non alza lo stato, quindi questi numeri nel report non significano di per sé «ATTENZIONE».

07. Licenza — inserimento e attivazione

I moduli dettagliati di monitoraggio (Lynis, UFW, ModSecurity, mappa degli attacchi, AIDE, ClamAV e altri) si sbloccano con una licenza valida. Senza di essa la dashboard, le impostazioni e l'account funzionano, mentre i moduli mostrano la scheda «Licenza richiesta».

Dopo l'acquisto, nell'area personale trova un codice di attivazione del tipo ARCIVEO-XXXX-XXXX-XXXX-XXXX. Deve «attivarlo» sul dominio del suo pannello: questo trasforma il codice in un file di licenza firmato (blocco [license]) che Lei incolla nel pannello.

Come attivare (3 passaggi):

  1. Prenda il codice di attivazione. Area personale my.arciveo.com → sezione «Licenze» / «Attivazione licenza» — copi il codice ARCIVEO-….
  2. Attivi il codice sul suo dominio. Sempre nell'area personale apra «Attivazione licenza», inserisca: codice di attivazione, la sua email e il dominio del pannello (l'indirizzo a cui si apre il monitor, es. monitor.example.com). Prema attiva: il sistema genererà un file di licenza associato a questo dominio e lo mostrerà in un campo con il pulsante «Copia».
  3. Incolli la chiave nel pannello. Copi tutto il testo della licenza → nel pannello apra «Impostazioni» → blocco «Licenza», incolli e prema «Salva». I moduli si sbloccano subito.

Il pannello verifica la chiave a livello crittografico: firma, associazione al dominio e validità.

Il dominio all'attivazione deve corrispondere esattamente all'indirizzo del pannello. Lo prenda dalla costante APP_URL in config.php e inserisca solo il nome host — senza https:// e senza prefisso www. L'attivazione è irripetibile: il codice diventa una licenza per il dominio inserito e non si riattiva — in caso di errore nel dominio la chiave non sarà valida per il suo pannello e il codice risulterà consumato. Inserisca quindi il dominio con attenzione.
Se la validità è scaduta o il dominio è cambiato, nell'intestazione del pannello comparirà un avviso. La licenza è associata al dominio per sempre e non è trasferibile su un altro dominio: per un nuovo periodo o un nuovo dominio serve una nuova chiave (si acquista nell'area personale e si attiva una sola volta).

08. File config.php — tutte le impostazioni del pannello

Tutti i parametri principali del pannello sono definiti in un unico file config.php nella radice (accanto alla cartella public/) tramite normali costanti define(). Il file viene creato durante l'installazione; raramente serve modificarlo a mano — principalmente al cambio di dominio, al trasferimento o alla connessione a un altro database. Dopo ogni modifica riavvii PHP-FPM (altrimenti, a causa di OPcache, le modifiche non verranno applicate).

Inserisca i suoi valori nei punti evidenziati; lasci il resto invariato:

// --- Database --- define('DB_HOST', 'localhost'); // lasciare define('DB_NAME', 'db_name'); // quello impostato alla creazione del DB define('DB_USER', 'user'); // quello impostato alla creazione del DB define('DB_PASS', 'db_password'); // quello impostato alla creazione del DB define('DB_CHARSET', 'utf8mb4'); // lasciare // --- Applicazione --- define('APP_URL', 'https://monitor.example.com'); // indirizzo del pannello, senza slash finale define('TIMEZONE', 'Europe/Rome'); // il suo fuso orario // --- Durata della sessione --- define('SESSION_LIFETIME', 28800); // inattività prima del nuovo accesso, sec (28800 = 8 h)

Database. Credenziali di connessione a MySQL/MariaDB:

  • DB_HOST — host del DBMS, quasi sempre localhost;
  • DB_NAME — nome del database del pannello;
  • DB_USER — utente del DB (accesso solo al proprio database);
  • DB_PASS — password di questo utente;
  • DB_CHARSET — codifica della connessione, lasci utf8mb4.

Applicazione.

  • APP_URL — indirizzo completo del pannello (es. https://monitor.example.com). Deve coincidere con il dominio su cui è attivata la licenza — altrimenti la chiave verrà rifiutata (veda la sezione «Licenza»);
  • TIMEZONE — fuso orario di PHP: influisce solo su come il pannello mostra date e ora. Sul momento di avvio delle attività cron non influisce — lì vale il fuso del sistema (veda «Tutte le attività cron»).

Durata della sessione. SESSION_LIFETIME — timeout di inattività della sessione in secondi (scorrevole: si aggiorna con l'attività). Per impostazione predefinita 28800 = 8 ore; dopo questo tempo di inattività il pannello chiederà di accedere di nuovo. Ad esempio, 3600 = 1 ora, 86400 = un giorno.

Registrazione degli errori. Gli errori non vengono mai mostrati ai visitatori, ma scritti in logs/php_errors.log — sono visibili nella pagina «Log dell'applicazione». Queste righe (display_errors=0, log_errors=1, percorso error_log) di solito non vanno modificate — le impostazioni sono definite direttamente nel file e non dipendono da php.ini.

config.php — file segreto. Contiene la password del DB. Si trova nella radice del pannello (accanto a public/), e per questo pannello la radice web (DocumentRoot) è proprio la radice del pannello, non public/. Di per sé il file non «trapela»: nel .htaccess della radice c'è per esso un divieto esplicito (Require all denied) — il server restituisce 403. Anche senza questa regola il sorgente non trapelerebbe: è PHP — il server lo esegue, non lo restituisce come testo. A ogni buon conto: non lo pubblichi in repository pubblici e non lo invii al supporto con la password reale. Permessi del file — 640.
In caso di trasferimento o ripristino dell'accesso questo file è la fonte principale delle credenziali: nome del DB, utente e password si prendono proprio da qui (veda le sezioni «Aggiornamento e trasferimento del pannello» e «Ripristino dell'accesso»).

Strumenti di sicurezza

09. Firewall UFW

UFW (Uncomplicated Firewall) è una semplice interfaccia per nftables/iptables. Chiude tutte le porte in ingresso tranne quelle esplicitamente consentite. La pagina «Firewall UFW» mostra lo stato e le regole.

sudo apt install ufw # Consenti SSH (obbligatorio PRIMA di abilitare!) e web sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Chiudi il DB dall'esterno (accesso solo locale) sudo ufw deny 3306 # Abilita e verifica sudo ufw enable sudo ufw status verbose
Prima di ufw enable consenta assolutamente SSH (ufw allow OpenSSH), altrimenti perderà l'accesso al server.
L'«Esposizione esterna» sulla dashboard tiene conto di UFW: una porta chiusa da una regola deny non è considerata accessibile dall'esterno.
Skipping adding existing rule non è un errore. È il modo in cui UFW segnala che una regola identica esiste già e non la aggiunge di nuovo. Alla riesecuzione della configurazione automatica (che è idempotente) questo è un messaggio normale: non serve intervenire.

10. Installazione di Fail2ban

Blocca automaticamente gli IP dopo il superamento del numero di tentativi di accesso falliti. Analizza i log di SSH, nginx, Apache e altri servizi.

sudo apt install fail2ban sudo systemctl enable --now fail2ban # Verificare lo stato: sudo fail2ban-client status
La configurazione operativa (jail.local con decine di jail e ban automatico da ipsum) è nella sezione successiva.

11. Configurazione operativa Fail2ban + ipsum

L'installazione di base è sopra. Qui trova la configurazione operativa che dà decine di jail attive e migliaia di ban: impostazioni generali, jail chiave e ban automatico degli IP dannosi dalla lista ipsum.

Il file /etc/fail2ban/jail.local — impostazioni generali e le jail più importanti:

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # Ban progressivo: ogni recidiva dura di più bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # ban permanente per brute-force SSH findtime = 3600 # Recidivi: chi ha collezionato più ban viene bannato per sempre [recidive] enabled = true logpath = /var/log/fail2ban.log banaction = %(banaction_allports)s bantime = -1 findtime = 86400 maxretry = 2 [http-get-dos] enabled = true maxretry = 100 findtime = 300 bantime = 1w # Servizi web (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog): [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … e le altre jail per servizio (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) — enabled = true
In ignoreip inserisca tassativamente il suo IP e le reti fidate, altrimenti rischia di bannare se stesso. Dopo le modifiche: sudo fail2ban-client reload.

Caricamento automatico della blocklist ipsum — nel cron di root (sudo crontab -e): il level 1 (100.000+ IP) viene caricato nel set ipsum, che viene tagliato sul firewall (maggiori dettagli nella sezione «Blocklist IPset»):

# 04:00 — aggiornamento ipset ipsum (level 1, copertura massima): 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
Il set deve chiamarsi ipsum — è esattamente quello che legge la dashboard (scheda «IPset ipsum»). Livelli: levels/1.txt — copertura massima, levels/3.txt — più preciso (3+ fonti).

Perché il «Monitor di sicurezza» è diviso in due zone. La protezione lavora su due livelli, e la dashboard non li mescola:

  • Attacchi reali (reattivo) — tutto ciò che ha catturato fail2ban: tentativi di intrusione reali (jail sshd, apache-*, nginx-* ecc.) e recidivi ostinati (jail recidive — quelli già bannati più volte). Sono gli IP che si sono davvero introdotti da lei — sono sulla mappa degli attacchi e sulla «Linea temporale».
  • Blocco preventivo (proattivo) — la blocklist pubblica di IP dannosi noti ipset ipsum, tagliata sul firewall con la regola DROP. Questi indirizzi nella maggior parte dei casi non hanno nemmeno toccato il suo server — vengono tagliati in anticipo; il contatore «IPset ipsum» mostra quanti sono stati bloccati preventivamente.

La differenza è semplice: reattivo — «questi hanno attaccato e sono stati bannati», preventivo — «questi sono stati bloccati prima del tentativo». Prima nel recidive veniva forzata artificialmente la list-3 ipsum (da qui la vecchia divisione «recidive da lista»); ora recidive contiene solo i veri recidivi, mentre il preventivo è interamente sul firewall.

12. Blocklist IPset (ipsum)

ipsum — elenco pubblico di IP dannosi, aggiornato quotidianamente. Il monitor mostra il numero di indirizzi caricati sulla dashboard e sulla mappa degli attacchi e lo considera nel Punteggio di sicurezza (−10 se il set non è caricato).

Variante minima senza fail2ban — un set ipsum separato con blocco tramite iptables:

# Creare il set (una volta sola): sudo ipset create ipsum hash:ip # Script di aggiornamento /usr/local/bin/update-ipsum.sh: #!/bin/bash ipset flush ipsum for ip in $(curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/3.txt); do ipset add ipsum "$ip" 2>/dev/null done iptables -C INPUT -m set --match-set ipsum src -j DROP 2>/dev/null \ || iptables -I INPUT -m set --match-set ipsum src -j DROP # Cron (sudo crontab -e, ogni giorno alle 4:00): 0 4 * * * /usr/local/bin/update-ipsum.sh
La variante avanzata con fail2ban-recidive è nella sezione «Configurazione operativa Fail2ban + ipsum».
ipset risiede in memoria e viene perso al riavvio. Il solo cron quotidiano lascerà il set vuoto dal momento del riavvio fino all'esecuzione successiva (la dashboard mostrerà 0). Caricare il set anche all'avvio — spostare il caricamento in uno script e agganciarlo a @reboot. Allo stesso tempo il comando create … -exist imposta il limite maxelem 300000 (il valore predefinito 65536 — il level 1 non ci sta, si otterrà «Hash is full»):
# /usr/local/bin/load-ipsum.sh #!/bin/bash curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/1.txt \ | grep -v '^#' | sed 's/^/add ipsum /' \ | (echo "create ipsum hash:ip hashsize 131072 maxelem 300000 -exist"; echo "flush ipsum"; cat) \ | ipset restore -exist # sudo crontab -e — ogni giorno alle 04:00 E a ogni avvio: 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
Come funziona con l'installazione automatica. Lo script carica l'elenco completo level 1 (100+ mila IP) nel set ipsum e, se il firewall è gestito dall'installer (VPS nuovo — profili «Completa»/«Alleggerita»), collega il set a UFW con una regola DROP — il traffico da questi IP viene effettivamente bloccato. La regola sta dopo ESTABLISHED,RELATED, quindi le connessioni correnti (incluso il Suo SSH) non si interrompono — vengono bloccate solo le nuove connessioni dall'elenco. Il set viene ripristinato all'avvio del servizio ipsum-load.service prima del firewall (altrimenti UFW non si avvierebbe) e aggiornato dal cron alle 04:00. Su un server già configurato (pannello, firewall proprio) l'installer non tocca il firewall — lì ipsum resta un elenco per la dashboard e la mappa degli attacchi, mentre la regola DROP si aggiunge manualmente se lo si desidera (la variante minima con iptables … --match-set ipsum … -j DROP è sopra). Con l'installazione automatica non serve fare nulla manualmente.

13. Installazione di CrowdSec

Sostituto moderno di Fail2ban con threat intelligence collaborativa: blocchi dalla community più regole proprie. Richiede un bouncer separato per applicare i blocchi al firewall.

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # Bouncer per iptables/nftables: sudo apt install crowdsec-firewall-bouncer-iptables # Verifica lo stato: sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
Lo stato «Non in esecuzione» nel pannello = il servizio è installato, ma il demone non è attivo (il monitor lo verifica tramite systemctl is-active crowdsec). Per avviarlo: sudo systemctl enable --now crowdsec; in caso di crash consultare sudo journalctl -u crowdsec -n 30. La stessa regola vale per qualsiasi servizio in stato «Non in esecuzione» (Suricata, Falco, Monit, MySQL).
«0 scenari» o «0 bouncer» nella dashboard. CrowdSec di serie è quasi vuoto: senza collezioni non rileva nulla, e senza un bouncer registrato i blocchi non vengono applicati al firewall. Installi le collezioni di base e verifichi che il bouncer sia nell'elenco:
# Collezioni di base (Linux + SSH + server web): sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # Il bouncer deve essere nell'elenco e con stato di connessione attivo: sudo cscli bouncers list
Nel log del bouncer stream halted / i blocchi non vengono applicati. È una api-key orfana: il bouncer è stato rimosso da cscli bouncers list, ma la sua vecchia chiave è rimasta in /etc/crowdsec/bouncers/*.yaml. Riregistri il bouncer e inserisca la chiave aggiornata:
sudo cscli bouncers add fw-bouncer # mostrerà la nuova api_key # inserisci questa chiave in api_key: in /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml sudo systemctl restart crowdsec-firewall-bouncer
L'installazione automatica (profilo «Protezione completa») installa da sé le collezioni e registra il firewall-bouncer: manualmente serve solo in caso di installazione manuale o dopo un intervento manuale su CrowdSec.

14. Installazione di AIDE

AIDE (Advanced Intrusion Detection Environment) crea un'istantanea del file system e a ogni controllo segnala le modifiche in /etc, /bin, /usr. Dopo l'installazione è obbligatoria l'inizializzazione del database (aideinit).

sudo apt install aide # Inizializzazione del database (5–15 minuti): sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04: la cartella /var/lib/aide viene creata in modalità 700 (proprietario _aide), # e il pannello (www-data) non vede il database → mostra «Non inizializzata». # Aprire la cartella in attraversamento (i file del database restano 600): sudo chmod 755 /var/lib/aide # Primo controllo CON SCRITTURA nel log che legge il monitor. # Su Ubuntu/Debian aide richiede un --config esplicito (altrimenti «missing configuration»; # il binario aide.wrapper nelle versioni recenti non viene fornito): sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Durante aideinit il terminale resta 5–15 minuti sulla riga Running aide --init... — è normale (hashing dell'intero file system, carico sul disco). Non interrompa con Ctrl+C. Se il processo sembra «bloccato» ma non scrive nulla, potrebbe attendere una risposta a una richiesta nascosta Overwrite existing aide.db.new [Yn]? (prema Y). Per verificare l'attività da un'altra sessione: pgrep -af aide.
Errore aideinit: «21_aide_spamassassin … printf: invalid number» (return code 20) — un bug noto dello snippet di configurazione di AIDE su Ubuntu 22.04. Il database non viene creato. Rimuova lo snippet difettoso e riprova:
sudo mv /etc/aide/aide.conf.d/21_aide_spamassassin /etc/aide/21_aide_spamassassin.disabled sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
Stati nel dashboard. «Non inizializzata» = il pannello non vede il file del database: o aideinit non è stato eseguito, oppure (Ubuntu 24.04) la cartella /var/lib/aide è stata creata in modalità 700 ed è inaccessibile a www-data — si risolve con sudo chmod 755 /var/lib/aide (vedere il blocco sopra). «Nessun controllo eseguito» = il database esiste, ma il controllo non è ancora stato eseguito — non è un errore. I risultati vengono letti dal monitor in /var/log/aide/aide.log.
Controllo periodico → log per il pannello. Lo standard /etc/cron.daily/aide sulle Ubuntu/Debian recenti potrebbe non scrivere /var/log/aide/aide.log nel formato richiesto (e in esse aide.wrapper non esiste più). È più affidabile aggiungere un proprio cron con --config esplicito — scrive il log come root in modalità 644, e il monitor lo legge senza gruppi aggiuntivi:
# sudo crontab -e — controllo giornaliero alle 02:00: 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # Eseguire ora, senza attendere la pianificazione: sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
L'installazione automatica fa già tutto questo: chmod 755 /var/lib/aide e il cron di controllo alle 02:00 — manualmente non serve nulla.
Esegua la prima inizializzazione su un server pulito — prima di installare le applicazioni web. Dopo modifiche legittime ricrei il database: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.

15. Installazione di ClamAV

Scanner antivirus per Linux. Particolarmente utile per controllare /var/www alla ricerca di shell PHP e codice dannoso.

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # Aggiornare il database delle firme: sudo freshclam # Scansionare una cartella manualmente: sudo clamscan -r /var/www --infected
Il demone clamd mostra «Inattivo» dopo enable --now? Tre cause tipiche:

1. Nella configurazione è rimasta la riga Example — clamd rifiuta di avviarsi finché è presente:

sudo sed -i '/^Example/d' /etc/clamav/clamd.conf sudo systemctl restart clamav-daemon

2. Il database delle firme non è stato scaricato — clamd non si avvia senza di esso:

sudo systemctl stop clamav-freshclam sudo freshclam sudo systemctl start clamav-freshclam sudo systemctl restart clamav-daemon

3. Si sta semplicemente caricando — clamd carica in memoria ~8 milioni di firme in 30–60 sec. Attenda e verifichi: systemctl is-active clamav-daemon (stato activating → sta ancora caricando).

Diagnostica: sudo journalctl -u clamav-daemon -n 30 --no-pager.
Sulla dashboard «File controllati: 0» / «Ultima scansione: —»? Il demone clamd tiene solo le firme in memoria, di per sé non scansiona nulla in modo pianificato. Il pannello mostra i risultati di una scansione pianificata, quindi serve un cron che scansiona e scrive un log. L'installazione automatica configura il wrapper /usr/local/bin/clamav-scan.sh e un cron alle 01:30 — dopo la prima esecuzione si popoleranno «File controllati» e «Ultima scansione». Per avviarla subito, senza attendere la pianificazione: sudo /usr/local/bin/clamav-scan.sh.

16. Installazione di Linux Malware Detect (maldet)

Linux Malware Detect (LMD) — scanner di malware per le minacce web: shell PHP, backdoor web, downloader. Usa il motore di ClamAV e lo integra con le proprie firme.

wget https://www.rfxn.com/downloads/maldetect-current.tar.gz tar -xzf maldetect-current.tar.gz dir=$(ls -d maldetect-*/ | head -1) && cd "$dir" && sudo bash install.sh && cd ~ rm -rf maldetect-* maldetect-current.tar.gz # Aggiornare le firme: sudo maldet -u # Scansionare /var/www: sudo maldet -a /var/www
LMD e ClamAV funzionano bene in coppia. Ultimo report: maldet --report.
Durante l'installazione può comparire la riga update-rc.d: error: unable to read /etc/init.d/maldet — è innocua. maldet non usa init.d, l'aggiornamento delle firme e le scansioni vengono avviati tramite /etc/cron.daily/maldet. Se sotto compare installation completed — tutto è installato correttamente.
Nella pagina LMD risulta «Non installato» anche se è installato? maldet non viene installato tramite apt, ma in /usr/local/maldetect, e con open_basedir attivo la sua presenza viene verificata tramite shell — vedere la sezione «La pagina è vuota anche se i dati sono presenti sul server».

17. Installazione di Suricata

Sistema di rilevamento delle intrusioni di rete: analizza il traffico a livello di pacchetti e conosce migliaia di firme di attacco. Completa ModSecurity (che opera a livello HTTP, mentre Suricata a livello TCP/IP).

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # Scaricare le regole aggiornate: sudo suricata-update sudo systemctl enable --now suricata
Suricata è «Attivo», ma il pannello non mostra avvisi / il numero di eventi è 0? Suricata scrive /var/log/suricata/eve.json come root con la directory in modalità 750, e il server web (www-data) non riesce a leggerlo. Apra la directory in attraversamento — i file al suo interno restano protetti:
sudo chmod o+rx /var/log/suricata
L'installazione automatica lo fa da sola — manualmente non serve.

18. Installazione di Falco

Intercetta le chiamate di sistema tramite eBPF/kernel module e rileva anomalie in tempo reale: shell da nginx, lettura di /etc/passwd da parte di un processo web, scrittura in /bin ecc.

curl -fsSL https://falco.org/repo/falcosecurity-packages.asc > /tmp/falco.asc gpg --dearmor < /tmp/falco.asc | sudo tee /usr/share/keyrings/falco-archive-keyring.gpg > /dev/null sudo chmod 644 /usr/share/keyrings/falco-archive-keyring.gpg && rm /tmp/falco.asc echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" \ | sudo tee /etc/apt/sources.list.d/falcosecurity.list sudo apt update && sudo apt install falco sudo systemctl enable --now falco
Il monitor legge gli eventi di Falco tramite journalctl -u falco (senza sudo — tramite il gruppo systemd-journal). Verifichi che www-data sia in questo gruppo — veda «Configurazione di sudo» (punto 2) nella pagina di installazione manuale.
«0 eventi in 24 ore» è normale, non è un errore. Falco è event-driven: resta silenzioso finché tutto è a posto e registra un evento solo in caso di anomalia (shell da un processo web, lettura di /etc/passwd, scrittura nelle directory di sistema). Zero eventi critici in un giorno su un server tranquillo è uno stato sano.
Per il pannello è più affidabile l'output su file. La lettura tramite journalctl richiede i permessi sul registro; affinché il pannello veda gli eventi in modo stabile, l'installazione automatica attiva in Falco file_output/var/log/falco/falco.log e imposta al servizio UMask=0022 (il log è leggibile dal server web). Su una nuova installazione non è necessario configurarlo manualmente.

19. Installazione di ModSecurity (WAF)

ModSecurity — web firewall (WAF) per Apache o Nginx. Blocca gli attacchi a livello applicativo: SQL injection, XSS, path traversal, scanner.

# Apache: sudo apt install libapache2-mod-security2 sudo a2enmod security2 # Set di regole OWASP Core Rule Set: sudo apt install modsecurity-crs # OBBLIGATORIO: senza questo file il motore delle regole è disattivato sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf sudo sed -i 's/^SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf sudo apache2ctl configtest && sudo systemctl reload apache2 # Verifica: deve restituire 403 curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
La sola installazione del pacchetto non protegge nulla. Apache include le configurazioni con la riga IncludeOptional /etc/modsecurity/*.conf, ma il pacchetto installa solo modsecurity.conf-recommended — che non rientra nella maschera *.conf. Se non lo si copia in modsecurity.conf, SecRuleEngine resta Off: il modulo è caricato, le regole CRS sono caricate, ma il traffico non viene controllato e il log di audit non viene creato. La modalità intermedia DetectionOnly si limita a registrare gli eventi nel log senza bloccare le richieste — il pannello la mostra in giallo.

Accesso del pannello al log di audit. Il log /var/log/apache2/modsec_audit.log appartiene a root (permessi 640), l'utente web non può leggerlo. Il pannello ottiene i dati tramite un wrapper — lo crei:

sudo tee /usr/local/bin/monitor-modsec >/dev/null <<'EOF' #!/bin/sh echo "ENGINE=$(grep -hE '^SecRuleEngine[[:space:]]+' /etc/modsecurity/*.conf 2>/dev/null | tail -1 | awk '{print $2}')" echo "---LOG---" tail -n 3000 /var/log/apache2/modsec_audit.log 2>/dev/null echo "---RULES---" for f in /etc/modsecurity/crs/rules/*.conf /usr/share/modsecurity-crs/rules/*.conf /etc/modsecurity/custom-rules.conf; do [ -f "$f" ] && { echo "===FILE:$(basename "$f")==="; cat "$f"; } done EOF sudo chown root:root /usr/local/bin/monitor-modsec sudo chmod 755 /usr/local/bin/monitor-modsec # in /etc/sudoers.d/monitor (utente = quello con cui gira PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
Il wrapper prende l'ultima direttiva SecRuleEngine senza rientro: le righe con rientro si trovano all'interno dei blocchi <LocationMatch>/<Directory> (ad esempio, la disattivazione del WAF per phpMyAdmin) e non definiscono la modalità globale.
L'utente in sudoers deve coincidere con l'utente del pool FPM: su Apache/Debian standard è www-data, in HestiaCP il pool del sito gira con il proprietario del sito (ad esempio, admin) — verifichi con grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.
Se il sito è dietro un proxy Nginx (HestiaCP), Apache vede come client il proxy stesso — il pannello ricava l'IP reale dell'attaccante dall'header X-Forwarded-For. Nelle statistiche rientrano solo le transazioni con una regola attivata: la direttiva SecAuditLogRelevantStatus scrive nel log di audit qualsiasi risposta 4xx/5xx, quindi vi rientrano anche i normali 403/500 — che il pannello non conta come eventi WAF.
Il blocco ---RULES--- serve alla sezione «Tutte le regole attive» — il pannello mostra non solo le regole attivate, ma tutte le regole CRS caricate + quelle personalizzate. I tre percorsi nel ciclo for f in … sono le posizioni tipiche per le regole CRS e le aggiunte locali; se ha una disposizione diversa (il pacchetto installa i file in una propria directory, oppure le regole personalizzate non si trovano in /etc/modsecurity/custom-rules.conf), individui i percorsi reali con il comando sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null e li inserisca nell'elenco. Se il wrapper è vecchio (senza questa sezione) — la sezione mostrerà semplicemente l'avviso «non disponibile», il resto della pagina funziona come prima.

20. Installazione di Auditd

Auditd (Linux Audit Daemon) registra le chiamate di sistema a livello di kernel: accessi e disconnessioni, comandi sudo, tentativi di autenticazione falliti, modifiche ai file. Il monitor mostra gli accessi, i tentativi falliti e i comandi sudo di oggi.

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # Verifica lo stato e gli eventi: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
Il monitor legge gli eventi tramite ausearch (/usr/sbin/ausearch) e, se necessario, da /var/log/audit/audit.log con il comando tail. Entrambi devono essere nel sudoers.

21. Installazione di Monit

Sorveglia i servizi (nginx, php-fpm, mysql, ecc.) e li riavvia in caso di caduta. Può inviare avvisi via email.

sudo apt install monit sudo systemctl enable --now monit # Configurazioni: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
Il monitor ottiene l'elenco dei servizi tramite monit status. In /etc/monit/monitrc deve essere abilitata l'interfaccia HTTP (blocco set httpd con allow localhost), altrimenti monit status restituirà un errore.
Sulla dashboard compare «0 servizi sotto osservazione»? Due cause. (1) L'interfaccia HTTP è disattivata — in monitrc la riga set httpd è commentata (di default appare come # set httpd port 2812 …). Decommenti il blocco e consenta localhost. (2) Un httpd abilitato di per sé non monitora nulla — Monit considera solo ciò che è descritto dagli stanza check; senza di essi l'elenco è vuoto anche con l'interfaccia attiva. Configurazione minima funzionante:
# /etc/monit/conf.d/00-httpd — interfaccia HTTP per localhost: set httpd port 2812 use address localhost allow localhost # esempi di stanza check (cosa monitorare): check process sshd with pidfile /run/sshd.pid start program = "/usr/bin/systemctl start ssh" stop program = "/usr/bin/systemctl stop ssh" check filesystem rootfs with path / if space usage > 90% then alert
sudo monit -t # verifica la sintassi (Control file syntax OK) sudo systemctl reload monit sudo monit status
L'installazione automatica predispone un conf.d pronto con httpd sulla 2812 e una serie di controlli — su una nuova installazione non serve configurare nulla manualmente.
Servizio con stato «Con errori»? Il monitor mostra soltanto lo stato e volutamente non riavvia i servizi dal pannello web (sarebbe un'esecuzione remota di comandi root in un pannello di sicurezza). La diagnostica e il riavvio si effettuano via SSH tramite Monit:
sudo monit status <service> # causa dell'errore sudo monit restart <service> # riavvio tramite Monit # se Monit non avvia il servizio, controlli la sua unit: sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

22. Installazione di PSAD (rilevamento scansione porte)

PSAD analizza il registro di iptables e rileva le scansioni delle porte e gli attacchi di rete, assegnando a ciascuna sorgente un livello di minaccia (1–5). Integra fail2ban e Suricata.

sudo apt install psad # PSAD legge il log di iptables — è necessario abilitare il logging (UFW lo fa da solo). # Per iptables puro, aggiungere le regole LOG alle catene INPUT/FORWARD. sudo psad --sig-update sudo systemctl enable --now psad
Il monitor legge i dati tramite psad --Status (necessario nei sudoers). Senza il logging di iptables la pagina sarà vuota — è normale finché non ci sono state scansioni.

23. AppArmor / SELinux (controllo degli accessi)

Mandatory Access Control limita a quali file e risorse può accedere un programma, anche se viene compromesso. Su Ubuntu/Debian per impostazione predefinita si usa AppArmor (di solito già installato e attivo).

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # verifica i profili
Il Monitor legge lo stato tramite aa-status (necessario in sudoers). Mostra il numero di profili in modalità enforce/complain e i processi senza profilo.

«Profili caricati» in numero maggiore di enforce + complain è normale. In AppArmor 4.x (Ubuntu 24.04 e successivi) è comparsa la modalità unconfined: il profilo è caricato nel kernel, ma non limita nulla. Ubuntu contrassegna così decine di profili per programmi che usano user namespaces (browser, client torrent e simili). Quando esistono profili di questo tipo, la scheda «Profili caricati» diventa ambrata e ne mostra il numero — per esempio unconfined: 90 con 120 caricati e 26 in enforce. Proteggono realmente solo i profili in enforce; su Ubuntu 22.04 (AppArmor 3.x) questa modalità non esiste e i numeri coincidono sempre.

sudo aa-status | grep -E "profiles are" # suddivisione per modalità sudo aa-enforce /etc/apparmor.d/profile-name # porta il profilo in enforce
Portare in enforce i profili che Ubuntu ha lasciato intenzionalmente in unconfined va fatto solo in modo consapevole: sono disattivati non per errore, ma perché altrimenti si comprometterebbe il funzionamento dei programmi stessi. I profili in complain sono un'altra cosa: lì le regole sono già scritte e semplicemente non vengono applicate.

24. Installazione di debsums (integrità dei pacchetti)

debsums verifica che i file dei pacchetti installati corrispondano ai checksum del repository — individua i binari di sistema alterati (completa AIDE). La verifica completa richiede 1–2 minuti, per questo viene avviata via cron, mentre il pannello legge il risultato da data/debsums/debsums.log e lo suddivide automaticamente per categoria (contano solo binari e librerie).

L'attività va nel cron di root (sudo crontab -e). Il wrapper pronto debsums-scan.sh va messo in /usr/local/bin/ (chmod +x; vedere il riepilogo delle attività cron) e scrive automaticamente il report in data/debsums/ del pannello.

sudo apt install debsums # Riga cron (ogni giorno alle 4:30): 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

Il wrapper debsums-scan.sh trova da solo la cartella data/ del pannello — non serve indicare il percorso.

Le modifiche in /etc/ (configurazioni) e /usr/share/ (risorse) sul server sono di solito normali — il pannello le evidenzia con un colore distinto. Sono preoccupanti le modifiche a binari e librerie (/bin, /sbin, /usr/lib ecc.) — la scheda «Binari / librerie» mostra proprio queste.

25. Configurazione dei report Lynis

Lynis si avvia manualmente o tramite cron. Il report deve essere salvato nella cartella data/lynis/ del progetto: il monitor legge il file lynis-report.dat.

# Esecuzione singola (inserisca il percorso alla radice del pannello): sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # Audit giornaliero — riga cron (wrapper pronto lynis-scan.sh in /usr/local/bin/, vedi riepilogo): 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
Dopo la prima esecuzione la pagina «Audit Lynis» mostrerà subito hardening index, avvisi e raccomandazioni.
Pulsante «Avvia audit» nella pagina Lynis. Avvia lynis-scan.sh in background direttamente dal pannello (senza attendere il cron): mostra «Scansione…» e al termine aggiorna da solo il report. Per questo l'utente web ha bisogno di una riga sudoers per eseguire lo script: il programma d'installazione la aggiunge automaticamente in /etc/sudoers.d/monitor. Se il pannello è stato installato manualmente/in precedenza, la aggiunga con lo stesso utente già indicato nel file:
u=$(sudo awk '/NOPASSWD/ && !/lynis-scan/ {print $1; exit}' /etc/sudoers.d/monitor) [ -n "$u" ] && echo "$u ALL=(ALL) NOPASSWD: /usr/local/bin/lynis-scan.sh" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c && sudo chmod 440 /etc/sudoers.d/monitor

26. Configurazione dei report Logwatch

Logwatch deve salvare i report giornalieri nella cartella data/logwatch/ del progetto in formato .txt. Il monitor mostra l'ultimo report e l'archivio.

# Giornaliero (6:00) — riga cron (wrapper pronto logwatch_daily.sh in /usr/local/bin/, vedere il riepilogo): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

Moduli del pannello

27. Monitor di rete (integrato)

Il monitor di rete non richiede installazione: è una pagina integrata nel pannello. Mostra lo stato di rete del server dalle sorgenti locali:

  • interfacce e traffico — da /proc/net/dev;
  • stato dei link (UP/DOWN) e IP — tramite ip;
  • connessioni e porte in ascolto — tramite ss;
  • eventi di rete del kernel nelle ultime 24 h — tramite journalctl -k.

Le prime tre sorgenti funzionano senza sudo, perciò interfacce, traffico, connessioni e porte sono subito visibili. Il blocco «Eventi del kernel» usa journalctl -k, letto tramite il gruppo systemd-journal («Configurazione di sudo», p.2), sudo non serve. Per verificare che tutto sia accessibile all'utente web:

# Verifica come www-data (con cui gira PHP): sudo -u www-data bash -c 'cat /proc/net/dev' sudo -u www-data bash -c 'ip -o link show' sudo -u www-data bash -c 'ss -s' sudo -u www-data journalctl -k --no-pager -n 5
Il blocco «Eventi di rete del kernel» mostra gli eventi dello stack di rete del kernel (cambio link up/down, errori di portante, «network unreachable»). Le voci del firewall UFW BLOCK non compaiono qui: sono nelle pagine «Firewall UFW» e «Mappa degli attacchi». Un blocco vuoto con spunta verde = nessun problema di rete nelle ultime 24 ore.

28. Disco e SMART

La pagina integrata mostra tre cose:

  • File system — riempimento delle partizioni (df); la barra diventa rossa a ≥90%;
  • Unità di archiviazione — elenco dei dischi (lsblk), solo quelli reali (loop/snap nascosti);
  • Salute (SMART) — stato del disco e attributi (smartctl).

Lo spazio e l'elenco dei dispositivi funzionano subito, senza configurazione. Per SMART serve il pacchetto smartmontools. Il processo web non ha accesso diretto ai dispositivi disco, perciò SMART viene rilevato tramite cron nel file data/disk/smart.txt, e il pannello lo legge.

L'attività va nel cron di root (sudo crontab -e). Il wrapper pronto smart-scan.sh va messo in /usr/local/bin/ (chmod +x; vedi il riepilogo delle attività cron) e scrive da solo in data/disk/ del pannello.

sudo apt install smartmontools # Riga cron (ogni 30 minuti): */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

Il wrapper smart-scan.sh trova da solo la data/ del pannello — non serve indicare il percorso. Al suo interno lsblk -e7,11 esclude loop/cdrom.

Sui dischi virtuali (QEMU/KVM e simili) di solito è disponibile solo lo stato generale «salute: OK», mentre temperatura, ore di funzionamento e settori riallocati possono risultare vuoti — è normale. Su un server fisico vengono mostrati tutti gli attributi.

29. Prestazioni (CPU/RAM/Rete/Disco)

La pagina mostra lo storico del carico del server nelle ultime 24 ore — Load Average, utilizzo CPU e attesa I/O, RAM/Swap, traffico di rete (ricezione/trasmissione), I/O del disco (lettura/scrittura), riempimento del disco e degli inode, descrittori di file aperti e connessioni MySQL, oltre al numero corrente di connessioni TCP e processi.

I dati vengono raccolti da cron/collect_metrics.php — una volta ogni 5 minuti scrive un'istantanea «grezza» dei contatori (/proc/loadavg, /proc/meminfo, /proc/stat, /proc/net/dev, /proc/diskstats, df/df -i, /proc/sys/fs/file-nr, SHOW GLOBAL STATUS LIKE 'Threads_connected') nella tabella del database system_metrics; le percentuali e le velocità le calcola la pagina stessa dalla differenza tra istantanee adiacenti (riempimento disco/inode/descrittori/connessioni MySQL sono valori istantanei, senza ricalcolo). Non serve sudo — le fonti vengono lette senza privilegi di root. I punti più vecchi di 24 ore vengono eliminati automaticamente a ogni scrittura.

# Riga Cron (ogni 5 minuti): */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

Il wrapper collect-metrics-all.sh (vedere il riepilogo delle attività cron) individua da solo tutte le istanze del pannello installate sul server ed esegue cron/collect_metrics.php di ciascuna a nome del proprietario del sito.

Finché il collettore non ha eseguito almeno due volte (i primi ~10 minuti dopo l'installazione), la pagina mostra «raccolta dati in corso» — ai grafici serve almeno una coppia di punti adiacenti per calcolare velocità e percentuali.

Avvisi di carico (sezione «Impostazioni» → «Avvisi di carico») — al superamento della soglia di CPU/RAM/disco/inode il pannello invia una notifica su Telegram/Email (gli stessi canali del report giornaliero — non è necessario attivarli separatamente per gli avvisi), e un'altra — quando la metrica torna nella norma. Non invia spam ripetuto mentre la soglia rimane superata: la notifica successiva arriva solo dopo un ciclo «ripristinato → di nuovo superato».

Le soglie vengono verificate dallo stesso collect_metrics.php a ogni esecuzione (una volta ogni 5 minuti) — non serve un cron separato. Lo stato «già notificato / non ancora» è memorizzato in data/alerts_state.json, le soglie nelle impostazioni del pannello.

30. Mappa degli attacchi (GeoIP)

La pagina «Mappa degli attacchi» determina il paese in base all'IP con il comando geoiplookup. Senza il pacchetto GeoIP i paesi non vengono rilevati e i punti sulla mappa non compaiono:

sudo apt install geoip-bin geoip-database # Verifica: geoiplookup 8.8.8.8
Sudo non è necessario: il database /usr/share/GeoIP/GeoIP.dat è leggibile da tutti, i risultati vengono memorizzati nella cache in tmp/geoip_cache.json. La mappa stessa (Leaflet + tile di OpenStreetMap) viene caricata nel browser: serve una connessione a internet sul computer da cui è aperto il pannello.

31. Esposizione esterna, aggiornamenti e aggiornamenti automatici

Due schede integrate della dashboard che mostrano non lo stato «on/off» di uno strumento, ma la reale sicurezza del server. Non richiedono installazione, vengono lette localmente senza sudo.

Esposizione esterna — quanti servizi ascoltano su tutte le interfacce (0.0.0.0/[::]) e sono raggiungibili dall'esterno. Evidenzia in rosso se all'esterno sono esposti DBMS o cache (MySQL, PostgreSQL, Redis, MongoDB, Memcached, Elasticsearch) — è una falla diretta (−10 al Punteggio di sicurezza). Fonte: ss -tuln.

Se la scheda è rossa, chiuda i DBMS verso l'esterno: li associ a 127.0.0.1 (bind-address nella configurazione MySQL/PostgreSQL, bind 127.0.0.1 in Redis) oppure chiuda la porta in UFW.
«Porta aperta» ≠ «raggiungibile dall'esterno». Un servizio che ascolta su 127.0.0.1 (loopback) è visibile solo al server stesso — dall'esterno non è raggiungibile, anche se la porta è «aperta». Per questo Postfix sulla porta 25, associato al loopback, è sicuro: la configurazione automatica imposta inet_interfaces = loopback-only (più un smtpd_banner neutro — che risolve il rilievo Lynis MAIL-8818 sulla divulgazione della versione). La scheda «Esposizione esterna» conta come esterno solo ciò che ascolta su 0.0.0.0/[::]; i servizi loopback non vi rientrano.
Lynis MAIL-8818 manualmente (se ha configurato la posta da sé): in /etc/postfix/main.cf imposti smtpd_banner = $myhostname ESMTP (senza versione né sistema operativo) e inet_interfaces = loopback-only, poi sudo systemctl restart postfix.

Aggiornamenti di sicurezza — quante patch di sicurezza attendono l'installazione e se è necessario un riavvio dopo l'aggiornamento del kernel (−5 al Punteggio di sicurezza in presenza di patch). Fonte: /usr/lib/update-notifier/apt-check, file /var/run/reboot-required. L'elenco dettagliato è nella pagina «Aggiornamenti di sicurezza».

# Installare gli aggiornamenti: sudo apt update && sudo apt upgrade # Verificare cosa ascolta verso l'esterno: ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
La scheda degli aggiornamenti funziona su Ubuntu/Debian (update-notifier-common). Se apt-check è assente, il monitor conta le patch tramite apt-get -s upgrade.

Aggiornamenti di sicurezza automatici (unattended-upgrades) — nella pagina «Aggiornamenti di sicurezza» una scheda dedicata mostra se l'installazione automatica delle patch di sicurezza è attiva e quando è stata eseguita l'ultima volta. Non serve sudo — lo stato viene letto tramite apt-config dump.

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # abilitare # Verificare cosa è abilitato: apt-config dump | grep Unattended-Upgrade

Manutenzione

32. Backup

Il backup è l'assicurazione principale: la perdita di dati è peggiore di qualsiasi attacco. Servono due cose — un backup del server/dei siti e separatamente un backup del DB del pannello (contiene utenti, chiavi WebAuthn, impostazioni, licenza).

Opzione A — HestiaCP: scheda Backup dell'utente → pulsante di creazione del backup (o pianificato nelle impostazioni del server). Il backup include i siti e i loro DB.

Opzione B — manuale (cron): dump del DB + archivio della cartella data/ del pannello:

# cron di root (sudo crontab -e) — backup giornaliero alle 2:30 (inserisca i propri nomi/percorsi): 30 2 * * * mysqldump -u root MY_DB | gzip > /var/backups/monitor-db-$(date +\%F).sql.gz 40 2 * * * tar czf /var/backups/monitor-data-$(date +\%F).tar.gz -C /path/to/monitor data # Eliminare gli archivi più vecchi di 14 giorni: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
Un backup sullo stesso server protegge dagli errori, ma non dalla perdita del server. Copi gli archivi su uno storage esterno (un altro server, S3, rclone nel cloud). Verifichi che il ripristino funzioni davvero.

33. Aggiornamento e migrazione del pannello

Aggiornamento a una nuova versione. Per prima cosa esegua un backup. Poi ricarichi i file del codice, conservando i suoi dati:

  • sovrascrivere (codice): public/, includes/, assets/, cron/, database/, oltre ai file .htaccess nella radice (front controller — il routing non può essere lasciato dalla versione precedente), manifest.json, sw.js;
  • non toccare: config.php (dati del DB), data/ (report), logs/, tmp/ (sessioni e cache).
# Dopo il caricamento — svuotare la cache PHP (se opcache è attivo): sudo systemctl reload php*-fpm
FileZilla segnala SSH_FX_PERMISSION_DENIEDPermission denied. I file del pannello appartengono a www-data (così sono stati impostati durante l'installazione), mentre il client SFTP si collega con il suo utente, che non ha il permesso di scrittura. Assegnare l'intero pannello a www-data «perché funzioni» è proprio ciò che porta a questo errore; qui sotto tre metodi, ognuno risolve il problema.
# Variante A (consigliata) — separare i proprietari: il codice è suo, le cartelle di lavoro del web server. # Il web server non ottiene alcun permesso di scrittura sul CODICE del pannello: sudo chown -R deploy:www-data /path/to/monitor sudo chown -R www-data:www-data /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo find /path/to/monitor -type d -exec chmod 755 {} \; sudo find /path/to/monitor -type f -exec chmod 644 {} \; sudo chmod 750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo chmod 640 /path/to/monitor/config.php # Variante B — ACL sopra i proprietari attuali (senza spostare nulla): sudo apt install -y acl sudo setfacl -R -m u:deploy:rwX /path/to/monitor sudo setfacl -R -d -m u:deploy:rwX /path/to/monitor # Variante C — tramite il gruppo www-data. Più semplice, ma la scrittura sui file # del pannello la ottiene anche il web server (con una falla in PHP il codice è sostituibile): sudo usermod -aG www-data deploy sudo find /path/to/monitor -type d -exec chmod 2775 {} \; sudo find /path/to/monitor -type f -exec chmod 664 {} \; sudo chmod 640 /path/to/monitor/config.php sudo chmod 2750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs
Perché la variante A è sicura. Il pannello scrive solo in tre cartelle — data/ (report), tmp/ (sessioni e cache), logs/; restano di proprietà di www-data. Il resto è codice, e al web server serve solo in lettura, garantita dal gruppo www-data con permessi 644. Vantaggio secondario: con una falla in PHP i file del pannello non sono più sovrascrivibili. Sui pannelli di hosting (HestiaCP e simili) la variante A non serve: lì i file del sito appartengono già all'account con cui accede via SFTP, e il web server li legge tramite il gruppo.
Trappola della variante B: qualsiasi chmod successivo sui file azzera la maschera ACL e l'accesso sparisce silenziosamente. Se dopo aver «rimesso ordine nei permessi» il caricamento si scontra di nuovo con Permission denied, ripeta entrambi i comandi setfacl.
Il bit 2 della variante C è setgid: i file caricati via SFTP restano nel gruppo www-data, altrimenti il pannello non potrà sovrascriverli. Dopo la variante C si ricolleghi in FileZilla: il nuovo gruppo ha effetto solo a un nuovo accesso. Verifica: id deploy (deve comparire il gruppo www-data) e ls -ld /path/to/monitor (drwxrwsr-x: la lettera s indica che setgid è attivo).

Migrazione su un altro server:

  1. Sul nuovo server configuri il sito + HTTPS (veda la pagina dell'installazione manuale).
  2. Copi tutti i file del pannello insieme a config.php, data/.
  3. Migri il DB: mysqldump sul vecchio → importazione sul nuovo; corregga i dati del DB in config.php.
  4. Ripeta sul nuovo server: sudoers, appartenenza al gruppo adm, attività cron.
  5. La licenza è associata al dominio — se il dominio è lo stesso, la chiave continuerà a funzionare.

34. Ripristino dell'accesso (chiave persa, password, blocco IP)

Se non riesce ad accedere, tutto si risolve direttamente nel DB dal server. Apra il DB (il nome è in config.php):

sudo mysql MY_DB

Chiave WebAuthn persa (il secondo fattore non funziona): disattivi la 2FA, acceda con la password e registri una nuova chiave:

UPDATE users SET webauthn_enabled = 0;

Password dimenticata: imposti un nuovo hash (lo generi sul server e lo inserisca):

# Generare l'hash della nuova password: php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # Nel DB (inserisca l'hash ottenuto): # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

Si è bloccato da solo con il filtro IP: disattivi la limitazione:

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
L'accesso al DB è sempre disponibile: sudo mysql sul server, oppure phpMyAdmin / la sezione DB nel pannello dell'hosting. Dopo il ripristino, riattivi WebAuthn e il filtro IP.

35. Tutti i cron job in un unico posto

Il riepilogo dei job è nel crontab di root del server (si aggiungono con sudo crontab -e). Lasci solo le righe degli strumenti che utilizza; adatti i percorsi al suo server.

# Cron del monitor lato server (root) — inserire con: sudo crontab -e # 01:30 — scansione ClamAV sui percorsi a rischio (web, home, temp) → schede «File controllati» e «Ultima scansione» 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — verifica dell'integrità dei file AIDE (serve --config esplicito) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # all'avvio — ripristina i permessi di /var/lib/aide (il file tmpfiles del pacchetto # aide-common.conf li reimposta a 0700 e il pannello smette di vedere il database) @reboot chmod 755 /var/lib/aide # 03:00 — audit di sicurezza Lynis 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — aggiornamento della lista di blocco ipsum (level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # all'avvio il set ipsum viene caricato dal servizio ipsum-load.service (PRIMA del firewall, altrimenti # UFW non vedrà il set in before.rules) — non è cron. Qui c'è solo il refresh giornaliero qui sopra. # 06:00 — report Logwatch 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # ogni 30 min — controllo SMART dei dischi */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — integrità dei pacchetti debsums 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — report pianificato su Email e Telegram 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # ogni ora — aggiornamento degli elenchi dei pacchetti (per la scheda «Aggiornamenti di sicurezza») 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # ogni 5 min — snapshot delle risorse (CPU/RAM/rete/disco) per la pagina «Prestazioni» */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1
I dettagli di ciascuno sono nelle rispettive sezioni. I job di backup (sezione precedente) si aggiungono a questo stesso cron. Dopo le modifiche verifichi: sudo crontab -l e che il servizio cron sia attivo.
L'orario di cron = fuso orario del server, non TIMEZONE di config.php. La costante TIMEZONE influisce solo su PHP (su come il pannello mostra le date), ma il demone cron esegue i job secondo l'ora di sistema del SO. Se il fuso del server non coincide con il suo, il report delle «08:00» arriverà a un orario sbagliato. Esempio: il server è in un altro fuso (Europe/London, UTC+1) e Lei è a Roma (UTC+2) → il report delle «08:00» arriverà alle 09:00 ora sua. Verifichi e, se necessario, allinei il fuso del sistema al suo:
# Verificare il fuso orario attuale del server: timedatectl # Impostare il proprio fuso (esempio) e riavviare cron: sudo timedatectl set-timezone Europe/Rome sudo systemctl restart cron
Dopodiché la riga 0 8 * * * scatterà alle 08:00 ora locale. Altrimenti bisognerebbe spostare cron stesso, ma con il passaggio ora solare/legale lo scostamento tornerebbe a disallinearsi — perciò è più corretto configurare il fuso del sistema.
Script wrapper pronti. Le loro copie funzionanti e un crontab di esempio (crontab.txt) si trovano nella cartella system/ accanto al progetto, fuori da public_html. Non fanno parte del sito — non serve caricarli nella web root; li collochi sul server nei percorsi di sistema (come nel crontab sopra):
  • lynis-scan.sh/usr/local/bin/ (chmod +x) — esegue lynis audit system, durante la scansione imposta il flag /tmp/lynis-running e copia lynis-report.dat in data/lynis/ del pannello;
  • logwatch_daily.sh/usr/local/bin/ (chmod +x) — genera il report giornaliero Logwatch (sshd, fail2ban, sudo, postfix) in data/logwatch/;
  • smart-scan.sh/usr/local/bin/ (chmod +x) — rileva lo stato dei dischi (smartctl) in data/disk/;
  • debsums-scan.sh/usr/local/bin/ (chmod +x) — verifica l'integrità dei pacchetti (debsums) in data/debsums/;
  • clamav-scan.sh/usr/local/bin/ (chmod +x) — scansione antivirus ClamAV sui percorsi a rischio (web, home, temp); scrive il riepilogo in /var/log/clamav/scan.log, da dove lo legge la pagina ClamAV (riga 01:30 nel crontab sopra);
  • load-ipsum.sh/usr/local/bin/ (chmod +x) — aggiorna il set ipset ipsum (level 1) sul posto, senza rompere le regole attive del firewall (riga 04:00 nel crontab qui sopra);
  • daily-report-all.sh/usr/local/bin/ (chmod +x) — avvia il report cron/daily_report.php del pannello (riga 08:00 nel crontab sopra);
  • daily_report.php — è già incluso nel pannello (cron/daily_report.php), viene eseguito da daily-report-all.sh, non serve installarlo a parte;
  • collect-metrics-all.sh/usr/local/bin/ (chmod +x) — avvia cron/collect_metrics.php del pannello (pagina «Prestazioni», riga */5 nel crontab sopra); collect_metrics.php è già incluso nel pannello, non serve installarlo a parte;
  • crontab.txt (system/cron/) — esempio di job; inserisca le righe necessarie con sudo crontab -e.
Il percorso dello script nel crontab deve coincidere con quello in cui l'ha collocato.
Come mettere lo script in /usr/local/bin/. Direttamente da FileZilla non è possibile scriverci — la cartella appartiene a root e il client SFTP riceverà SSH_FX_PERMISSION_DENIED. La procedura è questa: prima caricare il file in /tmp (dove tutti possono scrivere), poi spostarlo al suo posto con un solo comando:
# in FileZilla: nel campo «Sito remoto» inserire /tmp e caricarci lo script, # poi via SSH (install imposta subito proprietario e permessi, chown/chmod non servono): sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # verifica: il file è al suo posto, permessi rwxr-xr-x, sintassi integra bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
Non confonda le cartelle: serve /tmp nella radice del server — non /var/tmp e non tmp/ all'interno del pannello stesso (quest'ultimo appartiene a www-data ed è inaccessibile al suo utente). Nell'albero di FileZilla /tmp è un ramo di livello superiore, accanto a var, non al suo interno.
Ha installato il server con la configurazione automatica? Questi wrapper e i relativi cron job sono già installati dallo script (in /usr/local/bin/, log — /var/log/arciveo-cron.log) — non serve fare nulla manualmente.
Dove gli script cercano il pannello. I wrapper sono indipendenti dal dominio: trovano le installazioni del pannello scandendo /home/*/web/*/public_html e /var/www/*, e collocano i report nei rispettivi data/. Se il pannello si trova in un altro percorso, lo aggiunga alla riga for app in … all'interno degli script, altrimenti i report Lynis/SMART/debsums/Logwatch non arriveranno al pannello.
cron.log e permessi di accesso. Il file logs/cron.log viene creato per primo dal crontab di root — apparterrà a root, e la scheda «Registro cron» nel pannello non potrà né leggerlo né svuotarlo. Crei il file in anticipo come utente web (proprietario della cartella del sito; su HestiaCP è l'account, ad es. admin) — così il crontab di root si limiterà ad accodare, senza cambiare il proprietario:
# creare in anticipo come utente web (prima di aggiungere le righe cron): sudo -u OWNER touch /path/to/monitor/logs/cron.log # se cron.log è già stato creato dal crontab di root — assegnarlo all'utente web: sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
Per conoscere il proprietario della cartella: stat -c %U /path/to/monitor.
Gestione dal pannello. Nella sezione «Sistema» c'è la pagina «Crontab» — è possibile vedere e aggiungere job senza SSH. Il pannello modifica solo i job aggiunti tramite esso (un blocco separato nel crontab di root, contrassegnato da commenti di servizio); tutto ciò che è già presente nel crontab (l'elenco sopra) è mostrato lì in sola lettura nell'elenco «Altri job del server» con il pulsante «Copia nell'editor» — che si limita a trasferire pianificazione/comando nel modulo di aggiunta, senza toccare la riga originale. Per «portare» un job esistente sotto la gestione del pannello, lo copi nell'editor, salvi e poi elimini la vecchia riga manualmente (sudo crontab -e), altrimenti verrà eseguito due volte.
Configurazione una tantum sul server. Alla pagina serve uno script wrapper privilegiato — non un semplice sudo crontab (sarebbe un'escalation diretta a root per chiunque acceda alla sessione del pannello), ma uno script ristretto con due comandi (list/set) che tocca solo il proprio blocco tra i commenti di servizio. Lo installi una volta sola:
sudo install -m 0755 -o root -g root /dev/stdin /usr/local/sbin/arciveo-cron <<'ARCIVEO_CRON_EOF' #!/bin/bash set -euo pipefail BEGIN='# >>> ARCIVEO-CRON-MANAGED (edited from the panel; do not edit by hand) >>>' END='# <<< ARCIVEO-CRON-MANAGED <<<' cmd=${1:-} case "$cmd" in list) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} list" >&2; exit 2; } crontab -l -u root 2>/dev/null || true ;; set) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} set (body on stdin)" >&2; exit 2; } body=$(cat) if grep -qF "$BEGIN" <<<"$body" || grep -qF "$END" <<<"$body"; then echo "invalid body: markers not allowed" >&2; exit 2 fi current=$(crontab -l -u root 2>/dev/null || true) tmp=$(mktemp); trap 'rm -f "$tmp"' EXIT { if grep -qF "$BEGIN" <<<"$current"; then awk -v b="$BEGIN" '{print} $0==b{exit}' <<<"$current" else [ -n "$current" ] && printf '%s\n' "$current" echo "$BEGIN" fi printf '%s\n' "$body" echo "$END" if grep -qF "$END" <<<"$current"; then awk -v e="$END" 'f{print} $0==e{f=1}' <<<"$current" fi } > "$tmp" crontab -u root "$tmp" ;; *) echo "usage: ${0##*/} <list|set>" >&2; exit 2 ;; esac ARCIVEO_CRON_EOF echo "www-data ALL=(ALL) NOPASSWD: /usr/local/sbin/arciveo-cron" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c
L'utente web può essere diverso da www-data — verifichi con quale utente gira il pool PHP-FPM del sito (ps -o user= -C php-fpm) e lo sostituisca nella riga sudoers.
Il nuovo file è stato caricato con un proprietario sbagliato — la pagina risponde «Access denied.». Se il file public/crontab_monitor.php è stato caricato via FTP/SFTP con un utente di sistema diverso (ad esempio root) rispetto agli altri file del sito, il web server non potrà leggerlo. Confronti proprietario e permessi con un file vicino e li allinei:
ls -la public/crontab_monitor.php public/ssl_monitor.php sudo chown OWNER:OWNER public/crontab_monitor.php sudo chmod 644 public/crontab_monitor.php

Diagnostica

36. Lo strumento è installato, ma indica «Non installato»

Il monitor rileva la presenza degli strumenti tramite dpkg-query, il database dei pacchetti APT. Se lo strumento non è stato installato con apt (manualmente, da snap o dai sorgenti), dpkg non lo rileva.

# Verifica tramite dpkg: dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # Trova il percorso del binario: which ufw fail2ban-client auditctl # Test sudo da www-data: sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

37. Risoluzione dei problemi (500, dati assenti)

Errore 500 — controlli i log di PHP, nginx e del monitor stesso:

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # Log del monitor: tail -50 logs/monitor_$(date +%Y-%m-%d).log # Permessi sulle cartelle: ls -la data/ tmp/ logs/
Dashboard su un pannello di hosting (HestiaCP, ISPmanager, cPanel)? Lì PHP non gira come www-data, ma come account dell'utente (ad esempio admin — proprietario della directory del sito). Tutte le regole sudo e le appartenenze ai gruppi (adm, systemd-journal) vanno impostate su questo utente, altrimenti i moduli mostreranno «Inattivo / 0» pur con i servizi in esecuzione. Per individuare l'utente reale di PHP: ps -o user= -C php-fpm | sort -u oppure il proprietario della directory del sito stat -c '%U' /path/to/monitor. Poi, in tutti i comandi qui sotto, lo sostituisca a www-data. L'installazione automatica rileva da sé l'utente web e vi imposta i sudoers.

I dati non vengono visualizzati — quasi sempre sono permessi sudo non impostati. Controlli il comando specifico come utente web (sostituisca www-data con il suo). Il flag -n = senza password, come PHP — se chiede la password, significa che la regola nei sudoers non c'è:

sudo -u www-data sudo -n fail2ban-client status sudo -u www-data sudo -n ufw status verbose sudo -u www-data sudo -n ipset list -t ipsum sudo -u www-data sudo -n /usr/sbin/ausearch -m USER_LOGIN -ts today sudo -u www-data sudo -n /usr/sbin/aa-status sudo -u www-data sudo -n /usr/sbin/psad --Status sudo -u www-data journalctl -u falco --no-pager -n 5
Il modulo indica «Inattivo» / «0», sebbene lo strumento funzioni (ad esempio sudo aa-status nel terminale mostra i profili, ma la pagina «AppArmor» mostra «Inattivo»). Motivo: l'utente web non ha il permesso sudo per il comando di quel modulo. Lo verifichi dall'elenco qui sopra: se chiede la password, aggiunga la riga mancante a /etc/sudoers.d/monitor («Configurazione di sudo»). Comandi «nuovi» frequenti: /usr/sbin/aa-status (MAC), /usr/sbin/psad --Status (PSAD).
Se una pagina specifica (Falco, ModSecurity, Auditd, porte aperte UFW) è vuota, la confronti con l'elenco nella sezione su sudo: probabilmente non è consentito apache2ctl, ausearch, aa-status o ss, oppure l'utente web non è nei gruppi adm/systemd-journal (da lì si leggono i log di fail2ban/auth/modsec e journalctl — Falco ed eventi del kernel).

38. La pagina è vuota, anche se sul server i dati ci sono

Sintomo: sul server i dati sono presenti (visibili via shell), ma la pagina mostra «nessun dato» o uno stato errato — ad esempio AIDE riporta «Non inizializzata», anche se il database è stato creato.

La causa è open_basedir: molte pannelli e hosting limitano il pool PHP-FPM alla directory del dominio, perciò le funzioni PHP file_exists(), file_get_contents(), filemtime() sui percorsi di sistema (/var/lib/aide, /var/log, /proc…) vengono bloccate. Il monitor lo aggira leggendo tali percorsi con i comandi di sistema nativi (cat, test, stat).

# Il file è visibile via shell (così legge il monitor): sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # Valore attuale di open_basedir per il pool del dominio: grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
Se la shell «vede» il file (VISIBLE) ma la pagina no — è open_basedir. La soluzione corretta è la lettura tramite comandi di sistema (già fatta per AIDE e il Monitor di rete). Estendere open_basedir a /var, /proc non è necessario ed è meno sicuro.

39. La pagina SSL non funziona

Il monitor verifica i certificati collegandosi ai domini direttamente sulla porta 443. Se il dominio non è raggiungibile dal server stesso o la porta è chiusa dal firewall, il controllo non andrà a buon fine.

# Verificare il certificato manualmente: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # Verificare la raggiungibilità: curl -I https://monitor.example.com
Il monitor rileva automaticamente i domini dai file di configurazione di nginx (/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) e Apache (/etc/apache2/sites-enabled/), oltre all'host corrente da HTTP_HOST.
Rilevamento automatico dei sottodomini. I sottodomini vengono rilevati automaticamente dai log pubblici di Certificate Transparency e verificati in rete, anche se ospitati su altri server. Non è necessario aggiungere nulla manualmente.

40. È visibile solo un database su più esistenti

Il monitor si connette a MySQL con l'utente definito in config.php, che ha accesso solo al proprio database. MySQL mostra in information_schema solo i database su cui l'utente ha privilegi — perciò gli altri non sono visibili.

Affinché il monitor veda tutti i database, conceda a questo utente il permesso di sola lettura (una volta sola come root; sostituisca il nome utente da config.php):

sudo mysql -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* concede solo il permesso di lettura — non è possibile modificare, eliminare o creare nulla, quindi è sicuro per il monitoraggio.
Senza questo GRANT il pannello vede solo il proprio database — non è un errore, ma una limitazione dei permessi. Il pannello non usa alcun sudo mysql: l'elenco dei database viene ottenuto tramite la sua stessa connessione PDO.

41. PostgreSQL non compare nella pagina «Database»

PostgreSQL richiede l'accesso a livello dell'utente postgres, di cui l'utente web del pannello non dispone. Esporre da PHP un ampio sudo psql non è sicuro: il pannello richiama invece un wrapper ristretto senza parametri, che stampa soltanto la versione, il numero di connessioni e l'elenco dei database con le relative dimensioni. Crealo:

sudo tee /usr/local/bin/monitor-pgstat >/dev/null <<'EOF' #!/bin/sh # Arciveo Monitor - read-only PostgreSQL version, connections and per-database size sudo -u postgres psql -tAc "SELECT version();" | grep -oE 'PostgreSQL [0-9.]+' echo "---" sudo -u postgres psql -tAc "SELECT count(*) FROM pg_stat_activity;" echo "---" sudo -u postgres psql -tAc "SELECT datname || '|' || pg_size_pretty(pg_database_size(datname)) FROM pg_database WHERE datistemplate = false;" EOF sudo chown root:root /usr/local/bin/monitor-pgstat sudo chmod 755 /usr/local/bin/monitor-pgstat # in /etc/sudoers.d/monitor (utente = quello con cui gira PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
Se non utilizza PostgreSQL, rimuova la riga monitor-pgstat da sudoers (passo 13 dell'installazione manuale) e non crei lo script: la scheda PostgreSQL resterà semplicemente inattiva.

42. È scattato un avviso — cosa fare

Il pannello mostra cosa sta accadendo; qui sotto — cosa fare nelle situazioni tipiche. Principio generale: niente panico, verificare con l'attività legittima (le sue azioni, gli aggiornamenti, i backup) e reagire in base alla gravità.

  • Mappa degli attacchi / molti ban fail2ban — è normale per qualsiasi server in internet (i bot tentano continuamente SSH/web). L'importante è che i ban scattino. Si assicuri che l'accesso SSH avvenga solo tramite chiave (password disattivata) e che il suo IP sia in ignoreip.
  • ModSecurity ha bloccato delle richieste — il WAF respinge gli attacchi al sito, è il suo compito. Se viene bloccato il suo traffico legittimo (falso positivo), individui il rule id nei dettagli e aggiunga un'eccezione nella configurazione CRS.
  • AIDE: file modificati — confronti l'elenco con ciò che ha fatto (aggiornamento pacchetti, modifica configurazioni — normale). Le modifiche ai binari di sistema che non ha toccato sono un motivo di allarme. Dopo modifiche legittime, aggiorni il database AIDE.
  • debsums: binari/librerie modificati (fuori da /etc, fuori da /usr/share) — potenziale manomissione. Verifichi il pacchetto: debsums PACKAGE_NAME, in caso di dubbio lo reinstalli (apt install --reinstall).
  • ClamAV / maldet: minaccia rilevata — controlli il file in quarantena, non lo apra. Se è una web shell nella directory del sito, isoli il server e cerchi il punto di ingresso (plugin vulnerabile, fuga di credenziali).
  • Falco: eventi critici (avvio di una shell in un container, accesso a file sensibili) — analizzi l'evento: di chi è il processo, cosa lo ha avviato. Spesso si tratta di attività amministrativa legittima.
  • Esposizione esterna: DBMS/cache in rosso — chiuda subito: associ il servizio a 127.0.0.1 oppure chiuda la porta in UFW. È una falla reale.
  • SSL in scadenza / scaduto — rinnovi il certificato (Let's Encrypt si rinnova da solo; in caso contrario, verifichi certbot renew o le impostazioni nel pannello).
  • Aggiornamenti di sicurezza in attesa — li installi: sudo apt update && sudo apt upgrade; dopo l'aggiornamento del kernel, riavvii il server.
Segni di una reale compromissione (processi/utenti sconosciuti, binari modificati, spam in uscita, attività cron sconosciute): scolleghi il server dall'accesso esterno, salvi un backup per l'analisi e, se i dati sono critici, avvii un server pulito da un backup attendibile — rimuovere un rootkit in modo sicuro è difficile.
Arcivéo - Security Monitor © 2026