FAQ

To jest przewodnik po instalacji, konfiguracji i obsłudze Arcivéo Security Monitor. Sekcje pogrupowano: ogólny przegląd, wdrożenie panelu, podłączenie narzędzi bezpieczeństwa, wbudowane moduły i diagnostyka. Polecenia można kopiować przyciskiem po prawej.

Od czego zacząć

01. Instalacja panelu — wybierz metodę

Instalacja panelu opisana jest na osobnych stronach krok po kroku. Wybierz metodę:

Jeśli nie masz pewności — wybierz automatyczną. Ten poradnik pozostaje jednym źródłem wiedzy o SSL, narzędziach, cron i diagnostyce — strony instalacji odsyłają do jego sekcji, niczego nie powielając.

Przegląd

02. Czym jest Arcivéo Security Monitor

Arcivéo Security Monitor — panel bezpieczeństwa serwera. Zbiera dane z zainstalowanych narzędzi (Fail2ban, UFW, Lynis, ModSecurity, AIDE, ClamAV, Auditd, CrowdSec, Suricata, Falco i in.) i wyświetla je w jednym interfejsie z pulpitem, mapą ataków oraz szczegółowymi stronami dla każdego narzędzia.

Monitor nie jest aktywnym środkiem ochrony — sam nie blokuje ataków. Jego zadaniem jest agregowanie informacji z już działających narzędzi i przedstawianie ich w wygodnej formie.

03. Jak monitor działa na serwerze

Monitor działa tylko lokalnie — musi być zainstalowany na tym samym serwerze, który monitoruje. Nie ma żadnego SSH ani zdalnego API.

Wszystkie polecenia (fail2ban-client, ufw status, ipset list itd.) panel wykonuje w imieniu użytkownika serwera WWW (zwykle www-data, na panelach hostingowych — konto witryny) z wąskim zestawem uprawnień sudo — tylko do konkretnych narzędzi, bez ogólnego dostępu root. Wyniki są parsowane i wyświetlane w przeglądarce.

Dwa rytmy pracy. Strony panelu odpytują narzędzia w chwili otwarcia — zawsze widać stan bieżący. Poza tym w tle działają zadania uruchamiane według harmonogramu (cron):

  • co 5 minut — migawka obciążenia dla strony „Wydajność”;
  • co 5 minut — czujnik zdarzeń bezpieczeństwa: nowe bany, alerty IDS, zatrzymana ochrona, otwarty port (rozdział „Zdarzenia bezpieczeństwa”);
  • raz na dobę — raport bezpieczeństwa na Telegram i e-mail oraz ciężkie kontrole (Lynis, AIDE, ClamAV, debsums, Logwatch);
  • co 15 minut — podsumowanie serwera do panelu klienta, jeśli jest włączone.

Pełna lista zadań znajduje się w rozdziale „Wszystkie zadania cron w jednym miejscu”.

Przy kilku serwerach monitor instaluje się na każdym osobno, z własną domeną. Aby widzieć je na jednej liście, proszę włączyć przegląd serwerów (rozdział „Przegląd serwerów w panelu klienta”).

04. Jak obliczana jest Ocena bezpieczeństwa

Ocena zaczyna się od maksimum i spada za każdy wykryty problem:

  • UFW nieaktywna — −30
  • Fail2ban nie uruchomiony (brak aktywnych jail) — −25
  • Brak kluczy WebAuthn — −15
  • Lynis hardening index < 60 — −20; 60–79 — −10
  • IPset ipsum nie załadowany — −10
  • Zagrożenia wykryte przez ClamAV — −20
  • Zmiany plików AIDE — −15
  • SSL wygasł — −30, wygasa za <14 dni — −15, <30 dni — −5
  • CrowdSec zainstalowany, ale nie uruchomiony — −5
  • Suricata zainstalowana, ale nie uruchomiona — −5
  • Bazy danych/pamięć podręczna (MySQL, PostgreSQL, Redis…) dostępne z zewnątrz — −10
  • Włączone logowanie root przez SSH (PermitRootLogin yes) — −20
  • Włączone logowanie hasłem przez SSH (PasswordAuthentication yes) — −10
  • Drobne ustawienia SSH: włączony X11Forwarding, MaxAuthTries powyżej 3 lub sshd_config czytelny dla wszystkich kont — −5
  • Zabezpieczenie serwera WWW: odpowiedzi ujawniają wersję serwera WWW lub PHP, włączona jest metoda TRACE albo brakuje nagłówka Strict-Transport-Security lub X-Frame-Options — −5
  • Oczekujące aktualizacje zabezpieczeń — −5

Wynik: 80+ = Zabezpieczony, 60–79 = Uwaga, <60 = Zagrożony.

Odliczenia za ClamAV, AIDE, CrowdSec i Suricata stosowane są tylko wtedy, gdy narzędzie jest zainstalowane. Lynis i AIDE bez zainicjalizowanej bazy wyświetlane są jako „brak danych” i nie obniżają wyniku. Liczba ataków z dzisiaj pokazywana jest na pulpicie, ale nie wpływa na Ocenę bezpieczeństwa. Zabezpieczenie serwera WWW nie jest liczone, jeśli żadna witryna nie odpowiedziała na sprawdzenie.

Ustawienia i licencja

05. WebAuthn — uwierzytelnianie dwuskładnikowe

WebAuthn to standard uwierzytelniania bez hasła za pomocą sprzętowego klucza. Obsługuje YubiKey, Touch ID, Face ID, Windows Hello, Passkey.

Po zalogowaniu hasłem system prosi o potwierdzenie zarejestrowanym kluczem. Nawet jeśli hasło wycieknie — bez fizycznego klucza lub biometrii logowanie jest niemożliwe.

Aby to skonfigurować, otwórz Klucze WebAuthn w menu bocznym i kliknij „Zarejestruj klucz”. Proszę od razu zarejestrować dwa klucze: jeśli jedyny klucz zostanie zgubiony lub uszkodzony, logowanie nim do panelu będzie niemożliwe.

WebAuthn działa wyłącznie przez HTTPS. Przy połączeniu HTTP rejestracja i logowanie kluczem są niedostępne.

06. Powiadomienia: Telegram, Email, webhook i push

Kanały są cztery: Telegram, Email, webhook i push w przeglądarce. Są od siebie niezależne — można włączyć dowolne, nawet wszystkie cztery. Tymi samymi kanałami idą dzienny raport bezpieczeństwa, alerty o obciążeniu i znaleziska czujnika zdarzeń; konfiguruje się je raz, tutaj, w sekcji „Ustawienia”.

Telegram. Potrzebne są token bota oraz chat id:

  1. W Telegramie napisz do @BotFather → /newbot → otrzymasz token w postaci 123456:ABC....
  2. Napisz do swojego nowego bota dowolną wiadomość (aby mógł odpowiadać).
  3. Poznaj swój chat id: napisz do bota @userinfobot lub otwórz https://api.telegram.org/bot<TOKEN>/getUpdates i znajdź "chat":{"id":...}.
  4. Wklej token i chat id w „Ustawienia” → Telegram, kliknij „Zapisz i wyślij test”.

Email. Dwa sposoby do wyboru w „Ustawienia” → Email:

  • SMTP — host, port (465/SSL lub 587/TLS), login i hasło Pana skrzynki pocztowej;
  • Resend — nowoczesne API: podaj klucz API (re_...) oraz zweryfikowaną domenę nadawcy.

Webhook. Jeden adres przyjmujący POST obsługuje Slacka, Discorda, Mattermosta, n8n i własny skrypt. „Ustawienia” → Webhook: proszę wkleić adres i włączyć przełącznik.

  • do Slacka i Mattermosta wysyłane jest {"text": …}, do Discorda {"content": …} (Discord odrzuca wiadomości dłuższe niż 2000 znaków, więc tekst jest przycinany);
  • każdy inny adres dostaje ustrukturyzowany JSON: text, event, severity, source, count, host, ts — nie trzeba ponownie rozbierać treści wiadomości.

Powiadomienia push. Komunikat trafia wprost do przeglądarki, a jeśli panel jest dodany do ekranu głównego telefonu — jako zwykłe powiadomienie aplikacji, bez bota i bez serwera pocztowego po drodze. „Ustawienia” → Powiadomienia push: proszę włączyć przełącznik i nacisnąć „Podłącz to urządzenie” na każdym urządzeniu, które ma odbierać powiadomienia. Licznik „Podłączone urządzenia” pokazuje, ile ich jest.

Push działa wyłącznie przez HTTPS (tak jak WebAuthn), a zgoda potrzebna jest dwukrotnie: najpierw w przeglądarce — pytanie pojawi się po naciśnięciu „Podłącz to urządzenie” — a potem w samym systemie operacyjnym. Jeśli w Windows wyłączony jest główny przełącznik Ustawienia → System → Powiadomienia, przeglądarka zgodę otrzyma, panel zgłosi wysłanie, a na ekranie i tak nic się nie pojawi. To samo powoduje tryb „Nie przeszkadzać” oraz wyłączone powiadomienia przeglądarki w macOS (Ustawienia systemowe → Powiadomienia).
Każdy kanał ma własny przycisk „Wyślij test”, który sprawdza go od razu. Harmonogram automatycznego raportu obsługuje cron (rozdział „Wszystkie zadania cron”): cron uruchamia wysyłkę, a kanały brane są z ustawień.

Status raportu: „UWAGA” lub „OK”. Nagłówek staje się „UWAGA” tylko przy realnym problemie lub oczekującym działaniu: wykryto zagrożenie ClamAV, zmiany plików w AIDE, krytyczne zdarzenia Falco (Emergency/Alert/Critical z ostatnich 24 h), padłą usługę w Monit, wymagany restart, wygasający SSL (≤14 dni) lub oczekujące aktualizacje zabezpieczeń. Szum tła — próby zgadywania SSH przez boty, adresy IP zbanowane przez fail2ban, alerty Suricata, ostrzeżenia Lynis i już odparte żądania ModSecurity — nie podnosi statusu, dlatego takie liczby w raporcie same w sobie nie oznaczają „UWAGA”.

07. Licencja — wprowadzenie i aktywacja

Szczegółowe moduły monitorowania (Lynis, UFW, ModSecurity, mapa ataków, AIDE, ClamAV i inne) odblokowują się po posiadaniu ważnej licencji. Bez niej dashboard, ustawienia i konto działają, a moduły pokazują kartę „Wymagana licencja”.

Po zakupie w panelu klienta ma Pan/Pani kod aktywacyjny w postaci ARCIVEO-XXXX-XXXX-XXXX-XXXX. Trzeba go „aktywować” na domenę swojego panelu — zmieni to kod w podpisany plik licencyjny (blok [license]), który wkleja Pan/Pani do panelu.

Jak aktywować (3 kroki):

  1. Weź kod aktywacyjny. Panel klienta my.arciveo.com → sekcja „Licencje” / „Aktywacja licencji” — skopiuj kod ARCIVEO-….
  2. Aktywuj kod na swoją domenę. W tym samym panelu otwórz „Aktywacja licencji”, wprowadź: kod aktywacyjny, swój email i domenę panelu (adres, pod którym otwiera się monitor, np. monitor.example.com). Naciśnij aktywuj — system wygeneruje plik licencyjny powiązany z tą domeną i pokaże go w polu z przyciskiem „Kopiuj”.
  3. Wklej klucz do panelu. Skopiuj cały tekst licencji → w panelu otwórz „Ustawienia” → blok „Licencja”, wklej i naciśnij „Zapisz”. Moduły odblokują się natychmiast.

Panel weryfikuje klucz kryptograficznie: podpis, powiązanie z domeną i termin ważności.

Domena przy aktywacji musi dokładnie odpowiadać adresowi panelu. Weź ją ze stałej APP_URL w config.php i wprowadź tylko nazwę hosta — bez https:// i bez prefiksu www. Aktywacja jest jednorazowa: kod zmienia się w licencję dla wprowadzonej domeny i nie da się go aktywować ponownie — przy błędzie w domenie klucz nie będzie pasował do Pańskiego panelu, a kod zostanie zużyty. Dlatego proszę wprowadzać domenę uważnie.
Termin upłynął lub zmieniła się domena — w nagłówku panelu pojawi się ostrzeżenie. Licencja jest powiązana z domeną na stałe i nie przenosi się na inną domenę: na nowy termin lub nową domenę potrzebny jest nowy klucz (kupowany w panelu klienta i aktywowany jednorazowo).

08. Plik config.php — wszystkie ustawienia panelu

Wszystkie podstawowe parametry panelu są zdefiniowane w jednym pliku config.php w katalogu głównym (obok folderu public/) za pomocą zwykłych stałych define(). Plik jest tworzony podczas instalacji; ręczna edycja jest rzadko potrzebna — głównie przy zmianie domeny, przenoszeniu lub podłączaniu do innej bazy. Po każdej zmianie uruchom ponownie PHP-FPM (w przeciwnym razie zmiany nie zostaną zastosowane z powodu OPcache).

Wstaw własne wartości w podświetlonych miejscach; resztę pozostaw bez zmian:

// --- Baza danych --- define('DB_HOST', 'localhost'); // pozostaw define('DB_NAME', 'db_name'); // ustawione przy tworzeniu bazy define('DB_USER', 'user'); // ustawione przy tworzeniu bazy define('DB_PASS', 'db_password'); // ustawione przy tworzeniu bazy define('DB_CHARSET', 'utf8mb4'); // pozostaw // --- Aplikacja --- define('APP_URL', 'https://monitor.example.com'); // adres panelu, bez ukośnika na końcu define('TIMEZONE', 'Europe/Warsaw'); // Pana/Pani strefa czasowa // --- Czas sesji --- define('SESSION_LIFETIME', 28800); // bezczynność do ponownego logowania, s (28800 = 8 h)

Baza danych. Dane połączenia z MySQL/MariaDB:

  • DB_HOST — host bazy danych, prawie zawsze localhost;
  • DB_NAME — nazwa bazy danych panelu;
  • DB_USER — użytkownik bazy (dostęp tylko do własnej bazy);
  • DB_PASS — hasło tego użytkownika;
  • DB_CHARSET — kodowanie połączenia, pozostaw utf8mb4.

Aplikacja.

  • APP_URL — pełny adres panelu (np. https://monitor.example.com). Musi być zgodny z domeną, na którą aktywowano licencję — w przeciwnym razie klucz zostanie odrzucony (zob. sekcję „Licencja”);
  • TIMEZONE — strefa czasowa PHP: wpływa tylko na to, jak panel wyświetla daty i godziny. Na czas uruchamiania zadań cron nie wpływa — tam obowiązuje strefa systemu (zob. „Wszystkie zadania cron”).

Czas sesji. SESSION_LIFETIME — limit bezczynności sesji w sekundach (przesuwny: odnawia się przy aktywności). Domyślnie 28800 = 8 godzin; po tym czasie bezczynności panel poprosi o ponowne zalogowanie. Na przykład 3600 = 1 godzina, 86400 = doba.

Rejestrowanie błędów. Błędy nigdy nie są pokazywane odwiedzającym, lecz zapisywane w logs/php_errors.log — widać je na stronie „Logi aplikacji”. Tych wierszy (display_errors=0, log_errors=1, ścieżka error_log) zwykle nie trzeba zmieniać — ustawienia są zdefiniowane bezpośrednio w pliku i nie zależą od php.ini.

config.php — plik poufny. Zawiera hasło do bazy danych. Znajduje się w katalogu głównym panelu (obok public/), a w tym panelu katalog główny serwera (DocumentRoot) to właśnie katalog główny panelu, a nie public/. Sam plik nie „wycieka”: w głównym .htaccess ma dla niego ustawiony jawny zakaz (Require all denied) — serwer zwraca 403. Nawet bez tej reguły kod źródłowy by nie wyciekł: to PHP — serwer go wykonuje, a nie wysyła jako tekst. Na wszelki wypadek: nie publikuj go w publicznych repozytoriach i nie przekazuj do pomocy technicznej z prawdziwym hasłem. Uprawnienia pliku — 640.
Przy przenoszeniu lub przywracaniu dostępu ten plik jest głównym źródłem danych: nazwa bazy, użytkownik i hasło pochodzą właśnie stąd (zob. sekcje „Aktualizacja i przenoszenie panelu” oraz „Przywracanie dostępu”).

09. API tylko do odczytu (JSON i Prometheus)

Panel chroniony jest logowaniem hasłem oraz, jeśli jest włączony, filtrem IP. Programowi nie odpowiada ani jedno, ani drugie, dlatego systemy zewnętrzne mają własne wejście — dwa adresy tylko do odczytu zabezpieczone tokenem. Token niczego na serwerze nie zmienia: da się nim wyłącznie pobrać wskaźniki.

Włączenie — „Ustawienia” → „API tylko do odczytu”: przełącznik „Włączyć API”; token tworzony jest automatycznie przy pierwszym zapisie. Przycisk „Nowy token” wydaje inny i w tej samej chwili odcina wszystkich, którzy korzystali z poprzedniego.

  • /api/status.php — JSON: ocena bezpieczeństwa, stan modułów, ostatni pomiar obciążenia wraz z najcięższymi procesami oraz zdarzenia bezpieczeństwa z ostatnich 24 godzin;
  • /api/metrics.php — te same liczby w tekstowym formacie Prometheusa (metryki arciveo_*: arciveo_security_score, arciveo_fail2ban_banned, arciveo_cpu_busy_pct i tak dalej).
curl -H "Authorization: Bearer <TOKEN>" https://monitor.example.com/api/status.php curl -H "Authorization: Bearer <TOKEN>" https://monitor.example.com/api/metrics.php

Jeśli klient nie potrafi ustawić nagłówka, token przyjmowany jest także jako parametr — ?token=…. Proszę pamiętać, że w tej postaci trafia on do dziennika dostępu serwera WWW, więc nagłówek jest lepszy.

Prometheus. Gotowy blok do scrape_configs:

scrape_configs: - job_name: arciveo metrics_path: /api/metrics.php scheme: https authorization: credentials: <TOKEN> static_configs: - targets: ['monitor.example.com']
Odpowiedź jest buforowana na 5 minut: za tymi liczbami stoi to samo ciężkie odpytanie narzędzi co za pulpitem. Częste odpytywanie nic więc serwera nie kosztuje — wartości po prostu się powtarzają aż do kolejnego odświeżenia — ale interwał krótszy niż minuta również nie ma sensu.
401 — token jest nieprawidłowy albo go nie przesłano. 404 z tekstem api disabled — API jest wyłączone w ustawieniach. Odrzucone żądania zapisywane są w dzienniku aplikacji wraz z adresem IP nadawcy, więc próby zgadywania tokenu widać na stronie „Logi aplikacji”.

10. Przegląd serwerów w panelu klienta

Panel instaluje się na każdym serwerze osobno i dobrze odpowiada na pytanie „jak się miewa ta maszyna”. Na pytanie „jak się miewają wszystkie moje maszyny” nie odpowiada — po to każdy panel może wysyłać krótkie podsumowanie do panelu klienta, gdzie serwery widoczne są na jednej liście.

Co wziąć w panelu klienta my.arciveo.com → sekcja „Moje serwery”: wydawany jest tam token (postaci fk_…) i podany adres odbiorcy https://my.arciveo.com/api/fleet.php. Token jest jeden na konto — taki sam dla wszystkich Państwa serwerów.

Co ustawić w panelu — „Ustawienia” → „Przegląd serwerów”: adres odbiorcy, token, nazwa serwera (jak będzie podpisany na liście; pozostawiona pusta oznacza domenę panelu) oraz przełącznik „Wysyłać podsumowanie”. Przycisk „Wyślij teraz” sprawdza łączność natychmiast, bez czekania na harmonogram.

Dalej podsumowanie wychodzi przez cron, co 15 minut:

# co 15 minut — podsumowanie serwera do panelu klienta 3,18,33,48 * * * * /usr/local/bin/fleet-push-all.sh >> /path/to/monitor/logs/cron.log 2>&1

Co dokładnie wychodzi: nazwa i domena serwera, system operacyjny, czas pracy, wersja PHP; ocena bezpieczeństwa; stan modułów (UFW, liczba jaili Fail2ban, bany z dzisiaj, czy działają CrowdSec, Suricata i Falco, zagrożenia ClamAV, zmiany AIDE, indeks Lynis, oczekujące aktualizacje, czy potrzebny jest restart, dni do wygaśnięcia certyfikatu, liczba niebezpiecznie otwartych portów); ostatni pomiar obciążenia (load, CPU, RAM, dysk, inody); liczniki zdarzeń z ostatnich 24 godzin i ostatnie zdarzenie krytyczne. Dzienników, haseł ani treści plików w podsumowaniu nie ma.

Wysyła serwer, a panel klienta o nic nie pyta. Dlatego do Państwa sieci nie trzeba otwierać wejścia z zewnątrz, a panel zamknięty filtrem IP i tak się zgłasza. Węzeł rozpoznawany jest po domenie panelu, więc lista nie dubluje się po restarcie ani po zmianie IP.
Serwer oznaczony w panelu klienta jako milczący oznacza, że podsumowanie dawno nie przychodziło. Proszę sprawdzić po kolei: przełącznik „Wysyłać podsumowanie”, wiersz cron (sudo crontab -l) oraz to, co odpowiada przycisk „Wyślij teraz”. Błędy wysyłki zapisywane są w dzienniku aplikacji pod znacznikiem FLEET — widać tam, czy token został odrzucony (HTTP 401), czy też samo połączenie nie doszło do skutku.

Narzędzia bezpieczeństwa

11. Zapora UFW

UFW (Uncomplicated Firewall) to prosty interfejs do nftables/iptables. Blokuje wszystkie porty przychodzące poza jawnie dozwolonymi. Strona „Zapora UFW” pokazuje status i reguły.

sudo apt install ufw # Zezwól na SSH (koniecznie PRZED włączeniem!) oraz na ruch WWW sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Zablokuj dostęp do bazy z zewnątrz (dostęp tylko lokalny) sudo ufw deny 3306 # Włącz i sprawdź sudo ufw enable sudo ufw status verbose
Przed ufw enable koniecznie zezwól na SSH (ufw allow OpenSSH), w przeciwnym razie utracisz Pan dostęp do serwera.
„Ekspozycja zewnętrzna” w panelu uwzględnia UFW: port zablokowany regułą deny nie jest uznawany za dostępny z zewnątrz.
Skipping adding existing rule — to nie błąd. W ten sposób UFW informuje, że dokładnie taka reguła już istnieje i nie dodaje jej ponownie. Przy ponownym uruchomieniu autokonfiguracji (jest idempotentna) to standardowy komunikat — nie trzeba reagować.

12. Instalacja Fail2ban

Automatycznie blokuje adres IP po przekroczeniu liczby nieudanych prób logowania. Analizuje logi SSH, nginx, Apache i innych usług.

sudo apt install fail2ban sudo systemctl enable --now fail2ban # Sprawdź status: sudo fail2ban-client status
Działająca konfiguracja (jail.local z dziesiątkami jaili i automatycznym banowaniem z ipsum) — w następnej sekcji.

13. Działająca konfiguracja Fail2ban + ipsum

Instalacja podstawowa — powyżej. Tutaj — działająca konfiguracja dająca dziesiątki aktywnych jaili i tysiące blokad: ustawienia ogólne, kluczowe jaile oraz automatyczny ban złośliwych IP z listy ipsum.

Plik /etc/fail2ban/jail.local — ustawienia ogólne i najważniejsze jaile:

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # Ban progresywny: każde ponowienie — dłużej bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # trwały ban za brute-force SSH findtime = 3600 # Recydywiści: kto złapał kilka banów — banowany na zawsze [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 # Usługi webowe (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog): [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … i pozostałe jaile według usług (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) — enabled = true
W ignoreip koniecznie wpisz swój IP i zaufane sieci, w przeciwnym razie można zbanować samego siebie. Po zmianach: sudo fail2ban-client reload.

Automatyczne wczytywanie listy blokad ipsum — w cronie root (sudo crontab -e): level 1 (ponad 100 tysięcy IP) ładuje się do zbioru ipsum, który jest odcinany na zaporze (więcej — w sekcji „Lista blokad IPset”):

# 04:00 — aktualizacja ipset ipsum (level 1, maksymalny zasięg): 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
Zbiór musi nazywać się ipsum — właśnie jego odczytuje dashboard (karta „IPset ipsum”). Poziomy: levels/1.txt — maksymalny zasięg, levels/3.txt — dokładniejszy (3+ źródła).

Dlaczego „Monitor bezpieczeństwa” jest podzielony na dwie strefy. Ochrona działa na dwóch poziomach, a dashboard ich nie miesza:

  • Rzeczywiste ataki (reakcja) — wszystko, co złapał fail2ban: żywe próby włamania (jaile sshd, apache-*, nginx-* itp.) oraz groźni recydywiści (jail recidive — ci, którzy byli już banowani kilka razy). To adresy IP, które naprawdę się do Pana dobijały — są na mapie ataków i „Osi czasu”.
  • Blokada prewencyjna (proaktywna) — publiczna lista blokad znanych złośliwych IP ipset ipsum, odcinana na zaporze regułą DROP. Te adresy w większości nawet nie dotknęły Pana serwera — są odcinane z wyprzedzeniem; licznik „IPset ipsum” pokazuje, ile odcięto prewencyjnie.

Różnica jest prosta: reakcja — „ci zaatakowali i dostali bana”, prewencja — „tych zablokowano jeszcze przed próbą”. Wcześniej do recidive sztucznie wciągano list-3 ipsum (stąd dawny podział „recidive z listy”); teraz recidive to tylko prawdziwi recydywiści, a prewencja w całości na zaporze.

14. Lista blokad IPset (ipsum)

ipsum — publiczna lista złośliwych adresów IP, aktualizowana codziennie. Monitor pokazuje liczbę załadowanych adresów na pulpicie i mapie ataków oraz uwzględnia ją w Ocenie bezpieczeństwa (−10, jeśli zestaw nie jest załadowany).

Minimalny wariant bez fail2ban — osobny zestaw ipsum z blokowaniem przez iptables:

# Utwórz zestaw (jednorazowo): sudo ipset create ipsum hash:ip # Skrypt aktualizacji /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, codziennie o 4:00): 0 4 * * * /usr/local/bin/update-ipsum.sh
Rozszerzony wariant z fail2ban-recidive — w sekcji „Robocza konfiguracja Fail2ban + ipsum”.
ipset działa w pamięci i znika po ponownym uruchomieniu. Sam codzienny cron pozostawi zestaw pusty od momentu restartu do następnego uruchomienia (pulpit pokaże 0). Ładuj zestaw także przy starcie — przenieś ładowanie do skryptu i podepnij je pod @reboot. Przy okazji polecenie create … -exist ustawia limit maxelem 300000 (domyślnie 65536 — level 1 się nie mieści, wystąpi „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 — codziennie o 04:00 ORAZ przy każdym starcie: 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
Jak to działa przy automatycznej instalacji. Skrypt ładuje pełną listę level 1 (100+ tysięcy IP) do zestawu ipsum i — jeśli zaporą zarządza instalator (świeży VPS — profile „Pełna”/„Odchudzona”) — podłącza zestaw do UFW regułą DROP, dzięki czemu ruch z tych IP jest faktycznie blokowany. Reguła znajduje się po ESTABLISHED,RELATED, więc bieżące połączenia (w tym Pana SSH) nie są zrywane — blokowane są tylko nowe połączenia z listy. Zestaw jest odtwarzany przy uruchomieniu usługi ipsum-load.service przed zaporą (inaczej UFW by się nie podniosło) i aktualizowany cronem o 04:00. Na już skonfigurowanym serwerze (panel, własna zapora) instalator nie ingeruje w zaporę — tam ipsum pozostaje listą dla pulpitu i mapy ataków, a regułę DROP w razie potrzeby dodaje się ręcznie (minimalny wariant z iptables … --match-set ipsum … -j DROP — powyżej). Przy automatycznej instalacji nie trzeba nic robić ręcznie.

15. Instalacja CrowdSec

Nowoczesny zamiennik Fail2ban z kolektywnym threat intelligence: blokady od społeczności oraz własne reguły. Wymaga osobnego bouncera do stosowania blokad w zaporze.

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # Bouncer dla iptables/nftables: sudo apt install crowdsec-firewall-bouncer-iptables # Sprawdź status: sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
Status „Nie uruchomiono” w panelu = pakiet zainstalowany, ale usługa nie jest aktywna (monitor sprawdza ją przez systemctl is-active crowdsec). Uruchom: sudo systemctl enable --now crowdsec; przy awarii sprawdź sudo journalctl -u crowdsec -n 30. Ta sama zasada dotyczy każdej usługi w statusie „Nie uruchomiono” (Suricata, Falco, Monit, MySQL).
„0 scenariuszy” lub „0 bouncers” na pulpicie. CrowdSec zaraz po instalacji jest niemal pusty — bez kolekcji nic nie wykrywa, a bez zarejestrowanego bouncera blokady nie są stosowane w zaporze. Zainstaluj podstawowe kolekcje i upewnij się, że bouncer jest na liście:
# Podstawowe kolekcje (Linux + SSH + serwer WWW): sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # Bouncer powinien być na liście i mieć status aktywnego połączenia: sudo cscli bouncers list
W logu bouncera stream halted / blokady nie są stosowane. To osierocony klucz API: bouncer został usunięty z cscli bouncers list, ale jego stary klucz pozostał w /etc/crowdsec/bouncers/*.yaml. Ponownie zarejestruj bouncera i wpisz świeży klucz:
sudo cscli bouncers add fw-bouncer # wyświetli nowy api_key # wpisz ten klucz w api_key: w /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml sudo systemctl restart crowdsec-firewall-bouncer
Automatyczna instalacja (profil „Pełna ochrona”) sama instaluje kolekcje i rejestruje firewall-bouncer — ręcznie jest to potrzebne tylko przy instalacji ręcznej lub po ręcznej ingerencji w CrowdSec.

16. Instalacja AIDE

AIDE (Advanced Intrusion Detection Environment) tworzy migawkę systemu plików i przy każdym sprawdzeniu zgłasza zmiany w /etc, /bin, /usr. Po instalacji konieczna jest inicjalizacja bazy (aideinit).

sudo apt install aide # Inicjalizacja bazy (5–15 minut): sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04: katalog /var/lib/aide jest tworzony w trybie 700 (właściciel _aide), # a panel (www-data) nie widzi bazy → pokazuje „Nie zainicjalizowana”. # Otwórz katalog do przejścia (pliki bazy pozostają 600): sudo chmod 755 /var/lib/aide # Pierwsze sprawdzenie Z ZAPISEM do logu, który czyta monitor. # Na Ubuntu/Debian aide wymaga jawnego --config (inaczej „missing configuration”; # plik binarny aide.wrapper nie jest dostarczany w nowszych wersjach): sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Podczas aideinit terminal przez 5–15 minut stoi na linii Running aide --init... — to normalne (haszowanie całego systemu plików, obciążenie dysku). Nie przerywaj klawiszami Ctrl+C. Jeśli proces „wisi”, ale nic nie wypisuje — możliwe, że czeka na odpowiedź na ukryte zapytanie Overwrite existing aide.db.new [Yn]? (naciśnij Y). Sprawdź aktywność z innej sesji: pgrep -af aide.
Błąd aideinit: „21_aide_spamassassin … printf: invalid number” (return code 20) — znany błąd fragmentu konfiguracji AIDE w Ubuntu 22.04. Baza nie zostaje utworzona. Wynieś uszkodzony fragment i powtórz:
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
Statusy na pulpicie. „Nie zainicjalizowana” = panel nie widzi pliku bazy: albo aideinit nie był uruchomiony, albo (Ubuntu 24.04) katalog /var/lib/aide został utworzony w trybie 700 i jest niedostępny dla www-data — naprawisz to poleceniem sudo chmod 755 /var/lib/aide (patrz blok powyżej). „Brak sprawdzeń” = baza istnieje, ale sprawdzenie nie zostało jeszcze wykonane — to nie błąd. Wyniki monitor czyta z /var/log/aide/aide.log.
Regularne sprawdzenie → log dla panelu. Standardowy /etc/cron.daily/aide na nowych Ubuntu/Debian może nie zapisywać /var/log/aide/aide.log we właściwej postaci (a aide.wrapper już w nich nie ma). Pewniejsze jest dodanie własnego crona z jawnym --config — zapisuje on log od root w trybie 644, a monitor czyta go bez dodatkowych grup:
# sudo crontab -e — codzienne sprawdzenie o 02:00: 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # Uruchom teraz, nie czekając na harmonogram: sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Automatyczna instalacja robi już to wszystko: chmod 755 /var/lib/aide i cron sprawdzania o 02:00 — ręcznie nic nie trzeba.
Pierwszą inicjalizację wykonaj na czystym serwerze — przed instalacją aplikacji webowych. Po legalnych zmianach odtwórz bazę: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.

17. Instalacja ClamAV

Skaner antywirusowy dla Linux. Szczególnie przydatny do sprawdzania /var/www pod kątem PHP-shelli i złośliwego kodu.

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # Zaktualizuj bazę sygnatur: sudo freshclam # Przeskanuj katalog ręcznie: sudo clamscan -r /var/www --infected
Demon clamd pokazuje „Nieaktywny” po enable --now? Trzy typowe przyczyny:

1. W konfiguracji pozostał wiersz Example — clamd nie uruchomi się, dopóki tam jest:

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

2. Nie pobrano bazy sygnatur — clamd nie uruchomi się bez niej:

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

3. Po prostu się ładuje — clamd wczytuje ~8 mln sygnatur do pamięci przez 30–60 s. Proszę poczekać i sprawdzić: systemctl is-active clamav-daemon (status activating → nadal ładuje).

Diagnostyka: sudo journalctl -u clamav-daemon -n 30 --no-pager.
Na dashboardzie „Sprawdzonych plików: 0” / „Ostatnie skanowanie: —”? Demon clamd tylko trzyma sygnatury w pamięci, sam niczego nie skanuje według harmonogramu. Panel pokazuje wyniki zaplanowanego skanowania, dlatego potrzebny jest cron, który skanuje i zapisuje log. Automatyczna instalacja dodaje nakładkę /usr/local/bin/clamav-scan.sh oraz cron o 01:30 — po pierwszym uruchomieniu wypełnią się „Sprawdzonych plików” i „Ostatnie skanowanie”. Aby uruchomić od razu, bez czekania na harmonogram: sudo /usr/local/bin/clamav-scan.sh.

18. Instalacja Linux Malware Detect (maldet)

Linux Malware Detect (LMD) — skaner złośliwego oprogramowania nastawiony na zagrożenia webowe: powłoki PHP, webowe backdoory, loadery. Wykorzystuje silnik ClamAV i uzupełnia go własnymi sygnaturami.

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 # Zaktualizuj sygnatury: sudo maldet -u # Skanuj /var/www: sudo maldet -a /var/www
LMD i ClamAV dobrze działają w parze. Ostatni raport: maldet --report.
Podczas instalacji może pojawić się wiersz update-rc.d: error: unable to read /etc/init.d/maldet — jest on nieszkodliwy. maldet nie korzysta z init.d, aktualizacja sygnatur i skany uruchamiane są przez /etc/cron.daily/maldet. Jeśli poniżej widać installation completed — wszystko się zainstalowało.
Na stronie LMD wyświetla „Nie zainstalowano”, choć jest zainstalowany? maldet instaluje się nie przez apt, lecz w /usr/local/maldetect, a przy włączonym open_basedir jego obecność sprawdzana jest przez powłokę — zob. sekcję „Strona jest pusta, choć dane na serwerze istnieją”.

19. Instalacja Suricata

Sieciowy system wykrywania włamań: analizuje ruch na poziomie pakietów i zna tysiące sygnatur ataków. Uzupełnia ModSecurity (ten działa na poziomie HTTP, Suricata — na poziomie TCP/IP).

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # Pobierz aktualne reguły: sudo suricata-update sudo systemctl enable --now suricata
Suricata „Aktywna”, ale panel nie pokazuje alertów / liczba zdarzeń wynosi 0? Suricata zapisuje /var/log/suricata/eve.json jako root w trybie 750 dla katalogu, a serwer WWW (www-data) nie może go odczytać. Otwórz katalog na przejście — pliki wewnątrz pozostają chronione:
sudo chmod o+rx /var/log/suricata
Automatyczna instalacja robi to sama — ręcznie nie trzeba.

20. Instalacja Falco

Przechwytuje wywołania systemowe przez eBPF/kernel module i wykrywa anomalie w czasie rzeczywistym: shell z nginx, odczyt /etc/passwd przez proces webowy, zapis do /bin itd.

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
Monitor odczytuje zdarzenia Falco przez journalctl -u falco (bez sudo — przez grupę systemd-journal). Proszę upewnić się, że www-data należy do tej grupy — patrz „Konfiguracja sudo” (pkt 2) na stronie instalacji ręcznej.
„0 zdarzeń w ciągu 24 godzin” to norma, a nie błąd. Falco jest event-driven: milczy, gdy wszystko jest w porządku, i zapisuje zdarzenie tylko przy anomalii (shell z procesu webowego, odczyt /etc/passwd, zapis do katalogów systemowych). Zero zdarzeń krytycznych w ciągu doby na spokojnym serwerze to zdrowy stan.
Dla panelu pewniejszy jest zapis do pliku. Odczyt przez journalctl wymaga uprawnień do dziennika; aby panel widział zdarzenia stabilnie, autoinstalacja włącza w Falco file_output → /var/log/falco/falco.log, a usłudze ustawia UMask=0022 (log odczytywany przez serwer WWW). Przy nowej instalacji nie trzeba tego konfigurować ręcznie.

21. Instalacja ModSecurity (WAF)

ModSecurity — zapora webowa (WAF) dla Apache lub Nginx. Blokuje ataki na poziomie aplikacji: wstrzyknięcia SQL, XSS, obejście ścieżek, skanery.

# Apache: sudo apt install libapache2-mod-security2 sudo a2enmod security2 # Zestaw reguł OWASP Core Rule Set: sudo apt install modsecurity-crs # OBOWIĄZKOWO: bez tego pliku silnik reguł jest wyłączony 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 # Sprawdzenie: powinno zwrócić 403 curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
Sama instalacja pakietu niczego nie chroni. Apache dołącza konfiguracje wierszem IncludeOptional /etc/modsecurity/*.conf, a pakiet umieszcza tylko modsecurity.conf-recommended — pod maskę *.conf ten plik nie pasuje. Jeśli nie skopiuje go Pan do modsecurity.conf, SecRuleEngine pozostaje Off: moduł jest załadowany, reguły CRS załadowane, ale ruch nie jest sprawdzany, a log audytu nie powstaje. Tryb pośredni DetectionOnly tylko zapisuje zdarzenia do logu, nie blokując żądań — panel pokazuje go na żółto.

Dostęp panelu do logu audytu. Log /var/log/apache2/modsec_audit.log należy do root (uprawnienia 640), użytkownik webowy go nie odczyta. Panel pobiera dane przez nakładkę — proszę ją utworzyć:

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 # w /etc/sudoers.d/monitor (użytkownik = ten, pod którym działa PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
Nakładka pobiera ostatnią dyrektywę SecRuleEngine bez wcięcia: wiersze z wcięciem znajdują się wewnątrz bloków <LocationMatch>/<Directory> (na przykład wyłączenie WAF dla phpMyAdmin) i nie określają trybu globalnego.
Użytkownik w sudoers musi być zgodny z użytkownikiem puli FPM: na zwykłym Apache/Debian jest to www-data, w HestiaCP pula witryny działa jako właściciel witryny (na przykład admin) — proszę sprawdzić grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.
Jeśli witryna stoi za proxy Nginx (HestiaCP), Apache widzi jako klienta samo proxy — panel pobiera prawdziwy IP atakującego z nagłówka X-Forwarded-For. Do statystyk trafiają tylko transakcje z wyzwoloną regułą: dyrektywa SecAuditLogRelevantStatus zapisuje w logu audytu wszelkie odpowiedzi 4xx/5xx, dlatego trafiają tam także zwykłe 403/500 — panel nie traktuje ich jako zdarzeń WAF.
Blok ---RULES--- jest potrzebny sekcji „Wszystkie aktywne reguły” — panel pokazuje nie tylko wyzwolone, ale w ogóle wszystkie załadowane reguły CRS + niestandardowe. Trzy ścieżki w pętli for f in … to typowe miejsca dla reguł CRS i lokalnych dodatków; jeśli ma Pan inny układ (pakiet umieszcza pliki we własnym katalogu lub reguły niestandardowe leżą nie w /etc/modsecurity/custom-rules.conf), proszę znaleźć rzeczywiste ścieżki poleceniem sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null i podstawić je na liście. Jeśli nakładka jest stara (bez tej sekcji) — sekcja po prostu pokaże ostrzeżenie „niedostępne”, reszta strony działa jak wcześniej.

22. Instalacja Auditd

Auditd (Linux Audit Daemon) rejestruje wywołania systemowe na poziomie jądra: logowania i wylogowania, polecenia sudo, nieudane próby uwierzytelniania, zmiany plików. Monitor pokazuje logowania, nieudane próby i polecenia sudo z dzisiaj.

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # Sprawdź status i zdarzenia: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
Monitor odczytuje zdarzenia przez ausearch (/usr/sbin/ausearch), a w razie potrzeby z /var/log/audit/audit.log poleceniem tail. Oba muszą znajdować się w sudoers.

23. Instalacja Monit

Monitoruje usługi (nginx, php-fpm, mysql itd.) i restartuje je po awarii. Może wysyłać alerty na e-mail.

sudo apt install monit sudo systemctl enable --now monit # Pliki konfiguracyjne: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
Monitor pobiera listę usług przez monit status. W /etc/monit/monitrc musi być włączony interfejs HTTP (blok set httpd z allow localhost), w przeciwnym razie monit status zwróci błąd.
Na pulpicie widnieje „0 usług pod nadzorem”? Dwie przyczyny. (1) Interfejs HTTP jest wyłączony — w monitrc wiersz set httpd jest zakomentowany (domyślnie jako # set httpd port 2812 …). Odkomentuj blok i zezwól na localhost. (2) Samo włączenie httpd niczego nie monitoruje — Monit liczy tylko to, co opisano stansami check; bez nich lista jest pusta nawet przy działającym interfejsie. Minimalna działająca konfiguracja:
# /etc/monit/conf.d/00-httpd — interfejs HTTP dla localhost: set httpd port 2812 use address localhost allow localhost # przykłady stansów check (co monitorować): 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 # sprawdź składnię (Control file syntax OK) sudo systemctl reload monit sudo monit status
Automatyczna instalacja umieszcza gotowy conf.d z httpd na 2812 i zestawem sprawdzeń — przy nowej instalacji nie trzeba nic konfigurować ręcznie.
Usługa w statusie „Z błędami”? Monitor tylko pokazuje stan i celowo nie restartuje usług z panelu webowego (byłoby to zdalne wykonywanie poleceń roota w panelu bezpieczeństwa). Diagnostyka i restart — przez SSH za pomocą Monit:
sudo monit status <service> # przyczyna błędu sudo monit restart <service> # restart przez Monit # jeśli Monit nie podnosi usługi — sprawdź jej własny unit: sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

24. Instalacja PSAD (wykrywanie skanowania portów)

PSAD analizuje dziennik iptables i wykrywa skanowanie portów oraz ataki sieciowe, przypisując każdemu źródłu poziom zagrożenia (1–5). Uzupełnia fail2ban i Suricata.

sudo apt install psad # PSAD czyta dziennik iptables — trzeba włączyć logowanie (UFW robi to sam). # Dla czystego iptables dodaj reguły LOG do łańcuchów INPUT/FORWARD. sudo psad --sig-update sudo systemctl enable --now psad
Monitor odczytuje dane przez psad --Status (wymagany w sudoers). Bez logowania iptables strona będzie pusta — to normalne, dopóki nie wystąpiły skanowania.

25. AppArmor / SELinux (kontrola dostępu)

Mandatory Access Control ogranicza, do jakich plików i zasobów może sięgać program, nawet jeśli został przejęty. W Ubuntu/Debian domyślnie używany jest AppArmor (zwykle już zainstalowany i aktywny).

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # sprawdź profile
Monitor odczytuje status przez aa-status (wymagany w sudoers). Pokazuje liczbę profili w trybie enforce/complain oraz procesy bez profilu.

„Załadowanych profili” jest więcej niż enforce + complain — to normalne. W AppArmor 4.x (Ubuntu 24.04 i nowsze) pojawił się tryb unconfined: profil jest załadowany do jądra, ale niczego nie ogranicza. Ubuntu oznacza w ten sposób dziesiątki profili dla programów korzystających z user namespaces (przeglądarki, klienty torrent i podobne). Gdy takie profile występują, karta „Załadowanych profili” zmienia kolor na bursztynowy i pokazuje ich liczbę — na przykład unconfined: 90 przy 120 załadowanych i 26 w enforce. Realnie chronią tylko profile w enforce; w Ubuntu 22.04 (AppArmor 3.x) tego trybu nie ma i liczby zawsze się zgadzają.

sudo aa-status | grep -E "profiles are" # podział na tryby sudo aa-enforce /etc/apparmor.d/profile-name # przełącz profil w tryb enforce
Przełączanie w tryb enforce profili, które Ubuntu celowo zostawiła w unconfined, warto robić tylko świadomie: nie są one wyłączone przez pomyłkę, lecz dlatego, że inaczej psuje się działanie samych programów. Profile w complain to inna sprawa: reguły są już napisane, tylko nie są stosowane.

26. Instalacja debsums (integralność pakietów)

debsums sprawdza, czy pliki zainstalowanych pakietów zgadzają się z sumami kontrolnymi z repozytorium — wykrywa podmienione systemowe pliki binarne (uzupełnia AIDE). Pełne sprawdzenie trwa 1–2 minuty, dlatego uruchamiane jest przez cron, a panel odczytuje wynik z data/debsums/debsums.log i sam porządkuje go według kategorii (istotne są tylko pliki binarne i biblioteki).

Zadanie umieszcza się w cronie roota (sudo crontab -e). Gotowy skrypt debsums-scan.sh należy umieścić w /usr/local/bin/ (chmod +x; zob. podsumowanie zadań cron) i sam zapisuje raport w katalogu data/debsums/ panelu.

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

Skrypt debsums-scan.sh sam odnajduje katalog data/ panelu — nie trzeba podawać ścieżki.

Zmiany w /etc/ (konfiguracje) i /usr/share/ (zasoby) na serwerze są zwykle normalne — panel oznacza je osobnym kolorem. Niepokojące są zmiany plików binarnych i bibliotek (/bin, /sbin, /usr/lib itp.) — karta „Pliki binarne / biblioteki” pokazuje właśnie je.

27. Konfiguracja raportów Lynis

Lynis uruchamia się ręcznie lub przez cron. Raport musi być zapisywany w folderze data/lynis/ projektu — monitor odczytuje plik lynis-report.dat.

# Jednorazowe uruchomienie (podstaw własną ścieżkę do katalogu głównego panelu): sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # Codzienny audyt — wiersz cron (gotowy wrapper lynis-scan.sh w /usr/local/bin/, patrz podsumowanie): 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
Po pierwszym uruchomieniu strona „Audyt Lynis” od razu pokaże hardening index, ostrzeżenia i rekomendacje.
Przycisk „Uruchom audyt” na stronie Lynis. Uruchamia on lynis-scan.sh w tle bezpośrednio z panelu (bez czekania na cron): pokazuje „Skanowanie…”, a po zakończeniu sam odświeża raport. Wymaga to wiersza sudoers dla użytkownika serwera WWW na uruchomienie skryptu — instalator dodaje go automatycznie do /etc/sudoers.d/monitor. Jeśli panel był instalowany ręcznie/wcześniej, dopisz go dla tego samego użytkownika, który jest już wskazany w pliku:
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

28. Konfiguracja raportów Logwatch

Logwatch powinien zapisywać dzienne raporty w folderze data/logwatch/ projektu w formacie .txt. Monitor pokazuje ostatni raport oraz archiwum.

# Codziennie (6:00) — linia cron (gotowa nakładka logwatch_daily.sh w /usr/local/bin/, patrz podsumowanie): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

Moduły panelu

29. Monitor sieci (wbudowany)

Monitor sieci nie wymaga instalacji — to wbudowana strona panelu. Pokazuje stan sieci serwera z lokalnych źródeł:

  • interfejsy i ruch — z /proc/net/dev;
  • stan łączy (UP/DOWN) i IP — przez ip;
  • połączenia i nasłuchujące porty — przez ss;
  • sieciowe zdarzenia jądra z ostatnich 24 h — przez journalctl -k.

Pierwsze trzy źródła działają bez sudo, dlatego interfejsy, ruch, połączenia i porty są widoczne od razu. Blok „Zdarzenia jądra” korzysta z journalctl -k — odczyt odbywa się przez grupę systemd-journal („Konfiguracja sudo”, p.2), sudo nie jest potrzebne. Aby sprawdzić, czy wszystko jest dostępne dla użytkownika WWW:

# Sprawdzenie jako www-data (pod nim działa 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
Blok „Sieciowe zdarzenia jądra” pokazuje zdarzenia stosu sieciowego jądra (zmiana łącza up/down, błędy nośnej, „network unreachable”). Wpisy zapory UFW BLOCK tutaj nie trafiają — są na stronach „Zapora UFW” i „Mapa ataków”. Pusty blok z zielonym znaczkiem = w ciągu doby nie było awarii sieci.

30. Dysk i SMART

Wbudowana strona pokazuje trzy rzeczy:

  • Systemy plików — zapełnienie partycji (df); skala czerwienieje przy ≥90%;
  • Nośniki — lista dysków (lsblk), tylko rzeczywiste (loop/snap ukryte);
  • Kondycja (SMART) — status dysku i atrybuty (smartctl).

Miejsce i lista urządzeń działają od razu, bez konfiguracji. Dla SMART potrzebny jest pakiet smartmontools. Proces webowy nie ma bezpośredniego dostępu do urządzeń dyskowych, dlatego SMART jest zbierany przez cron do pliku data/disk/smart.txt, a panel go odczytuje.

Zadanie umieszcza się w root-cronie (sudo crontab -e). Gotowa nakładka smart-scan.sh jest umieszczana w /usr/local/bin/ (chmod +x; zob. zestawienie zadań cron) i sama zapisuje do data/disk/ panelu.

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

Nakładka smart-scan.sh sama znajduje data/ panelu — ścieżki nie trzeba podawać. Wewnątrz lsblk -e7,11 wyklucza loop/cdrom.

Na dyskach wirtualnych (QEMU/KVM i podobnych) zwykle dostępny jest tylko ogólny status „kondycja: OK”, a temperatura, godziny pracy i realokowane sektory mogą być puste — to normalne. Na serwerze fizycznym wyświetlane są wszystkie atrybuty.

31. Wydajność (CPU/RAM/Sieć/Dysk)

Strona pokazuje historię obciążenia serwera z ostatnich 24 godzin — Load Average, zajętość CPU i oczekiwanie I/O, RAM/Swap, ruch sieciowy (odbiór/wysyłka), I/O dysku (odczyt/zapis), zapełnienie dysku i i-węzłów, otwarte deskryptory plików i połączenia MySQL, a także bieżącą liczbę połączeń TCP i procesów. Domyślne jest okno 24 godzin: przyciski u góry przełączają je na 7 dni lub 30 dni.

Dane zbiera cron/collect_metrics.php — co 5 minut zapisuje jeden „surowy” zrzut liczników (/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') do tabeli bazy system_metrics; procenty i prędkości strona liczy sama na podstawie różnicy między sąsiednimi zrzutami (zapełnienie dysku/i-węzłów/deskryptorów/połączenia MySQL — wartości chwilowe, bez przeliczania). Sudo nie jest wymagane — źródła są odczytywane bez uprawnień root.

Dwie głębokości przechowywania. „Surowe” pięciominutowe migawki żyją dobę (plus godzina zapasu, żeby najstarszy punkt na wykresie miał jeszcze poprzednika do obliczeń). Każda zakończona godzina jest zwijana do jednego wiersza; ten szereg godzinowy przechowywany jest 30 dni i to właśnie jego czytają widoki „7 dni” i „30 dni”. Miesiąc historii kosztuje dzięki temu około 720 wierszy zamiast mniej więcej 8600.

W okresach 7 i 30 dni pokazywane są średnie godzinowe, a nie pojedyncze pomiary — pięciominutowy skok w tej skali zostaje wygładzony, o czym strona informuje. Dla Load Average wiersz godzinowy przechowuje zarówno średnią, jak i szczyt godziny, dlatego wykres pokazuje obie linie. Zaraz po aktualizacji długie okresy są puste („dla tego okresu nie ma jeszcze historii”) — szereg godzinowy zaczyna się gromadzić od tej chwili.
# Wiersz cron (co 5 minut): */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

Nakładka collect-metrics-all.sh (zob. zestawienie zadań cron) sama wykrywa wszystkie zainstalowane instancje panelu na serwerze i uruchamia cron/collect_metrics.php każdej z nich w imieniu właściciela witryny.

Dopóki kolektor nie zadziała co najmniej dwa razy (pierwsze ~10 minut po instalacji), strona pokazuje „trwa zbieranie danych” — wykresy potrzebują minimum jednej pary sąsiednich punktów, aby obliczyć prędkości i procenty.

Najcięższe procesy. Pod wykresami są dwie tabele z pięcioma procesami najbardziej obciążającymi CPU:

  • „Procesy w ostatnim pomiarze” — co obciążało serwer w chwili ostatniej migawki (godzina jest w nagłówku karty);
  • „Procesy w szczycie doby” — te same pięć, ale z pomiaru o najwyższym Load Average z ostatnich 24 godzin. To odpowiedź na pytanie „co to było w nocy”, gdy szczyt na wykresie już minął.

Obie tabele zawsze opisują ostatnie 24 godziny, nawet gdy wykresy pokazują miesiąc: migawkę procesów mają tylko wiersze pięciominutowe, a te nie są przechowywane dłużej niż dobę.

Własne procesy pomiaru (ps, skrypt PHP zbierający dane oraz łańcuch cron/sudo nad nim) są z listy wykluczane — inaczej zajmowałyby pierwsze miejsca w każdej migawce: ps w chwili startu pokazuje prawie 100 % CPU. Inne zadania cron uruchomione w tej samej minucie jak najbardziej się pojawiają — właśnie po to ta tabela jest.

Alerty o obciążeniu (sekcja „Ustawienia” → „Alerty o obciążeniu”) — po przekroczeniu progu CPU/RAM/dysku/inodów panel wysyła powiadomienie wszystkimi włączonymi kanałami (Telegram, Email, webhook, push — tymi samymi co raport dzienny; dla alertów nie trzeba włączać nic osobno), a drugie wtedy, gdy metryka wróci do normy. Dopóki próg jest utrzymywany, nie spamuje: kolejne powiadomienie przyjdzie dopiero po cyklu „wróciło do normy → znowu przekroczone”.

Progi są sprawdzane przez ten sam collect_metrics.php przy każdym uruchomieniu (co 5 minut) — osobny cron nie jest potrzebny. Stan „już powiadomiono / jeszcze nie” jest przechowywany w data/alerts_state.json, a progi — w ustawieniach panelu.

32. Zdarzenia bezpieczeństwa (czujnik co 5 minut)

Raport dzienny odpowiada na pytanie „jak się miewa serwer”. Czujnik odpowiada na inne — „co się właśnie stało”. Co 5 minut odpytuje szybkie źródła i zapisuje tylko to, co zmieniło się od poprzedniej kontroli; strona „Zdarzenia bezpieczeństwa” w menu pokazuje listę znalezisk.

Wolnych kontroli — Lynis, debsums, aktualizacji pakietów, terminu ważności SSL — czujnik nie rusza: ich liczby i tak zmieniają się raz na dobę i pozostają domeną raportu dziennego.

Co zauważa — dziewięć źródeł:

  • Fail2ban — nowe bany (porównywana jest lista zablokowanych adresów, więc odblokowanie nie jest zdarzeniem, a obowiązujący ban zgłaszany jest dokładnie raz);
  • CrowdSec — nowe decyzje;
  • Falco — zdarzenia poziomu Emergency, Alert i Critical;
  • Suricata — nowe alerty; oznaczenie [Priority: 1] podnosi znalezisko do rangi krytycznego;
  • ClamAV — nowe wiersze FOUND w dzienniku skanowania;
  • AIDE — zmiany plików według ostatniej kontroli (a więc nie częściej, niż ona sama uruchamia się według własnego harmonogramu);
  • SSH — udane logowanie na root (krytyczne) oraz logowanie z adresu, którego wcześniej nie było (ostrzeżenie);
  • Usługi ochrony — UFW, Fail2ban, CrowdSec, Suricata, Falco: jeden komunikat, gdy usługa się zatrzyma, i jeden, gdy znów działa;
  • Porty — port, który stał się dostępny z zewnątrz; jeśli stoi za nim baza danych albo cache, znalezisko jest krytyczne.

Narzędzie niezainstalowane albo takie, którego dziennika panel nie może odczytać, po prostu milczy — to nie błąd i nie zdarzenie.

Pierwsza kontrola niczego nie wysyła. Każde źródło przechowuje znacznik „dokąd już przeczytano” (plik data/secwatch_state.json), a pierwsze przejście tylko ten znacznik zapisuje. Inaczej po włączeniu nadzoru przyszłoby powiadomienie o wszystkim, co dzienniki zebrały przez miesiące.

Konfiguracja — „Ustawienia” → „Nadzór nad zdarzeniami”:

  • Włączyć nadzór — dopóki jest wyłączony, zadanie cron pracuje na pusto, a lista się nie zapełnia;
  • Powiadamiać od — „Ostrzeżenie” albo „Krytyczne”. Znaleziska poniżej progu i tak trafiają na listę, ale nie do powiadomień;
  • Przerwa, min — minimalny odstęp między powiadomieniami, domyślnie 15;
  • Godziny ciszy — okno w formacie 23-7, w którym ostrzeżenia nie są wysyłane.
Znaleziska krytyczne wychodzą od razu — nie wstrzymuje ich ani przerwa, ani godziny ciszy: właśnie dla nich istnieje czujnik działający co 5 minut. Bezpiecznik jednak jest: nie więcej niż 6 powiadomień na godzinę, na wypadek gdyby jakieś źródło zaczęło „migotać”.

Powiadomienia idą tymi samymi kanałami co raport dzienny (Telegram, Email, webhook, push) — nie trzeba włączać nic osobno. Wszystkie znaleziska trafiają na listę niezależnie od tego, czy powiadomienie wyszło, i są przechowywane 90 dni.

Lista zdarzeń. Filtry: okres (24 godziny, 7, 30 lub 90 dni), ważność i źródło; pokazywanych jest ostatnich 500 wpisów. Przycisk „Sprawdź teraz” uruchamia dokładnie to samo przejście co cron — wygodnie zaraz po konfiguracji, żeby nie czekać pięciu minut; podaje też powód, gdy powiadomienie nie wyszło („poniżej progu ważności”, „godziny ciszy”, „nie skonfigurowano żadnego kanału powiadomień”). Czerwony licznik przy pozycji menu to liczba zdarzeń krytycznych z ostatnich 24 godzin.

Karta adresu. Kliknięcie wiersza na liście zdarzeń lub w bloku Powiązane adresy otwiera wszystko, co wiadomo o jednym adresie: przebieg jego zdarzeń w czasie, kraj oraz w pierwszym wierszu wniosek. Wiersz listy otwiera kartę, gdy zdarzenie dotyczy dokładnie jednego adresu; jeśli wymienia kilka, odnośnikami są same adresy wewnątrz wiersza. Wnioski są cztery:

  • Możliwy dostęp — z adresu nastąpiło udane logowanie, a ten sam adres został wychwycony przez inne zabezpieczenia albo tuż po logowaniu zadziałały AIDE, ClamAV lub otworzył się nowy port. Jedyny przypadek, który warto sprawdzić od razu;
  • Logowanie z tego adresu — jest logowanie i nic poza tym. Zwykle to właściciel z nowego miejsca, dlatego karta nie robi się czerwona;
  • Zablokowany — adres został wychwycony i zablokowany: zabezpieczenie wykonało swoją pracę, nie trzeba nic robić;
  • Wymaga sprawdzenia — są wykrycia, ale blokady nie było. Jeśli adres wraca, proszę zamknąć go ręcznie.

Pod przebiegiem w czasie znajduje się blok Co działo się na serwerze w tym samym czasie: zdarzenia, które w ogóle nie mają adresu. AIDE nie wie, kto zmienił plik, ClamAV nie wie, kto przyniósł zakażony. Pokazane są te, które przypadają na aktywność adresu i sześć godzin po niej. To właśnie logowanie wraz z tym, co po nim nastąpiło, oddziela próbę od rzeczywistego dostępu.

Powiązanie buduje się według adresu. Napastnik, który zmienia adres, je zrywa. Karta pokazuje to, co zobaczyły zabezpieczenia, i nie podejmuje się odróżnić włamania od logowania Pana/Pani z nowego miejsca.

Harmonogram. Zadanie trafia do crontaba root (sudo crontab -e), przesunięte o 2 minuty względem zbierania metryk, żeby oba nie startowały jednocześnie:

# co 5 minut — kontrola zdarzeń bezpieczeństwa 2-57/5 * * * * /usr/local/bin/security-watch-all.sh >> /path/to/monitor/logs/cron.log 2>&1
Proszę uruchamiać kontrolę wyłącznie przez wrapper, nigdy bezpośrednio przez php cron/security_watch.php. Wrapper znajduje wszystkie instalacje panelu na serwerze i uruchamia kontrolę w imieniu właściciela witryny — z tymi samymi uprawnieniami sudo, z których panel korzysta z poziomu WWW. Bezpośrednie wywołanie z crontaba root utworzyłoby plik stanu data/secwatch_state.json należący do root, a przycisk „Sprawdź teraz” przestałby działać.
Jeśli serwer był przygotowany automatyczną instalacją, zadanie jest już dodane — pozostaje tylko włączyć nadzór w ustawieniach.

33. Serwer WWW: żądania i kody odpowiedzi

Strona „Serwer WWW” analizuje dziennik dostępu i pokazuje, ile było żądań, jaka ich część zakończyła się błędem i kto je wysyłał. ModSecurity odpowiada na pytanie „co zostało zablokowane”, ta strona — na pytanie „co w ogóle dotarło do witryny”.

Co widać: liczbę żądań, RPS, udział błędów, osobny licznik 5xx oraz objętość przesłanych danych; wykres żądań w godzinach; rozkład kodów 2xx/3xx/4xx/5xx; częste adresy, częste IP, częste błędy (kod plus ścieżka) i klientów (User-Agent). Okres: 24 godziny albo 7 dni.

Źródłem jest dziennik dostępu w formacie combined, dlatego nadają się zarówno Nginx, jak i Apache, a dodatkowy moduł nie jest potrzebny. Panel szuka dziennika wśród standardowych plików:

  • /var/log/nginx/access.log
  • /var/log/apache2/access.log
  • /var/log/apache2/other_vhosts_access.log
  • /var/log/httpd/access_log

Oprócz nich panel sprawdza pliki *access*.log i *requests*.log w katalogach /var/log/nginx/, /var/log/apache2/ i /var/log/httpd/. W ten sposób znajduje się także dziennik, który witryna zapisuje do własnego pliku dyrektywą CustomLog — na przykład arciveo-monitor_requests.log, tworzony przez instalator Arcivéo.

Czytany jest tylko jeden dziennik: gdy Nginx stoi przed Apache, zsumowanie dwóch dzienników liczyłoby każde żądanie dwa razy. Brany jest pierwszy dziennik z powyższej listy, do którego coś zapisano w ciągu ostatniej doby (pliki znalezione według maski sprawdzane są po nim, od najświeższych); jeśli przez dobę nie zapisano nic do żadnego, brany jest najświeższy niepusty. Dziennik opróżniony przez logrotate albo pozostały po wyłączonym serwerze WWW nie przesłania więc działającego. Czytany jest ostatni fragment dziennika (3 MB), a wynik trafia do pamięci podręcznej na 5 minut — na obciążonej witrynie wybrany okres może więc nie być pokryty w całości; faktyczny zasięg podany jest nad wykresem.

„Panel nie może odczytać dziennika serwera WWW”. Przyczyną są niemal zawsze uprawnienia: dzienniki i ich katalog należą do grupy adm, a użytkownik WWW do niej nie należy. Instalator Arcivéo dodaje go do tej grupy; po ręcznej instalacji pomoże mały wrapper roota, któremu wolno dokładnie jedno — pokazać dziennik (strona ModSecurity działa tak samo). Wrapper wybiera dziennik według tych samych zasad co panel. Polecenie wyświetlane jest na samej stronie; oto ono. Proszę wkleić je w całości — to jedno polecenie:
sudo tee /usr/local/bin/monitor-weblog >/dev/null <<'EOF' && sudo chmod 755 /usr/local/bin/monitor-weblog && echo 'www-data ALL=(root) NOPASSWD: /usr/local/bin/monitor-weblog' | sudo tee /etc/sudoers.d/monitor-weblog >/dev/null && sudo visudo -c #!/bin/sh L="/var/log/nginx/access.log /var/log/apache2/access.log /var/log/apache2/other_vhosts_access.log /var/log/httpd/access_log $(ls -t /var/log/nginx/*access*.log /var/log/apache2/*access*.log /var/log/apache2/*requests*.log /var/log/httpd/*access_log 2>/dev/null)" for f in $L; do [ -r "$f" ] && [ -n "$(find "$f" -size +0 -mmin -1440 2>/dev/null)" ] && exec tail -c 3000000 "$f"; done for f in $(ls -t $L 2>/dev/null); do [ -r "$f" ] && [ -s "$f" ] && exec tail -c 3000000 "$f"; done EOF

Użytkownik WWW może być inny niż www-data — proszę sprawdzić, na jakim użytkowniku działa pula PHP-FPM witryny (ps -o user= -C php-fpm), i wpisać go w wierszu sudoers. Jeśli nazwa dziennika nie zawiera ani access, ani requests, proszę dopisać jego ścieżkę na początku wiersza L=… wewnątrz wrappera.

„Brak wpisów dla wybranego okresu” — dziennik został odczytany, ale nie znaleziono w nim pasujących wierszy: albo witryna faktycznie nie była otwierana, albo dziennik właśnie się przewinął (logrotate) i zaczyna od zera.

Próby znanych exploitów. Panel porównuje zapytania z dziennika z sygnaturami około dwudziestu popularnych exploitów: PHP-CGI, przejście poza katalog w Apache, PHPUnit, Log4Shell, luki w routerach, kamerach i bramach VPN. Dla każdej próby podaje, czy może zadziałać na tym serwerze, i opiera się na faktach: zainstalowanych wersjach Apache i PHP, obecności Javy oraz kodzie, którym serwer odpowiedział na zapytanie o plik. Na czerwono oznaczone jest tylko to, co naprawdę może zadziałać.

Zabezpieczenie serwera WWW. Panel odpytuje każdą witrynę serwera przez 127.0.0.1 i sprawdza, co otrzymuje odwiedzający: czy widać wersje serwera WWW i PHP, czy włączona jest metoda TRACE oraz czy są nagłówki ochronne Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy i Content-Security-Policy. Witryny pochodzą z adresu, pod którym otwarto panel, oraz z plików hostów wirtualnych, jeśli panel może je odczytać. Wynik odświeża się co 10 minut.

Jeśli czegoś brakuje, pod tabelą pojawia się jedno polecenie. Tworzy osobny plik zz-arciveo-hardening.conf i przeładowuje serwer WWW dopiero po pomyślnym teście konfiguracji; nagłówki, które witryna już wysyła sama, nie są dublowane. CSP nie jest proponowane jako jedna linia: politykę buduje się pod konkretną witrynę, inaczej przestanie ona ładować własne skrypty, style i czcionki. Cofnięcie zmiany w Apache:

sudo a2disconf zz-arciveo-hardening && sudo systemctl reload apache2
Podpis na stronach błędów (ServerSignature) nie jest oznaczany, jeśli wersja serwera WWW jest już ukryta: bez wersji pokazuje tylko nazwę serwera i nazwę hosta.

34. Mapa ataków (GeoIP)

Strona „Mapa ataków” określa kraj na podstawie IP za pomocą polecenia geoiplookup. Bez pakietu GeoIP kraje nie zostaną określone, a punkty na mapie się nie pojawią:

sudo apt install geoip-bin geoip-database # Sprawdzenie: geoiplookup 8.8.8.8
Sudo nie jest wymagane — baza /usr/share/GeoIP/GeoIP.dat jest czytelna dla wszystkich, a wyniki są buforowane w tmp/geoip_cache.json. Sama mapa (Leaflet + kafelki OpenStreetMap) ładuje się w przeglądarce — potrzebny jest internet na komputerze, na którym otwarto panel.

35. Ekspozycja zewnętrzna, aktualizacje i automatyczne aktualizacje

Dwie wbudowane karty pulpitu, które pokazują nie „wł./wył.” narzędzia, lecz rzeczywisty poziom zabezpieczenia serwera. Nie wymagają instalacji, są odczytywane lokalnie bez sudo.

Ekspozycja zewnętrzna — ile usług nasłuchuje na wszystkich interfejsach (0.0.0.0/[::]) i jest dostępnych z zewnątrz. Zaznacza na czerwono, jeśli na zewnątrz wystawione są bazy danych lub cache (MySQL, PostgreSQL, Redis, MongoDB, Memcached, Elasticsearch) — to bezpośrednia luka (−10 do Oceny bezpieczeństwa). Źródło: ss -tuln.

Jeśli karta jest czerwona — zamknij bazy danych przed światem zewnętrznym: przypisz do 127.0.0.1 (bind-address w konfiguracji MySQL/PostgreSQL, bind 127.0.0.1 w Redis) lub zamknij port w UFW.
„Otwarty port” ≠ „dostępny z zewnątrz”. Usługa nasłuchująca na 127.0.0.1 (loopback) jest widoczna tylko dla samego serwera — z zewnątrz nie da się do niej dotrzeć, nawet jeśli port jest „otwarty”. Dlatego Postfix na porcie 25 przypisany do loopback jest bezpieczny: automatyczna konfiguracja ustawia inet_interfaces = loopback-only (plus neutralny smtpd_banner — usuwa uwagę Lynis MAIL-8818 o ujawnianiu wersji). Karta „Ekspozycja zewnętrzna” liczy jako zewnętrzne tylko to, co nasłuchuje na 0.0.0.0/[::]; usługi loopback tam nie trafiają.
Lynis MAIL-8818 ręcznie (jeśli pocztę konfigurował Pan samodzielnie): w /etc/postfix/main.cf ustaw smtpd_banner = $myhostname ESMTP (bez wersji i systemu) oraz inet_interfaces = loopback-only, następnie sudo systemctl restart postfix.

Aktualizacje zabezpieczeń — ile poprawek bezpieczeństwa czeka na instalację i czy po aktualizacji jądra wymagany jest restart (−5 do Oceny bezpieczeństwa przy dostępnych poprawkach). Źródło: /usr/lib/update-notifier/apt-check, plik /var/run/reboot-required. Szczegółowa lista — na stronie „Aktualizacje zabezpieczeń”.

# Zainstaluj aktualizacje: sudo apt update && sudo apt upgrade # Sprawdź, co nasłuchuje na zewnątrz: ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
Karta aktualizacji działa na Ubuntu/Debian (update-notifier-common). Jeśli apt-check jest niedostępny — monitor liczy poprawki poprzez apt-get -s upgrade.

Automatyczne aktualizacje zabezpieczeń (unattended-upgrades) — na stronie „Aktualizacje zabezpieczeń” osobna karta pokazuje, czy automatyczna instalacja poprawek bezpieczeństwa jest włączona i kiedy uruchomiono ją ostatnio. Sudo nie jest potrzebne — status jest odczytywany przez apt-config dump.

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # włącz # Sprawdź, co jest włączone: apt-config dump | grep Unattended-Upgrade

Konserwacja

36. Kopie zapasowe

Kopia zapasowa to główne zabezpieczenie: utrata danych jest gorsza niż każde włamanie. Potrzebne są dwie rzeczy — kopia serwera/witryn oraz osobno kopia bazy danych panelu (tam znajdują się użytkownicy, klucze WebAuthn, ustawienia, licencja).

Wariant A — HestiaCP: zakładka Backup u użytkownika → przycisk tworzenia kopii (lub według harmonogramu w ustawieniach serwera). Kopia obejmuje witryny i ich bazy danych.

Wariant B — ręcznie (cron): zrzut bazy danych + archiwum katalogu data/ panelu:

# cron roota (sudo crontab -e) — codzienna kopia o 2:30 (wstaw własne nazwy/ścieżki): 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 # Usuwaj archiwa starsze niż 14 dni: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
Kopia na tym samym serwerze chroni przed błędami, ale nie przed utratą serwera. Kopiuj archiwa do zewnętrznego magazynu (inny serwer, S3, rclone do chmury). Sprawdzaj, czy przywracanie faktycznie działa.

37. Aktualizacja i migracja panelu

Aktualizacja do nowej wersji. Najpierw proszę wykonać kopię zapasową. Następnie proszę ponownie wgrać pliki kodu, zachowując swoje dane:

  • nadpisać (kod): public/, includes/, assets/, cron/, database/, a także główne .htaccess (kontroler frontowy — routingu nie można pozostawić z poprzedniej wersji), manifest.json, sw.js;
  • nie ruszać: config.php (dane bazy danych), data/ (raporty), logs/, tmp/ (sesje i pamięć podręczna).
# Po wgraniu — wyczyścić pamięć podręczną PHP (jeśli opcache jest włączony): sudo systemctl reload php*-fpm
FileZilla zgłasza SSH_FX_PERMISSION_DENIED — Permission denied. Pliki panelu należą do www-data (tak zostały ustawione przy instalacji), a klient SFTP łączy się na własnym użytkowniku, który nie ma prawa zapisu. Oddanie www-data całego panelu „żeby działało” to właśnie to, co prowadzi do tego błędu; poniżej trzy sposoby, każdy rozwiązuje problem.
# Wariant A (zalecany) — rozdzielić właścicieli: kod Pański, katalogi robocze serwera WWW. # Serwer WWW nie dostaje prawa zapisu do KODU panelu w ogóle: 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 # Wariant B — ACL na istniejących właścicielach (nic nie przenosimy): 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 # Wariant C — przez grupę www-data. Prostszy, ale prawo zapisu do plików panelu # dostaje również serwer WWW (przy podatności w PHP kod da się podmienić): 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
Dlaczego wariant A jest bezpieczny. Panel zapisuje tylko do trzech katalogów — data/ (raporty), tmp/ (sesje i pamięć podręczna), logs/; pozostają one przy www-data. Reszta to kod, a serwer WWW potrzebuje go jedynie do odczytu, który zapewnia grupa www-data z prawami 644. Dodatkowa korzyść: przy podatności w PHP plików panelu już nie da się nadpisać. Na panelach hostingowych (HestiaCP i podobnych) wariant A nie jest potrzebny: tam pliki witryny i tak należą do konta, na które Pan loguje się przez SFTP, a serwer WWW czyta je po grupie.
Pułapka wariantu B: każdy kolejny chmod na plikach resetuje maskę ACL i dostęp po cichu znika. Jeśli po „uporządkowaniu praw” wgrywanie znów natrafia na Permission denied — proszę powtórzyć obie komendy setfacl.
Bit 2 w wariancie C to setgid: pliki wgrane przez SFTP pozostają w grupie www-data, inaczej panel nie będzie mógł ich nadpisać. Po wariancie C proszę połączyć się ponownie w FileZilli — nowa grupa działa dopiero przy nowym logowaniu. Sprawdzenie: id deploy (musi pojawić się grupa www-data) oraz ls -ld /path/to/monitor (drwxrwsr-x — litera s oznacza, że setgid jest ustawiony).

Migracja na inny serwer:

  1. Na nowym serwerze proszę uruchomić witrynę + HTTPS (zob. stronę ręcznej instalacji).
  2. Proszę skopiować wszystkie pliki panelu wraz z config.php, data/.
  3. Proszę przenieść bazę danych: mysqldump na starym → import na nowym; proszę poprawić dane bazy danych w config.php.
  4. Proszę powtórzyć na nowym serwerze: sudoers, przynależność do grupy adm, zadania cron.
  5. Licencja jest powiązana z domeną — jeśli domena jest ta sama, klucz będzie działał nadal.

38. Druga witryna na tym serwerze (kolejna domena)

Panel już działa, a na tym samym serwerze potrzebna jest jeszcze jedna witryna — z własną domeną obok monitora. Po automatycznej konfiguracji serwer to zwykły Apache + PHP-FPM, więc witrynie potrzebne są trzy rzeczy: własny katalog, własny vhost i własny certyfikat. Panelu przy tym nie ruszamy: obserwuje cały serwer, druga instancja dla drugiej domeny nie jest potrzebna.

Ten rozdział dotyczy serwera bez panelu hostingowego. Jeśli zainstalowany jest HestiaCP lub podobny panel, proszę dodać domenę w nim, a nie ręcznie. A jeśli planuje Pan/Pani wiele witryn (w dodatku z pocztą), panel instaluje się przed automatyczną konfiguracją: skrypt go wykryje i przejdzie w tryb addytywny, natomiast panel zainstalowany później przebudowuje konfigurację Apache, zapory i poczty.
Co podstawić w poleceniach. site2.example.com to adres nowej witryny. Może to być zarówno osobna domena (mycompany.com), jak i subdomena (shop.mycompany.com) — dla Apache nie ma różnicy, różnią się tylko wpis DNS (krok 1) i wystawienie certyfikatu (krok 5). site2 to po prostu krótka nazwa witryny: tak nazwano katalog /var/www/site2 i plik konfiguracyjny site2.conf. Z domeną nie ma to nic wspólnego, Apache bierze adres wyłącznie z ServerName; nazwa może być dowolna — dla shop.mycompany.com wygodnie jest wziąć shop: katalog /var/www/shop, konfiguracja shop.conf.

1. DNS. W panelu DNS u rejestratora domeny proszę utworzyć wpis A na IP tego serwera:

# Subdomena (shop.mycompany.com): Typ: A Nazwa: shop Wartość: 203.0.113.10 TTL: 3600 # Osobna domena (mycompany.com) — dwa wpisy, aby działał także adres z www: Typ: A Nazwa: @ Wartość: 203.0.113.10 TTL: 3600 Typ: A Nazwa: www Wartość: 203.0.113.10 TTL: 3600

Po kilku minutach proszę sprawdzić: dig +short site2.example.com powinno zwrócić IP serwera.

2. Katalog witryny.

sudo mkdir -p /var/www/site2 sudo chown -R www-data:www-data /var/www/site2

3. Vhost. Blok wkleja się do terminala w całości — ścieżka do gniazda PHP-FPM ustalana jest automatycznie:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # automatyczne wykrycie gniazda PHP-FPM sudo tee /etc/apache2/sites-available/site2.conf > /dev/null <<'EOF' <VirtualHost *:80> ServerName site2.example.com # Osobna domena: proszę usunąć # z poniższego wiersza, aby witryna otwierała się także z www. # Dla subdomeny ten wiersz nie jest potrzebny. #ServerAlias www.site2.example.com DocumentRoot /var/www/site2 <Directory /var/www/site2> Options -Indexes +FollowSymLinks AllowOverride All Require all granted </Directory> <FilesMatch \.php$> SetHandler "proxy:unix:__PHPSOCK__|fcgi://localhost" </FilesMatch> ErrorLog ${APACHE_LOG_DIR}/site2_error.log CustomLog ${APACHE_LOG_DIR}/site2_access.log combined </VirtualHost> EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/apache2/sites-available/site2.conf

4. Proszę włączyć witrynę i sprawdzić konfigurację przed przeładowaniem:

sudo a2ensite site2.conf sudo apache2ctl configtest # oczekiwane: Syntax OK sudo systemctl reload apache2

5. SSL. certbot został już zainstalowany przez automatyczną konfigurację; DNS musi w tym momencie wskazywać na serwer:

# Subdomena — jeden adres: sudo certbot --apache -d site2.example.com # Osobna domena — od razu z www (oba wpisy A z kroku 1 muszą wskazywać na serwer, # a www musi być w ServerAlias, inaczej certbot nie sprawdzi drugiego adresu): sudo certbot --apache -d site2.example.com -d www.site2.example.com

Trzecią i kolejne domeny dodaje się tak samo — proszę powtórzyć kroki 1–5, zmieniając domenę i katalog.

Kto odpowiada na „obce” żądania. Jeśli Host nie pasuje do żadnego ServerName (wejście po IP albo cudza domena skierowana na Pana/Pani serwer), Apache wydaje pierwszą alfabetycznie włączoną witrynę z /etc/apache2/sites-enabled/. Standardowy 000-default automatyczna konfiguracja wyłącza, więc tym „dyżurnym” pozostaje vhost panelu (arciveo-monitor.conf) — na witryny otwierane pod własnymi nazwami nie ma to wpływu. Jeśli po IP ma otwierać się inna witryna, proszę nazwać jej plik tak, aby był pierwszy alfabetycznie (na przykład 000-site2.conf).
Ochrona obejmuje cały serwer: UFW, Fail2ban, CrowdSec, ModSecurity i Suricata już działają i automatycznie osłaniają nową witrynę, nie trzeba ich konfigurować osobno. Porty 80/443 też są już otwarte. Witryna ma własne logi: site2_error.log i site2_access.log w /var/log/apache2/.
Katalogu nowej witryny proszę nie umieszczać wewnątrz katalogu panelu — katalog webowy panelu jest zarządzany własnym plikiem .htaccess (kontroler frontowy), a zagnieżdżona witryna zwracałaby coś innego, niż się Pan/Pani spodziewa. Proszę trzymać witryny obok siebie: /var/www/monitor, /var/www/site2.

39. Odzyskiwanie dostępu (utracony klucz, hasło, blokada IP)

Jeśli nie może się Pan zalogować — wszystko naprawia się bezpośrednio w bazie danych z serwera. Proszę otworzyć bazę danych (nazwa — z config.php):

sudo mysql MY_DB

Utracony klucz WebAuthn (drugi składnik nie przechodzi) — proszę wyłączyć 2FA, zalogować się hasłem i zarejestrować nowy klucz:

UPDATE users SET webauthn_enabled = 0;

Zapomniane hasło — proszę ustawić nowy hash (wygeneruj go na serwerze i podstaw):

# Wygeneruj hash nowego hasła: php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # W bazie danych (wstaw uzyskany hash): # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

Zablokowanie samego siebie filtrem IP — proszę wyłączyć ograniczenie:

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
Dostęp do bazy danych jest zawsze możliwy: sudo mysql na serwerze albo phpMyAdmin / sekcja bazy danych w panelu hostingu. Po odzyskaniu proszę ponownie włączyć WebAuthn i filtr IP.

40. Wszystkie zadania cron w jednym miejscu

Zestawienie zadań — w cronie root serwera (dodawane przez sudo crontab -e). Proszę zostawić tylko wiersze narzędzi, których Pan/Pani używa; ścieżki dopasować do swojego serwera.

# Serwerowy cron monitora (root) — proszę wpisać przez: sudo crontab -e # 01:30 — skan ClamAV po niebezpiecznych ścieżkach (web, home, temp) → karty „Sprawdzonych plików” i „Ostatnie skanowanie” 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — sprawdzenie integralności plików AIDE (wymagany jawny --config) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # przy starcie — przywrócić uprawnienia /var/lib/aide (pakietowy plik tmpfiles # aide-common.conf ustawia je na 0700 i panel przestaje widzieć bazę) @reboot chmod 755 /var/lib/aide # 03:00 — audyt bezpieczeństwa Lynis 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — aktualizacja listy blokad ipsum (level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # zestaw ipsum przy starcie podnosi usługa ipsum-load.service (PRZED zaporą, inaczej # UFW nie zobaczy zestawu w before.rules) — nie cron. Tu tylko codzienne odświeżanie powyżej. # 06:00 — raport Logwatch 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # co 30 min — sprawdzenie dysków SMART */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — integralność pakietów debsums 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — raport według harmonogramu na Email i Telegram 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # co godzinę — odświeżanie list pakietów (dla karty „Aktualizacje zabezpieczeń”) 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # co 5 min — zrzut zasobów (CPU/RAM/sieć/dysk) dla strony „Wydajność” */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # co 5 min, z przesunięciem względem migawki powyżej — czujnik zdarzeń bezpieczeństwa: # nowe bany, alerty IDS, zatrzymana ochrona, otwarty port. # Dopóki nadzór jest wyłączony w ustawieniach, zadanie nic nie robi. 2-57/5 * * * * /usr/local/bin/security-watch-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # co 15 min — podsumowanie serwera do panelu klienta, aby kilka serwerów było # widocznych na jednej liście. Dopóki podsumowanie jest wyłączone, zadanie nic nie robi. 3,18,33,48 * * * * /usr/local/bin/fleet-push-all.sh >> /path/to/monitor/logs/cron.log 2>&1
Szczegóły każdego — w odpowiednich sekcjach. Zadania kopii zapasowej (poprzednia sekcja) dodaje się do tego samego crona. Po zmianach proszę sprawdzić: sudo crontab -l oraz to, że usługa cron jest aktywna.
Czas cron = strefa czasowa serwera, a nie TIMEZONE z config.php. Stała TIMEZONE wpływa tylko na PHP (jak panel pokazuje daty), ale demon cron uruchamia zadania według czasu systemowego OS. Jeśli strefa serwera nie zgadza się z Pana/Pani strefą, raport „08:00” przyjdzie o innej porze. Przykład: serwer stoi w innej strefie (Europe/London, UTC+1), a Pan/Pani jest w Warszawie (UTC+2) → raport „08:00” przyjdzie o 09:00 według Pana/Pani czasu. Proszę sprawdzić i w razie potrzeby dostosować strefę systemu do swojej:
# Sprawdzić bieżącą strefę serwera: timedatectl # Ustawić swoją strefę (przykład) i zrestartować cron: sudo timedatectl set-timezone Europe/Warsaw sudo systemctl restart cron
Po tym wiersz 0 8 * * * zadziała o 08:00 czasu lokalnego. Inaczej trzeba by przesuwać sam cron, ale przy zmianie czasu zimowego/letniego przesunięcie znów się rozjedzie — dlatego właściwsze jest ustawienie strefy systemu.
Gotowe skrypty-nakładki. Ich robocze kopie i wzór crontab (crontab.txt) leżą w folderze system/ obok projektu, poza public_html. To nie część witryny — nie trzeba ich wgrywać do katalogu web; proszę umieścić je na serwerze pod ścieżkami systemowymi (jak w crontab powyżej):
  • lynis-scan.sh → /usr/local/bin/ (chmod +x) — uruchamia lynis audit system, na czas skanu ustawia flagę /tmp/lynis-running i kopiuje lynis-report.dat do data/lynis/ panelu;
  • logwatch_daily.sh → /usr/local/bin/ (chmod +x) — tworzy codzienny raport Logwatch (sshd, fail2ban, sudo, postfix) w data/logwatch/;
  • smart-scan.sh → /usr/local/bin/ (chmod +x) — zbiera stan dysków (smartctl) w data/disk/;
  • debsums-scan.sh → /usr/local/bin/ (chmod +x) — sprawdza integralność pakietów (debsums) w data/debsums/;
  • clamav-scan.sh → /usr/local/bin/ (chmod +x) — skan antywirusowy ClamAV po niebezpiecznych ścieżkach (web, home, temp); zapisuje zestawienie do /var/log/clamav/scan.log, skąd czyta je strona ClamAV (wiersz 01:30 w crontab powyżej);
  • load-ipsum.sh → /usr/local/bin/ (chmod +x) — aktualizuje zbiór ipset ipsum (level 1) w miejscu, bez zrywania aktywnych reguł zapory (wiersz 04:00 w crontabie powyżej);
  • daily-report-all.sh → /usr/local/bin/ (chmod +x) — uruchamia raport cron/daily_report.php panelu (wiersz 08:00 w crontab powyżej);
  • daily_report.php — już wchodzi w skład panelu (cron/daily_report.php), uruchamiany przez daily-report-all.sh, nie trzeba go osobno instalować;
  • collect-metrics-all.sh → /usr/local/bin/ (chmod +x) — uruchamia cron/collect_metrics.php panelu (strona „Wydajność”, wiersz */5 w crontab powyżej); collect_metrics.php już wchodzi w skład panelu, nie trzeba go osobno instalować;
  • security-watch-all.sh → /usr/local/bin/ (chmod +x) — uruchamia cron/security_watch.php panelu w imieniu właściciela witryny (strona „Zdarzenia bezpieczeństwa”, wiersz 2-57/5 w crontabie powyżej); security_watch.php wchodzi już w skład panelu i nie trzeba go instalować osobno;
  • fleet-push-all.sh → /usr/local/bin/ (chmod +x) — wysyła podsumowanie serwera do panelu klienta przez cron/fleet_push.php panelu (wiersz 3,18,33,48 powyżej);
  • crontab.txt (system/cron/) — wzór zadań; potrzebne wiersze proszę wpisać przez sudo crontab -e.
Ścieżka do skryptu w crontab musi zgadzać się z tym, gdzie go Pan/Pani umieścił.
Jak umieścić skrypt w /usr/local/bin/. Bezpośrednio z FileZilla nie da się tam zapisać — katalog należy do root, a klient SFTP otrzyma SSH_FX_PERMISSION_DENIED. Kolejność jest taka: najpierw wgrać plik do /tmp (tam piszą wszyscy), następnie przenieść go na miejsce jednym poleceniem:
# w FileZilla: w polu „Zdalny serwer” wpisać /tmp i wgrać tam skrypt, # następnie przez SSH (install od razu ustawia właściciela i uprawnienia, chown/chmod niepotrzebne): sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # sprawdzenie: plik na miejscu, uprawnienia rwxr-xr-x, składnia cała bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
Proszę nie pomylić katalogów: potrzebny jest /tmp w katalogu głównym serwera — nie /var/tmp i nie tmp/ wewnątrz samego panelu (ten ostatni należy do www-data i jest zamknięty dla Pana/Pani użytkownika). W drzewie FileZilla /tmp to gałąź najwyższego poziomu, obok var, a nie wewnątrz niej.
Stawiał Pan/Pani serwer autokonfiguracją? Te nakładki i ich zadania cron są już zainstalowane przez skrypt (w /usr/local/bin/, log — /var/log/arciveo-cron.log) — ręcznie nie trzeba nic robić.
Gdzie skrypty szukają panelu. Nakładki są neutralne domenowo: znajdują instalacje panelu, przeszukując /home/*/web/*/public_html i /var/www/*, i zapisują raporty do ich data/. Jeśli panel leży pod inną ścieżką — proszę dodać ją do wiersza for app in … wewnątrz skryptów, inaczej raporty Lynis/SMART/debsums/Logwatch nie trafią do panelu.
cron.log i uprawnienia dostępu. Plik logs/cron.log jako pierwszy tworzy cron root — będzie należał do root, a zakładka „Dziennik cron” w panelu nie będzie mogła go ani odczytać, ani wyczyścić. Proszę utworzyć plik wcześniej jako użytkownik web (właściciel katalogu witryny; w HestiaCP to konto, np. admin) — wtedy cron root będzie tylko dopisywać, nie zmieniając właściciela:
# utworzyć wcześniej jako użytkownik web (przed dodaniem wierszy cron): sudo -u OWNER touch /path/to/monitor/logs/cron.log # jeśli cron.log już utworzył cron root — przekazać użytkownikowi web: sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
Sprawdzenie właściciela katalogu: stat -c %U /path/to/monitor.
Zarządzanie z panelu. W sekcji „System” jest strona „Crontab” — można oglądać i dodawać zadania bez SSH. Panel edytuje tylko zadania dodane przez niego samego (osobny blok w crontab root, oznaczony komentarzami służbowymi); wszystko, co już jest w crontab (lista powyżej), pokazane jest tam jako lista tylko do odczytu „Pozostałe zadania serwera” z przyciskiem „Skopiuj do edytora” — przenosi on tylko harmonogram/polecenie do formularza dodawania, nie ruszając wiersza źródłowego. Aby „przenieść” istniejące zadanie pod zarządzanie panelu — proszę skopiować je do edytora, zapisać, a następnie ręcznie usunąć stary wiersz (sudo crontab -e), inaczej będzie wykonywane dwa razy.
Jednorazowa konfiguracja na serwerze. Strona potrzebuje uprzywilejowanego skryptu-nakładki — nie gołego sudo crontab (to byłaby bezpośrednia eskalacja do root dla każdego, kto uzyska dostęp do sesji panelu), lecz wąskiego skryptu z dwoma poleceniami (list/set), który rusza tylko swój blok między komentarzami służbowymi. Proszę zainstalować raz:
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
Użytkownik web może różnić się od www-data — proszę sprawdzić, na kim działa pula PHP-FPM witryny (ps -o user= -C php-fpm), i podstawić go w wierszu sudoers.
Nowy plik wgrał się z niewłaściwym właścicielem — strona odpowiada „Access denied.”. Jeśli plik public/crontab_monitor.php został wgrany przez FTP/SFTP jako inny użytkownik systemowy (np. root) niż pozostałe pliki witryny, serwer web nie zdoła go odczytać. Proszę porównać właściciela i uprawnienia z sąsiednim plikiem i doprowadzić do zgodności:
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

Diagnostyka

41. Narzędzie jest zainstalowane, ale widnieje „Nie zainstalowano”

Monitor wykrywa obecność narzędzi za pomocą dpkg-query — bazy pakietów APT. Jeśli narzędzie nie zostało zainstalowane przez apt (ręcznie, ze snap lub ze źródeł), dpkg go nie widzi.

# Sprawdź za pomocą dpkg: dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # Znajdź ścieżkę do pliku binarnego: which ufw fail2ban-client auditctl # Test sudo od www-data: sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

42. Rozwiązywanie problemów (500, brak danych)

Błąd 500 — proszę sprawdzić logi PHP, nginx oraz samego monitora:

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # Logi monitora: tail -50 logs/monitor_$(date +%Y-%m-%d).log # Uprawnienia do katalogów: ls -la data/ tmp/ logs/
Panel na panelu hostingowym (HestiaCP, ISPmanager, cPanel)? Tam PHP działa nie pod www-data, lecz pod kontem użytkownika (na przykład admin — właściciel katalogu witryny). Wszystkie reguły sudo i przynależność do grup (adm, systemd-journal) należy zapisać dla tego użytkownika, inaczej moduły pokażą „Nieaktywny / 0” przy działających usługach. Aby poznać rzeczywistego użytkownika PHP: ps -o user= -C php-fpm | sort -u lub właściciela katalogu witryny stat -c '%U' /path/to/monitor. Dalej we wszystkich poleceniach poniżej proszę podstawić go zamiast www-data. Automatyczna instalacja sama wykrywa użytkownika serwera WWW i zapisuje dla niego sudoers.

Dane się nie wyświetlają — prawie zawsze przyczyną są nieustawione uprawnienia sudo. Proszę sprawdzić konkretne polecenie jako użytkownik serwera WWW (zamieniając www-data na własnego). Flaga -n = bez hasła, tak jak w PHP — jeśli prosi o hasło, to znaczy, że reguły w sudoers nie ma:

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
Moduł pokazuje „Nieaktywny” / „0”, choć narzędzie działa (na przykład sudo aa-status w terminalu pokazuje profile, a strona „AppArmor” — „Nieaktywny”). Przyczyna: użytkownik serwera WWW nie ma uprawnienia sudo do polecenia tego modułu. Proszę je sprawdzić z listy powyżej: jeśli prosi o hasło — dodać brakujący wiersz do /etc/sudoers.d/monitor („Konfiguracja sudo”). Częste „nowe” polecenia: /usr/sbin/aa-status (MAC), /usr/sbin/psad --Status (PSAD).
Jeśli konkretna strona (Falco, ModSecurity, Auditd, otwarte porty UFW) jest pusta — proszę sprawdzić listę w sekcji o sudo: prawdopodobnie nie jest dozwolony apache2ctl, ausearch, aa-status lub ss, albo użytkownik serwera WWW nie należy do grup adm/systemd-journal (stamtąd czytane są logi fail2ban/auth/modsec oraz journalctl — Falco i zdarzenia jądra).

43. Strona jest pusta, choć dane na serwerze istnieją

Objaw: na serwerze dane są (widać je przez shell), a strona pokazuje „brak danych” lub nieprawidłowy status — na przykład AIDE wyświetla „Nie zainicjalizowana”, choć baza została utworzona.

Przyczyną jest open_basedir: wiele paneli i hostingów ogranicza pulę PHP-FPM do katalogu domeny, przez co funkcje PHP file_exists(), file_get_contents(), filemtime() na ścieżkach systemowych (/var/lib/aide, /var/log, /proc…) są blokowane. Monitor omija to, odczytując takie ścieżki standardowymi poleceniami systemowymi (cat, test, stat).

# Czy plik jest widoczny przez shell (tak odczytuje go monitor): sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # Bieżąca wartość open_basedir dla puli domeny: grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
Jeśli shell „widzi” plik (VISIBLE), a strona nie — to open_basedir. Właściwym rozwiązaniem jest odczyt poleceniami systemowymi (już zrobione dla AIDE i Monitora sieci). Rozszerzanie open_basedir na /var, /proc nie jest potrzebne i jest mniej bezpieczne.

44. Strona SSL nie działa

Monitor sprawdza certyfikaty, łącząc się z domenami bezpośrednio przez port 443. Jeśli domena jest niedostępna z samego serwera lub port jest zablokowany przez zaporę — sprawdzenie się nie powiedzie.

# Sprawdź certyfikat ręcznie: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # Sprawdź dostępność: curl -I https://monitor.example.com
Domeny monitor pobiera automatycznie z konfiguracji nginx (/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) i Apache (/etc/apache2/sites-enabled/) oraz z bieżącego hosta z HTTP_HOST.
Automatyczne wykrywanie subdomen. Subdomeny są wykrywane automatycznie z publicznych logów Certificate Transparency i sprawdzane przez sieć — nawet jeśli są hostowane na innych serwerach. Nie trzeba niczego dodawać ręcznie.

45. Widoczna tylko jedna baza z kilku

Monitor łączy się z MySQL jako użytkownik z config.php, który ma dostęp tylko do własnej bazy. MySQL pokazuje w information_schema jedynie bazy, do których użytkownik ma uprawnienia — dlatego pozostałe są niewidoczne.

Aby monitor widział wszystkie bazy, nadaj temu użytkownikowi prawo tylko do odczytu (jednorazowo jako root; wstaw nazwę użytkownika z config.php):

sudo mysql -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* daje wyłącznie prawo odczytu — nie można niczego zmienić, usunąć ani utworzyć, więc jest to bezpieczne dla monitorowania.
Bez tego GRANT panel widzi tylko własną bazę — to nie błąd, lecz ograniczenie uprawnień. Panel nie używa żadnego sudo mysql: lista baz jest pobierana przez jego własne połączenie PDO.

46. PostgreSQL nie pojawia się na stronie „Baza danych”

PostgreSQL wymaga dostępu na poziomie użytkownika postgres, którego użytkownik sieciowy panelu nie posiada. Otwieranie z poziomu PHP szerokiego sudo psql jest niebezpieczne — zamiast tego panel wywołuje wąską nakładkę bez parametrów, która wypisuje jedynie wersję, liczbę połączeń oraz listę baz z ich rozmiarami. Proszę ją utworzyć:

sudo tee /usr/local/bin/monitor-pgstat >/dev/null <<'EOF' #!/bin/sh # Arciveo Security 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 # w /etc/sudoers.d/monitor (użytkownik = ten, pod którym działa PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
Jeśli nie używa Pan PostgreSQL — proszę usunąć wiersz monitor-pgstat z sudoers (krok 13 instalacji ręcznej) i nie tworzyć samego skryptu: karta PostgreSQL pozostanie po prostu nieaktywna.

47. Zadziałał alert — co robić

Panel pokazuje, co się dzieje; poniżej — co robić w typowych sytuacjach. Ogólna zasada: nie panikować, porównać z legalną aktywnością (Pana działania, aktualizacje, kopie zapasowe) i reagować odpowiednio do powagi.

  • Mapa ataków / dużo banów fail2ban — to norma dla każdego serwera w internecie (boty stale próbują SSH/web). Najważniejsze, żeby bany działały. Proszę się upewnić, że logowanie SSH odbywa się tylko kluczem (hasło wyłączone), a Pana IP jest w ignoreip.
  • ModSecurity zablokował żądania — WAF odpiera ataki na witrynę, to jego zadanie. Jeśli blokowany jest Pana legalny ruch (fałszywy alarm) — proszę znaleźć rule id w szczegółach i dodać wyjątek w konfiguracji CRS.
  • AIDE: zmienione pliki — proszę porównać listę z tym, co Pan robił (aktualizacja pakietów, edycja konfiguracji — norma). Zmiany systemowych plików binarnych, których Pan nie dotykał, to powód do czujności. Po legalnych zmianach proszę zaktualizować bazę AIDE.
  • debsums: zmienione pliki binarne/biblioteki (poza /etc, poza /usr/share) — potencjalnie podmiana. Proszę sprawdzić pakiet: debsums PACKAGE_NAME, w razie wątpliwości zainstalować go ponownie (apt install --reinstall).
  • ClamAV / maldet: znaleziono zagrożenie — proszę sprawdzić plik w kwarantannie, nie otwierać go. Jeśli to webshell w katalogu witryny — proszę odizolować serwer i szukać punktu wejścia (podatna wtyczka, wyciek dostępów).
  • Falco: krytyczne zdarzenia (uruchomienie shella w kontenerze, dostęp do wrażliwych plików) — proszę przeanalizować zdarzenie: czyj proces, co uruchamiał. Często jest to legalna aktywność administratora.
  • Ekspozycja zewnętrzna: na czerwono baza danych/cache — proszę natychmiast zamknąć: przypiąć usługę do 127.0.0.1 lub zamknąć port w UFW. To realna luka.
  • SSL wygasa / wygasł — proszę odnowić certyfikat (Let's Encrypt odnawia się sam; jeśli nie — proszę sprawdzić certbot renew lub ustawienia w panelu).
  • Oczekują aktualizacje zabezpieczeń — proszę zainstalować: sudo apt update && sudo apt upgrade; po aktualizacji jądra proszę zrestartować serwer.
Oznaki realnego włamania (nieznane procesy/użytkownicy, zmienione pliki binarne, wychodzący spam, nieznane zadania cron): proszę odłączyć serwer od dostępu zewnętrznego, wykonać kopię zapasową do analizy, a jeśli dane są krytyczne — postawić czysty serwer z zaufanej kopii zapasowej; niezawodne usunięcie rootkita jest trudne.