FAQ

Acesta este ghidul de instalare, configurare și întreținere a Arcivéo Security Monitor. Secțiunile sunt grupate: prezentare generală, implementarea panoului, conectarea instrumentelor de securitate, module integrate și diagnosticare. Comenzile pot fi copiate cu butonul din dreapta.

De unde încep

01. Instalarea panoului — alegeți metoda

Instalarea panoului este descrisă pe pagini separate, pas cu pas. Alegeți metoda:

Dacă nu sunteți sigur, alegeți varianta automată. Acest ghid rămâne sursa unică pentru SSL, instrumente, cron și diagnosticare — paginile de instalare trimit la secțiunile sale, fără a dubla nimic.

Prezentare generală

02. Ce este Arcivéo Security Monitor

Arcivéo Security Monitor — panoul de securitate al serverului. Colectează date de la instrumentele instalate (Fail2ban, UFW, Lynis, ModSecurity, AIDE, ClamAV, Auditd, CrowdSec, Suricata, Falco etc.) și le afișează într-o interfață unică, cu dashboard, hartă a atacurilor și pagini detaliate pentru fiecare instrument.

Monitorul nu este un mijloc activ de protecție — el nu blochează atacurile de unul singur. Rolul său este să agrege informațiile de la instrumentele deja active și să le prezinte într-o formă comodă.

03. Cum funcționează monitorul pe server

Monitorul funcționează doar local — trebuie instalat pe același server pe care îl monitorizează. Nu există niciun SSH sau API la distanță.

Toate comenzile (fail2ban-client, ufw status, ipset list etc.) sunt executate de panou în numele utilizatorului serverului web (de obicei www-data, iar pe panourile de hosting — contul site-ului) cu un set restrâns de drepturi sudo — doar pentru utilitare concrete, fără acces root general. Rezultatele sunt analizate și afișate în browser.

Două ritmuri de lucru. Paginile panoului interoghează instrumentele în momentul deschiderii — vedeți întotdeauna starea curentă. Pe lângă asta, în fundal rulează sarcini după un orar (cron):

  • la fiecare 5 minute — o captură a încărcării pentru pagina „Performanță”;
  • la fiecare 5 minute — senzorul de evenimente de securitate: blocări noi, alerte IDS, o protecție oprită, un port deschis (secțiunea „Evenimente de securitate”);
  • o dată pe zi — raportul de securitate pe Telegram și prin e-mail, precum și verificările grele (Lynis, AIDE, ClamAV, debsums, Logwatch);
  • la fiecare 15 minute — rezumatul serverului către contul personal, dacă este activat.

Lista completă a sarcinilor se află în secțiunea „Toate sarcinile cron într-un singur loc”.

Pentru mai multe servere instalați monitorul pe fiecare separat, cu domeniu propriu. Ca să le vedeți într-o singură listă, activați prezentarea serverelor (secțiunea „Prezentarea serverelor în contul personal”).

04. Cum se calculează Scorul de securitate

Scorul pornește de la maxim și scade pentru fiecare problemă identificată:

  • UFW nu este activ — −30
  • Fail2ban nu este pornit (niciun jail activ) — −25
  • Nu există chei WebAuthn — −15
  • Lynis hardening index < 60 — −20; 60–79 — −10
  • IPset ipsum nu este încărcat — −10
  • Amenințări găsite de ClamAV — −20
  • Modificări de fișiere AIDE — −15
  • SSL expirat — −30, expiră în <14 zile — −15, <30 zile — −5
  • CrowdSec instalat, dar nepornit — −5
  • Suricata instalată, dar nepornită — −5
  • SGBD/cache (MySQL, PostgreSQL, Redis…) accesibile din exterior — −10
  • Autentificare root prin SSH permisă (PermitRootLogin yes) — −20
  • Autentificare cu parolă prin SSH permisă (PasswordAuthentication yes) — −10
  • Setări SSH minore: X11Forwarding activat, MaxAuthTries peste 3 sau sshd_config lizibil pentru toate conturile — −5
  • Protecția serverului web: răspunsurile dezvăluie versiunea serverului web sau a PHP, metoda TRACE este activată ori lipsește antetul Strict-Transport-Security sau X-Frame-Options — −5
  • Actualizări de securitate în așteptare — −5

Rezultat: 80+ = Protejat, 60–79 = Atenție, <60 = În pericol.

Scăderile pentru ClamAV, AIDE, CrowdSec și Suricata se aplică doar dacă instrumentul este instalat. Lynis și AIDE fără o bază inițializată sunt afișate ca „fără date” și nu reduc punctajul. Numărul de atacuri de azi este afișat pe dashboard, dar nu influențează Scorul de securitate. Protecția serverului web nu se ia în calcul dacă niciun site nu a răspuns la verificare.

Setări și licență

05. WebAuthn — autentificare cu doi factori

WebAuthn — standard de autentificare fără parolă printr-o cheie hardware. Acceptă YubiKey, Touch ID, Face ID, Windows Hello, Passkey.

După autentificarea cu parolă, sistemul solicită confirmarea prin cheia înregistrată. Chiar dacă parola este compromisă, fără cheia fizică sau biometrie autentificarea este imposibilă.

Pentru configurare, deschideți Chei WebAuthn din meniul lateral și apăsați „Înregistrează cheia”. Înregistrați imediat două chei: dacă singura cheie se pierde sau se defectează, autentificarea în panou cu ea va fi imposibilă.

WebAuthn funcționează doar prin HTTPS. Pe o conexiune HTTP, înregistrarea și autentificarea cu cheie nu sunt disponibile.

06. Notificări: Telegram, Email, webhook și push

Sunt patru canale: Telegram, Email, webhook și push în browser. Sunt independente — activați oricare, chiar și toate patru. Pe aceleași canale merg raportul zilnic de securitate, alertele de încărcare și descoperirile senzorului de evenimente; se configurează o singură dată, aici, în secțiunea „Setări”.

Telegram. Sunt necesare tokenul botului și chat id:

  1. În Telegram scrieți-i lui @BotFather → /newbot → primiți tokenul de forma 123456:ABC....
  2. Trimiteți noului bot orice mesaj (ca să vă poată răspunde).
  3. Aflați-vă chat id: scrieți-i botului @userinfobot sau deschideți https://api.telegram.org/bot<TOKEN>/getUpdates și găsiți "chat":{"id":...}.
  4. Introduceți tokenul și chat id în „Setări” → Telegram, apăsați „Salvează și trimite test”.

Email. Două metode la alegere în „Setări” → Email:

  • SMTP — host, port (465/SSL sau 587/TLS), utilizatorul și parola căsuței dumneavoastră de e-mail;
  • Resend — un API modern: indicați cheia API (re_...) și domeniul expeditor confirmat.

Webhook. O singură adresă care acceptă POST acoperă Slack, Discord, Mattermost, n8n și propriul dumneavoastră script. „Setări” → Webhook: lipiți adresa și activați comutatorul.

  • către Slack și Mattermost se trimite {"text": …}, către Discord {"content": …} (Discord respinge mesajele mai lungi de 2000 de caractere, așa că textul este scurtat);
  • orice altă adresă primește JSON structurat: text, event, severity, source, count, host, ts — textul mesajului nu mai trebuie analizat din nou.

Notificări push. Mesajul ajunge direct în browser, iar dacă panoul este adăugat pe ecranul principal al telefonului, ca o notificare obișnuită de aplicație — fără bot și fără server de e-mail la mijloc. „Setări” → Notificări push: activați comutatorul și apăsați „Conectați acest dispozitiv” pe fiecare dispozitiv care trebuie să primească notificări. Contorul „Dispozitive conectate” arată câte sunt.

Push funcționează doar prin HTTPS (la fel ca WebAuthn), iar permisiunea este necesară de două ori: mai întâi în browser — cererea apare după butonul „Conectați acest dispozitiv” — și apoi în sistemul de operare însuși. Dacă în Windows este oprit comutatorul general Setări → Sistem → Notificări, browserul acordă permisiunea, panoul raportează trimiterea, iar pe ecran tot nu apare nimic. La fel se întâmplă cu modul „Nu deranjați” și cu notificările browserului dezactivate în macOS (Setări de sistem → Notificări).
Fiecare canal are propriul buton „Trimite test”, care îl verifică imediat. Orarul raportului automat trece prin cron (secțiunea „Toate sarcinile cron”): cron declanșează trimiterea, iar canalele sunt luate din setări.

Statusul raportului: „ATENȚIE” sau „OK”. Titlul devine „ATENȚIE” doar la o problemă reală sau o acțiune în așteptare: amenințare găsită de ClamAV, modificări de fișiere în AIDE, evenimente critice Falco (Emergency/Alert/Critical în ultimele 24 h), un serviciu picat în Monit, este necesară o repornire, expiră SSL (≤14 zile) sau așteaptă actualizări de securitate. Zgomotul de fond — atacuri SSH prin forță brută ale boților, IP-uri banate de fail2ban, alerte Suricata, avertizări Lynis și cererile deja respinse de ModSecurity — nu ridică statusul, așa că astfel de cifre din raport nu înseamnă „ATENȚIE” prin ele însele.

07. Licență — introducere și activare

Modulele detaliate de monitorizare (Lynis, UFW, ModSecurity, harta atacurilor, AIDE, ClamAV etc.) se deblochează dacă aveți o licență validă. Fără ea funcționează dashboardul, setările și contul, iar modulele afișează cardul „Necesită licență”.

După achiziția din cont, aveți un cod de activare de forma ARCIVEO-XXXX-XXXX-XXXX-XXXX. Acesta trebuie „activat” pe domeniul panoului dumneavoastră — astfel codul devine un fișier de licență semnat (blocul [license]), pe care îl introduceți în panou.

Cum se activează (3 pași):

  1. Luați codul de activare. Contul personal my.arciveo.com → secțiunea „Licențe” / „Activare licență” — copiați codul ARCIVEO-….
  2. Activați codul pe domeniul dumneavoastră. Tot în cont, deschideți „Activare licență”, introduceți: codul de activare, emailul dumneavoastră și domeniul panoului (adresa la care se deschide monitorul, ex. monitor.example.com). Apăsați activare — sistemul va genera fișierul de licență legat de acest domeniu și îl va afișa în câmpul cu butonul „Copiază”.
  3. Introduceți cheia în panou. Copiați tot textul licenței → în panou deschideți „Setări” → blocul „Licență”, lipiți și apăsați „Salvează”. Modulele se deblochează imediat.

Panoul verifică cheia criptografic: semnătura, legarea la domeniu și termenul de valabilitate.

La activare, domeniul trebuie să coincidă exact cu adresa panoului. Luați-l din constanta APP_URL din config.php și introduceți doar numele hostului — fără https:// și fără prefixul www. Activarea este unică: codul devine licență pentru domeniul introdus și nu se mai poate activa a doua oară — la o greșeală în domeniu, cheia nu se va potrivi panoului dumneavoastră, iar codul va fi consumat. De aceea introduceți domeniul cu atenție.
Dacă termenul a expirat sau domeniul s-a schimbat, în antetul panoului va apărea un avertisment. Licența este legată de domeniu pentru totdeauna și nu se transferă pe alt domeniu: pentru un nou termen sau un nou domeniu este nevoie de o cheie nouă (se cumpără din cont și se activează o singură dată).

08. Fișierul config.php — toate setările panoului

Toți parametrii principali ai panoului sunt definiți într-un singur fișier config.php din rădăcină (lângă folderul public/) prin constante obișnuite define(). Fișierul este creat la instalare; rareori trebuie editat manual — în principal la schimbarea domeniului, la migrare sau la conectarea la altă bază de date. După orice modificare reporniți PHP-FPM (altfel, din cauza OPcache, modificările nu se aplică).

Introduceți propriile valori în locurile evidențiate; restul lăsați-l așa cum este:

// --- Bază de date --- define('DB_HOST', 'localhost'); // lăsați define('DB_NAME', 'db_name'); // ce ați stabilit la crearea BD define('DB_USER', 'user'); // ce ați stabilit la crearea BD define('DB_PASS', 'db_password'); // ce ați stabilit la crearea BD define('DB_CHARSET', 'utf8mb4'); // lăsați // --- Aplicație --- define('APP_URL', 'https://monitor.example.com'); // adresa panoului, fără slash la final define('TIMEZONE', 'Europe/Bucharest'); // fusul dumneavoastră orar // --- Durata sesiunii --- define('SESSION_LIFETIME', 28800); // inactivitate până la reautentificare, sec (28800 = 8 h)

Baza de date. Datele de conectare la MySQL/MariaDB:

  • DB_HOST — gazda SGBD, aproape întotdeauna localhost;
  • DB_NAME — numele bazei de date a panoului;
  • DB_USER — utilizatorul BD (acces doar la propria bază);
  • DB_PASS — parola acestui utilizator;
  • DB_CHARSET — codificarea conexiunii, lăsați utf8mb4.

Aplicație.

  • APP_URL — adresa completă a panoului (ex. https://monitor.example.com). Trebuie să coincidă cu domeniul pe care este activată licența — altfel cheia va fi respinsă (vezi secțiunea „Licență”);
  • TIMEZONE — fusul orar PHP: influențează doar modul în care panoul afișează datele și orele. Asupra momentului rulării sarcinilor cron nu influențează — acolo se aplică fusul sistemului (vezi „Toate sarcinile cron”).

Durata sesiunii. SESSION_LIFETIME — timpul de expirare al inactivității sesiunii, în secunde (glisant: se reînnoiește la activitate). Implicit 28800 = 8 ore; după acest timp de inactivitate panoul va cere reautentificarea. De exemplu, 3600 = 1 oră, 86400 = o zi.

Înregistrarea erorilor. Erorile nu sunt niciodată afișate vizitatorilor, ci sunt scrise în logs/php_errors.log — se văd pe pagina „Jurnale aplicație”. Aceste linii (display_errors=0, log_errors=1, calea error_log) de obicei nu trebuie modificate — setările sunt definite direct în fișier și nu depind de php.ini.

config.php — fișier secret. Conține parola BD. Se află în rădăcina panoului (lângă public/), iar la acest panou rădăcina web (DocumentRoot) este chiar rădăcina panoului, nu public/. Fișierul în sine nu „se scurge”: în .htaccess din rădăcină există o interdicție explicită pentru el (Require all denied) — serverul returnează 403. Chiar și fără această regulă, codul sursă nu s-ar scurge: este PHP — serverul îl execută, nu îl livrează ca text. Pentru orice eventualitate: nu îl publicați în depozite publice și nu îl trimiteți la suport cu parola reală. Drepturile fișierului — 640.
La migrare sau la restabilirea accesului, acest fișier este principala sursă de date de conectare: numele BD, utilizatorul și parola se preiau chiar de aici (vezi secțiunile „Actualizarea și migrarea panoului” și „Restabilirea accesului”).

09. API doar pentru citire (JSON și Prometheus)

Panoul este protejat de autentificarea cu parolă și, dacă este activat, de filtrul IP. Unui program nu îi convine niciuna dintre ele, așa că sistemele externe au propria intrare — două adrese doar pentru citire, protejate cu un token. Tokenul nu schimbă nimic pe server: cu el se pot doar obține indicatori.

Activare — „Setări” → „API doar pentru citire”: comutatorul „Activați API-ul”; tokenul este creat automat la prima salvare. Butonul „Token nou” emite altul și, în aceeași clipă, deconectează pe toți cei care îl foloseau pe cel anterior.

  • /api/status.php — JSON: scorul de securitate, starea modulelor, ultima măsurătoare de încărcare cu procesele de top și evenimentele de securitate din ultimele 24 de ore;
  • /api/metrics.php — aceleași cifre în formatul text Prometheus (metrici arciveo_*: arciveo_security_score, arciveo_fail2ban_banned, arciveo_cpu_busy_pct și așa mai departe).
curl -H "Authorization: Bearer <TOKEN>" https://monitor.example.com/api/status.php curl -H "Authorization: Bearer <TOKEN>" https://monitor.example.com/api/metrics.php

Dacă clientul dumneavoastră nu poate seta un antet, tokenul este acceptat și ca parametru — ?token=…. Rețineți că în această formă ajunge în jurnalul de acces al serverului web, deci antetul este de preferat.

Prometheus. Un bloc gata făcut pentru scrape_configs:

scrape_configs: - job_name: arciveo metrics_path: /api/metrics.php scheme: https authorization: credentials: <TOKEN> static_configs: - targets: ['monitor.example.com']
Răspunsul este păstrat în cache 5 minute: în spatele acestor cifre stă aceeași interogare grea a instrumentelor ca în spatele tabloului de bord. Interogarea frecventă nu costă deci nimic serverul — valorile pur și simplu se repetă până la următoarea reîmprospătare — dar nici un interval mai mic de un minut nu are sens.
401 — tokenul este greșit sau nu a fost transmis. 404 cu textul api disabled — API-ul este dezactivat în setări. Cererile respinse sunt scrise în jurnalul aplicației împreună cu IP-ul expeditorului, așa că încercarea de a ghici tokenul se vede pe pagina „Jurnale aplicație”.

10. Prezentarea serverelor în contul personal

Panoul se instalează pe fiecare server separat și răspunde bine la întrebarea „cum stă această mașină”. La întrebarea „cum stau toate mașinile mele” nu răspunde — de aceea fiecare panou poate trimite un rezumat scurt către contul personal, unde serverele se văd într-o singură listă.

Ce luați din contul personal my.arciveo.com → secțiunea „Serverele mele”: acolo se emite un token (de forma fk_…) și este indicată adresa destinatarului https://my.arciveo.com/api/fleet.php. Tokenul este unul singur pe cont — același pentru toate serverele dumneavoastră.

Ce configurați în panou — „Setări” → „Prezentarea serverelor”: adresa destinatarului, tokenul, numele serverului (cum va fi trecut în listă; lăsat gol, se ia domeniul panoului) și comutatorul „Trimiteți rezumatul”. Butonul „Trimite acum” verifică legătura imediat, fără să aștepte orarul.

Mai departe rezumatul pleacă prin cron, la fiecare 15 minute:

# la fiecare 15 minute — rezumatul serverului către contul personal 3,18,33,48 * * * * /usr/local/bin/fleet-push-all.sh >> /path/to/monitor/logs/cron.log 2>&1

Ce anume pleacă: numele și domeniul serverului, sistemul de operare, timpul de funcționare, versiunea PHP; scorul de securitate; starea modulelor (UFW, numărul de jail-uri Fail2ban, blocările de azi, dacă rulează CrowdSec, Suricata și Falco, amenințările ClamAV, modificările AIDE, indicele Lynis, actualizările în așteptare, dacă este nevoie de repornire, zile până la expirarea certificatului, numărul de porturi deschise periculoase); ultima măsurătoare de încărcare (load, CPU, RAM, disc, inode); contoarele de evenimente din ultimele 24 de ore și ultimul eveniment critic. Jurnale, parole sau conținut de fișiere nu există în rezumat.

Trimite serverul, contul personal nu întreabă. De aceea nu trebuie deschisă nicio intrare din exterior către rețeaua dumneavoastră, iar un panou închis cu filtru IP raportează totuși. Nodul este recunoscut după domeniul panoului, așa că lista nu se dublează după o repornire sau o schimbare de IP.
Un server marcat în cont ca tăcut înseamnă că rezumatul nu a mai sosit de mult. Verificați pe rând: comutatorul „Trimiteți rezumatul”, linia din cron (sudo crontab -l) și ce răspunde butonul „Trimite acum”. Erorile de trimitere sunt scrise în jurnalul aplicației cu eticheta FLEET — acolo se vede dacă tokenul a fost respins (HTTP 401) sau dacă însăși conexiunea nu s-a realizat.

Instrumente de securitate

11. Firewall UFW

UFW (Uncomplicated Firewall) — o interfață simplă pentru nftables/iptables. Închide toate porturile de intrare, cu excepția celor permise explicit. Pagina „Firewall UFW” afișează starea și regulile.

sudo apt install ufw # Permite SSH (obligatoriu ÎNAINTE de activare!) și web sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Închide BD din exterior (acces doar local) sudo ufw deny 3306 # Activează și verifică sudo ufw enable sudo ufw status verbose
Înainte de ufw enable permiteți obligatoriu SSH (ufw allow OpenSSH), altfel veți pierde accesul la server.
„Expunere externă” de pe dashboard ține cont de UFW: un port închis printr-o regulă deny nu este considerat accesibil din exterior.
Skipping adding existing rule — nu este o eroare. Astfel UFW anunță că exact aceeași regulă există deja și nu o adaugă din nou. La reluarea autoconfigurării (care este idempotentă) acesta este un mesaj normal — nu trebuie să reacționați.

12. Instalarea Fail2ban

Blochează automat adresa IP după depășirea numărului de încercări eșuate de autentificare. Analizează jurnalele SSH, nginx, Apache și ale altor servicii.

sudo apt install fail2ban sudo systemctl enable --now fail2ban # Verifică starea: sudo fail2ban-client status
Configurația funcțională (jail.local cu zeci de jail-uri și autoban din ipsum) — în secțiunea următoare.

13. Configurație funcțională Fail2ban + ipsum

Instalarea de bază — mai sus. Aici — configurația funcțională care oferă zeci de jail-uri active și mii de blocări: setări generale, jail-urile cheie și banarea automată a IP-urilor malițioase din lista ipsum.

Fișierul /etc/fail2ban/jail.local — setări generale și cele mai importante jail-uri:

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # Ban progresiv: fiecare recidivă — mai lung bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # ban permanent pentru brute-force SSH findtime = 3600 # Recidiviști: cine a prins mai multe ban-uri — se banează definitiv [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 # Servicii web (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog): [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … și restul jail-urilor pe servicii (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) — enabled = true
În ignoreip treceți obligatoriu propriul IP și rețelele de încredere, altfel vă puteți bana pe dumneavoastră înșivă. După modificări: sudo fail2ban-client reload.

Încărcarea automată a listei de blocare ipsum — în cronul root (sudo crontab -e): level 1 (100+ mii de IP-uri) se încarcă în setul ipsum, care este blocat pe firewall (mai multe — în secțiunea „Listă de blocare IPset”):

# 04:00 — actualizare ipset ipsum (level 1, acoperire maximă): 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
Setul trebuie să se numească ipsum — exact pe acesta îl citește dashboardul (cardul „IPset ipsum”). Niveluri: levels/1.txt — acoperire maximă, levels/3.txt — mai precis (3+ surse).

De ce „Monitorul de securitate” este împărțit în două zone. Protecția funcționează pe două niveluri, iar dashboardul nu le amestecă:

  • Atacuri reale (reactiv) — tot ce a prins fail2ban: încercări reale de intruziune (jail-urile sshd, apache-*, nginx-* etc.) și recidiviști periculoși (jail-ul recidive — cei care au fost deja banați de mai multe ori). Acestea sunt IP-uri care chiar au încercat să vă spargă serverul — apar pe harta atacurilor și în „Cronologie”.
  • Blocare preventivă (proactiv) — lista publică de blocare a IP-urilor malițioase cunoscute ipset ipsum, blocată pe firewall prin regula DROP. Aceste adrese, în majoritate, nici nu au atins serverul dumneavoastră — sunt blocate din timp; contorul „IPset ipsum” arată câte au fost blocate preventiv.

Diferența e simplă: reactiv — „aceștia au atacat și au primit ban”, preventiv — „aceștia au fost blocați încă înainte de încercare”. Anterior, în recidive se introducea artificial list-3 ipsum (de aici vechea împărțire „recidive de listă”); acum recidive conține doar recidiviști reali, iar preventivul este integral pe firewall.

14. Listă de blocare IPset (ipsum)

ipsum — listă publică de IP-uri periculoase, actualizată zilnic. Monitorul afișează numărul de adrese încărcate pe dashboard și pe harta atacurilor și îl ia în calcul în Scorul de securitate (−10 dacă setul nu este încărcat).

Varianta minimă fără fail2ban — un set separat ipsum cu blocare prin iptables:

# Creează setul (o singură dată): sudo ipset create ipsum hash:ip # Script de actualizare /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, zilnic la 4:00): 0 4 * * * /usr/local/bin/update-ipsum.sh
Varianta extinsă cu fail2ban-recidive — în secțiunea „Configurația funcțională Fail2ban + ipsum”.
ipset trăiește în memorie și se pierde la repornire. Un simplu cron zilnic va lăsa setul gol din momentul reboot-ului până la următoarea rulare (dashboard-ul va afișa 0). Încărcați setul și la pornire — mutați încărcarea într-un script și legați-l de @reboot. În același timp, comanda create … -exist stabilește limita maxelem 300000 (implicit 65536 — level 1 nu încape, va apărea „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 — zilnic la 04:00 ȘI la fiecare pornire: 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
Cum funcționează la instalarea automată. Scriptul încarcă lista completă level 1 (100+ mii de IP-uri) în setul ipsum și, dacă firewallul este gestionat de programul de instalare (VPS nou — profilurile „Completă”/„Ușoară”), conectează setul la UFW printr-o regulă DROP — traficul de la aceste IP-uri este blocat efectiv. Regula este plasată după ESTABLISHED,RELATED, așa că conexiunile curente (inclusiv SSH-ul dumneavoastră) nu se întrerup — se blochează doar conexiunile noi din listă. Setul se restaurează la pornirea serviciului ipsum-load.service înainte de firewall (altfel UFW nu ar porni) și se actualizează prin cron la 04:00. Pe un server deja configurat (panou, firewall propriu), programul de instalare nu atinge firewallul — acolo ipsum rămâne o listă pentru dashboard și harta atacurilor, iar regula DROP se adaugă manual la dorință (varianta minimă cu iptables … --match-set ipsum … -j DROP — mai sus). La instalarea automată nu trebuie făcut nimic manual.

15. Instalarea CrowdSec

Înlocuitor modern pentru Fail2ban cu threat intelligence colectiv: blocări de la comunitate plus reguli proprii. Necesită un bouncer separat pentru a aplica blocările la firewall.

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # Bouncer pentru iptables/nftables: sudo apt install crowdsec-firewall-bouncer-iptables # Verifică starea: sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
Starea „Nepornit” în panou = pachetul este instalat, dar serviciul nu este activ (monitorul îl verifică prin systemctl is-active crowdsec). Pornire: sudo systemctl enable --now crowdsec; la cădere consultați sudo journalctl -u crowdsec -n 30. Aceeași regulă pentru orice serviciu în starea „Nepornit” (Suricata, Falco, Monit, MySQL).
„0 scenarii” sau „0 bouncers” pe dashboard. CrowdSec vine implicit aproape gol — fără colecții nu detectează nimic, iar fără un bouncer înregistrat blocările nu se aplică la firewall. Instalați colecțiile de bază și asigurați-vă că bouncer-ul apare în listă:
# Colecții de bază (Linux + SSH + server web): sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # Bouncer-ul trebuie să fie în listă și cu starea de conexiune activă: sudo cscli bouncers list
În jurnalul bouncer-ului stream halted / blocările nu se aplică. Este o cheie api orfană: bouncer-ul a fost șters din cscli bouncers list, dar cheia lui veche a rămas în /etc/crowdsec/bouncers/*.yaml. Reînregistrați bouncer-ul și treceți cheia nouă:
sudo cscli bouncers add fw-bouncer # va afișa noul api_key # treceți această cheie în api_key: în /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml sudo systemctl restart crowdsec-firewall-bouncer
Instalarea automată (profilul „Protecție completă”) instalează singură colecțiile și înregistrează firewall-bouncer — manual acest lucru este necesar doar la instalarea manuală sau după o intervenție manuală în CrowdSec.

16. Instalarea AIDE

AIDE (Advanced Intrusion Detection Environment) realizează o imagine a sistemului de fișiere și, la fiecare verificare, raportează modificările din /etc, /bin, /usr. După instalare este obligatorie inițializarea bazei (aideinit).

sudo apt install aide # Inițializarea bazei (5–15 minute): sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04: directorul /var/lib/aide se creează în modul 700 (proprietar _aide), # iar panoul (www-data) nu vede baza → afișează „Neinițializat”. # Deschideți directorul pentru traversare (fișierele bazei rămân 600): sudo chmod 755 /var/lib/aide # Prima verificare CU SCRIERE în jurnalul pe care îl citește monitorul. # Pe Ubuntu/Debian aide necesită --config explicit (altfel „missing configuration”; # binarul aide.wrapper nu mai este livrat în versiunile noi): sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
În timpul aideinit terminalul rămâne 5–15 minute pe linia Running aide --init... — este normal (hashing-ul întregului sistem de fișiere, încărcare pe disc). Nu întrerupeți cu Ctrl+C. Dacă procesul „stă blocat”, dar nu scrie nimic — probabil așteaptă un răspuns la o solicitare ascunsă Overwrite existing aide.db.new [Yn]? (apăsați Y). Verificați activitatea dintr-o altă sesiune: pgrep -af aide.
Eroare aideinit: „21_aide_spamassassin … printf: invalid number” (return code 20) — un bug cunoscut al fragmentului de configurare AIDE în Ubuntu 22.04. Baza nu se creează. Scoateți fragmentul defect și reîncercați:
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
Stările pe dashboard. „Neinițializat” = panoul nu vede fișierul bazei: fie aideinit nu a fost rulat, fie (Ubuntu 24.04) directorul /var/lib/aide a fost creat în modul 700 și este inaccesibil pentru www-data — se rezolvă cu sudo chmod 755 /var/lib/aide (vezi blocul de mai sus). „Nicio verificare” = baza există, dar verificarea nu a fost încă efectuată — nu este o eroare. Monitorul citește rezultatele din /var/log/aide/aide.log.
Verificare periodică → jurnal pentru panou. Standardul /etc/cron.daily/aide pe versiunile noi de Ubuntu/Debian poate să nu scrie /var/log/aide/aide.log în forma necesară (iar aide.wrapper nu mai există în ele). Mai sigur este să adăugați propriul cron cu --config explicit — acesta scrie jurnalul de la root în modul 644, iar monitorul îl citește fără grupuri suplimentare:
# sudo crontab -e — verificare zilnică la 02:00: 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # Rulați acum, fără a aștepta programarea: sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Instalarea automată face deja toate acestea: chmod 755 /var/lib/aide și cronul de verificare la 02:00 — nu este nevoie de nimic manual.
Prima inițializare faceți-o pe un server curat — înainte de instalarea aplicațiilor web. După modificări legitime, recreați baza: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.

17. Instalarea ClamAV

Scaner antivirus pentru Linux. Este util mai ales pentru verificarea /var/www în privința shell-urilor PHP și a codului malițios.

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # Actualizează baza de semnături: sudo freshclam # Scanează un folder manual: sudo clamscan -r /var/www --infected
Demonul clamd afișează „Inactiv” după enable --now? Trei cauze tipice:

1. În configurație a rămas linia Example — clamd refuză să pornească atât timp cât aceasta există:

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

2. Baza de semnături nu a fost descărcată — clamd nu pornește fără ea:

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

3. Pur și simplu se încarcă — clamd încarcă ~8 mln. de semnături în memorie în 30–60 sec. Așteptați și verificați: systemctl is-active clamav-daemon (statusul activating → încă se încarcă).

Diagnosticare: sudo journalctl -u clamav-daemon -n 30 --no-pager.
Pe dashboard „Fișiere verificate: 0” / „Ultima scanare: —”? Demonul clamd doar ține semnăturile în memorie, el nu scanează nimic după un program. Panoul afișează rezultatele scanării programate, de aceea este nevoie de un cron care scanează și scrie în jurnal. Instalarea automată pune wrapper-ul /usr/local/bin/clamav-scan.sh și un cron la 01:30 — după prima rulare se vor completa „Fișiere verificate” și „Ultima scanare”. Porniți imediat, fără a aștepta programul: sudo /usr/local/bin/clamav-scan.sh.

18. Instalarea Linux Malware Detect (maldet)

Linux Malware Detect (LMD) — scaner de programe malițioase pentru amenințări web: shell-uri PHP, backdoor-uri web, downloadere. Folosește motorul ClamAV și îl completează cu propriile semnături.

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 # Actualizează semnăturile: sudo maldet -u # Scanează /var/www: sudo maldet -a /var/www
LMD și ClamAV funcționează bine împreună. Ultimul raport: maldet --report.
La instalare poate apărea linia update-rc.d: error: unable to read /etc/init.d/maldet — este inofensivă. maldet nu folosește init.d, actualizarea semnăturilor și scanările se lansează prin /etc/cron.daily/maldet. Dacă mai jos vedeți installation completed — totul s-a instalat.
Pe pagină LMD apare „Neinstalat”, deși este instalat? maldet nu se instalează prin apt, ci în /usr/local/maldetect, iar cu open_basedir activat prezența sa se verifică prin shell — vedeți secțiunea „Pagina este goală, deși datele există pe server”.

19. Instalarea Suricata

Sistem de detectare a intruziunilor la nivel de rețea: analizează traficul la nivel de pachete și cunoaște mii de semnături de atac. Completează ModSecurity (acela funcționează la nivel HTTP, Suricata — la nivel TCP/IP).

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # Descarcă regulile actuale: sudo suricata-update sudo systemctl enable --now suricata
Suricata este „Activ”, dar panoul nu afișează alerte / numărul de evenimente e 0? Suricata scrie în /var/log/suricata/eve.json sub root, cu modul 750 pe director, iar serverul web (www-data) nu îl poate citi. Deschideți directorul pentru parcurgere — fișierele din interior rămân protejate:
sudo chmod o+rx /var/log/suricata
Instalarea automată face acest lucru singură — nu este nevoie manual.

20. Instalarea Falco

Interceptează apelurile de sistem prin eBPF/kernel module și detectează anomalii în timp real: shell din nginx, citirea /etc/passwd de către un proces web, scriere în /bin etc.

curl -fsSL https://falco.org/repo/falcosecurity-packages.asc > /tmp/falco.asc gpg --dearmor < /tmp/falco.asc | sudo tee /usr/share/keyrings/falco-archive-keyring.gpg > /dev/null sudo chmod 644 /usr/share/keyrings/falco-archive-keyring.gpg && rm /tmp/falco.asc echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" \ | sudo tee /etc/apt/sources.list.d/falcosecurity.list sudo apt update && sudo apt install falco sudo systemctl enable --now falco
Monitorul citește evenimentele Falco prin journalctl -u falco (fără sudo — prin grupul systemd-journal). Asigurați-vă că www-data este în acest grup — vedeți „Configurarea sudo” (pct. 2) pe pagina de instalare manuală.
„0 evenimente în 24 de ore” este normal, nu o eroare. Falco este event-driven: tace cât timp totul e în ordine și scrie un eveniment doar la o anomalie (shell dintr-un proces web, citirea /etc/passwd, scriere în directoarele de sistem). Zero evenimente critice într-o zi pe un server liniștit este o stare sănătoasă.
Pentru dashboard este mai fiabilă ieșirea în fișier. Citirea prin journalctl necesită drepturi pe jurnal; pentru ca dashboardul să vadă evenimentele stabil, instalarea automată activează la Falco file_output → /var/log/falco/falco.log și setează serviciului UMask=0022 (jurnalul este citit de serverul web). Pe o instalare nouă nu este nevoie să configurați asta manual.

21. Instalarea ModSecurity (WAF)

ModSecurity — firewall web (WAF) pentru Apache sau Nginx. Blochează atacurile la nivel de aplicație: injecții SQL, XSS, traversare de căi, scanere.

# Apache: sudo apt install libapache2-mod-security2 sudo a2enmod security2 # Setul de reguli OWASP Core Rule Set: sudo apt install modsecurity-crs # OBLIGATORIU: fără acest fișier motorul de reguli este dezactivat 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 # Verificare: trebuie să returneze 403 curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
Instalarea pachetului în sine nu protejează cu nimic. Apache include configurațiile prin linia IncludeOptional /etc/modsecurity/*.conf, iar pachetul pune doar modsecurity.conf-recommended — care nu se încadrează în masca *.conf. Dacă nu îl copiați în modsecurity.conf, SecRuleEngine rămâne Off: modulul este încărcat, regulile CRS sunt încărcate, dar traficul nu este verificat și jurnalul de audit nu este creat. Modul intermediar DetectionOnly doar scrie evenimentele în jurnal, fără a bloca cererile — panoul îl afișează cu galben.

Accesul panoului la jurnalul de audit. Jurnalul /var/log/apache2/modsec_audit.log aparține root (drepturi 640), utilizatorul web nu îl poate citi. Panoul preia datele printr-un wrapper — creați-l:

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 # în /etc/sudoers.d/monitor (utilizatorul = cel sub care rulează PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
Wrapper-ul preia ultima directivă SecRuleEngine fără indentare: liniile indentate se află în interiorul blocurilor <LocationMatch>/<Directory> (de exemplu, dezactivarea WAF pentru phpMyAdmin) și nu definesc modul global.
Utilizatorul din sudoers trebuie să coincidă cu utilizatorul pool-ului FPM: pe un Apache/Debian obișnuit acesta este www-data, în HestiaCP pool-ul site-ului rulează de la proprietarul site-ului (de exemplu, admin) — verificați cu grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.
Dacă site-ul se află în spatele unui proxy Nginx (HestiaCP), Apache vede ca client proxy-ul însuși — panoul preia IP-ul real al atacatorului din antetul X-Forwarded-For. În statistici ajung doar tranzacțiile cu o regulă declanșată: directiva SecAuditLogRelevantStatus scrie în jurnalul de audit orice răspunsuri 4xx/5xx, așa că acolo ajung și obișnuitele 403/500 — panoul nu le consideră evenimente WAF.
Blocul ---RULES--- este necesar secțiunii „Toate regulile active” — panoul afișează nu doar regulile declanșate, ci în general toate regulile CRS încărcate + cele personalizate. Cele trei căi din bucla for f in … sunt locurile tipice pentru regulile CRS și adăugările locale; dacă aveți altă structură (pachetul pune fișierele în propriul director sau regulile personalizate nu se află în /etc/modsecurity/custom-rules.conf), găsiți căile reale cu comanda sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null și introduceți-le în listă. Dacă wrapper-ul este vechi (fără această secțiune) — secțiunea va afișa doar avertismentul „indisponibil”, restul paginii funcționează ca înainte.

22. Instalarea Auditd

Auditd (Linux Audit Daemon) înregistrează apelurile de sistem la nivelul nucleului: autentificări și deconectări, comenzi sudo, tentative de autentificare eșuate, modificări ale fișierelor. Monitorul afișează autentificările, tentativele eșuate și comenzile sudo de astăzi.

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # Verificați starea și evenimentele: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
Monitorul citește evenimentele prin ausearch (/usr/sbin/ausearch) și, la nevoie, din /var/log/audit/audit.log cu comanda tail. Ambele trebuie să fie în sudoers.

23. Instalarea Monit

Supraveghează serviciile (nginx, php-fpm, mysql etc.) și le repornește la cădere. Poate trimite alerte pe email.

sudo apt install monit sudo systemctl enable --now monit # Fișiere de configurare: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
Monitorul obține lista serviciilor prin monit status. În /etc/monit/monitrc trebuie activată interfața HTTP (blocul set httpd cu allow localhost), altfel monit status va returna o eroare.
Pe dashboard apare „0 servicii sub supraveghere”? Două cauze. (1) Interfața HTTP este dezactivată — în monitrc linia set httpd este comentată (implicit apare ca # set httpd port 2812 …). Decomentați blocul și permiteți localhost. (2) Doar httpd activat nu supraveghează nimic — Monit ia în calcul doar ceea ce este descris prin stanțe check; fără ele lista este goală chiar și cu interfața funcțională. Configurație minimă funcțională:
# /etc/monit/conf.d/00-httpd — interfața HTTP pentru localhost: set httpd port 2812 use address localhost allow localhost # exemple de stanțe check (ce se supraveghează): 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 # verifică sintaxa (Control file syntax OK) sudo systemctl reload monit sudo monit status
Instalarea automată pune un conf.d gata pregătit, cu httpd pe 2812 și un set de verificări — pe o instalare nouă nu este nevoie de configurare manuală.
Serviciul are statusul „Cu erori”? Monitorul doar afișează starea și, în mod intenționat, nu repornește serviciile din panoul web (ar însemna execuție la distanță de comenzi root într-un panou de securitate). Diagnosticarea și repornirea — prin SSH cu Monit:
sudo monit status <service> # cauza erorii sudo monit restart <service> # repornire prin Monit # dacă Monit nu pornește serviciul — verificați unitatea sa proprie: sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

24. Instalarea PSAD (detectarea scanării de porturi)

PSAD analizează jurnalul iptables și detectează scanările de porturi și atacurile de rețea, atribuind fiecărei surse un nivel de amenințare (1–5). Completează fail2ban și Suricata.

sudo apt install psad # PSAD citește jurnalul iptables — trebuie activată jurnalizarea (UFW o face singur). # Pentru iptables pur, adăugați reguli LOG în lanțurile INPUT/FORWARD. sudo psad --sig-update sudo systemctl enable --now psad
Monitorul citește datele prin psad --Status (necesar în sudoers). Fără jurnalizarea iptables, pagina va fi goală — este normal, atât timp cât nu au existat scanări.

25. AppArmor / SELinux (control acces)

Mandatory Access Control limitează fișierele și resursele pe care le poate accesa un program, chiar dacă acesta a fost compromis. În Ubuntu/Debian se folosește implicit AppArmor (de obicei deja instalat și activ).

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # verifică profilurile
Monitorul citește starea prin aa-status (necesar în sudoers). Afișează numărul de profiluri în modul enforce/complain și procesele fără profil.

„Profiluri încărcate” mai multe decât enforce + complain — este normal. În AppArmor 4.x (Ubuntu 24.04 și mai nou) a apărut modul unconfined: profilul este încărcat în nucleu, dar nu limitează nimic. Ubuntu marchează astfel zeci de profiluri pentru programe care folosesc user namespaces (browsere, clienți torrent și altele asemenea). Când există astfel de profiluri, cardul „Profiluri încărcate” devine chihlimbariu și le afișează numărul — de exemplu unconfined: 90 la 120 încărcate și 26 în enforce. Protejează cu adevărat doar profilurile în enforce; pe Ubuntu 22.04 (AppArmor 3.x) acest mod nu există și cifrele se potrivesc întotdeauna.

sudo aa-status | grep -E "profiles are" # defalcare pe moduri sudo aa-enforce /etc/apparmor.d/profile-name # trece profilul în enforce
Trecerea în enforce a profilurilor pe care Ubuntu le-a lăsat intenționat în unconfined merită făcută doar în cunoștință de cauză: ele nu sunt dezactivate din greșeală, ci pentru că altfel se strică funcționarea programelor înseși. Profilurile în complain sunt altă poveste: acolo regulile sunt deja scrise și doar nu se aplică.

26. Instalarea debsums (integritatea pachetelor)

debsums verifică dacă fișierele pachetelor instalate corespund sumelor de control din depozit — depistează binarele de sistem înlocuite (completează AIDE). Verificarea completă durează 1–2 minute, de aceea rulează prin cron, iar panoul citește rezultatul din data/debsums/debsums.log și îl împarte singur pe categorii (contează doar binarele și bibliotecile).

Sarcina se pune în cron-ul root (sudo crontab -e). Wrapper-ul gata pregătit debsums-scan.sh se pune în /usr/local/bin/ (chmod +x; vezi rezumatul sarcinilor cron) și scrie singur raportul în data/debsums/ al panoului.

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

Wrapper-ul debsums-scan.sh găsește singur data/ al panoului — nu trebuie să specificați calea.

Modificările din /etc/ (configurări) și /usr/share/ (resurse) de pe server sunt de obicei normale — panoul le marchează cu o culoare separată. Îngrijorătoare sunt modificările binarelor și bibliotecilor (/bin, /sbin, /usr/lib etc.) — cardul „Binare / biblioteci” le arată exact pe acestea.

27. Configurarea rapoartelor Lynis

Lynis se pornește manual sau prin cron. Raportul trebuie salvat în folderul data/lynis/ al proiectului — monitorul citește fișierul lynis-report.dat.

# Rulare unică (introduceți propria cale către rădăcina panoului): sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # Audit zilnic — linie cron (wrapper-ul lynis-scan.sh gata pregătit în /usr/local/bin/, vezi rezumatul): 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
După prima rulare, pagina „Audit Lynis” va afișa imediat hardening index-ul, avertismentele și recomandările.
Butonul „Pornește auditul” de pe pagina Lynis. Acesta rulează lynis-scan.sh în fundal direct din panou (fără a aștepta cron-ul): afișează „Se scanează…” și la finalizare actualizează singur raportul. Pentru aceasta, utilizatorul web are nevoie de o linie sudoers pentru rularea scriptului — programul de instalare o adaugă automat în /etc/sudoers.d/monitor. Dacă panoul a fost instalat manual/mai demult, adăugați-o cu același utilizator care este deja indicat în fișier:
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. Configurarea rapoartelor Logwatch

Logwatch trebuie să salveze rapoartele zilnice în folderul data/logwatch/ al proiectului, în format .txt. Monitorul afișează ultimul raport și arhiva.

# Zilnic (6:00) — linie cron (wrapperul gata logwatch_daily.sh în /usr/local/bin/, vezi sumarul): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

Module panou

29. Monitor de rețea (integrat)

Monitorul de rețea nu necesită instalare — este o pagină integrată în panou. Ea afișează starea rețelei serverului din surse locale:

  • interfețe și trafic — din /proc/net/dev;
  • starea legăturilor (UP/DOWN) și IP — prin ip;
  • conexiuni și porturi în ascultare — prin ss;
  • evenimente de rețea ale nucleului din ultimele 24 h — prin journalctl -k.

Primele trei surse funcționează fără sudo, de aceea interfețele, traficul, conexiunile și porturile sunt vizibile imediat. Blocul „Evenimente ale nucleului” folosește journalctl -k — se citește prin grupul systemd-journal („Configurarea sudo”, pct. 2), sudo nu este necesar. Verificați că totul este accesibil pentru utilizatorul web:

# Verificare în numele www-data (sub el rulează 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
Blocul „Evenimente de rețea ale nucleului” afișează evenimentele stivei de rețea a nucleului (schimbarea legăturii up/down, erori de purtătoare, „network unreachable”). Înregistrările firewall-ului UFW BLOCK nu ajung aici — ele se află pe paginile „Firewall UFW” și „Harta atacurilor”. Un bloc gol cu bifă verde = în ultimele 24 h nu au existat defecțiuni de rețea.

30. Disc și SMART

Pagina integrată afișează trei lucruri:

  • Sisteme de fișiere — gradul de ocupare al partițiilor (df); scala devine roșie la ≥90%;
  • Unități de stocare — lista discurilor (lsblk), doar cele reale (loop/snap sunt ascunse);
  • Sănătate (SMART) — starea discului și atributele (smartctl).

Spațiul și lista dispozitivelor funcționează imediat, fără configurare. Pentru SMART este necesar pachetul smartmontools. Procesul web nu are acces direct la dispozitivele de disc, de aceea SMART este preluat prin cron în fișierul data/disk/smart.txt, iar panoul îl citește.

Sarcina se pune în crontabul root (sudo crontab -e). Wrapperul gata făcut smart-scan.sh se plasează în /usr/local/bin/ (chmod +x; vedeți sumarul sarcinilor cron) și scrie singur în data/disk/ al panoului.

sudo apt install smartmontools # Linia cron (la fiecare 30 de minute): */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

Wrapperul smart-scan.sh găsește singur data/ al panoului — nu este nevoie să specificați calea. În interior, lsblk -e7,11 exclude loop/cdrom.

Pe discuri virtuale (QEMU/KVM și similare) este de obicei disponibil doar statusul general „sănătate: OK”, iar temperatura, orele de funcționare și sectoarele realocate pot fi goale — este normal. Pe un server fizic sunt afișate toate atributele.

31. Performanță (CPU/RAM/Rețea/Disc)

Pagina afișează istoricul încărcării serverului din ultimele 24 de ore — Load Average, ocuparea CPU și așteptarea I/O, RAM/Swap, traficul de rețea (recepție/transmisie), I/O de disc (citire/scriere), umplerea discului și inodes, descriptorii de fișiere deschiși și conexiunile MySQL, plus numărul curent de conexiuni TCP și procese. Fereastra de 24 de ore este cea implicită: butoanele de sus o comută pe 7 zile sau 30 de zile.

Datele sunt colectate de cron/collect_metrics.php — o dată la 5 minute scrie un instantaneu „brut” al contoarelor (/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') în tabelul BD system_metrics; procentele și vitezele sunt calculate de pagină însăși pe baza diferenței dintre instantaneele vecine (umplerea discului/inodes/descriptori/conexiuni MySQL — valori instantanee, fără recalculare). Sudo nu este necesar — sursele se citesc fără drepturi root.

Două adâncimi de păstrare. Capturile „brute” de cinci minute trăiesc o zi (plus o oră de rezervă, ca punctul cel mai vechi de pe grafic să aibă încă un predecesor pentru calcul). Fiecare oră încheiată este pliată într-un singur rând; această serie orară se păstrează 30 de zile și tocmai pe ea o citesc vizualizările „7 zile” și „30 de zile”. O lună de istoric costă astfel aproximativ 720 de rânduri în loc de circa 8600.

Pe perioadele de 7 și 30 de zile sunt arătate medii orare, nu măsurători individuale — un vârf de cinci minute se netezește la această scară, iar pagina avertizează despre asta. Pentru Load Average rândul orar păstrează atât media, cât și vârful orei, așa că graficul arată ambele linii. Imediat după o actualizare, perioadele lungi sunt goale („încă nu există istoric pentru această perioadă”) — seria orară începe să se adune din acel moment.
# Linie Cron (la fiecare 5 minute): */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

Wrapper-ul collect-metrics-all.sh (vezi sumarul sarcinilor cron) găsește singur toate instanțele panoului instalate pe server și rulează cron/collect_metrics.php al fiecăreia în numele proprietarului site-ului.

Până când colectorul nu rulează cel puțin de două ori (primele ~10 minute după instalare), pagina afișează „se colectează datele” — graficele au nevoie de cel puțin o pereche de puncte vecine pentru a calcula vitezele și procentele.

Procesele de top. Sub grafice sunt două tabele cu cele cinci procese care consumă cel mai mult CPU:

  • „Procese la ultima măsurătoare” — ce încărca serverul în momentul ultimei capturi (ora este în antetul cardului);
  • „Procese la vârful zilei” — aceleași cinci, dar luate la măsurătoarea cu cel mai mare Load Average din ultimele 24 de ore. Acesta este răspunsul la întrebarea „ce a fost asta noaptea”, când vârful de pe grafic a trecut demult.

Ambele tabele descriu întotdeauna ultimele 24 de ore, chiar și când graficele arată o lună: captura proceselor o poartă doar rândurile de cinci minute, iar acestea nu se păstrează mai mult de o zi.

Procesele proprii ale măsurătorii (ps, scriptul PHP de colectare și lanțul cron/sudo de deasupra lui) sunt excluse din listă — altfel ar ocupa primele rânduri în fiecare captură: ps, în momentul pornirii, arată aproape 100 % CPU. Celelalte sarcini cron pornite în același minut apar — exact pentru asta există tabelul.

Alerte de încărcare (secțiunea „Setări” → „Alerte de încărcare”) — la depășirea pragului pentru CPU/RAM/disc/inode panoul trimite o notificare pe toate canalele activate (Telegram, Email, webhook, push — aceleași ca la raportul zilnic; pentru alerte nu trebuie activat nimic separat) și încă una când valoarea revine la normal. Cât timp pragul este menținut, nu trimite repetat: următoarea notificare vine abia după un ciclu „a revenit la normal → a fost depășit din nou”.

Pragurile sunt verificate de același collect_metrics.php la fiecare rulare (o dată la 5 minute) — nu este nevoie de un cron separat. Starea „deja notificat / încă nu” este stocată în data/alerts_state.json, iar pragurile — în setările panoului.

32. Evenimente de securitate (un senzor la fiecare 5 minute)

Raportul zilnic răspunde la întrebarea „cum stă serverul”. Senzorul răspunde la alta — „ce tocmai s-a întâmplat”. La fiecare 5 minute interoghează sursele rapide și înregistrează doar ce s-a schimbat față de verificarea anterioară; pagina „Evenimente de securitate” din meniu arată lista descoperirilor.

Verificările lente — Lynis, debsums, actualizările de pachete, valabilitatea SSL — nu sunt atinse de senzor: cifrele lor se schimbă oricum o dată pe zi și rămân în sarcina raportului zilnic.

Ce observă — nouă surse:

  • Fail2ban — blocări noi (se compară lista adreselor blocate, deci o deblocare nu este eveniment, iar o blocare în vigoare este raportată exact o dată);
  • CrowdSec — decizii noi;
  • Falco — evenimente de nivel Emergency, Alert și Critical;
  • Suricata — alerte noi; marcajul [Priority: 1] ridică descoperirea la nivel critic;
  • ClamAV — linii FOUND noi în jurnalul de scanare;
  • AIDE — modificări de fișiere conform ultimei verificări (deci nu mai des decât rulează ea după propriul orar);
  • SSH — o autentificare reușită ca root (critic) și o autentificare de la o adresă nemaiîntâlnită (avertisment);
  • Servicii de protecție — UFW, Fail2ban, CrowdSec, Suricata, Falco: un mesaj când un serviciu se oprește și unul când funcționează din nou;
  • Porturi — un port devenit accesibil din exterior; dacă în spatele lui este o bază de date sau un cache, este critic.

Un instrument care nu este instalat sau al cărui jurnal panoul nu îl poate citi pur și simplu tace — asta nu este nici eroare, nici eveniment.

Prima verificare nu trimite nimic. Fiecare sursă păstrează un semn „până unde s-a citit deja” (fișierul data/secwatch_state.json), iar prima trecere doar scrie acest semn. Altfel, la activarea supravegherii ar sosi o notificare despre tot ce au adunat jurnalele în luni de zile.

Configurare — „Setări” → „Supravegherea evenimentelor”:

  • Activați supravegherea — cât timp este oprită, sarcina cron rulează în gol și lista nu se umple;
  • Notificare începând de la — „Avertisment” sau „Critic”. Descoperirile sub prag ajung oricum în listă, dar nu și în notificări;
  • Pauză, min — intervalul minim între notificări, implicit 15;
  • Ore de liniște — o fereastră în formatul 23-7 în care avertismentele nu sunt trimise.
Descoperirile critice pleacă imediat — nu le rețin nici pauza, nici orele de liniște: tocmai pentru ele există un senzor la 5 minute. Există totuși o siguranță: cel mult 6 notificări pe oră, în caz că o sursă începe să „pâlpâie”.

Notificările merg pe aceleași canale ca raportul zilnic (Telegram, Email, webhook, push) — nu trebuie activat nimic separat. Toate descoperirile se scriu în listă indiferent dacă a plecat o notificare sau nu și se păstrează 90 de zile.

Lista evenimentelor. Filtre: perioadă (24 de ore, 7, 30 sau 90 de zile), importanță și sursă; se afișează ultimele 500 de înregistrări. Butonul „Verifică acum” pornește exact aceeași trecere ca cron — util imediat după configurare, ca să nu așteptați cinci minute; el arată și motivul când nu a plecat nicio notificare („sub pragul de importanță”, „ore de liniște”, „niciun canal de notificare configurat”). Contorul roșu de lângă elementul de meniu este numărul de evenimente critice din ultimele 24 de ore.

Fișa adresei. Un clic pe un rând din lista evenimentelor sau din blocul Adrese corelate deschide tot ce se știe despre o adresă: cronologia evenimentelor ei, țara și, chiar pe primul rând, o concluzie. Rândul din listă deschide fișa atunci când evenimentul privește exact o adresă; dacă indică mai multe, legăturile sunt chiar adresele din interiorul rândului. Concluziile sunt patru:

  • Acces posibil — de la adresă a avut loc o autentificare reușită, iar aceeași adresă a fost surprinsă și de alte protecții, ori imediat după au reacționat AIDE, ClamAV sau s-a deschis un port nou. Singurul caz care merită verificat imediat;
  • Autentificare de la această adresă — există o autentificare și nimic altceva. De obicei este proprietarul dintr-un loc nou, de aceea fișa nu se colorează în roșu;
  • Blocată — adresa a fost surprinsă și blocată: protecția și-a făcut treaba, nu este nimic de făcut;
  • Necesită verificare — există detecții, dar nu a existat blocare. Dacă adresa revine, închideți-o manual.

Sub cronologie se află blocul Ce se întâmpla pe server în același timp: evenimente care nu au deloc adresă. AIDE nu știe cine a modificat fișierul, ClamAV nu știe cine l-a adus pe cel infectat. Sunt afișate cele care se încadrează în activitatea adresei și în cele șase ore de după. Tocmai autentificarea împreună cu ce a urmat deosebește o tentativă de un acces real.

Corelarea se face după adresă. Un atacator care își schimbă adresa o rupe. Fișa arată ce au văzut protecțiile și nu își asumă să deosebească o intruziune de propria dumneavoastră autentificare dintr-un loc nou.

Orar. Sarcina se pune în crontab-ul root (sudo crontab -e), decalată cu 2 minute față de colectarea metricilor, ca cele două să nu pornească deodată:

# la fiecare 5 minute — verificarea evenimentelor de securitate 2-57/5 * * * * /usr/local/bin/security-watch-all.sh >> /path/to/monitor/logs/cron.log 2>&1
Porniți verificarea numai prin wrapper, niciodată direct cu php cron/security_watch.php. Wrapper-ul găsește toate instalările panoului de pe server și pornește verificarea în numele proprietarului site-ului — cu exact acele drepturi sudo pe care panoul le folosește din web. Un apel direct din crontab-ul root ar crea fișierul de stare data/secwatch_state.json aparținând utilizatorului root, iar butonul „Verifică acum” ar înceta să funcționeze.
Dacă serverul a fost pregătit prin instalarea automată, sarcina este deja adăugată — rămâne doar să activați supravegherea în setări.

33. Server web: cereri și coduri de răspuns

Pagina „Server web” analizează jurnalul de acces și arată câte cereri au fost, ce parte s-a încheiat cu eroare și cine le-a făcut. ModSecurity răspunde la întrebarea „ce a fost blocat”, această pagină la întrebarea „ce a ajuns în general pe site”.

Ce se vede: numărul de cereri, RPS, ponderea erorilor, un contor separat 5xx și volumul de trafic servit; un grafic al cererilor pe ore; distribuția codurilor 2xx/3xx/4xx/5xx; adrese frecvente, IP-uri frecvente, erori frecvente (cod plus cale) și clienți (User-Agent). Perioada: 24 de ore sau 7 zile.

Sursa este jurnalul de acces în formatul combined, deci se potrivesc și Nginx, și Apache, iar un modul suplimentar nu este necesar. Panoul caută jurnalul printre fișierele standard:

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

Pe lângă acestea, panoul verifică fișierele *access*.log și *requests*.log din /var/log/nginx/, /var/log/apache2/ și /var/log/httpd/. Astfel este găsit și jurnalul pe care un site îl scrie într-un fișier propriu prin directiva CustomLog — de exemplu arciveo-monitor_requests.log, creat de programul de instalare Arcivéo.

Se citește un singur jurnal: când Nginx stă în fața Apache, adunarea a două jurnale ar număra fiecare cerere de două ori. Se ia primul jurnal din lista de mai sus în care s-a scris în ultimele 24 de ore (cele găsite după tiparul numelui sunt încercate după el, de la cele mai noi); dacă în niciunul nu s-a scris de o zi, se ia cel mai recent jurnal negol. Astfel, un jurnal golit de logrotate sau rămas de la un server web oprit nu îl mai ascunde pe cel activ. Se citește ultimul fragment al jurnalului (3 MB), iar rezultatul este păstrat în cache 5 minute — pe un site încărcat perioada aleasă poate deci să nu fie acoperită în întregime; acoperirea reală este scrisă deasupra graficului.

„Panoul nu poate citi jurnalul serverului web”. Cauza sunt aproape întotdeauna drepturile: jurnalele și directorul lor aparțin grupului adm, iar utilizatorul web nu face parte din el. Programul de instalare Arcivéo îl adaugă în acest grup; după o instalare manuală, ajută un mic wrapper de la root, căruia îi este permis exact un lucru — să afișeze jurnalul (pagina ModSecurity funcționează la fel). Wrapper-ul alege jurnalul după aceleași reguli ca panoul. Comanda este afișată chiar pe pagină; iat-o și aici. Lipiți-o integral — este o singură comandă:
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

Utilizatorul web poate diferi de www-data — verificați sub ce utilizator rulează pool-ul PHP-FPM al site-ului (ps -o user= -C php-fpm) și treceți-l în linia sudoers. Dacă numele jurnalului nu conține nici access, nici requests, adăugați calea lui la începutul liniei L=… în interiorul wrapper-ului.

„Nu există înregistrări pentru perioada aleasă” — jurnalul a fost citit, dar nu s-au găsit linii potrivite în el: fie site-ul chiar nu a fost deschis, fie jurnalul tocmai s-a rotit (logrotate) și a luat-o de la capăt.

Încercări de exploatare cunoscute. Panoul compară cererile din jurnal cu semnăturile a circa douăzeci de exploit-uri răspândite: PHP-CGI, traversarea directoarelor în Apache, PHPUnit, Log4Shell, vulnerabilități ale routerelor, camerelor și gateway-urilor VPN. Pentru fiecare încercare arată dacă poate funcționa pe acest server și se bazează pe fapte: versiunile instalate de Apache și PHP, prezența Java și codul cu care serverul a răspuns la cererea fișierului. Cu roșu este marcat doar ce poate funcționa cu adevărat.

Protecția serverului web. Panoul cere fiecare site de pe server prin 127.0.0.1 și verifică ce primește un vizitator: dacă se văd versiunile serverului web și ale PHP, dacă metoda TRACE este activată și dacă există antetele de protecție Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy și Content-Security-Policy. Site-urile sunt luate din adresa la care este deschis panoul și din fișierele gazdelor virtuale, dacă panoul le poate citi. Rezultatul se actualizează la fiecare 10 minute.

Dacă lipsește ceva, sub tabel apare o singură comandă. Ea creează un fișier separat, zz-arciveo-hardening.conf, și reîncarcă serverul web doar după un test de configurare reușit; antetele pe care site-ul le trimite deja nu se dublează. CSP nu este propus într-o singură linie: politica se construiește pentru site-ul respectiv, altfel acesta nu își mai încarcă propriile scripturi, stiluri și fonturi. Anularea modificării pe Apache:

sudo a2disconf zz-arciveo-hardening && sudo systemctl reload apache2
Semnătura de pe paginile de eroare (ServerSignature) nu este marcată dacă versiunea serverului web este deja ascunsă: fără versiune, ea arată doar numele serverului și numele gazdei.

34. Harta atacurilor (GeoIP)

Pagina „Harta atacurilor” determină țara după IP cu comanda geoiplookup. Fără pachetul GeoIP țările nu vor fi determinate și punctele nu vor apărea pe hartă:

sudo apt install geoip-bin geoip-database # Verificare: geoiplookup 8.8.8.8
Sudo nu este necesar — baza /usr/share/GeoIP/GeoIP.dat poate fi citită de toți, rezultatele sunt stocate în cache în tmp/geoip_cache.json. Harta în sine (Leaflet + dale OpenStreetMap) se încarcă în browser — este nevoie de internet pe computerul unde este deschis panoul.

35. Expunere externă, actualizări și actualizări automate

Două carduri integrate în tabloul de bord care arată nu „pornit/oprit” al unui instrument, ci gradul real de protecție al serverului. Nu necesită instalare, se citesc local fără sudo.

Expunere externă — câte servicii ascultă pe toate interfețele (0.0.0.0/[::]) și sunt accesibile din exterior. Evidențiază cu roșu dacă o bază de date sau un cache este expus în exterior (MySQL, PostgreSQL, Redis, MongoDB, Memcached, Elasticsearch) — este o breșă directă (−10 la Scorul de securitate). Sursă: ss -tuln.

Dacă cardul este roșu — închideți baza de date față de exterior: legați-o la 127.0.0.1 (bind-address în configurația MySQL/PostgreSQL, bind 127.0.0.1 în Redis) sau închideți portul în UFW.
„Port deschis” ≠ „accesibil din exterior”. Un serviciu care ascultă pe 127.0.0.1 (loopback) este vizibil doar pentru serverul însuși — din exterior nu poate fi accesat, chiar dacă portul este „deschis”. De aceea Postfix pe portul 25, legat la loopback, este sigur: configurarea automată setează inet_interfaces = loopback-only (plus un smtpd_banner neutru — închide observația Lynis MAIL-8818 despre dezvăluirea versiunii). Cardul „Expunere externă” consideră expus doar ceea ce ascultă pe 0.0.0.0/[::]; serviciile loopback nu intră acolo.
Lynis MAIL-8818 manual (dacă ați instalat poșta manual): în /etc/postfix/main.cf setați smtpd_banner = $myhostname ESMTP (fără versiune și OS) și inet_interfaces = loopback-only, apoi sudo systemctl restart postfix.

Actualizări de securitate — câte patch-uri de securitate așteaptă instalarea și dacă este necesară o repornire după actualizarea kernelului (−5 la Scorul de securitate când există patch-uri). Sursă: /usr/lib/update-notifier/apt-check, fișierul /var/run/reboot-required. Lista detaliată — pe pagina „Actualizări de securitate”.

# Instalați actualizările: sudo apt update && sudo apt upgrade # Verificați ce ascultă spre exterior: ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
Cardul de actualizări funcționează pe Ubuntu/Debian (update-notifier-common). Dacă apt-check lipsește — monitorul numără patch-urile prin apt-get -s upgrade.

Actualizări automate de securitate (unattended-upgrades) — pe pagina „Actualizări de securitate” un card separat arată dacă instalarea automată a patch-urilor de securitate este activată și când a rulat ultima dată. Nu este nevoie de sudo — starea se citește prin apt-config dump.

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # activare # Verificați ce este activat: apt-config dump | grep Unattended-Upgrade

Întreținere

36. Copii de rezervă

Copia de rezervă este asigurarea principală: pierderea datelor este mai gravă decât orice atac. Aveți nevoie de două lucruri — o copie de rezervă a serverului/site-urilor și separat o copie de rezervă a bazei de date a panoului (acolo se află utilizatorii, cheile WebAuthn, setările, licența).

Varianta A — HestiaCP: fila Backup a utilizatorului → butonul de creare a copiei de rezervă (sau programat în setările serverului). Copia include site-urile și bazele lor de date.

Varianta B — manual (cron): dump al bazei de date + arhivă a directorului data/ al panoului:

# cron root (sudo crontab -e) — copie zilnică la 2:30 (introduceți propriile nume/căi): 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 # Șterge arhivele mai vechi de 14 zile: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
O copie de rezervă pe același server vă salvează de erori, dar nu și de pierderea serverului. Copiați arhivele pe un spațiu de stocare extern (alt server, S3, rclone în cloud). Verificați că restaurarea chiar funcționează.

37. Actualizarea și migrarea panoului

Actualizarea la o versiune nouă. Faceți mai întâi o copie de rezervă. Apoi reîncărcați fișierele de cod, păstrându-vă datele:

  • de suprascris (cod): public/, includes/, assets/, cron/, database/, precum și fișierele rădăcină .htaccess (controllerul frontal — rutarea nu poate rămâne din versiunea veche), manifest.json, sw.js;
  • de nu atins: config.php (datele BD), data/ (rapoarte), logs/, tmp/ (sesiuni și cache).
# După încărcare — resetați cache-ul PHP (dacă opcache este activat): sudo systemctl reload php*-fpm
FileZilla afișează SSH_FX_PERMISSION_DENIED — Permission denied. Fișierele panoului aparțin lui www-data (așa au fost setate la instalare), în timp ce clientul SFTP se conectează cu utilizatorul dumneavoastră, care nu are drept de scriere. A da lui www-data întregul panou „ca să funcționeze” este tocmai ceea ce duce la această eroare; mai jos sunt trei metode, oricare rezolvă problema.
# Varianta A (recomandată) — separați proprietarii: codul e al dumneavoastră, folderele de lucru sunt ale serverului web. # Serverul web nu primește deloc drept de scriere asupra CODULUI panoului: 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 # Varianta B — ACL peste proprietarii actuali (nu mutăm nimic): 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 # Varianta C — prin grupul www-data. Mai simplă, dar dreptul de scriere asupra # fișierelor panoului îl primește și serverul web (cu o breșă în PHP codul e înlocuibil): 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
De ce varianta A este sigură. Panoul scrie doar în trei directoare — data/ (rapoarte), tmp/ (sesiuni și cache), logs/; acestea rămân ale lui www-data. Restul este cod, iar serverului web îi este necesar doar pentru citire, pe care o oferă grupul www-data cu drepturile 644. Câștig secundar: în caz de breșă în PHP, fișierele panoului nu mai pot fi rescrise. Pe panourile de hosting (HestiaCP și similare) varianta A nu este necesară: acolo fișierele site-ului aparțin oricum contului cu care vă conectați prin SFTP, iar serverul web le citește prin grup.
Capcana variantei B: orice chmod ulterior asupra fișierelor resetează masca ACL, iar accesul dispare în tăcere. Dacă după „punerea în ordine a drepturilor” încărcarea se lovește din nou de Permission denied — repetați ambele comenzi setfacl.
Bitul 2 din varianta C este setgid: fișierele încărcate prin SFTP rămân în grupul www-data, altfel panoul nu le va putea rescrie. După varianta C reconectați-vă în FileZilla — grupul nou se aplică abia la o autentificare nouă. Verificare: id deploy (trebuie să apară grupul www-data) și ls -ld /path/to/monitor (drwxrwsr-x — litera s înseamnă că setgid este activat).

Migrarea pe alt server:

  1. Pe noul server configurați site-ul + HTTPS (vezi pagina de instalare manuală).
  2. Copiați toate fișierele panoului împreună cu config.php, data/.
  3. Migrați BD: mysqldump pe cel vechi → import pe cel nou; corectați datele BD în config.php.
  4. Repetați pe noul server: sudoers, apartenența la grupul adm, sarcinile cron.
  5. Licența este legată de domeniu — dacă domeniul este același, cheia va continua să funcționeze.

38. Un al doilea site pe acest server (încă un domeniu)

Panoul funcționează deja și pe același server mai este nevoie de un site — cu domeniu propriu, alături de monitor. După configurarea automată serverul este un Apache + PHP-FPM obișnuit, așa că site-ului îi trebuie trei lucruri: propriul director, propriul vhost și propriul certificat. Panoul rămâne neatins: el supraveghează întregul server, o a doua instanță pentru al doilea domeniu nu este necesară.

Această secțiune este pentru un server fără panou de găzduire. Dacă aveți instalat HestiaCP sau un panou similar, adăugați domeniul acolo, nu manual. Iar dacă plănuiți multe site-uri (pe deasupra și cu e-mail), instalați panoul înainte de configurarea automată: scriptul îl detectează și trece în regim aditiv, în timp ce un panou instalat ulterior reconstruiește configurația Apache, a firewall-ului și a e-mailului.
Ce înlocuiți în comenzi. site2.example.com este adresa noului site. Poate fi atât un domeniu separat (mycompany.com), cât și un subdomeniu (shop.mycompany.com) — pentru Apache nu contează, diferă doar înregistrarea DNS (pasul 1) și emiterea certificatului (pasul 5). site2 este doar un nume scurt al site-ului: așa se numesc directorul /var/www/site2 și fișierul de configurare site2.conf. Cu domeniul nu are nicio legătură, Apache ia adresa numai din ServerName; numele poate fi oricare — pentru shop.mycompany.com, de exemplu, este comod shop: director /var/www/shop, configurație shop.conf.

1. DNS. În panoul DNS de la registratorul domeniului creați o înregistrare A către IP-ul acestui server:

# Subdomeniu (shop.mycompany.com): Tip: A Nume: shop Valoare: 203.0.113.10 TTL: 3600 # Domeniu separat (mycompany.com) — două înregistrări, ca să funcționeze și adresa cu www: Tip: A Nume: @ Valoare: 203.0.113.10 TTL: 3600 Tip: A Nume: www Valoare: 203.0.113.10 TTL: 3600

După câteva minute verificați: dig +short site2.example.com trebuie să returneze IP-ul serverului.

2. Directorul site-ului.

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

3. Vhost-ul. Blocul se lipește în terminal în întregime — calea către socketul PHP-FPM este determinată automat:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # detectarea automată a socketului PHP-FPM sudo tee /etc/apache2/sites-available/site2.conf > /dev/null <<'EOF' <VirtualHost *:80> ServerName site2.example.com # Domeniu separat: ștergeți # de pe linia de mai jos, ca site-ul să se deschidă și cu www. # Pentru un subdomeniu linia nu este necesară. #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. Activați site-ul și verificați configurația înainte de reîncărcare:

sudo a2ensite site2.conf sudo apache2ctl configtest # așteptat: Syntax OK sudo systemctl reload apache2

5. SSL. certbot este deja instalat de configurarea automată; până acum DNS-ul trebuie să indice spre server:

# Subdomeniu — o singură adresă: sudo certbot --apache -d site2.example.com # Domeniu separat — direct și cu www (ambele înregistrări A de la pasul 1 trebuie să indice spre # server, iar www trebuie să fie în ServerAlias, altfel certbot nu poate verifica a doua adresă): sudo certbot --apache -d site2.example.com -d www.site2.example.com

Al treilea domeniu și următoarele se adaugă la fel — repetați pașii 1–5, schimbând domeniul și directorul.

Cine răspunde la cererile „străine”. Dacă Host nu corespunde niciunui ServerName (accesare după IP sau un domeniu străin îndreptat către serverul dumneavoastră), Apache livrează primul site activat în ordine alfabetică din /etc/apache2/sites-enabled/. Configurarea automată dezactivează 000-default standard, așa că acest rol „de serviciu” rămâne vhost-ului panoului (arciveo-monitor.conf) — asupra site-urilor deschise după numele propriu nu are efect. Dacă doriți ca după IP să se deschidă alt site, denumiți fișierul acestuia astfel încât să fie primul alfabetic (de exemplu 000-site2.conf).
Protecția este la nivelul întregului server: UFW, Fail2ban, CrowdSec, ModSecurity și Suricata funcționează deja și acoperă automat noul site, nu trebuie configurate separat. Porturile 80/443 sunt de asemenea deja deschise. Site-ul are propriile jurnale: site2_error.log și site2_access.log în /var/log/apache2/.
Nu puneți directorul noului site în interiorul directorului panoului — rădăcina web a panoului este guvernată de propriul .htaccess (controler frontal), iar un site imbricat ar returna altceva decât vă așteptați. Țineți site-urile alături: /var/www/monitor, /var/www/site2.

39. Recuperarea accesului (cheie pierdută, parolă, blocare IP)

Dacă nu vă puteți autentifica — totul se repară direct în BD de pe server. Deschideți BD (numele — din config.php):

sudo mysql MY_DB

Cheie WebAuthn pierdută (nu trece al doilea factor) — dezactivați 2FA, autentificați-vă cu parola, înregistrați o cheie nouă:

UPDATE users SET webauthn_enabled = 0;

Ați uitat parola — setați un hash nou (generați-l pe server și inserați-l):

# Generați hash-ul noii parole: php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # În BD (inserați hash-ul obținut): # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

V-ați blocat singur cu filtrul IP — dezactivați restricția:

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
Accesul la BD este mereu disponibil: sudo mysql pe server, ori phpMyAdmin / secțiunea BD din panoul de hosting. După recuperare, reactivați WebAuthn și filtrul IP.

40. Toate sarcinile cron într-un singur loc

Sumarul sarcinilor — în cron-ul root al serverului (se adaugă prin sudo crontab -e). Păstrați doar liniile instrumentelor pe care le folosiți; ajustați căile la propriul server.

# Cron-ul de server al monitorului (root) — adăugați prin: sudo crontab -e # 01:30 — scanare ClamAV pe căi periculoase (web, home, temp) → cardurile „Fișiere verificate” și „Ultima scanare” 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — verificarea integrității fișierelor AIDE (necesită explicit --config) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # la pornire — restaurează drepturile /var/lib/aide (fișierul tmpfiles al pachetului # aide-common.conf le resetează la 0700, iar panoul nu mai vede baza) @reboot chmod 755 /var/lib/aide # 03:00 — audit de securitate Lynis 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — actualizarea listei de blocare ipsum (level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # setul ipsum la pornire este ridicat de serviciul ipsum-load.service (ÎNAINTEA firewallului, altfel # UFW nu va vedea setul în before.rules) — nu cron. Aici doar reîmprospătarea zilnică de mai sus. # 06:00 — raport Logwatch 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # la fiecare 30 min — verificarea discurilor SMART */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — integritatea pachetelor debsums 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — raport programat pe Email și Telegram 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # la fiecare oră — actualizarea listelor de pachete (pentru cardul „Actualizări de securitate”) 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # la fiecare 5 min — instantaneu al resurselor (CPU/RAM/rețea/disc) pentru pagina „Performanță” */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # la fiecare 5 min, decalat față de captura de mai sus — senzorul de evenimente de # securitate: blocări noi, alerte IDS, protecție oprită, port deschis. # Cât timp supravegherea este oprită în setări, sarcina nu face nimic. 2-57/5 * * * * /usr/local/bin/security-watch-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # la fiecare 15 min — rezumatul serverului către contul personal, ca mai multe # servere să fie vizibile într-o singură listă. Cât timp rezumatul este oprit, sarcina nu face nimic. 3,18,33,48 * * * * /usr/local/bin/fleet-push-all.sh >> /path/to/monitor/logs/cron.log 2>&1
Detalii pentru fiecare — în secțiunile corespunzătoare. Sarcinile de backup (secțiunea anterioară) se adaugă în același cron. După modificări verificați: sudo crontab -l și că serviciul cron este activ.
Ora cron = fusul orar al serverului, nu TIMEZONE din config.php. Constanta TIMEZONE afectează doar PHP (cum afișează panoul datele), dar demonul cron rulează sarcinile după ora de sistem a OS. Dacă fusul serverului nu coincide cu al dumneavoastră, raportul „08:00” va sosi la altă oră. Exemplu: serverul e în alt fus (Europe/Berlin, UTC+2), iar dumneavoastră — la București (UTC+3) → raportul „08:00” va sosi la 09:00 la dumneavoastră. Verificați și, dacă e nevoie, aliniați fusul sistemului la al dumneavoastră:
# Verificați fusul curent al serverului: timedatectl # Setați propriul fus (exemplu) și reporniți cron: sudo timedatectl set-timezone Europe/Bucharest sudo systemctl restart cron
După aceasta, linia 0 8 * * * se va declanșa la 08:00 ora locală. Altfel ar trebui deplasat cron-ul însuși, dar la trecerea la ora de iarnă/vară deplasarea s-ar dezacorda din nou — de aceea e mai corect să configurați fusul sistemului.
Scripturi-wrapper gata făcute. Copiile lor funcționale și un model de crontab (crontab.txt) se află în folderul system/ lângă proiect, în afara public_html. Aceasta nu face parte din site — nu trebuie încărcate în rădăcina web; plasați-le pe server la căile de sistem (ca în crontab de mai sus):
  • lynis-scan.sh → /usr/local/bin/ (chmod +x) — rulează lynis audit system, pe durata scanării pune indicatorul /tmp/lynis-running și copiază lynis-report.dat în data/lynis/ al panoului;
  • logwatch_daily.sh → /usr/local/bin/ (chmod +x) — generează raportul zilnic Logwatch (sshd, fail2ban, sudo, postfix) în data/logwatch/;
  • smart-scan.sh → /usr/local/bin/ (chmod +x) — preia starea discurilor (smartctl) în data/disk/;
  • debsums-scan.sh → /usr/local/bin/ (chmod +x) — verifică integritatea pachetelor (debsums) în data/debsums/;
  • clamav-scan.sh → /usr/local/bin/ (chmod +x) — scanare antivirus ClamAV pe căi periculoase (web, home, temp); scrie sumarul în /var/log/clamav/scan.log, de unde îl citește pagina ClamAV (linia 01:30 în crontab de mai sus);
  • load-ipsum.sh → /usr/local/bin/ (chmod +x) — actualizează setul ipset ipsum (level 1) pe loc, fără a rupe regulile active ale firewallului (linia 04:00 din crontabul de mai sus);
  • daily-report-all.sh → /usr/local/bin/ (chmod +x) — rulează raportul cron/daily_report.php al panoului (linia 08:00 în crontab de mai sus);
  • daily_report.php — este deja inclus în panou (cron/daily_report.php), se rulează prin daily-report-all.sh, nu trebuie instalat separat;
  • collect-metrics-all.sh → /usr/local/bin/ (chmod +x) — rulează cron/collect_metrics.php al panoului (pagina „Performanță”, linia */5 în crontab de mai sus); collect_metrics.php este deja inclus în panou, nu trebuie instalat separat;
  • security-watch-all.sh → /usr/local/bin/ (chmod +x) — pornește cron/security_watch.php al panoului în numele proprietarului site-ului (pagina „Evenimente de securitate”, linia 2-57/5 din crontab-ul de mai sus); security_watch.php face deja parte din panou și nu se instalează separat;
  • fleet-push-all.sh → /usr/local/bin/ (chmod +x) — trimite rezumatul serverului către contul personal prin cron/fleet_push.php al panoului (linia 3,18,33,48 de mai sus);
  • crontab.txt (system/cron/) — model de sarcini; introduceți liniile necesare prin sudo crontab -e.
Calea către script în crontab trebuie să coincidă cu locul unde l-ați pus.
Cum să puneți scriptul în /usr/local/bin/. Direct din FileZilla nu se poate scrie acolo — directorul aparține lui root, iar clientul SFTP va primi SSH_FX_PERMISSION_DENIED. Ordinea e următoarea: mai întâi încărcați fișierul în /tmp (acolo scriu toți), apoi mutați-l la locul lui cu o singură comandă:
# în FileZilla: în câmpul „Site la distanță” introduceți /tmp și încărcați acolo scriptul, # apoi prin SSH (install setează imediat proprietarul și drepturile, chown/chmod nu sunt necesare): sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # verificare: fișierul e la locul lui, drepturile rwxr-xr-x, sintaxa e intactă bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
Nu confundați directoarele: aveți nevoie de /tmp în rădăcina serverului — nu de /var/tmp și nici de tmp/ din interiorul panoului însuși (ultimul aparține lui www-data și e închis pentru utilizatorul dumneavoastră). În arborele FileZilla /tmp este o ramură de nivel superior, lângă var, nu în interiorul ei.
Ați instalat serverul cu autoconfigurare? Aceste wrappere și sarcinile lor cron sunt deja instalate de script (în /usr/local/bin/, jurnalul — /var/log/arciveo-cron.log) — nu trebuie făcut nimic manual.
Unde caută scripturile panoul. Wrapperele sunt neutre față de domeniu: găsesc instalările panoului parcurgând /home/*/web/*/public_html și /var/www/*, și pun rapoartele în data/ al acestora. Dacă panoul se află la altă cale — adăugați-o în linia for app in … din interiorul scripturilor, altfel rapoartele Lynis/SMART/debsums/Logwatch nu vor ajunge în panou.
cron.log și drepturile de acces. Fișierul logs/cron.log este creat prima dată de cron-ul root — va aparține lui root, iar fila „Jurnal cron” din panou nu îl va putea nici citi, nici goli. Creați fișierul din timp în numele utilizatorului web (proprietarul directorului site-ului; pe HestiaCP este contul, de ex. admin) — atunci cron-ul root doar va adăuga, fără a schimba proprietarul:
# creați din timp în numele utilizatorului web (înainte de a adăuga liniile cron): sudo -u OWNER touch /path/to/monitor/logs/cron.log # dacă cron.log a fost deja creat de cron-ul root — atribuiți-l utilizatorului web: sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
Aflați proprietarul directorului: stat -c %U /path/to/monitor.
Gestionare din panou. În secțiunea „Sistem” există pagina „Crontab” — puteți vedea și adăuga sarcini fără SSH. Panoul editează doar sarcinile adăugate prin el însuși (un bloc separat în root-crontab, marcat cu comentarii de serviciu); tot ce se află deja în crontab (lista de mai sus) este afișat acolo read-only într-o listă „Alte sarcini ale serverului” cu butonul „Copiază în editor” — acesta doar transferă programarea/comanda în formularul de adăugare, fără a atinge linia originală. Ca să „treceți” o sarcină existentă sub gestiunea panoului — copiați-o în editor, salvați, apoi ștergeți linia veche manual (sudo crontab -e), altfel se va executa de două ori.
Configurare unică pe server. Pagina are nevoie de un script-wrapper privilegiat — nu de un simplu sudo crontab (asta ar fi o escaladare directă la root pentru oricine obține acces la sesiunea panoului), ci de un script îngust cu două comenzi (list/set), care atinge doar propriul bloc dintre comentariile de serviciu. Instalați o singură dată:
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
Utilizatorul web poate diferi de www-data — verificați sub cine rulează pool-ul PHP-FPM al site-ului (ps -o user= -C php-fpm) și puneți-l în linia sudoers.
Fișierul nou a fost încărcat cu alt proprietar — pagina răspunde „Access denied.”. Dacă fișierul public/crontab_monitor.php a fost încărcat prin FTP/SFTP sub alt utilizator de sistem (de exemplu, root) decât restul fișierelor site-ului, serverul web nu îl va putea citi. Comparați proprietarul și drepturile cu un fișier vecin și aliniați-le:
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

Diagnosticare

41. Instrumentul este instalat, dar afișează „Neinstalat”

Monitorul detectează prezența instrumentelor prin dpkg-query — baza de pachete APT. Dacă instrumentul nu este instalat prin apt (manual, din snap sau din surse), dpkg nu îl vede.

# Verificare prin dpkg: dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # Găsește calea către binar: which ufw fail2ban-client auditctl # Test sudo ca www-data: sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

42. Rezolvarea problemelor (500, fără date)

Eroare 500 — verificați jurnalele PHP, nginx și ale monitorului:

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # Jurnalele monitorului: tail -50 logs/monitor_$(date +%Y-%m-%d).log # Permisiunile pe foldere: ls -la data/ tmp/ logs/
Panoul rulează pe un panou de hosting (HestiaCP, ISPmanager, cPanel)? Acolo PHP nu rulează sub www-data, ci sub contul utilizatorului (de exemplu admin — proprietarul directorului site-ului). Toate regulile sudo și apartenența la grupuri (adm, systemd-journal) trebuie configurate pentru acest utilizator, altfel modulele vor afișa „Inactiv / 0” deși serviciile funcționează. Aflați utilizatorul real al PHP: ps -o user= -C php-fpm | sort -u sau proprietarul directorului site-ului stat -c '%U' /path/to/monitor. Apoi, în toate comenzile de mai jos, folosiți-l în locul lui www-data. Instalarea automată detectează singură utilizatorul web și configurează sudoers pentru el.

Datele nu se afișează — aproape întotdeauna sunt drepturi sudo neconfigurate. Verificați comanda concretă în numele utilizatorului web (înlocuiți www-data cu al dumneavoastră). Indicatorul -n = fără parolă, ca la PHP — dacă cere parola, înseamnă că regula nu există în sudoers:

sudo -u www-data sudo -n fail2ban-client status sudo -u www-data sudo -n ufw status verbose sudo -u www-data sudo -n ipset list -t ipsum sudo -u www-data sudo -n /usr/sbin/ausearch -m USER_LOGIN -ts today sudo -u www-data sudo -n /usr/sbin/aa-status sudo -u www-data sudo -n /usr/sbin/psad --Status sudo -u www-data journalctl -u falco --no-pager -n 5
Modulul afișează „Inactiv” / „0”, deși instrumentul funcționează (de exemplu sudo aa-status în terminal arată profilurile, iar pagina „AppArmor” arată „Inactiv”). Cauza: utilizatorul web nu are dreptul sudo pentru comanda acestui modul. Verificați-o din lista de mai sus: dacă cere parola — adăugați linia lipsă în /etc/sudoers.d/monitor („Configurarea sudo”). Comenzi „noi” frecvente: /usr/sbin/aa-status (MAC), /usr/sbin/psad --Status (PSAD).
Dacă o anumită pagină (Falco, ModSecurity, Auditd, porturi deschise UFW) este goală — comparați cu lista din secțiunea despre sudo: probabil nu este permis apache2ctl, ausearch, aa-status sau ss, ori utilizatorul web nu este în grupurile adm/systemd-journal (de acolo se citesc jurnalele fail2ban/auth/modsec și journalctl — Falco și evenimentele nucleului).

43. Pagina este goală, deși pe server există date

Simptom: pe server există date (vizibile prin shell), dar pagina afișează „nu există date” sau un status incorect — de exemplu AIDE scrie „Neinițializat”, deși baza este creată.

Cauza este open_basedir: multe panouri și găzduiri restricționează pool-ul PHP-FPM la directorul domeniului, așa că funcțiile PHP file_exists(), file_get_contents(), filemtime() pe căi de sistem (/var/lib/aide, /var/log, /proc…) sunt blocate. Monitorul ocolește acest lucru citind astfel de căi prin comenzi de sistem standard (cat, test, stat).

# Este fișierul vizibil prin shell (așa citește monitorul): sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # Valoarea curentă open_basedir pentru pool-ul domeniului: grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
Dacă shell-ul „vede” fișierul (VISIBLE), dar pagina nu — este open_basedir. Soluția corectă este citirea prin comenzi de sistem (deja realizată pentru AIDE și Monitorul de rețea). Extinderea open_basedir la /var, /proc nu este necesară și este mai puțin sigură.

44. Pagina SSL nu funcționează

Monitorul verifică certificatele conectându-se direct la domenii prin portul 443. Dacă domeniul nu este accesibil de pe serverul însuși sau portul este blocat de firewall, verificarea nu va reuși.

# Verificați certificatul manual: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # Verificați disponibilitatea: curl -I https://monitor.example.com
Monitorul preia domeniile automat din configurațiile nginx (/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) și Apache (/etc/apache2/sites-enabled/), plus hostul curent din HTTP_HOST.
Detectarea automată a subdomeniilor. Subdomeniile sunt detectate automat din jurnalele publice Certificate Transparency și sunt verificate prin rețea — chiar dacă sunt găzduite pe alte servere. Nu trebuie să adăugați nimic manual.

45. Este vizibilă o singură bază din mai multe

Monitorul se conectează la MySQL cu utilizatorul din config.php, care are acces doar la propria bază. MySQL afișează în information_schema doar bazele pentru care există privilegii — de aceea celelalte nu sunt vizibile.

Pentru ca monitorul să vadă toate bazele, acordați acestui utilizator doar dreptul de citire (o singură dată, ca root; înlocuiți numele utilizatorului din config.php):

sudo mysql -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* acordă doar dreptul de citire — nu se poate modifica, șterge sau crea nimic, deci este sigur pentru monitorizare.
Fără acest GRANT, panoul vede doar propria bază — nu este o eroare, ci o limitare a drepturilor. Panoul nu folosește niciun sudo mysql: lista bazelor este preluată prin propria sa conexiune PDO.

46. PostgreSQL nu apare pe pagina „Baza de date”

PostgreSQL necesită acces la nivelul utilizatorului postgres, pe care utilizatorul web al panoului nu îl are. Expunerea unui sudo psql larg din PHP este nesigură — în schimb, panoul apelează un înveliș restrâns, fără parametri, care afișează doar versiunea, numărul de conexiuni și lista bazelor de date cu dimensiunile lor. Creați-l:

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 # în /etc/sudoers.d/monitor (utilizatorul = cel sub care rulează PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
Dacă nu folosiți PostgreSQL — eliminați linia monitor-pgstat din sudoers (pasul 13 al instalării manuale) și nu creați scriptul: cardul PostgreSQL va rămâne pur și simplu inactiv.

47. S-a declanșat o alertă — ce faceți

Panoul arată ce se întâmplă; mai jos — ce trebuie făcut în situații tipice. Principiul general: nu vă panicați, comparați cu activitatea legitimă (acțiunile dumneavoastră, actualizări, backupuri) și reacționați în funcție de gravitate.

  • Harta atacurilor / multe banuri fail2ban — este normal pentru orice server din internet (boții încearcă continuu SSH/web). Important e ca banurile să funcționeze. Asigurați-vă că autentificarea prin SSH se face doar cu cheie (parola dezactivată), iar IP-ul dumneavoastră este în ignoreip.
  • ModSecurity a blocat cereri — WAF-ul respinge atacurile asupra site-ului, asta e treaba lui. Dacă vă blochează traficul legitim (fals pozitiv) — găsiți rule id în detalii și adăugați o excepție în configurația CRS.
  • AIDE: fișiere modificate — comparați lista cu ceea ce ați făcut (actualizarea pachetelor, editarea configurațiilor — e normal). Modificările binarelor de sistem pe care nu le-ați atins sunt un motiv de îngrijorare. După modificări legitime, actualizați baza AIDE.
  • debsums: binare/biblioteci modificate (în afara /etc, în afara /usr/share) — posibil o substituire. Verificați pachetul: debsums PACKAGE_NAME, iar la dubii reinstalați-l (apt install --reinstall).
  • ClamAV / maldet: amenințare găsită — verificați fișierul din carantină, nu îl deschideți. Dacă e un web-shell în directorul site-ului — izolați serverul și căutați punctul de intrare (un plugin vulnerabil, o scurgere de accese).
  • Falco: evenimente critice (lansarea unui shell într-un container, accesul la fișiere sensibile) — analizați evenimentul: al cui proces, ce a lansat. Adesea e activitate legitimă de administrare.
  • Expunere externă: SGBD/cache în roșu — închideți imediat: legați serviciul la 127.0.0.1 sau închideți portul în UFW. Este o breșă reală.
  • SSL expiră / a expirat — reînnoiți certificatul (Let's Encrypt se reînnoiește singur; dacă nu — verificați certbot renew sau setările din panou).
  • Actualizări de securitate în așteptare — instalați: sudo apt update && sudo apt upgrade; după actualizarea nucleului, reporniți serverul.
Semnele unei spargeri reale (procese/utilizatori necunoscuți, binare modificate, spam de ieșire, sarcini cron necunoscute): deconectați serverul de la accesul extern, faceți un backup pentru analiză și, dacă datele sunt critice, ridicați un server curat dintr-un backup de încredere — un rootkit este greu de curățat sigur.