FAQ

Ce guide couvre l'installation, la configuration et la maintenance d'Arcivéo Monitor. Les sections sont regroupées : présentation générale, déploiement du tableau de bord, connexion des outils de sécurité, modules intégrés et diagnostic. Les commandes se copient via le bouton à droite.

Pour commencer

01. Installation du panneau — choisissez une méthode

L'installation du panneau se fait sur des pages dédiées, étape par étape. Choisissez une méthode :

En cas de doute, choisissez la méthode automatique. Ce guide reste la référence unique pour le SSL, les outils, cron et le diagnostic — les pages d'installation renvoient à ses sections sans rien dupliquer.

Aperçu

02. Qu'est-ce qu'Arcivéo Monitor

Arcivéo Monitor est un tableau de bord de sécurité serveur. Il collecte les données des outils installés (Fail2ban, UFW, Lynis, ModSecurity, AIDE, ClamAV, Auditd, CrowdSec, Suricata, Falco, etc.) et les affiche dans une interface unifiée avec tableau de bord, carte des attaques et pages détaillées par outil.

Le moniteur n'est pas un outil de protection active — il ne bloque pas les attaques lui-même. Son rôle est d'agréger les informations des outils déjà en place et de les présenter de façon claire.

03. Fonctionnement du moniteur sur le serveur

Le moniteur fonctionne uniquement en local — il doit être installé sur le serveur qu'il surveille. Aucun SSH ni API distante.

Le tableau de bord exécute toutes les commandes (fail2ban-client, ufw status, ipset list, etc.) sous l'utilisateur du serveur web (généralement www-data, sur les panneaux d'hébergement — le compte du site) avec un jeu de droits restreint sudo — limité à des utilitaires précis, sans accès root global. Les résultats sont analysés et affichés dans le navigateur.

Pour plusieurs serveurs, installez le moniteur séparément sur chacun, avec un domaine unique.

04. Comment est calculé le score de sécurité

Le score part du maximum et diminue à chaque problème détecté :

  • UFW inactif — −30
  • Fail2ban non démarré (aucune jail active) — −25
  • Aucune clé WebAuthn — −15
  • Lynis hardening index < 60 — −20 ; 60–79 — −10
  • IPset ipsum non chargé — −10
  • Menaces détectées par ClamAV — −20
  • Modifications de fichiers AIDE — −15
  • SSL expiré — −30, expire dans <14 jours — −15, <30 jours — −5
  • CrowdSec installé mais non démarré — −5
  • Suricata installée mais non démarrée — −5
  • SGBD/cache (MySQL, PostgreSQL, Redis…) accessibles depuis l'extérieur — −10
  • Connexion root autorisée via SSH (PermitRootLogin yes) — −20
  • Mises à jour de sécurité en attente d'installation — −5

Résultat : 80+ = Protégé, 60–79 = Attention, <60 = Menacé.

Les déductions pour ClamAV, AIDE, CrowdSec et Suricata ne s'appliquent que si l'outil est installé. Lynis et AIDE sans base initialisée s'affichent comme « aucune donnée » et ne diminuent pas le score. Le nombre d'attaques du jour est affiché sur le dashboard, mais n'influe pas sur le score de sécurité.

Paramètres et licence

05. WebAuthn — authentification à deux facteurs

WebAuthn — standard d'authentification sans mot de passe via une clé matérielle. Compatible avec YubiKey, Touch ID, Face ID, Windows Hello, Passkey.

Après la connexion par mot de passe, le système demande une confirmation via la clé enregistrée. Même en cas de fuite du mot de passe, la connexion est impossible sans la clé physique ou la biométrie.

Pour la configuration, ouvrez Clés WebAuthn dans le menu latéral et cliquez sur « Enregistrer une clé ». Enregistrez deux clés d'emblée : si votre unique clé est perdue ou endommagée, la connexion au dashboard avec celle-ci sera impossible.

WebAuthn fonctionne uniquement via HTTPS. En connexion HTTP, l'enregistrement et la connexion par clé sont indisponibles.

06. Notifications : Telegram et Email

Le tableau de bord peut envoyer le rapport de sécurité vers Telegram et par e-mail (sur clic ou selon une planification). Cela se configure dans « Paramètres ».

Telegram. Il faut un jeton de bot et un chat id :

  1. Dans Telegram, écrivez à @BotFather/newbot → obtenez un jeton du type 123456:ABC....
  2. Envoyez un message quelconque à votre nouveau bot (afin qu'il puisse vous répondre).
  3. Trouvez votre chat id : écrivez au bot @userinfobot, ou ouvrez https://api.telegram.org/bot<TOKEN>/getUpdates et repérez "chat":{"id":...}.
  4. Saisissez le jeton et le chat id dans « Paramètres » → Telegram, cliquez sur « Enregistrer et envoyer un test ».

Email. Deux méthodes au choix dans « Paramètres » → Email :

  • SMTP — hôte, port (465/SSL ou 587/TLS), identifiant et mot de passe de votre boîte mail ;
  • Resend — API moderne : indiquez la clé API (re_...) et un domaine d'expéditeur vérifié.
Le bouton « Envoyer un test » vérifie le canal immédiatement. La planification du rapport automatique passe par cron (section « Toutes les tâches cron ») : il déclenche l'envoi, et les canaux sont pris depuis les paramètres.

Statut du rapport : « ATTENTION » ou « OK ». Le titre devient « ATTENTION » uniquement en cas de problème réel ou d'action en attente : menace détectée par ClamAV, modifications de fichiers dans AIDE, événements critiques Falco (Emergency/Alert/Critical sur les 24 dernières h), service tombé dans Monit, redémarrage requis, SSL qui expire (≤14 jours) ou mises à jour de sécurité en attente. Le bruit de fond — attaques SSH par force brute des bots, IP bannies par fail2ban, alertes Suricata, avertissements Lynis et requêtes déjà repoussées par ModSecurity — n'élève pas le statut ; ces chiffres dans le rapport ne signifient donc pas « ATTENTION » à eux seuls.

07. Licence — saisie et activation

Les modules de surveillance détaillés (Lynis, UFW, ModSecurity, carte des attaques, AIDE, ClamAV, etc.) s'ouvrent avec une licence valide. Sans elle, le tableau de bord, les paramètres et le compte fonctionnent, mais les modules affichent la carte « Licence requise ».

Après l'achat, votre espace client contient un code d'activation de la forme ARCIVEO-XXXX-XXXX-XXXX-XXXX. Il faut l'« activer » sur le domaine de votre panneau — cela transforme le code en un fichier de licence signé (bloc [license]) que vous collez dans le panneau.

Comment activer (3 étapes) :

  1. Récupérez le code d'activation. Espace client my.arciveo.com → section « Licences » / « Activation de licence » — copiez le code ARCIVEO-….
  2. Activez le code sur votre domaine. Dans le même espace, ouvrez « Activation de licence », saisissez : le code d'activation, votre email et le domaine du panneau (l'adresse à laquelle s'ouvre le moniteur, p. ex. monitor.example.com). Cliquez sur activer — le système génère un fichier de licence lié à ce domaine et l'affiche dans un champ avec le bouton « Copier ».
  3. Collez la clé dans le panneau. Copiez tout le texte de la licence → dans le panneau, ouvrez « Paramètres » → bloc « Licence », collez et cliquez sur « Enregistrer ». Les modules se débloquent immédiatement.

Le panneau vérifie la clé de manière cryptographique : signature, liaison au domaine et durée de validité.

Lors de l'activation, le domaine doit correspondre exactement à l'adresse du panneau. Récupérez-le dans la constante APP_URL de config.php et saisissez uniquement le nom d'hôte — sans https:// ni le préfixe www. L'activation est à usage unique : le code se transforme en licence pour le domaine saisi et ne peut pas être réactivé — en cas d'erreur dans le domaine, la clé ne conviendra pas à votre panneau et le code sera consommé. Saisissez donc le domaine avec attention.
Si le délai est expiré ou si le domaine a changé, un avertissement apparaît dans l'en-tête du panneau. La licence est liée au domaine de façon définitive et ne peut pas être transférée vers un autre domaine : pour une nouvelle période ou un nouveau domaine, il faut une nouvelle clé (achetée dans l'espace client et activée une seule fois).

08. Fichier config.php — tous les réglages du tableau de bord

Tous les paramètres principaux du tableau de bord sont définis dans un seul fichier config.php à la racine (à côté du dossier public/) sous forme de constantes define(). Le fichier est créé lors de l'installation ; il est rarement nécessaire de le modifier à la main — surtout lors d'un changement de domaine, d'une migration ou d'une connexion à une autre base. Après chaque modification, redémarrez PHP-FPM (sinon, à cause d'OPcache, les changements ne s'appliqueront pas).

Renseignez vos valeurs aux endroits surlignés ; laissez le reste tel quel :

// --- Base de données --- define('DB_HOST', 'localhost'); // laisser define('DB_NAME', 'db_name'); // ce que vous avez défini à la création de la BD define('DB_USER', 'user'); // ce que vous avez défini à la création de la BD define('DB_PASS', 'db_password'); // ce que vous avez défini à la création de la BD define('DB_CHARSET', 'utf8mb4'); // laisser // --- Application --- define('APP_URL', 'https://monitor.example.com'); // adresse du tableau de bord, sans slash final define('TIMEZONE', 'Europe/Paris'); // votre fuseau horaire // --- Durée de session --- define('SESSION_LIFETIME', 28800); // inactivité avant reconnexion, s (28800 = 8 h)

Base de données. Paramètres de connexion à MySQL/MariaDB :

  • DB_HOST — hôte du SGBD, presque toujours localhost ;
  • DB_NAME — nom de la base de données du tableau de bord ;
  • DB_USER — utilisateur de la BD (accès uniquement à sa propre base) ;
  • DB_PASS — mot de passe de cet utilisateur ;
  • DB_CHARSET — encodage de la connexion, laissez utf8mb4.

Application.

  • APP_URL — adresse complète du tableau de bord (p. ex. https://monitor.example.com). Elle doit correspondre au domaine sur lequel la licence est activée — sinon la clé sera rejetée (voir la section « Licence ») ;
  • TIMEZONE — fuseau horaire PHP : n'affecte que la façon dont le tableau de bord affiche les dates et heures. Il n'a aucun effet sur l'heure de lancement des tâches cron — c'est le fuseau du système qui s'applique (voir « Toutes les tâches cron »).

Durée de session. SESSION_LIFETIME — délai d'inactivité de la session en secondes (glissant : réinitialisé à chaque activité). Par défaut 28800 = 8 heures ; passé ce délai d'inactivité, le tableau de bord demandera de se reconnecter. Par exemple, 3600 = 1 heure, 86400 = un jour.

Journalisation des erreurs. Les erreurs ne sont jamais montrées aux visiteurs ; elles sont écrites dans logs/php_errors.log — visibles sur la page « Logs de l'application ». Ces lignes (display_errors=0, log_errors=1, chemin error_log) n'ont généralement pas besoin d'être modifiées — les réglages sont définis directement dans le fichier et ne dépendent pas de php.ini.

config.php — fichier secret. Il contient le mot de passe de la BD. Il se trouve à la racine du tableau de bord (à côté de public/), et pour ce tableau de bord la racine web (DocumentRoot) est justement la racine du tableau de bord, pas public/. Le fichier ne « fuit » pas de lui-même : le .htaccess racine lui interdit explicitement l'accès (Require all denied) — le serveur renvoie une 403. Même sans cette règle, le code source ne fuirait pas : c'est du PHP — le serveur l'exécute au lieu de le renvoyer en texte. Par précaution : ne le publiez pas dans des dépôts publics et ne le transmettez pas au support avec le vrai mot de passe. Droits du fichier — 640.
Lors d'une migration ou d'une restauration d'accès, ce fichier est la principale source des identifiants : le nom de la BD, l'utilisateur et le mot de passe se trouvent ici (voir les sections « Mise à jour et migration du tableau de bord » et « Restauration d'accès »).

Outils de sécurité

09. Pare-feu UFW

UFW (Uncomplicated Firewall) est une interface simple pour nftables/iptables. Il ferme tous les ports entrants, sauf ceux explicitement autorisés. La page « Pare-feu UFW » affiche l'état et les règles.

sudo apt install ufw # Autoriser SSH (obligatoire AVANT l'activation !) et le web sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Fermer la BD depuis l'extérieur (accès local uniquement) sudo ufw deny 3306 # Activer et vérifier sudo ufw enable sudo ufw status verbose
Avant ufw enable, autorisez impérativement SSH (ufw allow OpenSSH), sinon vous perdrez l'accès au serveur.
L'« exposition externe » sur le tableau de bord prend en compte UFW : un port fermé par une règle deny n'est pas considéré comme accessible depuis l'extérieur.
Skipping adding existing rule n'est pas une erreur. C'est ainsi qu'UFW signale qu'une règle identique existe déjà et qu'il ne l'ajoute pas de nouveau. Lors d'une nouvelle exécution de la configuration automatique (elle est idempotente), ce message est normal — aucune action n'est requise.

10. Installation de Fail2ban

Bloque automatiquement une IP après un nombre excessif de tentatives de connexion échouées. Analyse les journaux SSH, nginx, Apache et d'autres services.

sudo apt install fail2ban sudo systemctl enable --now fail2ban # Vérifier le statut : sudo fail2ban-client status
La configuration opérationnelle (jail.local avec des dizaines de jails et bannissement automatique depuis ipsum) est présentée dans la section suivante.

11. Configuration opérationnelle de Fail2ban + ipsum

L'installation de base est décrite plus haut. Voici une configuration opérationnelle qui fournit des dizaines de jails actifs et des milliers de blocages : réglages généraux, jails essentiels et bannissement automatique des IP malveillantes issues de la liste ipsum.

Fichier /etc/fail2ban/jail.local — réglages généraux et jails essentiels :

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # Bannissement progressif : chaque récidive dure plus longtemps bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # bannissement permanent pour force brute SSH findtime = 3600 # Récidivistes : ceux qui cumulent plusieurs bannissements sont bannis à vie [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 # Services web (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog) : [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … et les autres jails par service (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) — enabled = true
Dans ignoreip, veillez à inscrire votre propre IP et vos réseaux de confiance, sinon vous risquez de vous bannir vous-même. Après modification : sudo fail2ban-client reload.

Chargement automatique de la liste de blocage ipsum — dans le cron root (sudo crontab -e) : le level 1 (100 000+ IP) est chargé dans l'ensemble ipsum, filtré au niveau du pare-feu (voir la section « Liste de blocage IPset ») :

# 04:00 — mise à jour de l'ipset ipsum (level 1, couverture maximale) : 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
L'ensemble doit s'appeler ipsum — c'est précisément celui que lit le tableau de bord (carte « IPset ipsum »). Niveaux : levels/1.txt — couverture maximale, levels/3.txt — plus précis (3+ sources).

Pourquoi le « Monitor de sécurité » est divisé en deux zones. La protection fonctionne sur deux niveaux, et le tableau de bord ne les mélange pas :

  • Attaques réelles (réactif) — tout ce que fail2ban a intercepté : tentatives d'intrusion en direct (jails sshd, apache-*, nginx-*, etc.) et récidivistes acharnés (jail recidive — ceux déjà bannis plusieurs fois). Ce sont des IP qui ont réellement tenté de vous attaquer — elles figurent sur la carte des attaques et la « Chronologie ».
  • Blocage préventif (proactif) — liste de blocage publique d'IP malveillantes connues ipset ipsum, filtrée au niveau du pare-feu par une règle DROP. Ces adresses n'ont, pour la plupart, jamais touché votre serveur — elles sont coupées à l'avance ; le compteur « IPset ipsum » indique combien ont été filtrées préventivement.

La différence est simple : le réactif, c'est « ceux-ci ont attaqué et se sont fait bannir » ; le préventif, c'est « ceux-ci ont été bloqués avant même toute tentative ». Auparavant, la list-3 ipsum était artificiellement injectée dans recidive (d'où l'ancienne distinction « recidive de liste ») ; désormais, recidive ne contient que de vrais récidivistes, et le préventif est entièrement géré au pare-feu.

12. Liste de blocage IPset (ipsum)

ipsum — liste publique d'IP malveillantes, mise à jour quotidiennement. Le Monitor affiche le nombre d'adresses chargées sur le tableau de bord et la carte des attaques, et le prend en compte dans le Score de sécurité (−10 si l'ensemble n'est pas chargé).

Variante minimale sans fail2ban — un ensemble ipsum dédié avec blocage via iptables :

# Créer l'ensemble (une seule fois) : sudo ipset create ipsum hash:ip # Script de mise à jour /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, tous les jours à 4:00) : 0 4 * * * /usr/local/bin/update-ipsum.sh
Variante avancée avec fail2ban-recidive — dans la section « Configuration opérationnelle Fail2ban + ipsum ».
ipset réside en mémoire et est perdu au redémarrage. Un simple cron quotidien laissera l'ensemble vide du redémarrage jusqu'à l'exécution suivante (le tableau de bord affichera 0). Chargez aussi l'ensemble au démarrage — déportez le chargement dans un script et attachez-le à @reboot. Par la même occasion, la commande create … -exist fixe la limite maxelem 300000 (par défaut 65536 — le level 1 ne rentre pas, on obtient « 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 — tous les jours à 04:00 ET à chaque démarrage : 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
Fonctionnement en installation automatique. Le script charge la liste complète level 1 (100 000+ IP) dans l'ensemble ipsum et, si le pare-feu est géré par l'installateur (VPS neuf — profils « Complète »/« Allégée »), rattache l'ensemble à UFW via une règle DROP — le trafic de ces IP est réellement bloqué. La règle se place après ESTABLISHED,RELATED, si bien que les connexions en cours (dont votre SSH) ne sont pas coupées — seules les nouvelles connexions de la liste le sont. L'ensemble est restauré au démarrage du service ipsum-load.service avant le pare-feu (sinon UFW ne monterait pas) et mis à jour par cron à 04:00. Sur un serveur déjà configuré (panneau, pare-feu propre), l'installateur ne touche pas au pare-feu — ipsum y reste une liste pour le tableau de bord et la carte des attaques, et la règle DROP s'ajoute manuellement au besoin (variante minimale avec iptables … --match-set ipsum … -j DROP — ci-dessus). En installation automatique, rien n'est à faire manuellement.

13. Installation de CrowdSec

Remplaçant moderne de Fail2ban avec threat intelligence collaboratif : blocages issus de la communauté et règles personnalisées. Nécessite un bouncer distinct pour appliquer les blocages au pare-feu.

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # Bouncer pour iptables/nftables : sudo apt install crowdsec-firewall-bouncer-iptables # Vérifier le statut : sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
Statut « Non démarré » dans le tableau de bord = le service est installé mais inactif (le moniteur le vérifie via systemctl is-active crowdsec). Pour le démarrer : sudo systemctl enable --now crowdsec ; en cas d'échec, consultez sudo journalctl -u crowdsec -n 30. Même règle pour tout service au statut « Non démarré » (Suricata, Falco, Monit, MySQL).
« 0 scénario » ou « 0 bouncer » sur le tableau de bord. CrowdSec est quasiment vide à l'installation — sans collections il ne détecte rien, et sans bouncer enregistré les blocages ne sont pas appliqués au pare-feu. Installez les collections de base et vérifiez que le bouncer figure dans la liste :
# Collections de base (Linux + SSH + serveur web) : sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # Le bouncer doit figurer dans la liste avec un statut de connexion actif : sudo cscli bouncers list
Dans le journal du bouncer stream halted / les blocages ne s'appliquent pas. Il s'agit d'une clé api orpheline : le bouncer a été supprimé de cscli bouncers list, mais son ancienne clé est restée dans /etc/crowdsec/bouncers/*.yaml. Réenregistrez le bouncer et renseignez la nouvelle clé :
sudo cscli bouncers add fw-bouncer # affichera une nouvelle api_key # renseignez cette clé dans api_key: dans /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml sudo systemctl restart crowdsec-firewall-bouncer
L'installation automatique (profil « Protection complète ») installe elle-même les collections et enregistre le firewall-bouncer — vous n'avez à le faire manuellement qu'en cas d'installation manuelle ou après une intervention manuelle dans CrowdSec.

14. Installation d'AIDE

AIDE (Advanced Intrusion Detection Environment) prend un instantané du système de fichiers et signale à chaque vérification les modifications dans /etc, /bin, /usr. Après l'installation, l'initialisation de la base est obligatoire (aideinit).

sudo apt install aide # Initialisation de la base (5 à 15 minutes) : sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04 : le répertoire /var/lib/aide est créé en mode 700 (propriétaire _aide), # et le tableau de bord (www-data) ne voit pas la base → affiche « Non initialisée ». # Ouvrir le répertoire en traversée (les fichiers de la base restent en 600) : sudo chmod 755 /var/lib/aide # Première vérification AVEC ÉCRITURE dans le log que lit le moniteur. # Sous Ubuntu/Debian, aide exige un --config explicite (sinon « missing configuration » ; # le binaire aide.wrapper n'est plus fourni dans les nouvelles versions) : sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Pendant aideinit, le terminal reste 5 à 15 minutes sur la ligne Running aide --init... — c'est normal (hachage de tout le système de fichiers, charge disque). N'interrompez pas avec Ctrl+C. Si le processus « se fige » sans rien écrire, il attend peut-être une réponse à une invite masquée Overwrite existing aide.db.new [Yn]? (appuyez sur Y). Pour vérifier l'activité depuis une autre session : pgrep -af aide.
Erreur aideinit : « 21_aide_spamassassin … printf: invalid number » (return code 20) — bug connu du snippet de configuration AIDE sous Ubuntu 22.04. La base n'est pas créée. Écartez le snippet défectueux et recommencez :
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
Statuts sur le tableau de bord. « Non initialisée » = le tableau de bord ne voit pas le fichier de base : soit aideinit n'a pas été lancé, soit (Ubuntu 24.04) le répertoire /var/lib/aide a été créé en mode 700 et est inaccessible à www-data — se corrige avec sudo chmod 755 /var/lib/aide (voir le bloc ci-dessus). « Aucune vérification » = la base existe, mais aucune vérification n'a encore été effectuée — ce n'est pas une erreur. Le moniteur lit les résultats depuis /var/log/aide/aide.log.
Vérification régulière → log pour le tableau de bord. Le /etc/cron.daily/aide standard sur les nouvelles versions d'Ubuntu/Debian peut ne pas écrire /var/log/aide/aide.log dans le format attendu (et aide.wrapper n'y est plus présent). Il est plus fiable d'ajouter votre propre cron avec un --config explicite — il écrit le log en tant que root en mode 644, et le moniteur le lit sans groupes supplémentaires :
# sudo crontab -e — vérification quotidienne à 02:00 : 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # Lancer maintenant, sans attendre la planification : sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
L'installation automatique fait déjà tout cela : chmod 755 /var/lib/aide et le cron de vérification à 02:00 — rien à faire manuellement.
Effectuez la première initialisation sur un serveur vierge — avant l'installation des applications web. Après des modifications légitimes, recréez la base : sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.

15. Installation de ClamAV

Antivirus pour Linux. Particulièrement utile pour analyser /var/www à la recherche de shells PHP et de code malveillant.

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # Mettre à jour la base de signatures : sudo freshclam # Analyser un dossier manuellement : sudo clamscan -r /var/www --infected
Le démon clamd affiche « Inactif » après enable --now ? Trois causes fréquentes :

1. La ligne Example est restée dans la config — clamd refuse de démarrer tant qu'elle est présente :

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

2. La base de signatures n'est pas téléchargée — clamd ne démarre pas sans elle :

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

3. Il est simplement en cours de chargement — clamd charge ~8 M de signatures en mémoire en 30 à 60 s. Patientez et vérifiez : systemctl is-active clamav-daemon (statut activating → toujours en cours de chargement).

Diagnostic : sudo journalctl -u clamav-daemon -n 30 --no-pager.
Le tableau de bord affiche « Fichiers analysés : 0 » / « Dernière analyse : — » ? Le démon clamd ne fait que garder les signatures en mémoire, il n'analyse rien de lui-même de façon planifiée. Le tableau de bord affiche les résultats de l'analyse planifiée ; il faut donc un cron qui analyse et écrit un journal. L'installation automatique met en place le wrapper /usr/local/bin/clamav-scan.sh et un cron à 01:30 — après la première exécution, « Fichiers analysés » et « Dernière analyse » se rempliront. Pour lancer tout de suite sans attendre la planification : sudo /usr/local/bin/clamav-scan.sh.

16. Installation de Linux Malware Detect (maldet)

Linux Malware Detect (LMD) — un scanner de logiciels malveillants ciblant les menaces web : shells PHP, backdoors web, téléchargeurs. Il s'appuie sur le moteur ClamAV et le complète avec ses propres signatures.

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 # Mettre à jour les signatures : sudo maldet -u # Scanner /var/www : sudo maldet -a /var/www
LMD et ClamAV fonctionnent bien ensemble. Dernier rapport : maldet --report.
Lors de l'installation, la ligne update-rc.d: error: unable to read /etc/init.d/maldet peut apparaître — elle est sans danger. maldet n'utilise pas init.d ; la mise à jour des signatures et les scans sont lancés via /etc/cron.daily/maldet. Si installation completed s'affiche ensuite, tout est bien installé.
La page indique « Non installé » pour LMD alors qu'il l'est ? maldet ne s'installe pas via apt, mais dans /usr/local/maldetect, et lorsque open_basedir est activé, sa présence est vérifiée via le shell — voir la section « La page est vide alors que les données sont bien sur le serveur ».

17. Installation de Suricata

Système de détection d'intrusion réseau : il analyse le trafic au niveau des paquets et connaît des milliers de signatures d'attaques. Il complète ModSecurity (qui agit au niveau HTTP, tandis que Suricata opère au niveau TCP/IP).

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # Télécharger les règles à jour : sudo suricata-update sudo systemctl enable --now suricata
Suricata est « Actif », mais le tableau de bord n'affiche aucune alerte / le nombre d'événements est 0 ? Suricata écrit dans /var/log/suricata/eve.json sous root avec des permissions 750 sur le répertoire, et le serveur web (www-data) ne peut pas le lire. Ouvrez le répertoire en traversée — les fichiers à l'intérieur restent protégés :
sudo chmod o+rx /var/log/suricata
L'installation automatique le fait elle-même — inutile de le faire manuellement.

18. Installation de Falco

Intercepte les appels système via eBPF/kernel module et détecte les anomalies en temps réel : shell depuis nginx, lecture de /etc/passwd par un processus web, écriture dans /bin, etc.

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
Le moniteur lit les événements Falco via journalctl -u falco (sans sudo — via le groupe systemd-journal). Vérifiez que www-data fait partie de ce groupe — voir « Configuration de sudo » (point 2) sur la page d'installation manuelle.
« 0 événement sur 24 heures » est normal, pas une erreur. Falco est event-driven : il reste silencieux tant que tout va bien et n'écrit un événement qu'en cas d'anomalie (shell depuis un processus web, lecture de /etc/passwd, écriture dans les répertoires système). Zéro événement critique par jour sur un serveur calme est un état sain.
Pour le tableau de bord, la sortie fichier est plus fiable. La lecture via journalctl nécessite des droits sur le journal ; pour que le tableau de bord voie les événements de façon stable, l'installation automatique active la file_output de Falco → /var/log/falco/falco.log et applique au service UMask=0022 (le log est lisible par le serveur web). Sur une nouvelle installation, il n'est pas nécessaire de le configurer manuellement.

19. Installation de ModSecurity (WAF)

ModSecurity — pare-feu applicatif (WAF) pour Apache ou Nginx. Il bloque les attaques au niveau applicatif : injections SQL, XSS, traversées de chemin, scanners.

# Apache : sudo apt install libapache2-mod-security2 sudo a2enmod security2 # Jeu de règles OWASP Core Rule Set : sudo apt install modsecurity-crs # OBLIGATOIRE : sans ce fichier, le moteur de règles est désactivé 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 # Vérification : doit renvoyer 403 curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
L'installation du paquet ne protège rien en elle-même. Apache inclut les configurations avec la ligne IncludeOptional /etc/modsecurity/*.conf, mais le paquet ne pose que modsecurity.conf-recommended — qui ne correspond pas au masque *.conf. Sans le copier dans modsecurity.conf, SecRuleEngine reste sur Off : le module est chargé, les règles CRS sont chargées, mais le trafic n'est pas inspecté et aucun journal d'audit n'est créé. Le mode intermédiaire DetectionOnly se contente d'enregistrer les événements dans le journal sans bloquer les requêtes — le tableau de bord l'affiche en jaune.

Accès du tableau de bord au journal d'audit. Le journal /var/log/apache2/modsec_audit.log appartient à root (droits 640), l'utilisateur web ne peut pas le lire. Le tableau de bord récupère les données via un wrapper — créez-le :

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 # dans /etc/sudoers.d/monitor (utilisateur = celui sous lequel tourne PHP-FPM) : # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
Le wrapper prend la dernière directive SecRuleEngine sans indentation : les lignes indentées se trouvent à l'intérieur de blocs <LocationMatch>/<Directory> (par exemple la désactivation du WAF pour phpMyAdmin) et ne définissent pas le mode global.
L'utilisateur dans sudoers doit correspondre à l'utilisateur du pool FPM : sur un Apache/Debian standard c'est www-data, dans HestiaCP le pool du site tourne sous le propriétaire du site (par exemple admin) — vérifiez avec grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.
Si le site est derrière un proxy Nginx (HestiaCP), Apache voit le proxy lui-même comme client — le tableau de bord récupère l'adresse IP réelle de l'attaquant dans l'en-tête X-Forwarded-For. Seules les transactions ayant déclenché une règle entrent dans les statistiques : la directive SecAuditLogRelevantStatus enregistre dans le journal d'audit toute réponse 4xx/5xx, si bien que les 403/500 ordinaires y figurent aussi — le tableau de bord ne les compte pas comme des événements WAF.
Le bloc ---RULES--- est nécessaire à la section « Toutes les règles actives » — le tableau de bord affiche non seulement les règles déclenchées, mais l'ensemble des règles CRS chargées + les règles personnalisées. Les trois chemins de la boucle for f in … sont les emplacements habituels des règles CRS et des ajouts locaux ; si votre disposition diffère (le paquet place les fichiers dans son propre répertoire, ou les règles personnalisées ne sont pas dans /etc/modsecurity/custom-rules.conf), trouvez les chemins réels avec sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null et ajoutez-les à la liste. Si le wrapper est ancien (sans cette section), la section affichera simplement l'avertissement « indisponible », le reste de la page fonctionne comme avant.

20. Installation d'Auditd

Auditd (Linux Audit Daemon) enregistre les appels système au niveau du noyau : connexions et déconnexions, commandes sudo, échecs d'authentification, modifications de fichiers. Le moniteur affiche les connexions, les tentatives échouées et les commandes sudo du jour.

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # Vérifier l'état et les événements : sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
Le moniteur lit les événements via ausearch (/usr/sbin/ausearch) et, si nécessaire, dans /var/log/audit/audit.log à l'aide de la commande tail. Les deux doivent figurer dans sudoers.

21. Installation de Monit

Surveille les services (nginx, php-fpm, mysql, etc.) et les redémarre en cas de panne. Peut envoyer des alertes par e-mail.

sudo apt install monit sudo systemctl enable --now monit # Fichiers de configuration : sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
Le monitor récupère la liste des services via monit status. Dans /etc/monit/monitrc, l'interface HTTP doit être activée (bloc set httpd avec allow localhost), sinon monit status renverra une erreur.
« 0 service surveillé » sur le tableau de bord ? Deux causes. (1) L'interface HTTP est désactivée — dans monitrc, la ligne set httpd est commentée (par défaut sous la forme # set httpd port 2812 …). Décommentez le bloc et autorisez localhost. (2) Le httpd activé ne surveille rien en soi — Monit ne compte que ce qui est décrit par des stances check ; sans elles, la liste reste vide même si l'interface fonctionne. Configuration minimale opérationnelle :
# /etc/monit/conf.d/00-httpd — interface HTTP pour localhost : set httpd port 2812 use address localhost allow localhost # exemples de stances check (ce qu'il faut surveiller) : 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 # vérifier la syntaxe (Control file syntax OK) sudo systemctl reload monit sudo monit status
L'installation automatique dépose un conf.d prêt à l'emploi avec httpd sur 2812 et un jeu de vérifications — sur une nouvelle installation, aucune configuration manuelle n'est nécessaire.
Un service en statut « En erreur » ? Le monitor se contente d'afficher l'état et ne redémarre volontairement pas les services depuis l'interface web (ce serait une exécution de commandes root à distance dans un panneau de sécurité). Diagnostic et redémarrage se font en SSH via Monit :
sudo monit status <service> # cause de l'erreur sudo monit restart <service> # redémarrage via Monit # si Monit ne relance pas le service — consultez son propre unit : sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

22. Installation de PSAD (détection de scan de ports)

PSAD analyse le journal iptables et détecte les scans de ports et les attaques réseau, en attribuant à chaque source un niveau de menace (1–5). Complète fail2ban et Suricata.

sudo apt install psad # PSAD lit le journal iptables — il faut activer la journalisation (UFW le fait lui-même). # Pour iptables seul, ajoutez des règles LOG aux chaînes INPUT/FORWARD. sudo psad --sig-update sudo systemctl enable --now psad
Le moniteur lit les données via psad --Status (requis dans sudoers). Sans journalisation iptables, la page restera vide — c'est normal tant qu'aucun scan n'a eu lieu.

23. AppArmor / SELinux (contrôle d'accès)

Mandatory Access Control limite les fichiers et ressources auxquels un programme peut accéder, même s'il est compromis. Sous Ubuntu/Debian, AppArmor est utilisé par défaut (généralement déjà installé et actif).

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # vérifier les profils
Le moniteur lit l'état via aa-status (requis dans sudoers). Il affiche le nombre de profils en mode enforce/complain et les processus sans profil.

« Profils chargés » supérieur à enforce + complain, c'est normal. AppArmor 4.x (Ubuntu 24.04 et plus récent) a introduit le mode unconfined : le profil est chargé dans le noyau, mais ne limite rien. Ubuntu marque ainsi des dizaines de profils pour les programmes utilisant les user namespaces (navigateurs, clients torrent et similaires). Lorsque de tels profils existent, la carte « Profils chargés » devient ambre et affiche leur nombre — par exemple unconfined: 90 pour 120 chargés et 26 en enforce. Seuls les profils en enforce protègent réellement ; sous Ubuntu 22.04 (AppArmor 3.x) ce mode n'existe pas et les chiffres concordent toujours.

sudo aa-status | grep -E "profiles are" # répartition par mode sudo aa-enforce /etc/apparmor.d/profile-name # passer un profil en enforce
Ne passez en enforce les profils qu'Ubuntu a délibérément laissés en unconfined qu'en toute connaissance de cause : ils ne sont pas désactivés par erreur, mais parce que cela casserait le fonctionnement des programmes eux-mêmes. Les profils en complain, c'est différent : les règles y sont déjà écrites, elles ne sont simplement pas appliquées.

24. Installation de debsums (intégrité des paquets)

debsums vérifie que les fichiers des paquets installés correspondent aux sommes de contrôle du dépôt — il détecte les binaires système altérés (en complément d'AIDE). Une analyse complète prend 1 à 2 minutes, elle est donc lancée par cron, et le tableau de bord lit le résultat depuis data/debsums/debsums.log et le classe lui-même par catégories (seuls les binaires et bibliothèques comptent).

La tâche va dans le cron root (sudo crontab -e). Le wrapper prêt à l'emploi debsums-scan.sh se place dans /usr/local/bin/ (chmod +x ; voir le récapitulatif des tâches cron) et écrit lui-même le rapport dans data/debsums/ du tableau de bord.

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

Le wrapper debsums-scan.sh trouve lui-même le dossier data/ du tableau de bord — inutile d'indiquer le chemin.

Les modifications dans /etc/ (configs) et /usr/share/ (ressources) sont généralement normales sur un serveur — le tableau de bord les signale d'une couleur distincte. Les modifications de binaires et bibliothèques (/bin, /sbin, /usr/lib, etc.) sont préoccupantes — la carte « Binaires / bibliothèques » montre précisément celles-ci.

25. Configuration des rapports Lynis

Lynis se lance manuellement ou via cron. Le rapport doit être enregistré dans le dossier data/lynis/ du projet — le moniteur lit le fichier lynis-report.dat.

# Lancement ponctuel (indiquez votre chemin vers la racine du panneau) : sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # Audit quotidien — ligne cron (wrapper lynis-scan.sh prêt à l'emploi dans /usr/local/bin/, voir le récapitulatif) : 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
Après le premier lancement, la page « Audit Lynis » affiche immédiatement le hardening index, les avertissements et les recommandations.
Le bouton « Lancer l'audit » sur la page Lynis. Il exécute lynis-scan.sh en arrière-plan directement depuis le panneau (sans attendre cron) : il affiche « Analyse… » et met à jour le rapport à la fin. Pour cela, l'utilisateur web a besoin d'une ligne sudoers pour lancer le script — l'installateur l'ajoute automatiquement dans /etc/sudoers.d/monitor. Si le panneau a été installé manuellement/plus tôt, ajoutez-la avec le même utilisateur que celui déjà indiqué dans le fichier :
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. Configuration des rapports Logwatch

Logwatch doit enregistrer les rapports quotidiens dans le dossier data/logwatch/ du projet au format .txt. Le moniteur affiche le dernier rapport et les archives.

# Quotidien (6:00) — ligne cron (wrapper logwatch_daily.sh prêt à l'emploi dans /usr/local/bin/, voir le récapitulatif) : 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

Modules du tableau de bord

27. Moniteur réseau (intégré)

Le moniteur réseau ne nécessite aucune installation — c'est une page intégrée au tableau de bord. Elle affiche l'état réseau du serveur à partir de sources locales :

  • interfaces et trafic — depuis /proc/net/dev ;
  • état des liens (UP/DOWN) et IP — via ip ;
  • connexions et ports en écoute — via ss ;
  • événements réseau du noyau sur 24 h — via journalctl -k.

Les trois premières sources fonctionnent sans sudo, les interfaces, le trafic, les connexions et les ports sont donc visibles immédiatement. Le bloc « Événements du noyau » utilise journalctl -k — sa lecture passe par le groupe systemd-journal (« Configuration de sudo », pt.2), sudo n'est pas requis. Vérifier que tout est accessible à l'utilisateur web :

# Vérification en tant que www-data (PHP s'exécute sous cet utilisateur) : 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
Le bloc « Événements réseau du noyau » affiche les événements de la pile réseau du noyau (changement de lien up/down, erreurs de porteuse, « network unreachable »). Les entrées du pare-feu UFW BLOCK n'y figurent pas — elles sont sur les pages « Pare-feu UFW » et « Carte des attaques ». Un bloc vide avec une coche verte = aucun incident réseau sur la journée.

28. Disque et SMART

La page intégrée affiche trois éléments :

  • Systèmes de fichiers — remplissage des partitions (df) ; la barre passe au rouge à ≥90 % ;
  • Périphériques de stockage — liste des disques (lsblk), réels uniquement (loop/snap masqués) ;
  • Santé (SMART) — état du disque et attributs (smartctl).

L'espace et la liste des périphériques fonctionnent immédiatement, sans configuration. SMART nécessite le paquet smartmontools. Le processus web n'a pas d'accès direct aux périphériques de disque ; SMART est donc relevé via cron dans le fichier data/disk/smart.txt, que le tableau de bord lit ensuite.

La tâche va dans le cron root (sudo crontab -e). Le script prêt à l'emploi smart-scan.sh se place dans /usr/local/bin/ (chmod +x ; voir le récapitulatif des tâches cron) et écrit lui-même dans data/disk/ du tableau de bord.

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

Le script smart-scan.sh trouve lui-même le dossier data/ du tableau de bord — pas besoin d'indiquer le chemin. En interne, lsblk -e7,11 exclut loop/cdrom.

Sur les disques virtuels (QEMU/KVM et similaires), seul l'état global « santé : OK » est généralement disponible ; la température, les heures de fonctionnement et les secteurs réalloués peuvent être vides — c'est normal. Sur un serveur physique, tous les attributs s'affichent.

29. Performances (CPU/RAM/Réseau/Disque)

La page affiche l'historique de la charge du serveur sur les dernières 24 heures — Load Average, occupation CPU et attente I/O, RAM/Swap, trafic réseau (réception/émission), I/O disque (lecture/écriture), remplissage du disque et des inodes, descripteurs de fichiers ouverts et connexions MySQL, plus le nombre actuel de connexions TCP et de processus.

Les données sont collectées par cron/collect_metrics.php — toutes les 5 minutes, il écrit un instantané « brut » des compteurs (/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') dans la table de base de données system_metrics ; les pourcentages et débits sont calculés par la page elle-même à partir de la différence entre deux instantanés voisins (remplissage du disque/inodes/descripteurs/connexions MySQL — valeurs instantanées, sans recalcul). Sudo n'est pas requis — les sources sont lues sans droits root. Les points de plus de 24 heures sont supprimés automatiquement à chaque écriture.

# Ligne cron (toutes les 5 minutes) : */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

Le wrapper collect-metrics-all.sh (voir le récapitulatif des tâches cron) détecte lui-même toutes les instances du panneau installées sur le serveur et lance le cron/collect_metrics.php de chacune au nom du propriétaire du site.

Tant que le collecteur n'a pas tourné au moins deux fois (les ~10 premières minutes après l'installation), la page affiche « collecte des données en cours » — les graphiques ont besoin d'au moins une paire de points voisins pour calculer les débits et les pourcentages.

Alertes de charge (section « Paramètres » → « Alertes de charge ») — en cas de dépassement du seuil CPU/RAM/disque/inodes, le panneau envoie une notification sur Telegram/Email (les mêmes canaux que le rapport quotidien — pas besoin de les activer séparément pour les alertes), et une autre — lorsque la métrique revient à la normale. Il ne spamme pas pendant le maintien du seuil : la notification suivante n'arrive qu'après un cycle « revenu à la normale → de nouveau dépassé ».

Les seuils sont vérifiés par ce même collect_metrics.php à chaque exécution (toutes les 5 minutes) — pas besoin d'un cron séparé. L'état « déjà notifié / pas encore » est stocké dans data/alerts_state.json, les seuils — dans les paramètres du panneau.

30. Carte des attaques (GeoIP)

La page « Carte des attaques » détermine le pays d'après l'IP grâce à la commande geoiplookup. Sans le paquet GeoIP, les pays ne sont pas identifiés et aucun point n'apparaît sur la carte :

sudo apt install geoip-bin geoip-database # Vérification : geoiplookup 8.8.8.8
Sudo n'est pas requis : la base /usr/share/GeoIP/GeoIP.dat est lisible par tous, les résultats sont mis en cache dans tmp/geoip_cache.json. La carte elle-même (Leaflet + tuiles OpenStreetMap) se charge dans le navigateur : une connexion internet est nécessaire sur l'ordinateur où le tableau de bord est ouvert.

31. Exposition externe, mises à jour et mises à jour automatiques

Deux cartes intégrées au tableau de bord qui n'affichent pas l'état « activé/désactivé » d'un outil, mais le niveau réel de protection du serveur. Aucune installation requise, lecture locale sans sudo.

Exposition externe — nombre de services à l'écoute sur toutes les interfaces (0.0.0.0/[::]) et accessibles de l'extérieur. Passe au rouge si une base de données ou un cache est exposé vers l'extérieur (MySQL, PostgreSQL, Redis, MongoDB, Memcached, Elasticsearch) — c'est une faille directe (−10 au Score de sécurité). Source : ss -tuln.

Si la carte est rouge — fermez l'accès externe à la base de données : liez-la à 127.0.0.1 (bind-address dans la config MySQL/PostgreSQL, bind 127.0.0.1 dans Redis) ou fermez le port dans UFW.
« Port ouvert » ≠ « accessible de l'extérieur ». Un service à l'écoute sur 127.0.0.1 (loopback) n'est visible que du serveur lui-même — impossible de l'atteindre de l'extérieur, même si le port est « ouvert ». C'est pourquoi Postfix sur le port 25, lié au loopback, est sûr : la configuration automatique définit inet_interfaces = loopback-only (plus un smtpd_banner neutre — ce qui lève la remarque Lynis MAIL-8818 sur la divulgation de version). La carte « Exposition externe » ne compte comme exposé que ce qui écoute sur 0.0.0.0/[::] ; les services loopback n'y figurent pas.
Lynis MAIL-8818 manuellement (si vous avez installé la messagerie vous-même) : dans /etc/postfix/main.cf définissez smtpd_banner = $myhostname ESMTP (sans version ni OS) et inet_interfaces = loopback-only, puis sudo systemctl restart postfix.

Mises à jour de sécurité — nombre de correctifs de sécurité en attente d'installation et nécessité de redémarrer après une mise à jour du noyau (−5 au Score de sécurité en présence de correctifs). Source : /usr/lib/update-notifier/apt-check, fichier /var/run/reboot-required. Liste détaillée — sur la page « Mises à jour de sécurité ».

# Installer les mises à jour : sudo apt update && sudo apt upgrade # Vérifier ce qui écoute vers l'extérieur : ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
La carte des mises à jour fonctionne sur Ubuntu/Debian (update-notifier-common). Si apt-check est absent — le moniteur compte les correctifs via apt-get -s upgrade.

Mises à jour de sécurité automatiques (unattended-upgrades) — sur la page « Mises à jour de sécurité », une carte dédiée indique si l'installation automatique des correctifs de sécurité est activée et quand elle s'est exécutée pour la dernière fois. Sudo n'est pas nécessaire — l'état est lu via apt-config dump.

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # activer # Vérifier ce qui est activé : apt-config dump | grep Unattended-Upgrade

Maintenance

32. Sauvegarde

La sauvegarde est votre principale assurance : perdre ses données est pire que n'importe quel piratage. Il faut deux choses — une sauvegarde du serveur/des sites et, séparément, une sauvegarde de la BDD du panneau (elle contient les utilisateurs, les clés WebAuthn, les paramètres, la licence).

Option A — HestiaCP : onglet Backup de l'utilisateur → bouton de création de sauvegarde (ou selon planification dans les paramètres du serveur). La sauvegarde inclut les sites et leurs BDD.

Option B — manuellement (cron) : dump de la BDD + archive du dossier data/ du panneau :

# cron root (sudo crontab -e) — sauvegarde quotidienne à 2:30 (adaptez vos noms/chemins) : 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 # Supprimer les archives de plus de 14 jours : 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
Une sauvegarde sur le même serveur protège des erreurs, mais pas de la perte du serveur. Copiez les archives vers un stockage externe (autre serveur, S3, rclone vers le cloud). Vérifiez que la restauration fonctionne réellement.

33. Mise à jour et migration du panneau

Mise à jour vers une nouvelle version. Faites d'abord une sauvegarde. Réimportez ensuite les fichiers de code en conservant vos données :

  • écraser (code) : public/, includes/, assets/, cron/, database/, ainsi que les .htaccess racine (contrôleur frontal — le routage ne doit pas rester celui de l'ancienne version), manifest.json, sw.js ;
  • ne pas toucher : config.php (données BDD), data/ (rapports), logs/, tmp/ (sessions et cache).
# Après l'import — vider le cache PHP (si opcache est activé) : sudo systemctl reload php*-fpm
FileZilla affiche SSH_FX_PERMISSION_DENIEDPermission denied. Les fichiers du panneau appartiennent à www-data (c'est ainsi qu'ils ont été réglés lors de l'installation), alors que le client SFTP se connecte sous votre propre utilisateur, qui n'a pas le droit d'écriture. Donner toute le panneau à www-data «pour que ça marche», c'est justement ce qui provoque cette erreur ; ci-dessous trois méthodes, n'importe laquelle résout le problème.
# Variante A (recommandée) — séparer les propriétaires : le code à vous, les dossiers de travail au serveur web. # Le serveur web n'obtient aucun droit d'écriture sur le CODE du panneau : 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 par-dessus les propriétaires actuels (rien n'est déplacé) : 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 — via le groupe www-data. Plus simple, mais le droit d'écriture sur les # fichiers du panneau va aussi au serveur web (en cas de faille PHP, code remplaçable) : 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
Pourquoi la variante A est sûre. Le panneau écrit uniquement dans trois répertoires — data/ (rapports), tmp/ (sessions et cache), logs/ ; ils restent la propriété de www-data. Le reste, c'est du code, et le serveur web n'en a besoin qu'en lecture, ce que lui donne le groupe www-data avec les droits 644. Bénéfice annexe : en cas de faille PHP, les fichiers du panneau ne peuvent plus être réécrits. Sur les panneaux d'hébergement (HestiaCP et similaires), la variante A n'est pas nécessaire : les fichiers du site appartiennent déjà au compte sous lequel vous vous connectez en SFTP, et le serveur web les lit via le groupe.
Piège de la variante B : tout chmod ultérieur sur les fichiers réinitialise le masque ACL, et l'accès disparaît silencieusement. Si après une «remise en ordre des droits» le transfert bute de nouveau sur Permission denied — relancez les deux commandes setfacl.
Le bit 2 de la variante C, c'est setgid : les fichiers transférés par SFTP restent dans le groupe www-data, sinon le panneau ne pourra pas les réécrire. Après la variante C, reconnectez-vous dans FileZilla — le nouveau groupe ne prend effet qu'à une nouvelle connexion. Vérification : id deploy (le groupe www-data doit apparaître) et ls -ld /path/to/monitor (drwxrwsr-x — la lettre s signifie que setgid est activé).

Migration vers un autre serveur :

  1. Sur le nouveau serveur, mettez en place le site + HTTPS (voir la page d'installation manuelle).
  2. Copiez tous les fichiers du panneau avec config.php, data/.
  3. Migrez la BDD : mysqldump sur l'ancien → import sur le nouveau ; corrigez les données BDD dans config.php.
  4. Refaites sur le nouveau serveur : sudoers, appartenance au groupe adm, tâches cron.
  5. La licence est liée au domaine — si le domaine est identique, la clé continuera de fonctionner.

34. Récupération de l'accès (clé, mot de passe ou blocage IP perdus)

Si la connexion échoue, tout se répare directement dans la BDD depuis le serveur. Ouvrez la BDD (le nom se trouve dans config.php) :

sudo mysql MY_DB

Clé WebAuthn perdue (second facteur impossible) — désactivez la 2FA, connectez-vous par mot de passe, enregistrez une nouvelle clé :

UPDATE users SET webauthn_enabled = 0;

Mot de passe oublié — définissez un nouveau hachage (générez-le sur le serveur, puis insérez-le) :

# Générer le hachage du nouveau mot de passe : php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # Dans la BDD (insérez le hachage obtenu) : # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

Vous vous êtes bloqué par le filtre IP — désactivez la restriction :

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
L'accès à la BDD reste toujours possible : sudo mysql sur le serveur, ou phpMyAdmin / la section BDD du panneau d'hébergement. Après la récupération, réactivez WebAuthn et le filtre IP.

35. Toutes les tâches cron au même endroit

Le récapitulatif des tâches se trouve dans le cron root du serveur (ajoutées via sudo crontab -e). Ne gardez que les lignes des outils que vous utilisez ; ajustez les chemins à votre serveur.

# Cron serveur du moniteur (root) — à saisir via : sudo crontab -e # 01:30 — scan ClamAV sur les chemins à risque (web, home, temp) → cartes « Fichiers analysés » et « Dernière analyse » 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — vérification d'intégrité des fichiers AIDE (nécessite un --config explicite) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # au démarrage — restaurer les droits de /var/lib/aide (le fichier tmpfiles du paquet # aide-common.conf les remet à 0700, et le tableau de bord ne voit plus la base) @reboot chmod 755 /var/lib/aide # 03:00 — audit de sécurité Lynis 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — mise à jour de la liste de blocage ipsum (level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # le set ipsum est chargé au démarrage par le service ipsum-load.service (AVANT le pare-feu, sinon # UFW ne voit pas le set dans before.rules) — pas via cron. Ici uniquement le rafraîchissement quotidien ci-dessus. # 06:00 — rapport Logwatch 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # toutes les 30 min — contrôle SMART des disques */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — intégrité des paquets debsums 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — rapport planifié par Email et Telegram 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # toutes les heures — mise à jour des listes de paquets (pour la carte « Mises à jour de sécurité ») 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # toutes les 5 min — instantané des ressources (CPU/RAM/réseau/disque) pour la page « Performances » */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1
Les détails de chacune se trouvent dans les sections correspondantes. Les tâches de sauvegarde (section précédente) s'ajoutent au même cron. Après modification, vérifiez : sudo crontab -l et que le service cron est actif.
L'heure cron = fuseau horaire du serveur, pas le TIMEZONE de config.php. La constante TIMEZONE n'influe que sur PHP (affichage des dates par le tableau de bord), mais le démon cron lance les tâches selon l'heure système de l'OS. Si le fuseau du serveur ne correspond pas au vôtre, le rapport « 08:00 » arrivera à la mauvaise heure. Exemple : le serveur est dans un autre fuseau (Europe/London, UTC+1), et vous à Paris (UTC+2) → le rapport « 08:00 » arrivera à 09:00 chez vous. Vérifiez et, au besoin, alignez le fuseau du système sur le vôtre :
# Vérifier le fuseau actuel du serveur : timedatectl # Définir votre fuseau (exemple) et redémarrer cron : sudo timedatectl set-timezone Europe/Paris sudo systemctl restart cron
Après cela, la ligne 0 8 * * * se déclenchera à 08:00 heure locale. Sinon il faudrait décaler cron lui-même, mais au passage à l'heure d'hiver/été le décalage se briserait de nouveau — mieux vaut donc régler le fuseau du système.
Scripts wrapper prêts à l'emploi. Leurs copies de travail et un exemple de crontab (crontab.txt) se trouvent dans le dossier system/ à côté du projet, hors de public_html. Ce n'est pas une partie du site — inutile de les téléverser dans la racine web ; placez-les sur le serveur aux chemins système (comme dans le crontab ci-dessus) :
  • lynis-scan.sh/usr/local/bin/ (chmod +x) — lance lynis audit system, pose le drapeau /tmp/lynis-running pendant le scan et copie lynis-report.dat dans data/lynis/ du tableau de bord ;
  • logwatch_daily.sh/usr/local/bin/ (chmod +x) — génère le rapport quotidien Logwatch (sshd, fail2ban, sudo, postfix) dans data/logwatch/ ;
  • smart-scan.sh/usr/local/bin/ (chmod +x) — relève l'état des disques (smartctl) dans data/disk/ ;
  • debsums-scan.sh/usr/local/bin/ (chmod +x) — vérifie l'intégrité des paquets (debsums) dans data/debsums/ ;
  • clamav-scan.sh/usr/local/bin/ (chmod +x) — scan antivirus ClamAV sur les chemins à risque (web, home, temp) ; écrit le récapitulatif dans /var/log/clamav/scan.log, d'où la page ClamAV le lit (ligne 01:30 du crontab ci-dessus) ;
  • load-ipsum.sh/usr/local/bin/ (chmod +x) — met à jour l'ensemble ipset ipsum (level 1) sur place, sans casser les règles actives du pare-feu (ligne 04:00 dans le crontab ci-dessus);
  • daily-report-all.sh/usr/local/bin/ (chmod +x) — lance le rapport cron/daily_report.php du tableau de bord (ligne 08:00 du crontab ci-dessus) ;
  • daily_report.php — déjà inclus dans le tableau de bord (cron/daily_report.php), lancé via daily-report-all.sh, inutile de l'installer séparément ;
  • collect-metrics-all.sh/usr/local/bin/ (chmod +x) — lance cron/collect_metrics.php du tableau de bord (page « Performances », ligne */5 du crontab ci-dessus) ; collect_metrics.php est déjà inclus dans le tableau de bord, inutile de l'installer séparément ;
  • crontab.txt (system/cron/) — exemple de tâches ; saisissez les lignes voulues via sudo crontab -e.
Le chemin du script dans le crontab doit correspondre à l'endroit où vous l'avez placé.
Comment placer un script dans /usr/local/bin/. Impossible d'y écrire directement depuis FileZilla — le répertoire appartient à root, et le client SFTP recevra SSH_FX_PERMISSION_DENIED. Voici la marche à suivre : d'abord téléverser le fichier dans /tmp (où tout le monde peut écrire), puis le déplacer à sa place en une seule commande :
# dans FileZilla : dans le champ « Site distant » saisir /tmp et y téléverser le script, # puis en SSH (install définit d'emblée le propriétaire et les droits, chown/chmod inutiles) : sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # vérification : le fichier est en place, droits rwxr-xr-x, syntaxe intacte bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
Ne confondez pas les répertoires : il faut /tmp à la racine du serveur — pas /var/tmp ni tmp/ à l'intérieur du tableau de bord lui-même (ce dernier appartient à www-data et est fermé à votre utilisateur). Dans l'arborescence FileZilla, /tmp est une branche de premier niveau, à côté de var, et non à l'intérieur de celle-ci.
Serveur installé par configuration automatique ? Ces wrappers et leurs tâches cron sont déjà installés par le script (dans /usr/local/bin/, log — /var/log/arciveo-cron.log) — rien à faire manuellement.
Où les scripts cherchent le tableau de bord. Les wrappers sont indépendants du domaine : ils trouvent les installations du tableau de bord en parcourant /home/*/web/*/public_html et /var/www/*, et déposent les rapports dans leur data/. Si le tableau de bord se trouve à un autre chemin — ajoutez-le à la ligne for app in … dans les scripts, sinon les rapports Lynis/SMART/debsums/Logwatch n'arriveront pas au tableau de bord.
cron.log et droits d'accès. Le fichier logs/cron.log est créé en premier par le cron root — il appartiendra à root, et l'onglet « Journal cron » du tableau de bord ne pourra ni le lire ni le vider. Créez le fichier à l'avance au nom de l'utilisateur web (propriétaire du répertoire du site ; sous HestiaCP c'est le compte, p. ex. admin) — le cron root ne fera alors qu'y ajouter, sans changer le propriétaire :
# créer à l'avance depuis l'utilisateur web (avant d'ajouter les lignes cron) : sudo -u OWNER touch /path/to/monitor/logs/cron.log # si cron.log est déjà créé par le cron root — le donner à l'utilisateur web : sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
Connaître le propriétaire du répertoire : stat -c %U /path/to/monitor.
Gestion depuis le tableau de bord. La section « Système » comporte une page « Crontab » — on peut consulter et ajouter des tâches sans SSH. Le tableau de bord n'édite que les tâches ajoutées par lui-même (bloc distinct dans le crontab root, marqué par des commentaires de service) ; tout ce qui figure déjà dans le crontab (liste ci-dessus) y est affiché en lecture seule dans une liste « Autres tâches du serveur » avec un bouton « Copier vers l'éditeur » — il ne fait que reporter la planification/commande dans le formulaire d'ajout, sans toucher la ligne d'origine. Pour « faire passer » une tâche existante sous la gestion du tableau de bord — copiez-la dans l'éditeur, enregistrez, puis supprimez l'ancienne ligne manuellement (sudo crontab -e), sinon elle s'exécutera deux fois.
Configuration unique sur le serveur. La page a besoin d'un script wrapper privilégié — pas d'un simple sudo crontab (ce serait une escalade directe vers root pour quiconque accède à la session du tableau de bord), mais d'un script étroit à deux commandes (list/set) qui ne touche que son propre bloc entre les commentaires de service. Installez-le une fois :
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'utilisateur web peut différer de www-data — vérifiez sous quel compte tourne le pool PHP-FPM du site (ps -o user= -C php-fpm), et indiquez-le dans la ligne sudoers.
Un nouveau fichier a été téléversé avec le mauvais propriétaire — la page répond « Access denied. ». Si le fichier public/crontab_monitor.php a été chargé par FTP/SFTP sous un autre utilisateur système (par exemple root) que le reste des fichiers du site, le serveur web ne pourra pas le lire. Comparez le propriétaire et les droits avec un fichier voisin et alignez-les :
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

Diagnostic

36. L'outil est installé, mais affiche « Non installé »

Le moniteur détecte la présence des outils via dpkg-query — la base de paquets APT. Si un outil n'est pas installé via apt (manuellement, depuis snap ou depuis les sources), dpkg ne le voit pas.

# Vérifier via dpkg : dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # Trouver le chemin du binaire : which ufw fail2ban-client auditctl # Test sudo depuis www-data : sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

37. Résolution des problèmes (500, absence de données)

Erreur 500 — vérifiez les journaux de PHP, nginx et du moniteur lui-même :

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # Journaux du moniteur : tail -50 logs/monitor_$(date +%Y-%m-%d).log # Droits sur les dossiers : ls -la data/ tmp/ logs/
Tableau de bord sur un panneau d'hébergement (HestiaCP, ISPmanager, cPanel) ? Là, PHP ne fonctionne pas sous www-data, mais sous le compte de l'utilisateur (par exemple admin — propriétaire du répertoire du site). Toutes les règles sudo et l'appartenance aux groupes (adm, systemd-journal) doivent être définies pour cet utilisateur, sinon les modules afficheront « Inactif / 0 » alors que les services fonctionnent. Pour connaître l'utilisateur PHP réel : ps -o user= -C php-fpm | sort -u ou le propriétaire du répertoire du site stat -c '%U' /path/to/monitor. Ensuite, dans toutes les commandes ci-dessous, remplacez www-data par cet utilisateur. L'installation automatique détecte l'utilisateur web elle-même et configure sudoers pour lui.

Les données ne s'affichent pas — il s'agit presque toujours de droits sudo non définis. Vérifiez la commande précise en tant qu'utilisateur web (remplacez www-data par le vôtre). L'option -n = sans mot de passe, comme PHP — si un mot de passe est demandé, c'est qu'il n'y a pas de règle dans sudoers :

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
Le module indique « Inactif » / « 0 » alors que l'outil fonctionne (par exemple sudo aa-status affiche des profils dans le terminal, mais la page « AppArmor » indique « Inactif »). Cause : l'utilisateur web n'a pas de droit sudo sur la commande de ce module. Vérifiez-la dans la liste ci-dessus : si un mot de passe est demandé — ajoutez la ligne manquante dans /etc/sudoers.d/monitor (« Configuration de sudo »). Commandes « nouvelles » fréquentes : /usr/sbin/aa-status (MAC), /usr/sbin/psad --Status (PSAD).
Si une page précise (Falco, ModSecurity, Auditd, ports UFW ouverts) est vide — comparez avec la liste de la section sur sudo : il est probable que apache2ctl, ausearch, aa-status ou ss ne soit pas autorisé, ou que l'utilisateur web ne soit pas dans les groupes adm/systemd-journal (c'est de là que sont lus les journaux fail2ban/auth/modsec et journalctl — Falco et les événements du noyau).

38. La page est vide alors que les données existent sur le serveur

Symptôme : les données existent sur le serveur (visibles via shell), mais la page affiche « aucune donnée » ou un statut incorrect — par exemple AIDE indique « Non initialisée » alors que la base est créée.

La cause est open_basedir : de nombreux panneaux et hébergeurs restreignent le pool PHP-FPM au répertoire du domaine, si bien que les fonctions PHP file_exists(), file_get_contents(), filemtime() sont bloquées sur les chemins système (/var/lib/aide, /var/log, /proc…). Le moniteur contourne cela en lisant ces chemins avec les commandes système standard (cat, test, stat).

# Le fichier est-il visible via shell (ainsi le lit le moniteur) : sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # Valeur actuelle de open_basedir pour le pool du domaine : grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
Si le shell « voit » le fichier (VISIBLE) mais pas la page — c'est open_basedir. La bonne solution est la lecture par commandes système (déjà en place pour AIDE et le moniteur réseau). Étendre open_basedir à /var, /proc est inutile et moins sûr.

39. La page SSL ne fonctionne pas

Le monitor vérifie les certificats en se connectant directement aux domaines via le port 443. Si le domaine est inaccessible depuis le serveur lui-même ou si le port est fermé par le pare-feu, la vérification échouera.

# Vérifier le certificat manuellement : echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # Vérifier la disponibilité : curl -I https://monitor.example.com
Le monitor récupère automatiquement les domaines à partir des configs Nginx (/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) et Apache (/etc/apache2/sites-enabled/) ainsi que l'hôte actuel depuis HTTP_HOST.
Détection automatique des sous-domaines. Les sous-domaines sont détectés automatiquement à partir des journaux publics Certificate Transparency et vérifiés via le réseau, même s'ils sont hébergés sur d'autres serveurs. Aucun ajout manuel n'est nécessaire.

40. Une seule BDD visible sur plusieurs

Le moniteur se connecte à MySQL avec l'utilisateur défini dans config.php, qui n'a accès qu'à sa propre base. MySQL n'affiche dans information_schema que les bases sur lesquelles l'utilisateur a des privilèges — les autres restent donc invisibles.

Pour que le moniteur voie toutes les BDD, accordez à cet utilisateur un droit en lecture seule (une seule fois en tant que root ; remplacez par le nom d'utilisateur de config.php) :

sudo mysql -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* ne donne qu'un droit de lecture — impossible de modifier, supprimer ou créer quoi que ce soit, c'est sans danger pour la surveillance.
Sans ce GRANT, le tableau de bord ne voit que sa propre base — ce n'est pas une erreur, mais une limitation de droits. Le tableau de bord n'utilise aucun sudo mysql : la liste des bases passe par sa propre connexion PDO.

41. PostgreSQL n'apparaît pas sur la page « Base de données »

PostgreSQL requiert un accès de niveau utilisateur postgres, dont ne dispose pas l'utilisateur web du panneau. Ouvrir un large sudo psql depuis PHP n'est pas sûr — à la place, le panneau appelle un wrapper restreint sans paramètres, qui affiche uniquement la version, le nombre de connexions et la liste des bases avec leurs tailles. Créez-le :

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 # dans /etc/sudoers.d/monitor (utilisateur = celui sous lequel tourne PHP-FPM) : # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
Si vous n'utilisez pas PostgreSQL — retirez la ligne monitor-pgstat de sudoers (étape 13 de l'installation manuelle) et ne créez pas le script : la carte PostgreSQL restera simplement inactive.

42. Une alerte s'est déclenchée — que faire

Le tableau de bord montre ce qui se passe ; ci-dessous, que faire dans les cas courants. Principe général : ne pas paniquer, comparer avec l'activité légitime (vos actions, mises à jour, sauvegardes), et réagir selon la gravité.

  • Carte des attaques / nombreux bannissements fail2ban — c'est normal pour tout serveur exposé sur Internet (les bots testent en permanence SSH/web). L'essentiel est que les bannissements se déclenchent. Vérifiez que la connexion SSH se fait uniquement par clé (mot de passe désactivé) et que votre IP figure dans ignoreip.
  • ModSecurity a bloqué des requêtes — le WAF repousse les attaques sur le site, c'est son rôle. S'il bloque votre trafic légitime (faux positif), trouvez le rule id dans les détails et ajoutez une exception à la configuration CRS.
  • AIDE : fichiers modifiés — comparez la liste avec ce que vous avez fait (mise à jour de paquets, édition de configs : normal). Des modifications de binaires système auxquels vous n'avez pas touché sont un motif de vigilance. Après des modifications légitimes, mettez à jour la base AIDE.
  • debsums : binaires/bibliothèques modifiés (hors /etc, hors /usr/share) — falsification potentielle. Vérifiez le paquet : debsums PACKAGE_NAME, en cas de doute réinstallez-le (apt install --reinstall).
  • ClamAV / maldet : menace détectée — examinez le fichier en quarantaine, ne l'ouvrez pas. S'il s'agit d'un web-shell dans le répertoire du site, isolez le serveur et cherchez le point d'entrée (extension vulnérable, fuite d'accès).
  • Falco : événements critiques (lancement d'un shell dans un conteneur, accès à des fichiers sensibles) — analysez l'événement : à quel processus il appartient, ce qui l'a lancé. Il s'agit souvent d'une activité admin légitime.
  • Exposition externe : SGBD/cache en rouge — fermez immédiatement : liez le service à 127.0.0.1 ou fermez le port dans UFW. C'est une vraie faille.
  • SSL expire / a expiré — renouvelez le certificat (Let's Encrypt se renouvelle seul ; sinon, vérifiez certbot renew ou les réglages dans le tableau de bord).
  • Mises à jour de sécurité en attente — installez-les : sudo apt update && sudo apt upgrade ; après une mise à jour du noyau, redémarrez le serveur.
Signes d'une compromission réelle (processus/utilisateurs inconnus, binaires modifiés, spam sortant, tâches cron inconnues) : coupez l'accès externe au serveur, faites une sauvegarde pour analyse et, si les données sont critiques, reconstruisez un serveur propre à partir d'une sauvegarde de confiance — éliminer un rootkit de façon fiable est difficile.
Arcivéo - Security Monitor © 2026