FAQ

Это руководство по установке, настройке и обслуживанию Arcivéo Monitor. Разделы сгруппированы: общий обзор, развёртывание панели, подключение инструментов безопасности, встроенные модули и диагностика. Команды можно копировать кнопкой справа.

С чего начать

01. Установка панели — выберите способ

Установка панели — на отдельных пошаговых страницах. Выберите способ:

Не уверены — берите автоматическую. Этот справочник остаётся единым источником по SSL, инструментам, cron и диагностике — install-страницы ссылаются на его разделы, ничего не дублируя.

Обзор

02. Что такое Arcivéo Monitor

Arcivéo Monitor — панель безопасности сервера. Собирает данные от установленных инструментов (Fail2ban, UFW, Lynis, ModSecurity, AIDE, ClamAV, Auditd, CrowdSec, Suricata, Falco и др.) и отображает их в едином интерфейсе с дашбордом, картой атак и детальными страницами по каждому инструменту.

Монитор не является активным средством защиты — он не блокирует атаки сам по себе. Его задача — агрегировать информацию от уже работающих инструментов и представить её в удобном виде.

03. Как монитор работает на сервере

Монитор работает только локально — он должен быть установлен на том же сервере, который мониторит. Никакого SSH или удалённого API нет.

Все команды (fail2ban-client, ufw status, ipset list и т.д.) панель выполняет от имени пользователя веб-сервера (обычно www-data, на хостинг-панелях — аккаунт сайта) с узким набором прав sudo — только на конкретные утилиты, без общего root-доступа. Результаты парсятся и отображаются в браузере.

Для нескольких серверов устанавливайте монитор на каждом отдельно, с уникальным доменом.

04. Как считается Оценка безопасности

Оценка стартует с максимума и снижается за каждую выявленную проблему:

  • UFW не активен — −30
  • Fail2ban не запущен (нет активных jail) — −25
  • Нет ключей WebAuthn — −15
  • Lynis hardening index < 60 — −20; 60–79 — −10
  • IPset ipsum не загружен — −10
  • Угрозы найдены ClamAV — −20
  • Изменения файлов AIDE — −15
  • SSL истёк — −30, истекает <14 дней — −15, <30 дней — −5
  • CrowdSec установлен, но не запущен — −5
  • Suricata установлена, но не запущена — −5
  • СУБД/кэш (MySQL, PostgreSQL, Redis…) доступны извне — −10
  • Разрешён вход root по SSH (PermitRootLogin yes) — −20
  • Ожидают установки security-обновления — −5

Итог: 80+ = Защищён, 60–79 = Внимание, <60 = Под угрозой.

Вычеты за ClamAV, AIDE, CrowdSec и Suricata применяются только если инструмент установлен. Lynis и AIDE без инициализированной базы показываются как «нет данных» и балл не снижают. Количество атак за сегодня выводится на дашборде, но на Оценку безопасности не влияет.

Настройки и лицензия

05. WebAuthn — двухфакторная аутентификация

WebAuthn — стандарт аутентификации без пароля через аппаратный ключ. Поддерживает YubiKey, Touch ID, Face ID, Windows Hello, Passkey.

После входа по паролю система запрашивает подтверждение через зарегистрированный ключ. Даже если пароль утечёт — без физического ключа или биометрии войти невозможно.

Для настройки откройте Ключи WebAuthn в боковом меню и нажмите «Зарегистрировать ключ». Зарегистрируйте сразу два ключа: если единственный ключ потеряется или сломается, вход в панель по нему будет невозможен.

WebAuthn работает только через HTTPS. На HTTP-соединении регистрация и вход по ключу недоступны.

06. Уведомления: Telegram и Email

Панель умеет слать отчёт безопасности в Telegram и на почту (по кнопке и по расписанию). Настраивается в разделе «Настройки».

Telegram. Нужны токен бота и chat id:

  1. В Telegram напишите @BotFather/newbot → получите токен вида 123456:ABC....
  2. Напишите своему новому боту любое сообщение (чтобы он мог вам отвечать).
  3. Узнайте свой chat id: напишите боту @userinfobot, либо откройте https://api.telegram.org/bot<TOKEN>/getUpdates и найдите "chat":{"id":...}.
  4. Вставьте токен и chat id в «Настройки» → Telegram, нажмите «Сохранить и отправить тест».

Email. Два способа на выбор в «Настройки» → Email:

  • SMTP — хост, порт (465/SSL или 587/TLS), логин и пароль вашего почтового ящика;
  • Resend — современный API: укажите API-ключ (re_...) и подтверждённый домен отправителя.
Кнопка «Отправить тест» сразу проверит канал. Расписание автоматического отчёта — через cron (раздел «Все cron-задания»): он вызывает отправку, а каналы берутся из настроек.

Статус отчёта: «ВНИМАНИЕ» или «ОК». Заголовок становится «ВНИМАНИЕ» только при реальной проблеме или ожидающем действии: найдена угроза ClamAV, изменения файлов в AIDE, критические события Falco (Emergency/Alert/Critical за последние 24 ч), упавший сервис в Monit, требуется перезагрузка, истекает SSL (≤14 дней) или ждут security-обновления. Фоновый шум — SSH-переборы ботов, забаненные fail2ban IP, алерты Suricata, предупреждения Lynis и уже отбитые запросы ModSecurity — статус не поднимает, поэтому такие цифры в отчёте не означают «ВНИМАНИЕ» сами по себе.

07. Лицензия — ввод и активация

Детальные модули мониторинга (Lynis, UFW, ModSecurity, карта атак, AIDE, ClamAV и др.) открываются при наличии действующей лицензии. Без неё дашборд, настройки и аккаунт работают, а модули показывают карточку «Требуется лицензия».

После покупки в кабинете у вас есть код активации вида ARCIVEO-XXXX-XXXX-XXXX-XXXX. Его нужно «активировать» на домен вашей панели — это превратит код в подписанный лицензионный файл (блок [license]), который вы вставляете в панель.

Как активировать (3 шага):

  1. Возьмите код активации. Личный кабинет my.arciveo.com → раздел «Лицензии» / «Активация лицензии» — скопируйте код ARCIVEO-….
  2. Активируйте код на свой домен. Там же в кабинете откройте «Активация лицензии», введите: код активации, ваш email и домен панели (адрес, по которому открывается монитор, напр. monitor.example.com). Нажмите активировать — система сгенерирует лицензионный файл, привязанный к этому домену, и покажет его в поле с кнопкой «Копировать».
  3. Вставьте ключ в панель. Скопируйте весь текст лицензии → в панели откройте «Настройки» → блок «Лицензия», вставьте и нажмите «Сохранить». Модули разблокируются сразу.

Панель проверяет ключ криптографически: подпись, привязку к домену и срок действия.

Домен при активации должен точно совпадать с адресом панели. Возьмите его из константы APP_URL в config.php и введите только имя хоста — без https:// и без префикса www. Активация одноразовая: код превращается в лицензию для введённого домена и повторно не активируется — при ошибке в домене ключ не подойдёт вашей панели, а код будет израсходован. Поэтому вводите домен внимательно.
Срок истёк или сменился домен — в шапке панели появится предупреждение. Лицензия привязана к домену навсегда и на другой домен не переносится: на новый срок или новый домен нужен новый ключ (покупается в кабинете и активируется одноразово).

08. Файл config.php — все настройки панели

Все основные параметры панели заданы в одном файле config.php в корне (рядом с папкой public/) обычными константами define(). Файл создаётся при установке; править его вручную нужно редко — в основном при смене домена, переносе или подключении к другой базе. После любой правки перезапустите PHP-FPM (иначе из-за OPcache изменения не применятся).

Подставьте свои значения в подсвеченные места; остальное оставьте как есть:

// --- База данных --- define('DB_HOST', 'localhost'); // оставить define('DB_NAME', 'db_name'); // что задали при создании БД define('DB_USER', 'user'); // что задали при создании БД define('DB_PASS', 'db_password'); // что задали при создании БД define('DB_CHARSET', 'utf8mb4'); // оставить // --- Приложение --- define('APP_URL', 'https://monitor.example.com'); // адрес панели, без слэша в конце define('TIMEZONE', 'Europe/Kyiv'); // ваш часовой пояс // --- Время сессии --- define('SESSION_LIFETIME', 28800); // простой до повторного входа, сек (28800 = 8 ч)

База данных. Реквизиты подключения к MySQL/MariaDB:

  • DB_HOST — хост СУБД, почти всегда localhost;
  • DB_NAME — имя базы данных панели;
  • DB_USER — пользователь БД (доступ только к своей базе);
  • DB_PASS — пароль этого пользователя;
  • DB_CHARSET — кодировка соединения, оставьте utf8mb4.

Приложение.

  • APP_URL — полный адрес панели (напр. https://monitor.example.com). Должен совпадать с доменом, на который активирована лицензия — иначе ключ будет отклонён (см. раздел «Лицензия»);
  • TIMEZONE — часовой пояс PHP: влияет только на то, как панель показывает даты и время. На время запуска cron-заданий не влияет — там действует пояс системы (см. «Все cron-задания»).

Время сессии. SESSION_LIFETIME — таймаут простоя сессии в секундах (скользящий: обновляется при активности). По умолчанию 28800 = 8 часов; после этого времени бездействия панель попросит войти заново. Например, 3600 = 1 час, 86400 = сутки.

Логирование ошибок. Ошибки никогда не показываются посетителям, а пишутся в logs/php_errors.log — их видно на странице «Логи приложения». Эти строки (display_errors=0, log_errors=1, путь error_log) менять обычно не нужно — настройки заданы прямо в файле и не зависят от php.ini.

config.php — секретный файл. В нём пароль БД. Лежит он в корне панели (рядом с public/), а у этой панели веб-корень (DocumentRoot) — именно корень панели, не public/. Сам по себе файл не «утекает»: в корневом .htaccess для него стоит явный запрет (Require all denied) — сервер возвращает 403. Даже без этого правила исходник не утёк бы: это PHP — сервер его исполняет, а не отдаёт текстом. На всякий случай: не выкладывайте его в публичные репозитории и не передавайте в поддержку с реальным паролем. Права на файл — 640.
При переносе или восстановлении доступа этот файл — главный источник реквизитов: имя БД, пользователь и пароль берутся именно отсюда (см. разделы «Обновление и перенос панели» и «Восстановление доступа»).

Инструменты безопасности

09. Брандмауэр UFW

UFW (Uncomplicated Firewall) — простой интерфейс к nftables/iptables. Закрывает все входящие порты, кроме явно разрешённых. Страница «Брандмауэр UFW» показывает статус и правила.

sudo apt install ufw # Разрешить SSH (обязательно ДО включения!) и веб sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Закрыть БД снаружи (доступ только локально) sudo ufw deny 3306 # Включить и проверить sudo ufw enable sudo ufw status verbose
Перед ufw enable обязательно разрешите SSH (ufw allow OpenSSH), иначе потеряете доступ к серверу.
«Внешняя экспозиция» на дашборде учитывает UFW: порт, закрытый правилом deny, не считается доступным извне.
Skipping adding existing rule — это не ошибка. UFW так сообщает, что точно такое правило уже есть, и повторно его не добавляет. При повторном запуске автонастройки (она идемпотентна) это штатное сообщение — реагировать не нужно.

10. Установка Fail2ban

Автоматически блокирует IP после превышения числа неудачных попыток входа. Анализирует логи SSH, nginx, Apache и других сервисов.

sudo apt install fail2ban sudo systemctl enable --now fail2ban # Проверить статус: sudo fail2ban-client status
Рабочая конфигурация (jail.local с десятками jail'ов и автобаном из ipsum) — в следующем разделе.

11. Рабочая конфигурация Fail2ban + ipsum

Базовая установка — выше. Здесь — рабочая конфигурация, дающая десятки активных jail'ов и тысячи блокировок: общие настройки, ключевые jail'ы и автобан вредоносных IP из списка ipsum.

Файл /etc/fail2ban/jail.local — общие настройки и важнейшие jail'ы:

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # Прогрессивный бан: каждый повтор — дольше bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # перманентный бан за брутфорс SSH findtime = 3600 # Рецидивисты: кто словил несколько банов — банится навсегда [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 # Веб-сервисы (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog): [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … и остальные jail'ы по сервисам (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) — enabled = true
В ignoreip обязательно впишите свой IP и доверенные сети, иначе можно забанить самого себя. После правок: sudo fail2ban-client reload.

Автоподгрузка блок-листа ipsum — в root-крон (sudo crontab -e): level 1 (100+ тысяч IP) грузится в набор ipsum, который отсекается на файрволе (подробнее — в разделе «Блок-лист IPset»):

# 04:00 — обновление ipset ipsum (level 1, максимальный охват): 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
Набор должен называться ipsum — именно его читает дашборд (карточка «IPset ipsum»). Уровни: levels/1.txt — максимальный охват, levels/3.txt — точнее (3+ источника).

Почему «Монитор безопасности» разделён на две зоны. Защита работает на двух уровнях, и дашборд их не смешивает:

  • Реальные атаки (реактив) — всё, что поймал fail2ban: живые попытки взлома (jail'ы sshd, apache-*, nginx-* и т.п.) и злостные рецидивисты (jail recidive — те, кого уже банили несколько раз). Это IP, которые реально к вам ломились — они на карте атак и «Временной шкале».
  • Превентивный блок (проактив) — публичный блок-лист известных вредоносных IP ipset ipsum, отсекаемый на файрволе правилом DROP. Эти адреса в большинстве вашего сервера и не касались — их режут заранее; счётчик «IPset ipsum» показывает, сколько отсечено превентивно.

Разница простая: реактив — «эти атаковали и получили бан», превентив — «этих заблокировали ещё до попытки». Раньше в recidive искусственно загонялся list-3 ipsum (отсюда старое деление «списочные recidive»); теперь recidive — только настоящие рецидивисты, а превентив целиком на файрволе.

12. Блок-лист IPset (ipsum)

ipsum — публичный список вредоносных IP, обновляемый ежедневно. Монитор показывает число загруженных адресов на дашборде и карте атак и учитывает его в Оценке безопасности (−10, если набор не загружен).

Минимальный вариант без fail2ban — отдельный набор ipsum с блокировкой через iptables:

# Создать набор (один раз): sudo ipset create ipsum hash:ip # Скрипт обновления /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, ежедневно в 4:00): 0 4 * * * /usr/local/bin/update-ipsum.sh
Расширенный вариант с fail2ban-recidive — в разделе «Рабочая конфигурация Fail2ban + ipsum».
ipset живёт в памяти и теряется при перезагрузке. Один лишь ежедневный крон оставит набор пустым с момента ребута до следующего запуска (дашборд покажет 0). Грузите набор ещё и при старте — вынесите загрузку в скрипт и повесьте на @reboot. Заодно команда create … -exist задаёт лимит maxelem 300000 (по умолчанию 65536 — level 1 не влезает, будет «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 — ежедневно в 04:00 И при каждом старте: 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
Как устроено при автоустановке. Скрипт грузит полный список level 1 (100+ тысяч IP) в набор ipsum и, если файрволом управляет установщик (свежий VPS — профили «Полная»/«Облегчённая»), подключает набор к UFW правилом DROP — трафик с этих IP реально блокируется. Правило стоит после ESTABLISHED,RELATED, поэтому текущие соединения (в т.ч. ваш SSH) не рвутся — режутся только новые подключения из списка. Набор восстанавливается при загрузке сервиса ipsum-load.service до файрвола (иначе UFW не поднялся бы), и обновляется кроном в 04:00. На уже настроенном сервере (панель, свой файрвол) установщик в файрвол не лезет — там ipsum остаётся списком для дашборда и карты атак, а правило DROP при желании добавляется вручную (минимальный вариант с iptables … --match-set ipsum … -j DROP — выше). При автоустановке вручную ничего делать не нужно.

13. Установка CrowdSec

Современная замена Fail2ban с коллективным threat intelligence: блокировки от сообщества плюс собственные правила. Требует отдельного bouncer для применения блокировок к файрволу.

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # Bouncer для iptables/nftables: sudo apt install crowdsec-firewall-bouncer-iptables # Проверить статус: sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
Статус «Не запущен» в панели = сервис установлен, но служба не активна (монитор проверяет её через systemctl is-active crowdsec). Поднять: sudo systemctl enable --now crowdsec; при падении смотреть sudo journalctl -u crowdsec -n 30. То же правило для любого сервиса в статусе «Не запущен» (Suricata, Falco, Monit, MySQL).
«0 сценариев» или «0 bouncers» на дашборде. CrowdSec из коробки идёт почти пустым — без коллекций он ничего не детектит, а без зарегистрированного bouncer'а блокировки не применяются к файрволу. Доставьте базовые коллекции и убедитесь, что bouncer в списке:
# Базовые коллекции (Linux + SSH + веб-сервер): sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # Bouncer должен быть в списке и со статусом активного подключения: sudo cscli bouncers list
В логе bouncer'а stream halted / блокировки не применяются. Это осиротевший api-ключ: bouncer был удалён из cscli bouncers list, но его старый ключ остался в /etc/crowdsec/bouncers/*.yaml. Перерегистрируйте bouncer и пропишите свежий ключ:
sudo cscli bouncers add fw-bouncer # выведет новый api_key # впишите этот ключ в api_key: в /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml sudo systemctl restart crowdsec-firewall-bouncer
Автоустановка (профиль «Полная защита») сама ставит коллекции и регистрирует firewall-bouncer — вручную это нужно только при ручной установке или после ручного вмешательства в CrowdSec.

14. Установка AIDE

AIDE (Advanced Intrusion Detection Environment) делает снимок файловой системы и при каждой проверке сообщает об изменениях в /etc, /bin, /usr. После установки обязательна инициализация базы (aideinit).

sudo apt install aide # Инициализация базы (5–15 минут): sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04: каталог /var/lib/aide создаётся в режиме 700 (владелец _aide), # и панель (www-data) не видит базу → показывает «Не инициализирована». # Открыть каталог на проход (файлы базы остаются 600): sudo chmod 755 /var/lib/aide # Первая проверка С ЗАПИСЬЮ в лог, который читает монитор. # На Ubuntu/Debian aide требует явный --config (иначе «missing configuration»; # бинарь aide.wrapper в новых версиях не поставляется): sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Во время aideinit терминал 5–15 минут стоит на строке Running aide --init... — это нормально (хеширование всей ФС, нагрузка на диск). Не прерывайте Ctrl+C. Если процесс «висит», но ничего не пишет — возможно, он ждёт ответа на скрытый запрос Overwrite existing aide.db.new [Yn]? (нажмите Y). Проверить активность из другой сессии: pgrep -af aide.
Ошибка aideinit: «21_aide_spamassassin … printf: invalid number» (return code 20) — известный баг конфиг-сниппета AIDE в Ubuntu 22.04. База не создаётся. Вынесите битый сниппет и повторите:
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
Статусы на дашборде. «Не инициализирована» = панель не видит файл базы: либо aideinit не запускался, либо (Ubuntu 24.04) каталог /var/lib/aide создан в режиме 700 и недоступен www-data — лечится sudo chmod 755 /var/lib/aide (см. блок выше). «Проверок не было» = база есть, но проверка ещё не выполнялась — это не ошибка. Результаты монитор читает из /var/log/aide/aide.log.
Регулярная проверка → лог для панели. Штатный /etc/cron.daily/aide на новых Ubuntu/Debian может не писать /var/log/aide/aide.log в нужном виде (и aide.wrapper в них уже нет). Надёжнее добавить свой крон с явным --config — он пишет лог от root в режиме 644, и монитор читает его без доп. групп:
# sudo crontab -e — ежедневная проверка в 02:00: 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # Запустить сейчас, не дожидаясь расписания: sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Автоустановка уже делает всё это: chmod 755 /var/lib/aide и крон проверки в 02:00 — вручную ничего не нужно.
Первую инициализацию делайте на чистом сервере — до установки веб-приложений. После легитимных изменений пересоздавайте базу: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.

15. Установка ClamAV

Антивирусный сканер для Linux. Особенно полезен для проверки /var/www на PHP-шеллы и вредоносный код.

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # Обновить базу сигнатур: sudo freshclam # Сканировать папку вручную: sudo clamscan -r /var/www --infected
Демон clamd показывает «Неактивен» после enable --now? Три типичные причины:

1. В конфиге осталась строка Example — clamd отказывается стартовать, пока она есть:

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

2. Не скачана база сигнатур — clamd не стартует без неё:

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

3. Просто загружается — clamd грузит ~8 млн сигнатур в память 30–60 сек. Подождите и проверьте: systemctl is-active clamav-daemon (статус activating → ещё грузит).

Диагностика: sudo journalctl -u clamav-daemon -n 30 --no-pager.
На дашборде «Файлов проверено: 0» / «Последнее сканирование: —»? Демон clamd только держит сигнатуры в памяти, сам он ничего не сканирует по расписанию. Панель показывает результаты планового скана, поэтому нужен крон, который сканирует и пишет лог. Автоустановка ставит обёртку /usr/local/bin/clamav-scan.sh и крон в 01:30 — после первого запуска заполнятся «Файлов проверено» и «Последнее сканирование». Запустить сразу, не дожидаясь расписания: sudo /usr/local/bin/clamav-scan.sh.

16. Установка Linux Malware Detect (maldet)

Linux Malware Detect (LMD) — сканер вредоносного ПО под веб-угрозы: PHP-шеллы, веб-бэкдоры, загрузчики. Использует движок ClamAV и дополняет его своими сигнатурами.

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 # Обновить сигнатуры: sudo maldet -u # Сканировать /var/www: sudo maldet -a /var/www
LMD и ClamAV хорошо работают в паре. Последний отчёт: maldet --report.
При установке возможна строка update-rc.d: error: unable to read /etc/init.d/maldet — она безвредна. maldet не использует init.d, обновление сигнатур и сканы запускаются через /etc/cron.daily/maldet. Если ниже видно installation completed — всё установилось.
На странице LMD пишет «Не установлен», хотя установлен? maldet ставится не через apt, а в /usr/local/maldetect, и при включённом open_basedir его наличие проверяется через shell — см. раздел «Страница пустая, хотя данные на сервере есть».

17. Установка Suricata

Сетевая система обнаружения вторжений: анализирует трафик на уровне пакетов и знает тысячи сигнатур атак. Дополняет ModSecurity (тот работает на уровне HTTP, Suricata — на уровне TCP/IP).

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # Скачать актуальные правила: sudo suricata-update sudo systemctl enable --now suricata
Suricata «Активен», но панель не показывает алерты / число событий 0? Suricata пишет /var/log/suricata/eve.json под root в режиме 750 на каталог, и веб-сервер (www-data) не может его прочитать. Откройте каталог на проход — файлы внутри остаются защищёнными:
sudo chmod o+rx /var/log/suricata
Автоустановка делает это сама — вручную не нужно.

18. Установка Falco

Перехватывает системные вызовы через eBPF/kernel module и обнаруживает аномалии в реальном времени: shell из nginx, чтение /etc/passwd веб-процессом, запись в /bin и т.д.

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
Монитор читает события Falco через journalctl -u falco (без sudo — через группу systemd-journal). Убедитесь, что www-data в этой группе — см. «Настройка sudo» (п.2) на странице ручной установки.
«0 событий за 24 часа» — это норма, а не ошибка. Falco event-driven: он молчит, пока всё в порядке, и пишет событие только при аномалии (shell из веб-процесса, чтение /etc/passwd, запись в системные каталоги). Ноль критических за сутки на спокойном сервере — здоровое состояние.
Для панели надёжнее файл-вывод. Чтение через journalctl требует прав на журнал; чтобы панель видела события стабильно, автоустановка включает у Falco file_output/var/log/falco/falco.log и службе ставит UMask=0022 (лог читается веб-сервером). Вручную на новой установке настраивать это не нужно.

19. Установка ModSecurity (WAF)

ModSecurity — веб-файрвол (WAF) для Apache или Nginx. Блокирует атаки на уровне приложения: SQL-инъекции, XSS, обход путей, сканеры.

# Apache: sudo apt install libapache2-mod-security2 sudo a2enmod security2 # Набор правил OWASP Core Rule Set: sudo apt install modsecurity-crs # ОБЯЗАТЕЛЬНО: без этого файла движок правил выключен 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 # Проверка: должно вернуть 403 curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
Установка пакета сама по себе ничего не защищает. Apache подключает конфиги строкой IncludeOptional /etc/modsecurity/*.conf, а пакет кладёт только modsecurity.conf-recommended — под маску *.conf он не попадает. Если не скопировать его в modsecurity.conf, SecRuleEngine остаётся Off: модуль загружен, правила CRS загружены, но трафик не проверяется и аудит-лог не создаётся. Промежуточный режим DetectionOnly только пишет события в лог, не блокируя запросы — панель показывает его жёлтым.

Доступ панели к аудит-логу. Лог /var/log/apache2/modsec_audit.log принадлежит root (права 640), веб-пользователь его не прочитает. Панель берёт данные через обёртку — создайте её:

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 # в /etc/sudoers.d/monitor (пользователь = тот, под которым работает PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
Обёртка берёт последнюю директиву SecRuleEngine без отступа: строки с отступом находятся внутри блоков <LocationMatch>/<Directory> (например, отключение WAF для phpMyAdmin) и глобальный режим не определяют.
Пользователь в sudoers должен совпадать с пользователем FPM-пула: на обычном Apache/Debian это www-data, в HestiaCP пул сайта работает от владельца сайта (например, admin) — проверьте grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.
Если сайт стоит за Nginx-прокси (HestiaCP), Apache видит клиентом сам прокси — панель берёт реальный IP атакующего из заголовка X-Forwarded-For. В статистику попадают только транзакции со сработавшим правилом: директива SecAuditLogRelevantStatus пишет в аудит-лог любые ответы 4xx/5xx, поэтому туда попадают и обычные 403/500 — событиями WAF панель их не считает.
Блок ---RULES--- нужен разделу «Все активные правила» — панель показывает не только сработавшие, но вообще все загруженные правила CRS + кастомные. Три пути в цикле for f in … — типичные места для CRS-правил и локальных дополнений; если у вас другая раскладка (пакет ставит файлы в свой каталог, или кастомные правила лежат не в /etc/modsecurity/custom-rules.conf), найдите реальные пути командой sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null и подставьте их в список. Если обёртка старая (без этой секции) — раздел просто покажет предупреждение «недоступно», остальная страница работает как раньше.

20. Установка Auditd

Auditd (Linux Audit Daemon) фиксирует системные вызовы на уровне ядра: входы и выходы, sudo-команды, неудачные попытки аутентификации, изменения файлов. Монитор показывает входы, неудачные попытки и sudo-команды за сегодня.

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # Проверить статус и события: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
Монитор читает события через ausearch (/usr/sbin/ausearch) и при необходимости из /var/log/audit/audit.log командой tail. Оба должны быть в sudoers.

21. Установка Monit

Следит за сервисами (nginx, php-fpm, mysql и т.д.) и перезапускает их при падении. Может слать алерты на email.

sudo apt install monit sudo systemctl enable --now monit # Конфиги: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
Монитор получает список сервисов через monit status. В /etc/monit/monitrc должен быть включён HTTP-интерфейс (блок set httpd с allow localhost), иначе monit status вернёт ошибку.
На дашборде «0 сервисов под наблюдением»? Две причины. (1) HTTP-интерфейс выключен — в monitrc строка set httpd закомментирована (по умолчанию идёт как # set httpd port 2812 …). Раскомментируйте блок и разрешите localhost. (2) Сам по себе включённый httpd ничего не отслеживает — Monit считает только то, что описано check-стансами; без них список пуст даже при работающем интерфейсе. Минимальный рабочий конфиг:
# /etc/monit/conf.d/00-httpd — HTTP-интерфейс для localhost: set httpd port 2812 use address localhost allow localhost # примеры check-стансов (что отслеживать): 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 # проверить синтаксис (Control file syntax OK) sudo systemctl reload monit sudo monit status
Автоустановка кладёт готовый conf.d с httpd на 2812 и набором проверок — на новой установке настраивать вручную не нужно.
Сервис в статусе «С ошибками»? Монитор только показывает состояние и намеренно не перезапускает сервисы из веб-панели (это было бы удалённым исполнением root-команд в панели безопасности). Диагностика и перезапуск — по SSH через Monit:
sudo monit status <service> # причина ошибки sudo monit restart <service> # перезапуск через Monit # если Monit не поднимает сервис — смотрите его собственный юнит: sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

22. Установка PSAD (детект сканирования портов)

PSAD анализирует журнал iptables и выявляет сканирование портов и сетевые атаки, присваивая каждому источнику уровень угрозы (1–5). Дополняет fail2ban и Suricata.

sudo apt install psad # PSAD читает лог iptables — нужно включить логирование (UFW делает это сам). # Для чистого iptables добавьте правила LOG в цепочки INPUT/FORWARD. sudo psad --sig-update sudo systemctl enable --now psad
Монитор читает данные через psad --Status (нужен в sudoers). Без логирования iptables страница будет пустой — это нормально, пока не было сканирований.

23. AppArmor / SELinux (контроль доступа)

Mandatory Access Control ограничивает, к каким файлам и ресурсам может обращаться программа, даже если её взломали. В Ubuntu/Debian по умолчанию используется AppArmor (обычно уже установлен и активен).

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # проверить профили
Монитор читает статус через aa-status (нужен в sudoers). Показывает число профилей в режиме enforce/complain и процессы без профиля.

«Профилей загружено» больше, чем enforce + complain — это нормально. В AppArmor 4.x (Ubuntu 24.04 и новее) появился режим unconfined: профиль загружен в ядро, но ничего не ограничивает. Ubuntu помечает так десятки профилей для программ, использующих user namespaces (браузеры, torrent-клиенты и подобное). Когда такие профили есть, карточка «Профилей загружено» становится янтарной и показывает их число — например unconfined: 90 при 120 загруженных и 26 в enforce. Реально защищают только профили в enforce; на Ubuntu 22.04 (AppArmor 3.x) этого режима нет и цифры всегда сходятся.

sudo aa-status | grep -E "profiles are" # разбивка по режимам sudo aa-enforce /etc/apparmor.d/profile-name # перевести профиль в enforce
Переводить в enforce профили, которые Ubuntu намеренно оставила в unconfined, стоит только осознанно: они выключены не по ошибке, а потому что иначе ломается работа самих программ. Профили в complain — другое дело: там правила уже написаны и лишь не применяются.

24. Установка debsums (целостность пакетов)

debsums проверяет, что файлы установленных пакетов совпадают с контрольными суммами из репозитория — выявляет подменённые системные бинарники (дополняет AIDE). Полная проверка идёт 1–2 минуты, поэтому запускается по cron, а панель читает результат из data/debsums/debsums.log и сама раскладывает по категориям (важны только бинарники и библиотеки).

Задание — в root-крон (sudo crontab -e). Готовая обёртка debsums-scan.sh кладётся в /usr/local/bin/ (chmod +x; см. сводку cron-задач) и сама пишет отчёт в data/debsums/ панели.

sudo apt install debsums # Cron-строка (ежедневно 4:30): 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

Обёртка debsums-scan.sh сама находит data/ панели — путь прописывать не нужно.

Изменения в /etc/ (конфиги) и /usr/share/ (ресурсы) на сервере обычно нормальны — панель помечает их отдельным цветом. Тревожны изменения бинарников и библиотек (/bin, /sbin, /usr/lib и т.п.) — карточка «Бинарники / библиотеки» показывает именно их.

25. Настройка отчётов Lynis

Lynis запускается вручную или по cron. Отчёт должен сохраняться в папку data/lynis/ проекта — монитор читает файл lynis-report.dat.

# Разовый запуск (подставьте свой путь к корню панели): sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # Ежедневный аудит — cron-строка (готовая обёртка lynis-scan.sh в /usr/local/bin/, см. сводку): 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
После первого запуска страница «Аудит Lynis» сразу покажет hardening index, предупреждения и рекомендации.
Кнопка «Запустить аудит» на странице Lynis. Она запускает lynis-scan.sh в фоне прямо из панели (не дожидаясь cron): показывает «Сканирование…» и по завершении сама обновляет отчёт. Для этого веб-пользователю нужна строка sudoers на запуск скрипта — установщик добавляет её автоматически в /etc/sudoers.d/monitor. Если панель ставилась вручную/раньше, допишите её тем же пользователем, что уже указан в файле:
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. Настройка отчётов Logwatch

Logwatch должен сохранять ежедневные отчёты в папку data/logwatch/ проекта в формате .txt. Монитор показывает последний отчёт и архив.

# Ежедневно (6:00) — cron-строка (готовая обёртка logwatch_daily.sh в /usr/local/bin/, см. сводку): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

Модули панели

27. Сетевой монитор (встроенный)

Сетевой монитор не требует установки — это встроенная страница панели. Она показывает сетевое состояние сервера из локальных источников:

  • интерфейсы и трафик — из /proc/net/dev;
  • состояние линков (UP/DOWN) и IP — через ip;
  • соединения и слушающие порты — через ss;
  • сетевые события ядра за 24 ч — через journalctl -k.

Первые три источника работают без sudo, поэтому интерфейсы, трафик, соединения и порты видны сразу. Блок «События ядра» использует journalctl -k — он читается через группу systemd-journal («Настройка sudo», п.2), sudo не нужен. Проверить, что всё доступно веб-пользователю:

# Проверка от имени www-data (под ним работает PHP): sudo -u www-data bash -c 'cat /proc/net/dev' sudo -u www-data bash -c 'ip -o link show' sudo -u www-data bash -c 'ss -s' sudo -u www-data journalctl -k --no-pager -n 5
Блок «Сетевые события ядра» показывает события сетевого стека ядра (смена линка up/down, ошибки несущей, «network unreachable»). Записи файрвола UFW BLOCK сюда не попадают — они на страницах «Брандмауэр UFW» и «Карта атак». Пустой блок с зелёной галкой = за сутки сетевых сбоев не было.

28. Диск и SMART

Встроенная страница показывает три вещи:

  • Файловые системы — заполнение разделов (df); шкала краснеет при ≥90%;
  • Накопители — список дисков (lsblk), только реальные (loop/snap скрыты);
  • Здоровье (SMART) — статус диска и атрибуты (smartctl).

Место и список устройств работают сразу, без настройки. Для SMART нужен пакет smartmontools. Веб-процесс не имеет прямого доступа к дисковым устройствам, поэтому SMART снимается по cron в файл data/disk/smart.txt, а панель его читает.

Задание — в root-крон (sudo crontab -e). Готовая обёртка smart-scan.sh кладётся в /usr/local/bin/ (chmod +x; см. сводку cron-задач) и сама пишет в data/disk/ панели.

sudo apt install smartmontools # Cron-строка (каждые 30 минут): */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

Обёртка smart-scan.sh сама находит data/ панели — путь прописывать не нужно. Внутри lsblk -e7,11 исключает loop/cdrom.

На виртуальных дисках (QEMU/KVM и подобных) обычно доступен только общий статус «здоровье: OK», а температура, часы работы и перераспределённые секторы могут быть пустыми — это нормально. На физическом сервере отображаются все атрибуты.

29. Производительность (CPU/RAM/Сеть/Диск)

Страница показывает историю нагрузки сервера за последние 24 часа — Load Average, занятость CPU и ожидание I/O, RAM/Swap, сетевой трафик (приём/передача), дисковый I/O (чтение/запись), заполнение диска и inodes, открытые файловые дескрипторы и MySQL-соединения, плюс текущее число TCP-соединений и процессов.

Данные собирает cron/collect_metrics.php — раз в 5 минут пишет один «сырой» снимок счётчиков (/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') в таблицу БД system_metrics; проценты и скорости страница считает сама по разнице между соседними снимками (заполнение диска/inodes/дескрипторов/MySQL-соединения — мгновенные значения, без пересчёта). Sudo не требуется — источники читаются без прав root. Точки старше 24 часов удаляются автоматически при каждой записи.

# Cron-строка (каждые 5 минут): */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

Обёртка collect-metrics-all.sh (см. сводку cron-задач) сама находит все установленные инстансы панели на сервере и запускает cron/collect_metrics.php каждого от имени владельца сайта.

Пока коллектор не отработал хотя бы дважды (первые ~10 минут после установки), страница показывает «данные собираются» — графикам нужна минимум одна пара соседних точек, чтобы посчитать скорости и проценты.

Оповещения о нагрузке (раздел «Настройки» → «Оповещения о нагрузке») — при превышении порога CPU/RAM/диска/inodes панель шлёт уведомление в Telegram/Email (те же каналы, что и ежедневный отчёт — отдельно включать их для алертов не нужно), и ещё одно — когда метрика вернулась в норму. Повторно во время удержания порога не спамит: следующее уведомление придёт только после цикла «восстановилось → снова превысило».

Пороги проверяются тем же collect_metrics.php при каждом запуске (раз в 5 минут) — отдельный cron не нужен. Состояние «уже уведомили / ещё нет» хранится в data/alerts_state.json, пороги — в настройках панели.

30. Карта атак (GeoIP)

Страница «Карта атак» определяет страну по IP командой geoiplookup. Без пакета GeoIP страны не определятся и точки на карте не появятся:

sudo apt install geoip-bin geoip-database # Проверка: geoiplookup 8.8.8.8
Sudo не требуется — база /usr/share/GeoIP/GeoIP.dat читается всеми, результаты кешируются в tmp/geoip_cache.json. Сама карта (Leaflet + плитки OpenStreetMap) грузится в браузере — нужен интернет на компьютере, где открыта панель.

31. Внешняя экспозиция, обновления и автообновления

Две встроенные карточки дашборда, показывающие не «вкл/выкл» инструмента, а реальную защищённость сервера. Установки не требуют, читаются локально без sudo.

Внешняя экспозиция — сколько сервисов слушают все интерфейсы (0.0.0.0/[::]) и доступны извне. Подсвечивает красным, если наружу торчат СУБД или кэш (MySQL, PostgreSQL, Redis, MongoDB, Memcached, Elasticsearch) — это прямая дыра (−10 к Оценке безопасности). Источник: ss -tuln.

Если карточка красная — закройте СУБД для внешнего мира: привяжите к 127.0.0.1 (bind-address в конфиге MySQL/PostgreSQL, bind 127.0.0.1 в Redis) или закройте порт в UFW.
«Открытый порт» ≠ «доступен снаружи». Сервис, слушающий 127.0.0.1 (loopback), виден только самому серверу — извне на него не попасть, даже если порт «открыт». Поэтому Postfix на порту 25, привязанный к loopback, безопасен: автонастройка ставит inet_interfaces = loopback-only (плюс нейтральный smtpd_banner — закрывает замечание Lynis MAIL-8818 о раскрытии версии). Карточка «Внешняя экспозиция» считает наружу только то, что слушает 0.0.0.0/[::]; loopback-сервисы туда не попадают.
Lynis MAIL-8818 вручную (если ставили почту сами): в /etc/postfix/main.cf задайте smtpd_banner = $myhostname ESMTP (без версии и ОС) и inet_interfaces = loopback-only, затем sudo systemctl restart postfix.

Обновления безопасности — сколько security-патчей ждут установки и нужна ли перезагрузка после обновления ядра (−5 к Оценке безопасности при наличии патчей). Источник: /usr/lib/update-notifier/apt-check, файл /var/run/reboot-required. Детальный список — на странице «Обновления безопасности».

# Установить обновления: sudo apt update && sudo apt upgrade # Проверить, что слушает наружу: ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
Карточка обновлений работает на Ubuntu/Debian (update-notifier-common). Если apt-check отсутствует — монитор считает патчи через apt-get -s upgrade.

Автообновления безопасности (unattended-upgrades) — на странице «Обновления безопасности» отдельная карточка показывает, включена ли автоматическая установка security-патчей и когда она запускалась в последний раз. Sudo не нужен — статус читается через apt-config dump.

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # включить # Проверить, что включено: apt-config dump | grep Unattended-Upgrade

Обслуживание

32. Резервное копирование

Бэкап — главная страховка: потеря данных страшнее любого взлома. Нужны две вещи — бэкап сервера/сайтов и отдельно бэкап БД панели (там пользователи, ключи WebAuthn, настройки, лицензия).

Вариант A — HestiaCP: вкладка Backup у пользователя → кнопка создания бэкапа (или по расписанию в настройках сервера). Бэкап включает сайты и их БД.

Вариант B — вручную (cron): дамп БД + архив каталога data/ панели:

# root-крон (sudo crontab -e) — ежедневный бэкап в 2:30 (подставьте свои имена/пути): 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 # Удалять архивы старше 14 дней: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
Бэкап на том же сервере спасает от ошибок, но не от потери сервера. Копируйте архивы на внешнее хранилище (другой сервер, S3, rclone в облако). Проверяйте, что восстановление реально работает.

33. Обновление и перенос панели

Обновление до новой версии. Сначала сделайте бэкап. Затем перезалейте файлы кода, сохранив свои данные:

  • перезаписать (код): public/, includes/, assets/, cron/, database/, а также корневые .htaccess (фронт-контроллер — маршрутизацию нельзя оставлять от старой версии), manifest.json, sw.js;
  • не трогать: config.php (данные БД), data/ (отчёты), logs/, tmp/ (сессии и кэш).
# После заливки — сбросить кеш PHP (если включён opcache): sudo systemctl reload php*-fpm
FileZilla пишет SSH_FX_PERMISSION_DENIEDPermission denied. Файлы панели принадлежат www-data (так их выставили при установке), а SFTP-клиент подключается под вашим пользователем — прав на запись у него нет. Отдавать www-data всю панель «чтобы работало» — как раз то, что приводит к этой ошибке; ниже три способа, любой решает проблему.
# Вариант A (рекомендуется) — разделить владельцев: код ваш, рабочие папки веб-сервера. # Веб-сервер права на запись в КОД панели не получает вовсе: 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 # Вариант B — ACL поверх текущих владельцев (ничего не переносим): 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 # Вариант C — через группу www-data. Проще, но запись в файлы панели # получает и веб-сервер (при уязвимости в PHP код можно подменить): 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
Почему вариант A безопасен. Панель пишет только в три каталога — data/ (отчёты), tmp/ (сессии и кэш), logs/; они остаются за www-data. Остальное — код, и веб-серверу он нужен лишь на чтение, которое даёт группа www-data с правами 644. Побочный выигрыш: при уязвимости в PHP файлы панели уже не переписать. На хостинг-панелях (HestiaCP и подобные) вариант A не нужен: там файлы сайта и так принадлежат аккаунту, под которым вы заходите по SFTP, а веб-сервер читает их по группе.
Ловушка варианта B: любой последующий chmod по файлам сбрасывает ACL-маску, и доступ молча пропадает. Если после «наведения порядка в правах» заливка снова упирается в Permission denied — повторите обе команды setfacl.
Бит 2 в варианте C — это setgid: файлы, залитые по SFTP, остаются в группе www-data, иначе панель не сможет их перезаписать. После варианта C переподключитесь в FileZilla — новая группа применяется только при новом входе. Проверка: id deploy (должна появиться группа www-data) и ls -ld /path/to/monitor (drwxrwsr-x — буква s означает, что setgid установлен).

Перенос на другой сервер:

  1. На новом сервере поднимите сайт + HTTPS (см. страницу ручной установки).
  2. Скопируйте все файлы панели вместе с config.php, data/.
  3. Перенесите БД: mysqldump на старом → импорт на новом; поправьте данные БД в config.php.
  4. Повторите на новом сервере: sudoers, членство в группе adm, cron-задания.
  5. Лицензия привязана к домену — если домен тот же, ключ продолжит работать.

34. Восстановление доступа (потерян ключ, пароль, IP-блок)

Если вы не можете войти — всё чинится напрямую в БД с сервера. Откройте БД (имя — из config.php):

sudo mysql MY_DB

Потерян ключ WebAuthn (не проходит второй фактор) — отключите 2FA, войдите по паролю, зарегистрируйте новый ключ:

UPDATE users SET webauthn_enabled = 0;

Забыли пароль — задайте новый хеш (сгенерируйте его на сервере и подставьте):

# Сгенерировать хеш нового пароля: php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # В БД (вставьте полученный хеш): # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

Заблокировали себя IP-фильтром — отключите ограничение:

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
Доступ к БД есть всегда: sudo mysql на сервере, либо phpMyAdmin / раздел БД в панели хостинга. После восстановления заново включите WebAuthn и IP-фильтр.

35. Все cron-задания в одном месте

Сводка заданий — в root-крон сервера (добавляются через sudo crontab -e). Оставьте только строки тех инструментов, что используете; поправьте пути под свой сервер.

# Серверный крон монитора (root) — впишите через: sudo crontab -e # 01:30 — скан ClamAV по опасным путям (web, home, temp) → карточки «Файлов проверено» и «Последнее сканирование» 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — проверка целостности файлов AIDE (нужен явный --config) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # при старте — восстановить права /var/lib/aide (пакетный tmpfiles-файл # aide-common.conf сбрасывает их в 0700, и панель перестаёт видеть базу) @reboot chmod 755 /var/lib/aide # 03:00 — аудит безопасности Lynis 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — обновление блок-листа ipsum (level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # набор ipsum при старте поднимает сервис ipsum-load.service (ДО файрвола, иначе # UFW не увидит набор в before.rules) — не крон. В кроне только ежедневный рефреш выше. # 06:00 — отчёт Logwatch 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # каждые 30 мин — проверка дисков SMART */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — целостность пакетов debsums 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — отчёт по расписанию на Email и Telegram 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # каждый час — обновление списков пакетов (для карточки «Обновления безопасности») 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # каждые 5 мин — снимок ресурсов (CPU/RAM/сеть/диск) для страницы «Производительность» */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1
Подробности по каждому — в соответствующих разделах. Бэкап-задания (предыдущий раздел) добавляются в этот же крон. После правок проверьте: sudo crontab -l и что cron-служба активна.
Время cron = часовой пояс сервера, а не TIMEZONE из config.php. Константа TIMEZONE влияет только на PHP (как панель показывает даты), но cron-демон запускает задания по системному времени ОС. Если пояс сервера не совпадает с вашим, отчёт «08:00» придёт не в то время. Пример: сервер стоит в другом поясе (Europe/Berlin, UTC+2), а вы — в Киеве (UTC+3) → отчёт «08:00» придёт в 09:00 по-вашему. Проверьте и при необходимости приведите пояс системы к своему:
# Проверить текущий пояс сервера: timedatectl # Установить свой пояс (пример) и перезапустить cron: sudo timedatectl set-timezone Europe/Kyiv sudo systemctl restart cron
После этого строка 0 8 * * * сработает в 08:00 по местному времени. Иначе сдвигать пришлось бы сам cron, но при переходе на зимнее/летнее время сдвиг снова разойдётся — поэтому правильнее настроить пояс системы.
Готовые скрипты-обёртки. Их рабочие копии и образец crontab (crontab.txt) лежат в папке system/ рядом с проектом, вне public_html. Это не часть сайта — в веб-корень их загружать не нужно; разместите на сервере по системным путям (как в crontab выше):
  • lynis-scan.sh/usr/local/bin/ (chmod +x) — запускает lynis audit system, на время скана ставит флаг /tmp/lynis-running и копирует lynis-report.dat в data/lynis/ панели;
  • logwatch_daily.sh/usr/local/bin/ (chmod +x) — формирует ежедневный отчёт Logwatch (sshd, fail2ban, sudo, postfix) в data/logwatch/;
  • smart-scan.sh/usr/local/bin/ (chmod +x) — снимает состояние дисков (smartctl) в data/disk/;
  • debsums-scan.sh/usr/local/bin/ (chmod +x) — проверяет целостность пакетов (debsums) в data/debsums/;
  • clamav-scan.sh/usr/local/bin/ (chmod +x) — антивирусный скан ClamAV по опасным путям (web, home, temp); пишет сводку в /var/log/clamav/scan.log, откуда её читает страница ClamAV (строка 01:30 в crontab выше);
  • load-ipsum.sh/usr/local/bin/ (chmod +x) — обновляет ipset-набор ipsum (level 1) на месте, не разрывая действующих правил файрвола (строка 04:00 в crontab выше);
  • daily-report-all.sh/usr/local/bin/ (chmod +x) — запускает отчёт cron/daily_report.php панели (строка 08:00 в crontab выше);
  • daily_report.php — уже входит в панель (cron/daily_report.php), запускается через daily-report-all.sh, отдельно ставить не нужно;
  • collect-metrics-all.sh/usr/local/bin/ (chmod +x) — запускает cron/collect_metrics.php панели (страница «Производительность», строка */5 в crontab выше); collect_metrics.php уже входит в панель, отдельно ставить не нужно;
  • crontab.txt (system/cron/) — образец заданий; нужные строки впишите через sudo crontab -e.
Путь к скрипту в crontab должен совпадать с тем, куда вы его положили.
Как положить скрипт в /usr/local/bin/. Напрямую из FileZilla туда не записать — каталог принадлежит root, и SFTP-клиент получит SSH_FX_PERMISSION_DENIED. Порядок такой: сначала залить файл в /tmp (туда пишут все), затем перенести его на место одной командой:
# в FileZilla: в поле «Удалённый сайт» ввести /tmp и загрузить туда скрипт, # затем по SSH (install сразу ставит владельца и права, chown/chmod не нужны): sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # проверка: файл на месте, права rwxr-xr-x, синтаксис цел bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
Не перепутайте каталоги: нужен /tmp в корне сервера — не /var/tmp и не tmp/ внутри самой панели (последний принадлежит www-data и закрыт от вашего пользователя). В дереве FileZilla /tmp — ветка верхнего уровня, рядом с var, а не внутри неё.
Ставили сервер автонастройкой? Эти обёртки и их cron-задания уже установлены скриптом (в /usr/local/bin/, лог — /var/log/arciveo-cron.log) — вручную ничего делать не нужно.
Где скрипты ищут панель. Обёртки доменно-нейтральные: находят установки панели, перебирая /home/*/web/*/public_html и /var/www/*, и кладут отчёты в их data/. Если панель лежит по другому пути — добавьте его в строку for app in … внутри скриптов, иначе отчёты Lynis/SMART/debsums/Logwatch в панель не попадут.
cron.log и права доступа. Файл logs/cron.log первым создаёт root-крон — он будет принадлежать root, и вкладка «Журнал cron» в панели не сможет его ни прочитать, ни очистить. Создайте файл заранее от имени веб-пользователя (владелец каталога сайта; на HestiaCP это аккаунт, напр. admin) — тогда root-крон будет лишь дописывать, не меняя владельца:
# создать заранее от веб-пользователя (до добавления cron-строк): sudo -u OWNER touch /path/to/monitor/logs/cron.log # если cron.log уже создан root-кроном — отдать веб-пользователю: sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
Узнать владельца каталога: stat -c %U /path/to/monitor.
Управление из панели. В разделе «Система» есть страница «Crontab» — можно смотреть и добавлять задания без SSH. Панель редактирует только задания, добавленные через неё же (отдельный блок в root-crontab, отмеченный служебными комментариями); всё, что уже стоит в crontab (список выше), показано там read-only списком «Прочие задания сервера» с кнопкой «Скопировать в редактор» — она только переносит расписание/команду в форму добавления, исходную строку не трогает. Чтобы «перевести» существующее задание под управление панели — скопируйте его в редактор, сохраните, затем удалите старую строку вручную (sudo crontab -e), иначе оно будет выполняться дважды.
Разовая настройка на сервере. Странице нужен привилегированный скрипт-обёртка — не голый sudo crontab (это была бы прямая эскалация до root любым, кто получит доступ к сессии панели), а узкий скрипт с двумя командами (list/set), который трогает только свой блок между служебными комментариями. Установите один раз:
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
Веб-пользователь может отличаться от www-data — проверьте, под кем работает пул PHP-FPM сайта (ps -o user= -C php-fpm), и подставьте его в sudoers-строку.
Новый файл залился не тем владельцем — страница отвечает «Access denied.». Если файл public/crontab_monitor.php загружен по FTP/SFTP под другим системным пользователем (например, root), чем остальные файлы сайта, веб-сервер не сможет его прочитать. Сверьте владельца и права с соседним файлом и приведите в соответствие:
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

Диагностика

36. Инструмент установлен, но пишет «Не установлен»

Монитор определяет наличие инструментов через dpkg-query — базу пакетов APT. Если инструмент установлен не через apt (вручную, из snap или из исходников), dpkg его не видит.

# Проверить через dpkg: dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # Найти путь к бинарнику: which ufw fail2ban-client auditctl # Тест sudo от www-data: sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

37. Устранение проблем (500, нет данных)

Ошибка 500 — проверьте логи PHP, nginx и самого монитора:

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # Логи монитора: tail -50 logs/monitor_$(date +%Y-%m-%d).log # Права на папки: ls -la data/ tmp/ logs/
Панель на хостинг-панели (HestiaCP, ISPmanager, cPanel)? Там PHP работает не под www-data, а под аккаунтом пользователя (например admin — владелец каталога сайта). Все sudo-правила и членство в группах (adm, systemd-journal) нужно прописывать на этого пользователя, иначе модули покажут «Не активен / 0» при работающих сервисах. Узнать реального пользователя PHP: ps -o user= -C php-fpm | sort -u или владелец каталога сайта stat -c '%U' /path/to/monitor. Дальше во всех командах ниже подставляйте его вместо www-data. Автоустановка определяет веб-пользователя сама и прописывает sudoers на него.

Данные не отображаются — почти всегда незаданные sudo-права. Проверьте конкретную команду от имени веб-пользователя (замените www-data на своего). Флаг -n = без пароля, как у PHP — если просит пароль, значит правила в 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
Модуль пишет «Не активен» / «0», хотя инструмент работает (например sudo aa-status в терминале показывает профили, а страница «AppArmor» — «Неактивен»). Причина: у веб-пользователя нет sudo-права на команду этого модуля. Проверьте её из списка выше: если просит пароль — добавьте недостающую строку в /etc/sudoers.d/monitor («Настройка sudo»). Частые «новые» команды: /usr/sbin/aa-status (MAC), /usr/sbin/psad --Status (PSAD).
Если конкретная страница (Falco, ModSecurity, Auditd, открытые порты UFW) пуста — сверьтесь со списком в разделе про sudo: вероятно, не разрешён apache2ctl, ausearch, aa-status или ss, либо веб-пользователь не в группах adm/systemd-journal (оттуда читаются логи fail2ban/auth/modsec и journalctl — Falco и события ядра).

38. Страница пустая, хотя данные на сервере есть

Симптом: на сервере данные есть (через shell видны), а страница показывает «нет данных» или неверный статус — например AIDE пишет «Не инициализирована», хотя база создана.

Причина — open_basedir: многие панели и хостинги ограничивают пул PHP-FPM каталогом домена, поэтому PHP-функции file_exists(), file_get_contents(), filemtime() по системным путям (/var/lib/aide, /var/log, /proc…) блокируются. Монитор обходит это, читая такие пути штатными системными командами (cat, test, stat).

# Виден ли файл через shell (так читает монитор): sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # Текущее значение open_basedir для пула домена: grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
Если shell «видит» файл (VISIBLE), а страница нет — это open_basedir. Правильное решение — чтение системными командами (уже сделано для AIDE и Сетевого монитора). Расширять open_basedir на /var, /proc не нужно и менее безопасно.

39. SSL-страница не работает

Монитор проверяет сертификаты, подключаясь к доменам напрямую через порт 443. Если домен недоступен с самого сервера или порт закрыт файрволом — проверка не пройдёт.

# Проверить сертификат вручную: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # Проверить доступность: curl -I https://monitor.example.com
Домены монитор берёт автоматически из конфигов nginx (/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) и Apache (/etc/apache2/sites-enabled/) плюс текущий хост из HTTP_HOST.
Автоопределение поддоменов. Поддомены определяются автоматически из публичных логов Certificate Transparency и проверяются по сети — даже если размещены на других серверах. Вручную ничего добавлять не нужно.

40. Видна только одна БД из нескольких

Монитор подключается к MySQL под пользователем из config.php, у которого доступ только к своей базе. MySQL показывает в information_schema лишь базы с привилегиями — поэтому остальные не видны.

Чтобы монитор видел все БД, выдайте этому пользователю право только на чтение (один раз от root; подставьте имя пользователя из config.php):

sudo mysql -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* даёт только право чтения — изменить, удалить или создать что-либо нельзя, это безопасно для мониторинга.
Без этого GRANT панель видит только свою базу — это не ошибка, а ограничение прав. Никакого sudo mysql панель не использует: список баз берётся через её собственное PDO-подключение.

41. PostgreSQL не отображается на странице «База данных»

PostgreSQL требует доступ уровня пользователя postgres, которого у веб-пользователя панели нет. Открывать из PHP широкий sudo psql небезопасно — вместо этого панель вызывает узкую обёртку без параметров, которая только печатает версию, число соединений и список баз с размерами. Создайте её:

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 # в /etc/sudoers.d/monitor (пользователь = тот, под которым работает PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
Если PostgreSQL не используете — уберите строку monitor-pgstat из sudoers (шаг 13 ручной установки) и сам скрипт не создавайте: карточка PostgreSQL останется просто неактивной.

42. Сработал алерт — что делать

Панель показывает что происходит; ниже — что делать в типовых ситуациях. Общий принцип: не паниковать, сверить с легитимной активностью (ваши действия, обновления, бэкапы), и реагировать по серьёзности.

  • Карта атак / много банов fail2ban — это норма для любого сервера в интернете (боты постоянно перебирают SSH/веб). Главное, чтобы баны срабатывали. Убедитесь, что вход по SSH — только по ключу (пароль отключён), и ваш IP в ignoreip.
  • ModSecurity заблокировал запросы — WAF отбивает атаки на сайт, это его работа. Если блокируется ваш легитимный трафик (ложное срабатывание) — найдите rule id в деталях и добавьте исключение в конфиг CRS.
  • AIDE: изменены файлы — сверьте список с тем, что вы делали (обновление пакетов, правка конфигов — норма). Изменения системных бинарников, которые вы не трогали — повод насторожиться. После легитимных изменений обновите базу AIDE.
  • debsums: изменены бинарники/библиотеки (вне /etc, вне /usr/share) — потенциально подмена. Сверьте пакет: debsums PACKAGE_NAME, при сомнении переустановите его (apt install --reinstall).
  • ClamAV / maldet: найдена угроза — проверьте файл в карантине, не открывайте его. Если это веб-шелл в каталоге сайта — изолируйте сервер и ищите точку входа (уязвимый плагин, утечка доступов).
  • Falco: критические события (запуск shell в контейнере, доступ к чувствительным файлам) — разберите событие: чей процесс, что запускал. Часто это легитимная админ-активность.
  • Внешняя экспозиция: красным СУБД/кэш — немедленно закройте: привяжите сервис к 127.0.0.1 или закройте порт в UFW. Это реальная дыра.
  • SSL истекает / истёк — продлите сертификат (Let's Encrypt продлевается сам; если нет — проверьте certbot renew или настройки в панели).
  • Обновления безопасности ждут — установите: sudo apt update && sudo apt upgrade; после обновления ядра перезагрузите сервер.
Признаки реального взлома (неизвестные процессы/пользователи, изменённые бинарники, исходящий спам, неизвестные cron-задания): отключите сервер от внешнего доступа, снимите бэкап для анализа и, если данные критичны, поднимайте чистый сервер из доверенного бэкапа — вычистить руткит надёжно сложно.
Arcivéo - Security Monitor © 2026