Instalacja ręczna

Pełna instalacja ręczna: od świeżo zakupionego VPS do działającego panelu, krok po kroku. Przygotowanie serwera, utworzenie witryny, baza danych, config.php i SSL są opisane tutaj. Polecenia dla każdego narzędzia zabezpieczeń i dla cron znajdują się w materiałach FAQ, odnośniki w tekście.

Najważniejsza zasada: zmieniając SSH lub zaporę, nie zamykaj bieżącego połączenia, dopóki nie sprawdzisz nowego w osobnym oknie. Jeśli mimo to utracisz dostęp — niemal wszyscy hostingodawcy udostępniają awaryjną konsolę (VNC/Recovery) w panelu zarządzania.

01. Kupiłem VPS z Ubuntu/Debian — od czego zacząć

Po zakupie hoster przesyła: adres IP, nazwę użytkownika (zwykle root) i hasło (lub klucz SSH). To wystarczy do zalogowania. Kolejność działań (każdy krok — sekcja poniżej):

  1. Połącz się z serwerem przez SSH;
  2. Zaktualizuj system, ustaw nazwę hosta i strefę czasową;
  3. Utwórz zwykłego użytkownika z uprawnieniami sudo (nie pracuj jako root);
  4. Skonfiguruj logowanie kluczem SSH i wyłącz logowanie hasłem;
  5. Włącz zaporę i automatyczną ochronę;
  6. (opcjonalnie) zainstaluj panel zarządzania HestiaCP — serwer WWW, bazę danych i pocztę „od ręki”.

02. Pierwsze połączenie przez SSH

SSH to bezpieczny terminal do serwera. W miejsce 203.0.113.10 proszę wpisać własny adres IP.

203.0.113.10 to przykład, adres nieistniejący (zarezerwowany do dokumentacji). Proszę nie wpisywać go dosłownie — należy zastąpić go prawdziwym adresem IP serwera z wiadomości od hostingodawcy. Inaczej połączenie się nie powiedzie.

Windows 10/11: proszę otworzyć PowerShell lub „Terminal” i użyć wbudowanego ssh (albo klientów PuTTY / MobaXterm).
macOS / Linux: proszę otworzyć „Terminal”.

# Logowanie jako root (hasło przysłał hostingodawca): ssh root@203.0.113.10 # Jeśli hostingodawca dał plik klucza zamiast hasła: ssh -i ścieżka/do/klucza root@203.0.113.10
Przy pierwszym połączeniu SSH zapyta o „authenticity of host” — proszę wpisać yes. Hasło podczas wpisywania nie jest wyświetlane (to normalne). Jeśli hostingodawca dał hasło tymczasowe — proszę zmienić je poleceniem passwd.

03. Aktualizacja systemu i konfiguracja podstawowa

Na początek zaktualizuj wszystkie pakiety oraz ustaw nazwę hosta i strefę czasową.

# Zaktualizuj system: apt update && apt upgrade -y # Podstawowe narzędzia: apt install -y curl wget ufw fail2ban unattended-upgrades # Strefa czasowa (przykład) i nazwa hosta: timedatectl set-timezone Europe/Warsaw hostnamectl set-hostname myserver # Automatyczne aktualizacje zabezpieczeń: dpkg-reconfigure -plow unattended-upgrades
Lista stref czasowych — timedatectl list-timezones. Jeśli na końcu aktualizacji pojawi się niebieskie okno „Daemons using outdated libraries” — zaznacz wszystkie usługi (spacja) i naciśnij OK, jest to bezpieczne.

04. Tworzenie użytkownika z sudo

Stała praca jako root jest niebezpieczna. Proszę utworzyć zwykłego użytkownika i nadać mu uprawnienia sudo (uruchamianie poleceń administratora w razie potrzeby). Proszę zastąpić deploy dowolną nazwą.

# Utwórz użytkownika (ustawi hasło i zapyta o dane — można Enter): adduser deploy # Dodaj do grupy sudo: usermod -aG sudo deploy # Sprawdź (jako root): su - deploy sudo whoami # powinno wypisać: root exit
Następnie proszę logować się na serwer już jako ten użytkownik: ssh deploy@203.0.113.10, a polecenia administratora uruchamiać z przedrostkiem sudo.

05. Klucze SSH i wyłączenie logowania hasłem

Logowanie kluczem jest bezpieczniejsze niż hasłem: hasło można odgadnąć, klucza — praktycznie nie. Najpierw tworzymy klucz na własnym komputerze, kopiujemy go na serwer, sprawdzamy logowanie — i dopiero potem wyłączamy hasło.

Krok 1. Utwórz klucz na własnym komputerze (Windows PowerShell / macOS / Linux):

ssh-keygen -t ed25519 -C "my-laptop" # Enter przy każdym pytaniu (klucz trafi do ~/.ssh/id_ed25519)

Krok 2. Skopiuj klucz publiczny na serwer:

# macOS / Linux: ssh-copy-id deploy@203.0.113.10 # Windows (PowerShell): type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh deploy@203.0.113.10 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

Krok 3. Sprawdź logowanie kluczem w nowym oknie — powinno wpuszczać bez hasła:

ssh deploy@203.0.113.10
Nie wykonuj Wariantu B, dopóki logowanie kluczem nie zostanie sprawdzone i nie zadziała (Kroki 1–3), i nie zamykaj bieżącej sesji. Wyłącza on logowanie hasłem dla wszystkich użytkowników, łącznie z root. Bez działającego klucza całkowicie utraci Pan dostęp do serwera — przywrócić go będzie można tylko przez konsolę hostingu. Nie ma klucza — wybierz Wariant A.

Krok 4. Zaostrz dostęp przez SSH. Ustawienia umieszczamy w osobnym pliku, głównej konfiguracji nie ruszamy. Wybierz wariant odpowiedni do sytuacji:

Wariant A — tylko zablokuj root, hasło pozostaw. Klucz nie jest potrzebny, dostępu Pan nie utraci:

echo 'PermitRootLogin no' | sudo tee /etc/ssh/sshd_config.d/00-hardening.conf >/dev/null sudo systemctl restart ssh

Wariant B — pełne wzmocnienie. Wyłącz logowanie hasłem i pozostaw root tylko przez klucz. Wykonaj to tylko po upewnieniu się, że logowanie kluczem działa:

sudo tee /etc/ssh/sshd_config.d/00-hardening.conf >/dev/null <<'EOF' PubkeyAuthentication yes PasswordAuthentication no PermitRootLogin prohibit-password KbdInteractiveAuthentication no EOF sudo systemctl restart ssh
W obu wariantach root przez hasło jest zablokowany. PermitRootLogin no całkowicie zabrania logowania root, prohibit-password — pozostawia logowanie tylko przez klucz (do administrowania loguj się jako deploy i używaj sudo). Jeśli chce Pan zmienić port SSH — dodaj wiersz Port 2222, ale najpierw otwórz nowy port w zaporze (następny rozdział) i sprawdź logowanie, inaczej odetnie Pan sobie dostęp.

06. Podstawowa zapora i automatyczna ochrona

Zablokuj wszystko, co zbędne, za pomocą zapory i włącz fail2ban (banuje próby łamania hasła po SSH). Najpierw zezwól na SSH, w przeciwnym razie po włączeniu UFW utracisz dostęp.

# Zezwól na SSH (lub własny port, jeśli był zmieniony) oraz web: sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Włącz zaporę: sudo ufw enable sudo ufw status verbose # fail2ban — ochrona SSH przed łamaniem hasła (profil podstawowy aktywny od razu): sudo systemctl enable --now fail2ban sudo fail2ban-client status sshd
To niezbędne minimum. Robocze ustawienia fail2ban, lista blokad ipsum, rozszerzone UFW i pozostałe narzędzia znajdziesz w grupie „Narzędzia bezpieczeństwa” w informatorze. Sam panel Arcivéo Monitor przejrzyście pokaże status tego wszystkiego.

07. Instalacja panelu HestiaCP (opcjonalnie)

HestiaCP — bezpłatny panel zarządzania hostingiem: instaluje i konfiguruje serwer WWW (nginx + apache), PHP, bazę danych (MariaDB), pocztę, DNS oraz certyfikaty SSL, udostępnia interfejs webowy dla witryn. Wygodny, gdy nie chce się Pan/Pani konfigurować wszystkiego ręcznie i planuje hostować witryny (w tym sam panel Arcivéo Monitor).

Instaluj HestiaCP na czystym serwerze (świeży, wspierany Ubuntu/Debian, minimum ~1–2 GB RAM), przed instalacją innych serwerów WWW i baz danych — inaczej wystąpią konflikty. Instalacja trwa 10–20 minut i zrestartuje serwer.
# Pobierz instalator i uruchom: wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh sudo bash hst-install.sh

Instalator zapyta o email i nazwę hosta, następnie zainstaluje cały stos. Po restarcie panel będzie dostępny pod adresem https://YOUR_IP:8083 (login i hasło instalator pokaże na końcu).

HestiaCP samodzielnie zarządza UFW i fail2ban — nie trzeba ich konfigurować osobno, zrobi to sama. Klucze SSH i wyłączenie hasła (poprzedni rozdział) i tak proszę wykonać.

08. Wymagania systemowe i ionCube

Panel to aplikacja PHP działająca na typowym stosie LAMP/LEMP:

  • System: Linux (zalecane Ubuntu/Debian);
  • Serwer WWW: nginx lub Apache z PHP-FPM;
  • PHP 8.0+ z rozszerzeniami: pdo_mysql, openssl, curl, json, mbstring;
  • ionCube Loader — rozszerzenie PHP niezbędne do działania panelu;
  • Baza danych: MySQL 5.7+ lub MariaDB 10.3+;
  • HTTPS — obowiązkowo (logowanie i WebAuthn działają tylko przez https);
  • sudo dla użytkownika serwera WWW (wąski zestaw — krok 13).
# Sprawdź wersję PHP i rozszerzenia: php -v php -m | grep -iE 'pdo_mysql|openssl|curl|mbstring|ioncube'

Instalacja ionCube Loader (jeśli jeszcze nie jest zainstalowany). Na hostingu z panelem (HestiaCP, cPanel) ionCube włącza się polem wyboru w ustawieniach PHP. Ręcznie na Ubuntu/Debian:

# Sprawdź wersję PHP i katalog rozszerzeń: php -v EXTDIR=$(php -r 'echo ini_get("extension_dir");'); PHPVER=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;') # Pobierz i rozpakuj loadery (64-bit): cd /tmp wget -q https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz tar xzf ioncube_loaders_lin_x86-64.tar.gz # Skopiuj loader dla Pana wersji PHP do katalogu rozszerzeń: sudo cp ioncube/ioncube_loader_lin_${PHPVER}.so "$EXTDIR"/ # Podłącz (CLI + PHP-FPM) i uruchom ponownie: echo "zend_extension=ioncube_loader_lin_${PHPVER}.so" | sudo tee /etc/php/${PHPVER}/mods-available/ioncube.ini sudo phpenmod ioncube sudo systemctl restart php${PHPVER}-fpm # Weryfikacja — w wyniku pojawi się wiersz "with the ionCube PHP Loader": php -v
Wersja loadera musi być zgodna z wersją PHP (np. ioncube_loader_lin_8.1.so dla PHP 8.1). Jeśli używa Pan kilku wersji PHP — podłącz loader dla każdej z nich.

09. Domena i DNS

Aby otwierać panel pod adresem w rodzaju monitor.example.com i uzyskać bezpłatny SSL — potrzebna jest domena wskazująca na Pana serwer. W panelu zarządzania DNS proszę utworzyć rekord A:

Typ: A Nazwa: monitor (subdomena → monitor.example.com) lub @ (korzeń domeny → example.com) Wartość: 203.0.113.10 ← IP Pana serwera TTL: 3600

Po kilku minutach proszę sprawdzić, czy domena wskazuje na serwer:

dig +short monitor.example.com # powinien zwrócić Pana IP # lub, jeśli nie ma dig: getent hosts monitor.example.com
Certyfikat SSL Let's Encrypt jest wydawany wyłącznie na domenę — DNS musi wskazywać na serwer przed wydaniem certyfikatu.

10. Utwórz witrynę i wgraj pliki panelu

Apache: DocumentRoot — na katalog główny panelu, NIE na public/. Style (CSS/JS), sw.js, manifest.json znajdują się w assets/ obok public/ i są pobierane z katalogu głównego witryny. Główny .htaccess to front-controller. Jeśli w Apache ustawi się DocumentRoot na public/, panel otworzy się bez stylów. W czystym nginx jest odwrotnie: katalogiem głównym jest public/, a assets/ są udostępniane osobną regułą (patrz blok nginx poniżej).
Pliki panelu (archiwum dystrybucji) pobiera się po zakupie w panelu my.arciveo.com„Pobieranie”. Rozpakuj archiwum przed wgraniem.

1) Utwórz katalog panelu i wgraj do niego zawartość dystrybucji (aby w środku znalazły się public/, assets/, config.php itd.):

sudo mkdir -p /var/www/monitor # następnie wgraj pliki dystrybucji do /var/www/monitor (FileZilla / WinSCP / scp)

2) Skonfiguruj serwer WWW. Apache: DocumentRoot — na katalog główny panelu (NIE na /public); AllowOverride All jest wymagany. Ścieżka do gniazda PHP-FPM jest wykrywana automatycznie. Blok wkleja się do terminala w całości:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # automatyczne wykrywanie gniazda PHP-FPM sudo tee /etc/apache2/sites-available/monitor.conf > /dev/null <<'EOF' <VirtualHost *:80> ServerName monitor.example.com DocumentRoot /var/www/monitor <Directory /var/www/monitor> AllowOverride All Require all granted </Directory> <FilesMatch \.php$> SetHandler "proxy:unix:__PHPSOCK__|fcgi://localhost" </FilesMatch> </VirtualHost> EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/apache2/sites-available/monitor.conf sudo a2dissite 000-default.conf sudo a2ensite monitor.conf sudo apache2ctl configtest sudo systemctl reload apache2

nginx: nginx nie ma .htaccess, dlatego katalogiem głównym bierzemy public/, a assets/, sw.js, manifest.json (poziom wyżej) udostępniamy osobną regułą:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # automatyczne wykrywanie gniazda PHP-FPM sudo tee /etc/nginx/sites-available/monitor.conf > /dev/null <<'EOF' server { listen 80; server_name monitor.example.com; root /var/www/monitor/public; index index.php; # assets, service worker i manifest znajdują się poziom wyżej niż public/ location ~ ^/(assets/|sw\.js|manifest\.json) { root /var/www/monitor; } location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:__PHPSOCK__; } } EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/nginx/sites-available/monitor.conf sudo ln -s /etc/nginx/sites-available/monitor.conf /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx

Wgrywanie plików — SFTP/SCP (FileZilla, WinSCP) lub scp:

# Przykład przez scp z komputera lokalnego: scp -r ./monitor/* deploy@203.0.113.10:/var/www/monitor/
Ustaw uprawnienia do plików — to krok obowiązkowy. Jeśli wgrywano jako root lub przez SFTP, pliki należą do root i serwer WWW (www-data) nie będzie mógł ich odczytać — panel otworzy się pusty lub z błędem 403 (w logu: .htaccess unreadable / directory not executable). Poniższe polecenie to naprawia:
# Normalizujemy uprawnienia całego webroota: katalog utworzony przez root jest niedostępny # dla serwera WWW (www-data) — bez tego panel zwraca pustą stronę lub 403. # Apache działa jako www-data; jeśli masz innego użytkownika WWW — zamień. cd /var/www/monitor # Foldery robocze tworzymy PRZED chown — inaczej nowe katalogi pozostaną root:root # i przy chmod 750 serwer WWW (www-data) nie będzie mógł w nich zapisywać. sudo mkdir -p data/lynis data/logwatch tmp logs sudo chown -R www-data:www-data /var/www/monitor sudo find /var/www/monitor -type d -exec chmod 755 {} \; sudo find /var/www/monitor -type f -exec chmod 644 {} \; sudo chmod 640 /var/www/monitor/config.php sudo chmod 750 data tmp logs

3) Proszę otworzyć sobie wgrywanie plików przez SFTP. Po powyższym poleceniu wszystkie pliki należą do www-data, a FileZilla / WinSCP łączą się na własnym użytkowniku — wgrywanie zakończy się wtedy błędem SSH_FX_PERMISSION_DENIED (Permission denied). Logowanie jako root w celu wgrania plików nie wchodzi w grę — logowanie root wyłączono w kroku 05. Proszę wybrać jeden z dwóch wariantów.

Wariant A — ACL tylko dla własnego użytkownika (zalecany). Prawo zapisu otrzymuje wyłącznie użytkownik; serwer WWW nadal nie może nadpisać kodu panelu:

sudo apt install -y acl # Prawo zapisu dla własnego użytkownika do całego katalogu panelu: sudo setfacl -R -m u:deploy:rwX /var/www/monitor # Ta sama reguła domyślnie — dla plików i katalogów utworzonych później: sudo setfacl -R -d -m u:deploy:rwX /var/www/monitor

Wariant B — przez grupę www-data. Prostszy, ale prawo zapisu do plików panelu dostaje również serwer WWW: przy podatności w PHP kod dałoby się podmienić. Kolejność poleceń ma znaczenie — config.php i katalogi robocze zamyka się na końcu:

sudo usermod -aG www-data deploy # Zapis dla grupy + setgid (bit 2): pliki wgrane przez SFTP pozostają # w grupie www-data — inaczej panel nie będzie mógł ich nadpisać. sudo find /var/www/monitor -type d -exec chmod 2775 {} \; sudo find /var/www/monitor -type f -exec chmod 664 {} \; sudo chmod 640 /var/www/monitor/config.php sudo chmod 2750 /var/www/monitor/data /var/www/monitor/tmp /var/www/monitor/logs
Po wariancie B proszę połączyć się ponownie w FileZilli (Serwer → Rozłącz, następnie zalogować się na nowo) — nowa grupa działa dopiero przy nowym logowaniu, wcześniej uprawnień nadal nie będzie. Sprawdzenie: id deploy — na liście grup musi pojawić się www-data; ls -ld /var/www/monitor — uprawnienia drwxrwsr-x, litera s zamiast x oznacza, że setgid jest ustawiony.

11. Baza danych

Proszę utworzyć bazę i użytkownika, a następnie zaimportować schemat. Blok wkleja się do terminala w całości. monitor_db i monitor_user to nazwy przykładowe, może Pan/Pani ustawić dowolne własne; proszę zapamiętać nazwę bazy, użytkownika i hasło — wpisze je Pan/Pani w config.php w następnym kroku:

# 1. Baza danych. Hasło ustawia się RAZ w DBPASS i jest podstawiane do wszystkich linii. # Blok wkleja się do terminala W CAŁOŚCI; sudo mysql loguje root przez gniazdo unix # (hasło root nie jest potrzebne). NIE używaj interaktywnego `sudo mysql -u root -p` # z kopiuj-wklej — przy wklejaniu linie SQL trafią do zapytania o hasło i przepadną. DBPASS='CHOOSE_A_PASSWORD' # ← zmień tylko tę linię sudo mysql <<SQL CREATE DATABASE IF NOT EXISTS monitor_db CHARACTER SET utf8mb4; CREATE USER IF NOT EXISTS 'monitor_user'@'localhost' IDENTIFIED BY '$DBPASS'; GRANT ALL ON monitor_db.* TO 'monitor_user'@'localhost'; FLUSH PRIVILEGES; SQL # Sprawdzenie (powinno pokazać monitor_db): mysql -u monitor_user -p"$DBPASS" -e "SHOW DATABASES;" # To samo hasło wpisz w config.php → DB_PASS.
Schematu nie trzeba importować — panel sam tworzy tabele i konto admin przy pierwszym wejściu w przeglądarce (z database/db.sql), jeśli baza jest pusta. Ręczny import schematu jest potrzebny tylko wtedy, gdy automatyczna inicjalizacja nie zadziałała.
Jeśli użyto przeglądarkowego instalatora public/start_db.php — proszę go usunąć zaraz po instalacji: pozwala on bez autoryzacji odtworzyć bazę. Dopóki plik znajduje się w katalogu głównym panelu lub w public/, panel wyświetla czerwone ostrzeżenie.

12. Konfiguracja config.php

config.php w katalogu głównym panelu (/var/www/monitor/config.php) to jedyny plik, który trzeba edytować ręcznie. Wszystkie ustawienia panelu są w nim zdefiniowane jako stałe define(). Proszę otworzyć go w edytorze:

sudo nano /var/www/monitor/config.php

Proszę wstawić własne wartości w podświetlonych miejscach; resztę proszę zostawić bez zmian:

// --- Baza danych (z kroku 11) --- define('DB_HOST', 'localhost'); // zostawić define('DB_NAME', 'db_name'); // utworzona w kroku 11 define('DB_USER', 'user'); // utworzony w kroku 11 define('DB_PASS', 'db_password'); // ustawione w kroku 11 define('DB_CHARSET', 'utf8mb4'); // zostawić // --- 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)

Co zmienić:

  • DB_NAME, DB_USER, DB_PASS — dokładnie te same nazwa bazy, użytkownik i hasło, które ustawiono przy tworzeniu bazy w kroku 11 (jeśli pozostawiono przykłady — monitor_db / monitor_user). DB_HOST i DB_CHARSET proszę zostawić bez zmian.
  • APP_URL — pełny adres panelu z https://, bez ukośnika na końcu i bez www. Musi być zgodny z domeną, na której aktywuje się licencję (krok 16), inaczej klucz zostanie odrzucony.
  • TIMEZONE — Pana/Pani strefa czasowa (lista — timedatectl list-timezones). Wpływa tylko na to, jak panel wyświetla daty; na czas uruchamiania zadań cron nie wpływa (tam obowiązuje strefa systemu).
  • SESSION_LIFETIME — po ilu sekundach bezczynności panel poprosi o ponowne logowanie (domyślnie 8 godzin). Np. 3600 = 1 godzina, 86400 = doba.
  • Blok logowania błędów (display_errors, log_errors, error_log) — proszę zostawić domyślnie.

Proszę zapisać plik (Ctrl+O, Enter, następnie Ctrl+X) i uruchomić ponownie PHP-FPM — inaczej zmiany nie zostaną zastosowane przez OPcache:

sudo systemctl restart php*-fpm
config.php — plik tajny (zawiera hasło bazy danych). Znajduje się w katalogu głównym panelu, który jest zarazem katalogiem głównym serwera WWW, ale jest zabezpieczony: uprawnienia 640 (ustawione w kroku 10) i jawny zakaz w głównym .htaccess. Proszę nie umieszczać go w publicznych repozytoriach i nie przekazywać do wsparcia z prawdziwym hasłem.
Szczegółowe omówienie wszystkich parametrów — w FAQ: „Plik config.php — wszystkie ustawienia panelu”.

13. Konfiguracja sudo dla serwera WWW

PHP działa jako użytkownik serwera WWW, który nie ma uprawnień do poleceń systemowych. Dostęp przyznaje się wąsko: punktowe sudo na konkretne narzędzia oraz odczyt logów przez grupy (bez sudo). Włamanie do warstwy WWW nie daje roota.

W przykładach www-data to standardowy użytkownik Apache. Jeśli u Pana jest inny (w niektórych panelach PHP działa pod odrębnym użytkownikiem) — proszę zamienić wszędzie. Sprawdzenie: ps -o user= -C php-fpm | sort -u.

1. Proszę utworzyć /etc/sudoers.d/monitor poleceniem sudo visudo -f /etc/sudoers.d/monitor i wkleić (proszę usunąć wiersze nieużywanych modułów):

# UFW — status i reguły (strona „Zapora”) www-data ALL=(ALL) NOPASSWD: /usr/sbin/ufw status, /usr/sbin/ufw status verbose, /usr/sbin/ufw status numbered www-data ALL=(ALL) NOPASSWD: /usr/sbin/ufw allow [0-9]*, /usr/sbin/ufw deny [0-9]*, /usr/sbin/ufw --force delete [0-9]* # Fail2ban — status, ban i unban (banned zwraca bany wszystkich jaili jednym poleceniem; # ban/unban potrzebne przyciskom panelu) www-data ALL=(ALL) NOPASSWD: /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status *, /usr/bin/fail2ban-client banned, /usr/bin/fail2ban-client set * banip *, /usr/bin/fail2ban-client set * unbanip * # Aktualizacje zabezpieczeń (karta „Aktualizacje”). Tylko odczyt, ale właśnie od roota: # cache apt (~70 MB) dostępny jest tylko rootowi, użytkownik nie-root przebudowuje go przy każdym wywołaniu # (4.2 s CPU wobec 0.01 s). Bez wildcarda — dokładnie to jedno polecenie, niczego nie instaluje. www-data ALL=(ALL) NOPASSWD: /usr/bin/apt list --upgradable # IPset (mapa ataków, dashboard) www-data ALL=(ALL) NOPASSWD: /usr/sbin/ipset list -t ipsum # CrowdSec www-data ALL=(ALL) NOPASSWD: /usr/bin/cscli decisions list *, /usr/bin/cscli alerts list *, /usr/bin/cscli bouncers list *, /usr/bin/cscli scenarios list * # Auditd — wyszukiwanie zdarzeń + odczyt ostatnich wierszy dziennika (dokładna ścieżka) www-data ALL=(ALL) NOPASSWD: /usr/sbin/ausearch -m * www-data ALL=(ALL) NOPASSWD: /usr/bin/tail -n 300 /var/log/audit/audit.log # Monit / ModSecurity / AppArmor / PSAD www-data ALL=(ALL) NOPASSWD: /usr/bin/monit status www-data ALL=(ALL) NOPASSWD: /usr/sbin/apache2ctl -M www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec www-data ALL=(ALL) NOPASSWD: /usr/sbin/aa-status www-data ALL=(ALL) NOPASSWD: /usr/sbin/psad --Status # Otwarte porty (dzienniki jądra/SSH/Falco czytane są BEZ sudo — przez grupę # systemd-journal, patrz p.2; sudo dla journalctl NIE trzeba dawać i jest to niebezpieczne) www-data ALL=(ALL) NOPASSWD: /usr/bin/ss -tuln, /usr/sbin/ss -tuln, /bin/ss -tuln # PostgreSQL (tylko jeśli używane) — stały skrypt read-only, # utworzyć wg FAQ „PostgreSQL się nie wyświetla”; bez niego proszę usunąć wiersz www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
sudo chmod 440 /etc/sudoers.d/monitor sudo visudo -c # powinno być "parsed OK"

2. Dostęp do logów i dziennika systemd. Moduły czytają /var/log/fail2ban.log, auth.log, ufw.log, apache2/*, aide bezpośrednio (na Debian/Ubuntu logi te należą do grupy adm). Zdarzenia jądra, SSH i Falco pobierane są z journald poleceniem journalctl bez sudo, przez grupę systemd-journal. Proszę dodać użytkownika WWW do obu grup i zrestartować PHP-FPM:

sudo usermod -aG adm,systemd-journal www-data sudo systemctl restart php*-fpm # obowiązkowo, inaczej grupy się nie zastosują

3. Jeśli ClamAV lub Suricata piszą logi nie do grupy adm (bywa root:root) — proszę nadać dostęp przez ACL:

sudo apt install acl sudo setfacl -R -m u:www-data:rX /var/log/clamav /var/log/suricata 2>/dev/null sudo setfacl -d -m u:www-data:rX /var/log/clamav /var/log/suricata 2>/dev/null

4. Nakładka ModSecurity. Dziennik audytu WAF (/var/log/apache2/modsec_audit.log) należy do roota z prawami 640, użytkownik WWW nie może go czytać bezpośrednio. Strona ModSecurity pobiera tryb silnika, zdarzenia i listę aktywnych reguł przez stały skrypt read-only — to on jest dozwolony w sudoers wierszem powyżej:

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
Bez pliku /etc/modsecurity/modsecurity.conf sam WAF nie działa: pakiet umieszcza tylko modsecurity.conf-recommended, a silnik reguł pozostaje wyłączony — jak go włączyć, patrz FAQ → „Instalacja ModSecurity”.
Użytkownik we wszystkich wierszach sudoers musi zgadzać się z użytkownikiem puli FPM: na zwykłym Apache/Debian jest to www-data, w HestiaCP pula witryny działa od właściciela witryny (np. admin) — proszę sprawdzić grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.

5. Jeśli przed Apache stoi Nginx (HestiaCP, ISPmanager i inne panele — tam Nginx proksuje PHP do Apache, a statykę serwuje sam). Katalogi służbowe są zabezpieczone plikami .htaccess, ale Nginx ich nie czyta: dowolny plik statyczny (.json, .txt, .log, .dat) zwróci bezpośrednio, z pominięciem Apache. Na zewnątrz wyciekną cache panelu i dane — na przykład tmp/modsec_cache.json ze zdarzeniami WAF i IP atakujących. Proszę dodać blokadę do konfiguracji witryny Nginx:

location ^~ /data/ { deny all; } location ^~ /tmp/ { deny all; } location ^~ /logs/ { deny all; } location ^~ /includes/ { deny all; } location ^~ /cron/ { deny all; } location ^~ /database/ { deny all; } location = /config.php { deny all; }
Prefiks ^~ jest obowiązkowy: jest wybierany wcześniej niż reguła wyrażenia regularnego dla statyki wewnątrz location /, inaczej blokada nie zadziała.
W HestiaCP proszę umieścić to jako osobny plik /home/<user>/conf/web/<domain>/nginx.ssl.conf_deny (oraz nginx.conf_deny dla HTTP) — konfiguracja witryny dołącza nginx.ssl.conf_* i przy przebudowie takich plików nie nadpisuje. Zastosowanie: sudo nginx -t && sudo systemctl reload nginx.
Sprawdzenie: curl -s -o /dev/null -w '%{http_code}\n' https://monitor.example.com/tmp/modsec_cache.json — powinno być 403. Jeśli Apache działa bez Nginx (nasłuchuje 80/443 sam), nic dodawać nie trzeba — .htaccess wystarczy.
Ścieżki do plików binarnych proszę sprawdzić przez which (np. which ufw cscli ausearch ss). Sudoers proszę edytować wyłącznie przez visudo. Listę wszystkich baz MySQL włącza się osobnym GRANT (FAQ → „Widoczna tylko jedna baza”).

14. Ograniczenie dostępu według IP

Proszę ograniczyć dostęp do monitora według adresu IP — nawet jeśli adres URL stanie się znany, strona logowania się nie otworzy. Można to zrobić na poziomie serwera WWW (przykład dla nginx poniżej) lub w samym panelu („Ustawienia” → „Ograniczenie dostępu według IP”). Jeśli używa Pan Apache, proszę skorzystać z ograniczenia w panelu.

Jeśli witryna nginx jest już skonfigurowana według kroku 10, nie należy dodawać drugiego location / — proszę wpisać wiersze allow/deny do już istniejącego bloku. Dwa jednakowe location / w jednym server { } to błąd konfiguracji i nginx się nie zrestartuje.
# W konfiguracji nginx (wewnątrz server { }): # Ścieżkę ACME Let's Encrypt zostawiamy otwartą z pominięciem ograniczenia IP — # aby wystawianie i automatyczne odnawianie SSL (krok 15) nie zależały od filtra IP. location ^~ /.well-known/acme-challenge/ { allow all; } location / { allow 203.0.113.10; # ← proszę wpisać swój IP allow 10.0.0.0/8; # sieć lokalna (jeśli potrzebne) deny all; try_files $uri $uri/ /index.php?$query_string; } # Przeładuj nginx: sudo nginx -t && sudo systemctl reload nginx

15. Wystawianie certyfikatu SSL (HTTPS)

Panel działa wyłącznie przez HTTPS. Sesja logowania korzysta z zabezpieczonego cookie, a WebAuthn (2FA) działa tylko na HTTPS. Przez http:// nie można się zalogować.

Certyfikat jest darmowy (Let's Encrypt). DNS domeny musi już wskazywać na serwer. Polecenie zależy od serwera WWW:

# Apache: sudo certbot --apache -d monitor.example.com # nginx — TYLKO jeśli używasz właśnie nginx. Na Apache NIE uruchamiaj: # apt pobierze nginx i zajmie port 80, konflikt z Apache. # sudo apt install python3-certbot-nginx # sudo certbot --nginx -d monitor.example.com # certbot sam wpisze HTTPS do konfiguracji i skonfiguruje automatyczne odnawianie
O co zapyta certbot: e-mail → zgoda na Terms (Y) → przekazanie e-maila do EFF (według uznania). Następnie sam wystawi certyfikat, wpisze <VirtualHost *:443>, skonfiguruje przekierowanie http→https oraz automatyczne odnawianie.
DNS musi wskazywać na serwer PRZED uruchomieniem certbot (weryfikacja własności przez port 80). Sprawdzenie: dig +short monitor.example.com → IP serwera. Porty 80/443 otwarte: sudo ufw allow 80,443/tcp.

Po wystawieniu: https://monitor.example.com otwiera się z kłódką, http:// przekierowuje na https:// (APP_URL w config.php ustawiono już w kroku 12).

16. Logowanie i konfiguracja początkowa

Proszę otworzyć https://monitor.example.com, zalogować się admin / useradmin i przejść listę kontrolną:

  1. Zmień hasło admin — sekcja „Użytkownicy” w menu.
  2. Włącz WebAuthn (2FA) — „Klucze WebAuthn” → zarejestruj klucz/passkey (wymaga HTTPS). Proszę od razu zarejestrować dwa: w razie utraty jedynego klucza logowanie przy jego użyciu będzie niemożliwe. Więcej.
  3. Ogranicz dostęp według IP — „Ustawienia” → „Ograniczenie dostępu według IP” (proszę wpisać swój IP przed włączeniem, inaczej odetnie sobie Pan/Pani dostęp).
  4. Wprowadź licencję — kod ARCIVEO-… z panelu klienta proszę aktywować na swojej domenie i wkleić klucz w „Ustawienia” → „Licencja”. Więcej.
  5. Skonfiguruj powiadomienia — Telegram i/lub Email w „Ustawienia”. Więcej.
  6. Usuń instalator public/start_db.php, jeśli pozostał (krok 11).

17. Narzędzia bezpieczeństwa (opcjonalnie)

Panel już działa. Narzędzia instaluje się według potrzeb — instaluje Pan/Pani to, co potrzebne, a panel od razu pokaże status. Polecenia instalacji każdego z nich znajdują się w dokumentacji (osobne sekcje dla poszczególnych narzędzi):

18. Cron i konserwacja

Konfiguruje się raz, również opcjonalnie, ale zalecane. Szczegółowe polecenia znajdują się w podręczniku:

  1. Zadania cron (raporty, aktualizacja list, sprawdzenia);
  2. Kopie zapasowe;
  3. Aktualizacja i przenoszenie panelu;
  4. Przywracanie dostępu — na wypadek utraty klucza/hasła.
Coś nie działa lub wyświetla „brak danych”? Proszę zajrzeć do grupy „Diagnostyka” w podręczniku.
Arcivéo - Security Monitor © 2026