Ce guide couvre l'installation, la configuration et la maintenance d'Arcivéo Security 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.
L'installation du panneau se fait sur des pages dédiées, étape par étape. Choisissez une méthode :
Arcivéo Security 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.
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.
Deux rythmes de travail. Les pages du tableau de bord interrogent les outils au moment où vous les ouvrez — vous voyez toujours l’état courant. À côté de cela, des tâches de fond tournent selon un planning (cron) :
La liste complète des tâches se trouve dans la section « Toutes les tâches cron au même endroit ».
Le score part du maximum et diminue à chaque problème détecté :
PermitRootLogin yes) — −20PasswordAuthentication yes) — −10X11Forwarding activé, MaxAuthTries supérieur à 3 ou sshd_config lisible par tous les comptes — −5TRACE est activée, ou l’en-tête Strict-Transport-Security ou X-Frame-Options manque — −5Résultat : 80+ = Protégé, 60–79 = Attention, <60 = Menacé.
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.
Il y a quatre canaux : Telegram, Email, webhook et push dans le navigateur. Ils sont indépendants — activez ceux que vous voulez, ou les quatre. Le rapport de sécurité quotidien, les alertes de charge et les découvertes du capteur d’événements passent par ces mêmes canaux, configurés une seule fois ici, dans « Paramètres ».
Telegram. Il faut un jeton de bot et un chat id :
@BotFather → /newbot → obtenez un jeton du type 123456:ABC....@userinfobot, ou ouvrez https://api.telegram.org/bot<TOKEN>/getUpdates et repérez "chat":{"id":...}.Email. Deux méthodes au choix dans « Paramètres » → Email :
re_...) et un domaine d'expéditeur vérifié.Webhook. Une seule adresse acceptant un POST couvre Slack, Discord, Mattermost, n8n et votre propre gestionnaire. « Paramètres » → Webhook : collez l’adresse et activez l’interrupteur.
{"text": …}, Discord reçoit {"content": …} (Discord rejette les messages de plus de 2000 caractères, le texte est donc tronqué) ;text, event, severity, source, count, host, ts — inutile d’analyser à nouveau le texte du message.Notifications push. Le message arrive directement dans le navigateur et, si le tableau de bord est ajouté à l’écran d’accueil du téléphone, sous forme de notification d’application ordinaire — sans bot ni serveur de messagerie entre les deux. « Paramètres » → Notifications push : activez l’interrupteur et appuyez sur « Connecter cet appareil » sur chaque appareil qui doit recevoir les notifications. Le compteur « Appareils connectés » indique combien il y en a.
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.
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) :
my.arciveo.com → section « Licences » / « Activation de licence » — copiez le code ARCIVEO-….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 ».Le panneau vérifie la clé de manière cryptographique : signature, liaison au domaine et durée de validité.
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.
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. 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.
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.
Le tableau de bord est protégé par une connexion avec mot de passe et, s’il est activé, par le filtre IP. Aucun des deux ne convient à un programme : les systèmes externes disposent donc de leur propre entrée — deux adresses en lecture seule protégées par un jeton. Le jeton ne change rien sur le serveur : il permet uniquement de récupérer des indicateurs.
Activation — « Paramètres » → « API en lecture seule » : l’interrupteur « Activer l’API » ; le jeton est créé automatiquement au premier enregistrement. Le bouton « Nouveau jeton » en délivre un autre et coupe au même instant tous ceux qui utilisaient le précédent.
/api/status.php — JSON : score de sécurité, état des modules, dernière mesure de charge avec le top des processus et les événements de sécurité des dernières 24 heures ;/api/metrics.php — les mêmes chiffres au format texte de Prometheus (métriques arciveo_* : arciveo_security_score, arciveo_fail2ban_banned, arciveo_cpu_busy_pct, etc.).Si votre client ne sait pas poser d’en-tête, le jeton est aussi accepté en paramètre — ?token=…. Sachez que sous cette forme il se retrouve dans le journal d’accès du serveur web ; l’en-tête est donc préférable.
Prometheus. Un bloc prêt pour scrape_configs :
api disabled — l’API est désactivée dans les paramètres. Les requêtes rejetées sont écrites dans le journal de l’application avec l’IP de l’émetteur : une tentative de deviner le jeton est donc visible sur la page « Logs de l’application ».
Le tableau de bord s’installe sur chaque serveur et répond très bien à la question « comment va cette machine ». Il ne répond pas à la question « comment vont toutes mes machines » — pour cela, chaque tableau de bord peut envoyer un résumé bref vers l’espace client, où les serveurs apparaissent dans une seule liste.
Ce qu’il faut prendre dans l’espace client my.arciveo.com → section « Mes serveurs » : un jeton y est délivré (de la forme fk_…) et l’adresse du destinataire https://my.arciveo.com/api/fleet.php y est indiquée. Le jeton est unique par compte — le même pour tous vos serveurs.
Ce qu’il faut régler dans le tableau de bord — « Paramètres » → « Vue d’ensemble des serveurs » : adresse du destinataire, jeton, nom du serveur (tel qu’il sera libellé dans la liste ; laissé vide, c’est le domaine du tableau de bord qui est pris) et l’interrupteur « Envoyer le résumé ». Le bouton « Envoyer maintenant » vérifie la liaison immédiatement, sans attendre le planning.
Ensuite le résumé part par cron, toutes les 15 minutes :
Ce qui part exactement : le nom et le domaine du serveur, l’OS, la durée de fonctionnement, la version de PHP ; le score de sécurité ; l’état des modules (UFW, nombre de jails Fail2ban, bannissements du jour, si CrowdSec, Suricata et Falco tournent, menaces ClamAV, modifications AIDE, indice Lynis, mises à jour en attente, si un redémarrage est nécessaire, jours avant l’expiration du certificat, nombre de ports ouverts dangereux) ; la dernière mesure de charge (load, CPU, RAM, disque, inodes) ; les compteurs d’événements des dernières 24 heures et le dernier événement critique. Le résumé ne contient ni journaux, ni mots de passe, ni contenu de fichiers.
sudo crontab -l) et ce que répond le bouton « Envoyer maintenant ». Les erreurs d’envoi sont écrites dans le journal de l’application sous l’étiquette FLEET — on y voit si le jeton a été rejeté (HTTP 401) ou si la connexion elle-même n’a pas abouti.
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.
ufw enable, autorisez impérativement SSH (ufw allow OpenSSH), sinon vous perdrez l'accès au serveur.
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.
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.
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 :
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 ») :
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 :
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 ».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.
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 :
@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 ») :
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.
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.
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).
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é :
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).
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.
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 :
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.
/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 :
chmod 755 /var/lib/aide et le cron de vérification à 02:00 — rien à faire manuellement.
sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.
Antivirus pour Linux. Particulièrement utile pour analyser /var/www à la recherche de shells PHP et de code malveillant.
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 :
2. La base de signatures n'est pas téléchargée — clamd ne démarre pas sans elle :
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).
sudo journalctl -u clamav-daemon -n 30 --no-pager.
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.
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.
maldet --report.
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é.
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 ».
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).
/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 :
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.
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.
/etc/passwd, écriture dans les répertoires système). Zéro événement critique par jour sur un serveur calme est un état sain.
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.
ModSecurity — pare-feu applicatif (WAF) pour Apache ou Nginx. Il bloque les attaques au niveau applicatif : injections SQL, XSS, traversées de chemin, scanners.
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 :
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.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.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.---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.
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.
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.
Surveille les services (nginx, php-fpm, mysql, etc.) et les redémarre en cas de panne. Peut envoyer des alertes par e-mail.
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.
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 :
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.
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.
psad --Status (requis dans sudoers). Sans journalisation iptables, la page restera vide — c'est normal tant qu'aucun scan n'a eu lieu.
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).
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.
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.
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.
Le wrapper debsums-scan.sh trouve lui-même le dossier data/ du tableau de bord — inutile d'indiquer le chemin.
/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.
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.
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 :
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.
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 :
/proc/net/dev ;ip ;ss ;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 :
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.
La page intégrée affiche trois éléments :
df) ; la barre passe au rouge à ≥90 % ;lsblk), réels uniquement (loop/snap masqués) ;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.
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.
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. La fenêtre de 24 heures est celle par défaut : les boutons en haut la basculent sur 7 jours ou 30 jours.
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.
Deux profondeurs de conservation. Les instantanés « bruts » de cinq minutes vivent une journée (plus une heure de marge, pour que le point le plus ancien du graphique ait encore un prédécesseur servant au calcul). Chaque heure achevée est repliée en une seule ligne ; cette série horaire est conservée 30 jours et c’est précisément elle que lisent les vues « 7 jours » et « 30 jours ». Un mois d’historique coûte ainsi environ 720 lignes au lieu de près de 8600.
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.
Top des processus. Sous les graphiques figurent deux tableaux des cinq processus les plus gourmands en CPU :
Les deux tableaux décrivent toujours les dernières 24 heures, même quand les graphiques montrent un mois : seules les lignes de cinq minutes portent un instantané des processus, et elles ne sont pas conservées plus d’une journée.
ps, le script PHP de collecte et la chaîne cron/sudo au-dessus) sont exclus de la liste — sinon ils occuperaient les premières places de chaque instantané : au moment de son lancement, ps affiche près de 100 % de CPU. Les autres tâches cron lancées dans la même minute apparaissent bien, et c’est précisément à cela que sert le tableau.
Alertes de charge (section « Paramètres » → « Alertes de charge ») — lorsque le seuil CPU/RAM/disque/inodes est dépassé, le tableau de bord envoie une notification sur tous les canaux activés (Telegram, Email, webhook, push — les mêmes que pour le rapport quotidien ; rien à activer séparément pour les alertes), puis une autre lorsque la métrique revient à la normale. Tant que le seuil est maintenu, il n’y a pas de spam : la notification suivante n’arrive qu’après un cycle « revenu à la normale → dépassé de nouveau ».
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.
Le rapport quotidien répond à la question « comment va le serveur ». Le capteur répond à une autre — « que vient-il de se passer ». Toutes les 5 minutes, il interroge les sources rapides et n’enregistre que ce qui a changé depuis le contrôle précédent ; la page « Événements de sécurité » du menu affiche le journal des découvertes.
Le capteur ne touche pas aux contrôles lents — Lynis, debsums, mises à jour des paquets, expiration du SSL : leurs chiffres ne changent de toute façon qu’une fois par jour et restent du ressort du rapport quotidien.
Ce qu’il remarque — neuf sources :
[Priority: 1] élève la découverte au niveau critique ;FOUND dans le journal d’analyse ;root (critique) et une connexion depuis une adresse jamais vue auparavant (avertissement) ;Un outil qui n’est pas installé, ou dont le tableau de bord ne peut pas lire le journal, se tait simplement — ce n’est ni une erreur ni un événement.
data/secwatch_state.json), et le premier passage ne fait qu’écrire cette marque. Sinon, activer la surveillance déclencherait une notification sur tout ce que les journaux ont accumulé pendant des mois.
Configuration — « Paramètres » → « Surveillance des événements » :
23-7 pendant laquelle les avertissements ne sont pas envoyés.Les notifications passent par les mêmes canaux que le rapport quotidien (Telegram, Email, webhook, push) — rien à activer en plus. Toutes les découvertes sont inscrites au journal indépendamment du fait qu’une notification soit partie ou non, et y sont conservées 90 jours.
Le journal. Filtres : période (24 heures, 7, 30 ou 90 jours), importance et source ; les 500 derniers enregistrements sont affichés. Le bouton « Vérifier maintenant » lance exactement le même passage que cron — pratique juste après la configuration, pour ne pas attendre cinq minutes ; il indique aussi la raison lorsque aucune notification n’est partie (« en dessous du seuil d’importance », « heures silencieuses », « aucun canal de notification configuré »). Le compteur rouge près de l’élément de menu est le nombre d’événements critiques des dernières 24 heures.
La fiche d'adresse. Un clic sur une ligne du journal des événements ou du bloc Adresses corrélées ouvre tout ce que l'on sait d'une adresse : la chronologie de ses événements, son pays et, dès la première ligne, une conclusion. La ligne du journal ouvre la fiche lorsque l'événement porte sur exactement une adresse ; s'il en cite plusieurs, ce sont les adresses à l'intérieur de la ligne qui sont cliquables. Il y a quatre conclusions :
Sous la chronologie figure le bloc Ce qui se passait sur le serveur au même moment : des événements qui n'ont aucune adresse. AIDE ignore qui a modifié le fichier, ClamAV ignore qui a apporté le fichier infecté. Sont affichés ceux qui tombent dans l'activité de l'adresse et les six heures qui suivent. C'est justement la connexion associée à ce qui a suivi qui sépare une tentative d'un accès réel.
Planning. La tâche se place dans le crontab root (sudo crontab -e), décalée de 2 minutes par rapport à la collecte des métriques, pour que les deux ne démarrent pas en même temps :
php cron/security_watch.php. Le wrapper trouve toutes les installations du tableau de bord sur le serveur et lance le contrôle au nom du propriétaire du site — avec exactement les droits sudo que le tableau de bord utilise depuis le web. Un appel direct depuis le crontab root créerait le fichier d’état data/secwatch_state.json appartenant à root, et le bouton « Vérifier maintenant » cesserait de fonctionner.
La page « Serveur web » analyse le journal d’accès et montre combien il y a eu de requêtes, quelle part s’est soldée par une erreur et qui les a émises. ModSecurity répond à la question « qu’est-ce qui a été bloqué », cette page à la question « qu’est-ce qui est arrivé sur le site ».
Ce que l’on y voit : le nombre de requêtes, le RPS, le taux d’erreurs, un compteur 5xx distinct et le volume servi ; un graphique des requêtes par heure ; la répartition des codes 2xx/3xx/4xx/5xx ; les adresses fréquentes, les IP fréquentes, les erreurs fréquentes (code plus chemin) et les clients (User-Agent). La période : 24 heures ou 7 jours.
La source est le journal d’accès au format combined, ce qui convient aussi bien à Nginx qu’à Apache, sans module supplémentaire. Le tableau de bord cherche le journal parmi les fichiers standard :
/var/log/nginx/access.log/var/log/apache2/access.log/var/log/apache2/other_vhosts_access.log/var/log/httpd/access_logEn plus de ceux-ci, le tableau de bord examine les fichiers *access*.log et *requests*.log dans /var/log/nginx/, /var/log/apache2/ et /var/log/httpd/. Il trouve ainsi aussi le journal qu’un site écrit dans son propre fichier avec la directive CustomLog — par exemple arciveo-monitor_requests.log, créé par l’installateur Arcivéo.
Un seul journal est lu : lorsque Nginx est placé devant Apache, additionner deux journaux compterait chaque requête deux fois. Le tableau de bord prend le premier journal de la liste ci-dessus dans lequel on a écrit au cours des dernières 24 heures (les fichiers trouvés par motif de nom sont essayés ensuite, du plus récent au plus ancien) ; si aucun n’a été écrit depuis une journée, il prend le plus récent non vide. Un journal vidé par logrotate ou laissé par un serveur web arrêté ne masque donc plus le journal actif. Le dernier fragment du journal est lu (3 Mo) et le résultat est mis en cache 5 minutes — sur un site chargé, la période choisie peut donc ne pas être couverte entièrement ; la couverture réelle est indiquée au-dessus du graphique.
adm, et l’utilisateur web n’en fait pas partie. L’installateur Arcivéo l’ajoute à ce groupe ; après une installation manuelle, un petit wrapper appartenant à root, autorisé à faire une seule chose — afficher le journal —, règle le problème (la page ModSecurity fonctionne de la même façon). Le wrapper choisit le journal selon les mêmes règles que le tableau de bord. La commande est affichée sur la page elle-même ; la voici également. Collez-la en entier — c’est une seule commande :
L’utilisateur web peut différer de www-data — vérifiez sous quel utilisateur tourne le pool PHP-FPM du site (ps -o user= -C php-fpm) et indiquez-le dans la ligne sudoers. Si le nom du journal ne contient ni access ni requests, ajoutez son chemin au début de la ligne L=… à l’intérieur du wrapper.
Tentatives d’exploits connus. Le panneau compare les requêtes du journal aux signatures d’une vingtaine d’exploits courants : PHP-CGI, traversée de répertoires dans Apache, PHPUnit, Log4Shell, failles de routeurs, de caméras et de passerelles VPN. Pour chaque tentative, il indique si elle peut fonctionner sur ce serveur en s’appuyant sur des faits : les versions installées d’Apache et de PHP, la présence de Java, le code de réponse du serveur à la requête de fichier. Seul ce qui peut réellement fonctionner est marqué en rouge.
Durcissement du serveur web. Le panneau interroge chaque site du serveur via 127.0.0.1 et vérifie ce que reçoit un visiteur : si les versions du serveur web et de PHP sont visibles, si la méthode TRACE est activée et si les en-têtes de protection Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy et Content-Security-Policy sont présents. Les sites proviennent de l’adresse à laquelle le panneau est ouvert et des fichiers d’hôtes virtuels, si le panneau peut les lire. Le résultat est actualisé toutes les 10 minutes.
S’il manque quelque chose, une seule commande apparaît sous le tableau. Elle crée un fichier séparé, zz-arciveo-hardening.conf, et ne recharge le serveur web qu’après un test de configuration réussi ; les en-têtes déjà envoyés par un site ne sont pas dupliqués. La CSP n’est pas proposée en une ligne : la politique se construit pour chaque site, sinon il ne charge plus ses propres scripts, styles et polices. Pour annuler la modification sous Apache :
ServerSignature) n’est pas signalée lorsque la version du serveur web est déjà masquée : sans version, elle n’affiche que le nom du serveur et le nom d’hôte.
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 :
/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.
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.
127.0.0.1 (bind-address dans la config MySQL/PostgreSQL, bind 127.0.0.1 dans Redis) ou fermez le port dans UFW.
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.
/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é ».
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.
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 :
Mise à jour vers une nouvelle version. Faites d'abord une sauvegarde. Réimportez ensuite les fichiers de code en conservant vos données :
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 ;config.php (données BDD), data/ (rapports), logs/, tmp/ (sessions et cache).SSH_FX_PERMISSION_DENIED — Permission 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.
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.
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.
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 :
config.php, data/.mysqldump sur l'ancien → import sur le nouveau ; corrigez les données BDD dans config.php.adm, tâches cron.Le panneau fonctionne déjà et il vous faut un site de plus sur le même serveur — son propre domaine à côté du moniteur. Après la configuration automatique, le serveur est un Apache + PHP-FPM ordinaire : le site a donc besoin de trois choses — son répertoire, son vhost et son certificat. Le panneau, lui, reste intact : il surveille l'ensemble du serveur, une seconde instance pour le second domaine est inutile.
site2.example.com est l'adresse du nouveau site. Ce peut être aussi bien un domaine distinct (mycompany.com) qu'un sous-domaine (shop.mycompany.com) — pour Apache cela ne change rien, seuls l'enregistrement DNS (étape 1) et l'émission du certificat (étape 5) diffèrent. site2 n'est qu'un nom court du site : il désigne le répertoire /var/www/site2 et le fichier de configuration site2.conf. Il n'a aucun lien avec le domaine, Apache ne prend l'adresse que dans ServerName ; le nom est libre — pour shop.mycompany.com, par exemple, shop est pratique : répertoire /var/www/shop, configuration shop.conf.
1. DNS. Dans l'interface DNS de votre registrar, créez un enregistrement A vers l'IP de ce serveur :
Vérifiez au bout de quelques minutes : dig +short site2.example.com doit renvoyer l'IP du serveur.
2. Répertoire du site.
3. Le vhost. Le bloc se colle dans le terminal en entier — le chemin du socket PHP-FPM est détecté tout seul :
4. Activez le site et vérifiez la configuration avant de recharger :
5. SSL. certbot est déjà installé par la configuration automatique ; à ce stade, le DNS doit pointer vers le serveur :
Un troisième domaine et les suivants s'ajoutent de la même façon — répétez les étapes 1 à 5 en changeant le domaine et le répertoire.
Host ne correspond à aucun ServerName (accès par l'IP ou domaine d'un tiers pointé vers votre serveur), Apache sert le premier site actif dans l'ordre alphabétique de /etc/apache2/sites-enabled/. Le 000-default standard est désactivé par la configuration automatique : ce rôle « de garde » revient donc au vhost du panneau (arciveo-monitor.conf) — cela n'affecte pas les sites appelés par leur propre nom. Si vous voulez qu'un autre site s'ouvre par l'IP, nommez son fichier de manière à ce qu'il passe en premier dans l'ordre alphabétique (par exemple 000-site2.conf).
site2_error.log et site2_access.log, dans /var/log/apache2/.
.htaccess (contrôleur frontal), et un site imbriqué renverrait autre chose que ce que vous attendez. Gardez les sites côte à côte : /var/www/monitor, /var/www/site2.
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) :
Clé WebAuthn perdue (second facteur impossible) — désactivez la 2FA, connectez-vous par mot de passe, enregistrez une nouvelle clé :
Mot de passe oublié — définissez un nouveau hachage (générez-le sur le serveur, puis insérez-le) :
Vous vous êtes bloqué par le filtre IP — désactivez la restriction :
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.
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.
sudo crontab -l et que le service cron est actif.
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 :
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.
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 ;security-watch-all.sh → /usr/local/bin/ (chmod +x) — lance cron/security_watch.php du tableau de bord au nom du propriétaire du site (page « Événements de sécurité », ligne 2-57/5 du crontab ci-dessus) ; security_watch.php fait déjà partie du tableau de bord et n’a pas besoin d’être installé séparément ;fleet-push-all.sh → /usr/local/bin/ (chmod +x) — envoie le résumé du serveur vers l’espace client via cron/fleet_push.php du tableau de bord (ligne 3,18,33,48 ci-dessus) ;crontab.txt (system/cron/) — exemple de tâches ; saisissez les lignes voulues via sudo crontab -e./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 :
/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.
/usr/local/bin/, log — /var/log/arciveo-cron.log) — rien à faire manuellement.
/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.
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 :
stat -c %U /path/to/monitor.
sudo crontab -e), sinon elle s'exécutera deux fois.
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 :
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.
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 :
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.
Erreur 500 — vérifiez les journaux de PHP, nginx et du moniteur lui-même :
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 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).
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).
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).
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.
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.
/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) et Apache (/etc/apache2/sites-enabled/) ainsi que l'hôte actuel depuis HTTP_HOST.
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 : la liste des bases passe par sa propre connexion PDO.
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 :
monitor-pgstat de sudoers (étape 13 de l'installation manuelle) et ne créez pas le script : la carte PostgreSQL restera simplement inactive.
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é.
ignoreip./etc, hors /usr/share) — falsification potentielle. Vérifiez le paquet : debsums PACKAGE_NAME, en cas de doute réinstallez-le (apt install --reinstall).127.0.0.1 ou fermez le port dans UFW. C'est une vraie faille.certbot renew ou les réglages dans le tableau de bord).sudo apt update && sudo apt upgrade ; après une mise à jour du noyau, redémarrez le serveur.