FAQ

Dies ist die Anleitung zur Installation, Konfiguration und Wartung von Arcivéo Monitor. Die Abschnitte sind gruppiert: allgemeine Übersicht, Bereitstellung des Dashboards, Anbindung von Sicherheitswerkzeugen, integrierte Module und Diagnose. Befehle lassen sich per Schaltfläche rechts kopieren.

Erste Schritte

01. Panel installieren — Methode wählen

Die Installation des Panels ist auf eigene Schritt-für-Schritt-Seiten ausgelagert. Wählen Sie eine Methode:

Unsicher? Nehmen Sie die automatische Methode. Dieses Handbuch bleibt die zentrale Quelle für SSL, Tools, cron und Diagnose — die Install-Seiten verweisen auf seine Abschnitte, ohne etwas zu duplizieren.

Übersicht

02. Was ist Arcivéo Monitor

Arcivéo Monitor — Sicherheits-Dashboard für Ihren Server. Sammelt Daten der installierten Tools (Fail2ban, UFW, Lynis, ModSecurity, AIDE, ClamAV, Auditd, CrowdSec, Suricata, Falco u. a.) und zeigt sie in einer einheitlichen Oberfläche mit Dashboard, Angriffskarte und Detailseiten zu jedem Tool.

Der Monitor ist kein aktives Schutzmittel — er blockiert Angriffe nicht selbst. Seine Aufgabe ist es, Informationen der bereits laufenden Tools zu bündeln und übersichtlich darzustellen.

03. Wie der Monitor auf dem Server arbeitet

Der Monitor arbeitet ausschließlich lokal — er muss auf demselben Server installiert sein, den er überwacht. Es gibt kein SSH und keine Remote-API.

Alle Befehle (fail2ban-client, ufw status, ipset list usw.) führt das Dashboard unter dem Benutzer des Webservers aus (in der Regel www-data, bei Hosting-Panels das Website-Konto) mit einem eng begrenzten Satz an sudo-Rechten — nur für bestimmte Werkzeuge, ohne allgemeinen Root-Zugriff. Die Ergebnisse werden geparst und im Browser angezeigt.

Für mehrere Server installieren Sie den Monitor auf jedem einzeln, mit einer eigenen Domain.

04. Wie der Sicherheitswert berechnet wird

Der Wert beginnt beim Maximum und sinkt für jedes erkannte Problem:

  • UFW nicht aktiv — −30
  • Fail2ban nicht gestartet (keine aktiven jails) — −25
  • Keine WebAuthn-Schlüssel — −15
  • Lynis hardening index < 60 — −20; 60–79 — −10
  • IPset ipsum nicht geladen — −10
  • Bedrohungen von ClamAV gefunden — −20
  • Dateiänderungen durch AIDE — −15
  • SSL abgelaufen — −30, läuft in <14 Tagen ab — −15, <30 Tagen — −5
  • CrowdSec installiert, aber nicht gestartet — −5
  • Suricata installiert, aber nicht gestartet — −5
  • DBMS/Cache (MySQL, PostgreSQL, Redis…) von außen erreichbar — −10
  • root-Login per SSH erlaubt (PermitRootLogin yes) — −20
  • Security-Updates stehen zur Installation aus — −5

Ergebnis: 80+ = Geschützt, 60–79 = Achtung, <60 = Gefährdet.

Abzüge für ClamAV, AIDE, CrowdSec und Suricata gelten nur, wenn das Werkzeug installiert ist. Lynis und AIDE ohne initialisierte Datenbank werden als „keine Daten“ angezeigt und senken den Wert nicht. Die Anzahl der Angriffe von heute wird im Dashboard angezeigt, wirkt sich aber nicht auf den Sicherheitswert aus.

Einstellungen und Lizenz

05. WebAuthn — Zwei-Faktor-Authentifizierung

WebAuthn ist ein Standard für die passwortlose Authentifizierung über einen Hardware-Schlüssel. Unterstützt werden YubiKey, Touch ID, Face ID, Windows Hello und Passkey.

Nach der Anmeldung mit Passwort fordert das System eine Bestätigung über den registrierten Schlüssel an. Selbst bei einem Passwortleck ist eine Anmeldung ohne physischen Schlüssel oder Biometrie unmöglich.

Zum Einrichten öffnen Sie WebAuthn-Schlüssel im Seitenmenü und klicken auf „Schlüssel registrieren“. Registrieren Sie gleich zwei Schlüssel: Geht der einzige Schlüssel verloren oder wird er beschädigt, ist die Anmeldung im Dashboard darüber nicht mehr möglich.

WebAuthn funktioniert nur über HTTPS. Über eine HTTP-Verbindung sind Registrierung und Anmeldung per Schlüssel nicht verfügbar.

06. Benachrichtigungen: Telegram und E-Mail

Das Dashboard kann den Sicherheitsbericht an Telegram und per E-Mail senden (per Schaltfläche und nach Zeitplan). Einstellbar im Bereich „Einstellungen“.

Telegram. Erforderlich sind Bot-Token und chat id:

  1. Schreiben Sie in Telegram an @BotFather/newbot → Sie erhalten ein Token der Form 123456:ABC....
  2. Senden Sie Ihrem neuen Bot eine beliebige Nachricht (damit er Ihnen antworten kann).
  3. Ermitteln Sie Ihre chat id: schreiben Sie an den Bot @userinfobot oder öffnen Sie https://api.telegram.org/bot<TOKEN>/getUpdates und suchen Sie "chat":{"id":...}.
  4. Tragen Sie Token und chat id unter „Einstellungen“ → Telegram ein und klicken Sie auf „Speichern und Test senden“.

E-Mail. Zwei Möglichkeiten zur Auswahl unter „Einstellungen“ → E-Mail:

  • SMTP — Host, Port (465/SSL oder 587/TLS), Login und Passwort Ihres Postfachs;
  • Resend — moderne API: geben Sie den API-Schlüssel (re_...) und die bestätigte Absender-Domain an.
Die Schaltfläche „Test senden“ prüft den Kanal sofort. Der Zeitplan des automatischen Berichts läuft über cron (Bereich „Alle cron-Aufgaben“): er löst den Versand aus, die Kanäle stammen aus den Einstellungen.

Berichtsstatus: „ACHTUNG“ oder „OK“. Der Titel wird nur bei einem echten Problem oder einer ausstehenden Aktion zu „ACHTUNG“: ClamAV hat eine Bedrohung gefunden, Dateiänderungen in AIDE, kritische Falco-Ereignisse (Emergency/Alert/Critical in den letzten 24 h), ein ausgefallener Dienst in Monit, ein Neustart ist erforderlich, ein SSL-Zertifikat läuft ab (≤14 Tage) oder Sicherheitsupdates stehen an. Hintergrundrauschen — SSH-Bruteforce durch Bots, von fail2ban gebannte IP-Adressen, Suricata-Alerts, Lynis-Warnungen und bereits abgewehrte ModSecurity-Anfragen — hebt den Status nicht an, daher bedeuten solche Zahlen im Bericht für sich allein noch kein „ACHTUNG“.

07. Lizenz – Eingabe und Aktivierung

Die detaillierten Monitoring-Module (Lynis, UFW, ModSecurity, Angriffskarte, AIDE, ClamAV usw.) werden mit einer gültigen Lizenz freigeschaltet. Ohne sie funktionieren Dashboard, Einstellungen und Konto, während die Module die Karte „Lizenz erforderlich“ anzeigen.

Nach dem Kauf im Benutzerkonto liegt Ihnen ein Aktivierungscode der Form ARCIVEO-XXXX-XXXX-XXXX-XXXX vor. Er muss auf die Domain Ihres Panels „aktiviert“ werden – dadurch wird der Code in eine signierte Lizenzdatei (Block [license]) umgewandelt, die Sie in das Panel einfügen.

So aktivieren Sie (3 Schritte):

  1. Nehmen Sie den Aktivierungscode. Benutzerkonto my.arciveo.com → Bereich „Lizenzen“ / „Lizenzaktivierung“ – kopieren Sie den Code ARCIVEO-….
  2. Aktivieren Sie den Code auf Ihre Domain. Öffnen Sie ebenfalls im Benutzerkonto die „Lizenzaktivierung“ und geben Sie ein: Aktivierungscode, Ihre E-Mail und die Panel-Domain (die Adresse, unter der der Monitor erreichbar ist, z. B. monitor.example.com). Klicken Sie auf Aktivieren – das System erzeugt eine an diese Domain gebundene Lizenzdatei und zeigt sie im Feld mit der Schaltfläche „Kopieren“ an.
  3. Fügen Sie den Schlüssel ins Panel ein. Kopieren Sie den gesamten Lizenztext → öffnen Sie im Panel „Einstellungen“ → Block „Lizenz“, fügen Sie ihn ein und klicken Sie auf „Speichern“. Die Module werden sofort freigeschaltet.

Das Panel prüft den Schlüssel kryptografisch: Signatur, Domain-Bindung und Gültigkeitsdauer.

Die Domain bei der Aktivierung muss exakt mit der Panel-Adresse übereinstimmen. Entnehmen Sie sie der Konstante APP_URL in config.php und geben Sie nur den Hostnamen ein – ohne https:// und ohne Präfix www. Die Aktivierung ist einmalig: Der Code wird zur Lizenz für die eingegebene Domain und lässt sich nicht erneut aktivieren – bei einem Fehler in der Domain passt der Schlüssel nicht zu Ihrem Panel, und der Code ist verbraucht. Geben Sie die Domain daher sorgfältig ein.
Ist die Gültigkeit abgelaufen oder die Domain gewechselt, erscheint im Panel-Kopf eine Warnung. Die Lizenz ist dauerhaft an die Domain gebunden und wird nicht auf eine andere Domain übertragen: für einen neuen Zeitraum oder eine neue Domain wird ein neuer Schlüssel benötigt (im Benutzerkonto zu kaufen und einmalig zu aktivieren).

08. Datei config.php — alle Einstellungen des Dashboards

Alle wesentlichen Parameter des Dashboards stehen in einer einzigen Datei config.php im Stammverzeichnis (neben dem Ordner public/) als gewöhnliche Konstanten via define(). Die Datei wird bei der Installation erstellt; ein manuelles Bearbeiten ist selten nötig — meist beim Wechsel der Domain, beim Umzug oder bei der Anbindung an eine andere Datenbank. Nach jeder Änderung starten Sie PHP-FPM neu (sonst greifen die Änderungen wegen OPcache nicht).

Tragen Sie Ihre Werte an den hervorgehobenen Stellen ein; den Rest lassen Sie unverändert:

// --- Datenbank --- define('DB_HOST', 'localhost'); // belassen define('DB_NAME', 'db_name'); // beim Erstellen der DB festgelegt define('DB_USER', 'user'); // beim Erstellen der DB festgelegt define('DB_PASS', 'db_password'); // beim Erstellen der DB festgelegt define('DB_CHARSET', 'utf8mb4'); // belassen // --- Anwendung --- define('APP_URL', 'https://monitor.example.com'); // Adresse des Dashboards, ohne Schrägstrich am Ende define('TIMEZONE', 'Europe/Berlin'); // Ihre Zeitzone // --- Sitzungsdauer --- define('SESSION_LIFETIME', 28800); // Leerlauf bis zur erneuten Anmeldung, Sek. (28800 = 8 Std.)

Datenbank. Verbindungsdaten zu MySQL/MariaDB:

  • DB_HOST — Host des DBMS, fast immer localhost;
  • DB_NAME — Name der Datenbank des Dashboards;
  • DB_USER — DB-Benutzer (Zugriff nur auf die eigene Datenbank);
  • DB_PASS — Passwort dieses Benutzers;
  • DB_CHARSET — Zeichensatz der Verbindung, belassen Sie utf8mb4.

Anwendung.

  • APP_URL — vollständige Adresse des Dashboards (z. B. https://monitor.example.com). Muss mit der Domain übereinstimmen, für die die Lizenz aktiviert wurde — sonst wird der Schlüssel abgelehnt (siehe Abschnitt „Lizenz“);
  • TIMEZONE — Zeitzone von PHP: wirkt sich nur darauf aus, wie das Dashboard Datum und Uhrzeit anzeigt. Auf die Startzeit der Cron-Aufträge hat sie keinen Einfluss — dort gilt die Systemzeitzone (siehe „Alle Cron-Aufträge“).

Sitzungsdauer. SESSION_LIFETIME — Leerlauf-Timeout der Sitzung in Sekunden (gleitend: wird bei Aktivität erneuert). Standard ist 28800 = 8 Stunden; nach dieser Zeit ohne Aktivität verlangt das Dashboard eine erneute Anmeldung. Beispiel: 3600 = 1 Stunde, 86400 = ein Tag.

Fehlerprotokollierung. Fehler werden Besuchern nie angezeigt, sondern in logs/php_errors.log geschrieben — sie sind auf der Seite „Anwendungsprotokolle“ sichtbar. Diese Zeilen (display_errors=0, log_errors=1, Pfad error_log) müssen in der Regel nicht geändert werden — die Einstellungen stehen direkt in der Datei und hängen nicht von php.ini ab.

config.php — eine geheime Datei. Sie enthält das DB-Passwort. Sie liegt im Stammverzeichnis des Dashboards (neben public/), und bei diesem Dashboard ist der Web-Stamm (DocumentRoot) genau dieses Stammverzeichnis, nicht public/. Für sich genommen „leckt“ die Datei nicht: In der .htaccess im Stammverzeichnis gilt für sie ein explizites Verbot (Require all denied) — der Server liefert 403. Auch ohne diese Regel würde der Quelltext nicht austreten: Es ist PHP — der Server führt es aus und gibt es nicht als Text zurück. Sicherheitshalber: Legen Sie sie nicht in öffentliche Repositorys und geben Sie sie nicht mit dem echten Passwort an den Support weiter. Dateirechte — 640.
Bei einem Umzug oder der Wiederherstellung des Zugangs ist diese Datei die wichtigste Quelle für die Zugangsdaten: DB-Name, Benutzer und Passwort stammen genau von hier (siehe Abschnitte „Aktualisierung und Umzug des Dashboards“ und „Zugang wiederherstellen“).

Sicherheitstools

09. UFW-Firewall

UFW (Uncomplicated Firewall) — eine einfache Schnittstelle zu nftables/iptables. Sie sperrt alle eingehenden Ports außer den ausdrücklich erlaubten. Die Seite „UFW-Firewall“ zeigt Status und Regeln an.

sudo apt install ufw # SSH freigeben (zwingend VOR dem Aktivieren!) und Web sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # DB von außen sperren (Zugriff nur lokal) sudo ufw deny 3306 # Aktivieren und prüfen sudo ufw enable sudo ufw status verbose
Geben Sie vor ufw enable unbedingt SSH frei (ufw allow OpenSSH), sonst verlieren Sie den Zugriff auf den Server.
Die „externe Exposition“ im Dashboard berücksichtigt UFW: Ein per deny-Regel gesperrter Port gilt nicht als von außen erreichbar.
Skipping adding existing rule ist kein Fehler. UFW meldet damit, dass genau diese Regel bereits existiert und nicht erneut hinzugefügt wird. Beim erneuten Ausführen der automatischen Einrichtung (sie ist idempotent) ist das eine normale Meldung — es ist keine Reaktion nötig.

10. Fail2ban installieren

Sperrt IP-Adressen automatisch nach zu vielen fehlgeschlagenen Anmeldeversuchen. Analysiert die Logs von SSH, nginx, Apache und anderen Diensten.

sudo apt install fail2ban sudo systemctl enable --now fail2ban # Status prüfen: sudo fail2ban-client status
Die einsatzfertige Konfiguration (jail.local mit Dutzenden jails und Auto-Ban aus ipsum) folgt im nächsten Abschnitt.

11. Funktionierende Konfiguration Fail2ban + ipsum

Die Basisinstallation steht oben. Hier folgt eine funktionierende Konfiguration mit Dutzenden aktiver jails und Tausenden Sperren: allgemeine Einstellungen, zentrale jails und Auto-Bann bösartiger IPs aus der Liste ipsum.

Datei /etc/fail2ban/jail.local — allgemeine Einstellungen und wichtigste jails:

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # Progressiver Bann: jede Wiederholung dauert länger bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # dauerhafter Bann bei SSH-Brute-Force findtime = 3600 # Wiederholungstäter: wer mehrere Banns kassiert, wird dauerhaft gesperrt [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 # Webdienste (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog): [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … und die übrigen jails nach Diensten (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) — enabled = true
Tragen Sie in ignoreip unbedingt Ihre eigene IP und vertrauenswürdige Netze ein, sonst sperren Sie sich womöglich selbst aus. Nach Änderungen: sudo fail2ban-client reload.

Automatisches Laden der Blockliste ipsum per Root-Cron (sudo crontab -e): Level 1 (100.000+ IPs) wird in das Set ipsum geladen, das auf der Firewall abgeblockt wird (mehr dazu im Abschnitt „IPset-Blockliste“):

# 04:00 — Aktualisierung des ipset ipsum (level 1, maximale Abdeckung): 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
Das Set muss ipsum heißen — genau dieses liest das Dashboard (Karte „IPset ipsum“). Level: levels/1.txt — maximale Abdeckung, levels/3.txt — genauer (3+ Quellen).

Warum der „Sicherheitsmonitor“ in zwei Zonen unterteilt ist. Der Schutz arbeitet auf zwei Ebenen, und das Dashboard vermischt sie nicht:

  • Reale Angriffe (reaktiv) — alles, was fail2ban abgefangen hat: aktive Einbruchsversuche (jails sshd, apache-*, nginx-* usw.) und hartnäckige Wiederholungstäter (jail recidive — jene, die schon mehrfach gesperrt wurden). Das sind IPs, die tatsächlich bei Ihnen einzubrechen versuchten — sie erscheinen auf der Angriffskarte und in der „Zeitleiste“.
  • Präventive Sperre (proaktiv) — die öffentliche Blockliste bekannter bösartiger IPs ipset ipsum, die auf der Firewall per DROP-Regel abgeblockt wird. Diese Adressen haben Ihren Server meist nie berührt — sie werden vorab abgeschnitten; der Zähler „IPset ipsum“ zeigt, wie viele präventiv abgeblockt wurden.

Der Unterschied ist einfach: reaktiv — „diese haben angegriffen und wurden gesperrt“, präventiv — „diese wurden schon vor dem Versuch blockiert“. Früher wurde in recidive künstlich list-3 ipsum eingespeist (daher die alte Trennung „gelistete recidive“); jetzt sind in recidive nur echte Wiederholungstäter, und die Prävention läuft vollständig auf der Firewall.

12. IPset-Blockliste (ipsum)

ipsum — eine öffentliche, täglich aktualisierte Liste bösartiger IP-Adressen. Der Monitor zeigt die Anzahl der geladenen Adressen im Dashboard und auf der Angriffskarte und berücksichtigt sie in der Sicherheitsbewertung (−10, wenn das Set nicht geladen ist).

Die Minimalvariante ohne fail2ban — ein eigenes Set ipsum mit Sperrung über iptables:

# Set anlegen (einmalig): sudo ipset create ipsum hash:ip # Update-Skript /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, täglich um 4:00): 0 4 * * * /usr/local/bin/update-ipsum.sh
Die erweiterte Variante mit fail2ban-recidive — im Abschnitt „Funktionierende Konfiguration Fail2ban + ipsum“.
ipset liegt im Arbeitsspeicher und geht beim Neustart verloren. Ein täglicher Cron allein lässt das Set vom Reboot bis zum nächsten Lauf leer (das Dashboard zeigt 0). Laden Sie das Set auch beim Start — verlagern Sie das Laden in ein Skript und hängen Sie es an @reboot. Zugleich setzt der Befehl create … -exist das Limit maxelem 300000 (Standard ist 65536 — Level 1 passt nicht hinein, es kommt „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 — täglich um 04:00 UND bei jedem Start: 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
Wie es bei der Autoinstallation funktioniert. Das Skript lädt die vollständige Liste Level 1 (100.000+ IP) in das Set ipsum und bindet, falls die Firewall vom Installer verwaltet wird (frisches VPS — Profile „Vollständig“/„Reduziert“), das Set über die UFW-Regel DROP ein — Traffic von diesen IP-Adressen wird tatsächlich blockiert. Die Regel steht nach ESTABLISHED,RELATED, sodass bestehende Verbindungen (auch Ihr SSH) nicht abreißen — es werden nur neue Verbindungen aus der Liste gekappt. Das Set wird beim Start des Dienstes ipsum-load.service vor der Firewall wiederhergestellt (sonst würde UFW nicht hochkommen) und per Cron um 04:00 aktualisiert. Auf einem bereits eingerichteten Server (Panel, eigene Firewall) greift der Installer nicht in die Firewall ein — dort bleibt ipsum eine Liste für Dashboard und Angriffskarte, und die Regel DROP wird bei Bedarf manuell hinzugefügt (die Minimalvariante mit iptables … --match-set ipsum … -j DROP — oben). Bei der Autoinstallation ist manuell nichts zu tun.

13. CrowdSec installieren

Moderner Ersatz für Fail2ban mit kollektiver Threat Intelligence: Sperren aus der Community plus eigene Regeln. Benötigt einen separaten bouncer, um Sperren auf die Firewall anzuwenden.

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # Bouncer für iptables/nftables: sudo apt install crowdsec-firewall-bouncer-iptables # Status prüfen: sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
Status „Nicht gestartet“ im Dashboard = Dienst installiert, aber nicht aktiv (der Monitor prüft ihn über systemctl is-active crowdsec). Starten: sudo systemctl enable --now crowdsec; bei Absturz siehe sudo journalctl -u crowdsec -n 30. Dieselbe Regel gilt für jeden Dienst im Status „Nicht gestartet“ (Suricata, Falco, Monit, MySQL).
„0 Szenarien“ oder „0 bouncers“ im Dashboard. CrowdSec ist ab Werk nahezu leer — ohne Collections erkennt es nichts, und ohne registrierten bouncer werden Sperren nicht auf die Firewall angewendet. Installieren Sie die Basis-Collections und stellen Sie sicher, dass der bouncer in der Liste steht:
# Basis-Collections (Linux + SSH + Webserver): sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # Der Bouncer muss in der Liste stehen und den Status einer aktiven Verbindung haben: sudo cscli bouncers list
Im bouncer-Log stream halted / Sperren werden nicht angewendet. Das ist ein verwaister API-Schlüssel: Der bouncer wurde aus cscli bouncers list entfernt, aber sein alter Schlüssel blieb in /etc/crowdsec/bouncers/*.yaml. Registrieren Sie den bouncer neu und tragen Sie den frischen Schlüssel ein:
sudo cscli bouncers add fw-bouncer # gibt den neuen api_key aus # tragen Sie diesen Schlüssel unter api_key: in /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml ein sudo systemctl restart crowdsec-firewall-bouncer
Die automatische Installation (Profil „Vollständiger Schutz“) installiert die Collections selbst und registriert den firewall-bouncer — manuell ist das nur bei manueller Installation oder nach manuellem Eingriff in CrowdSec nötig.

14. Installation von AIDE

AIDE (Advanced Intrusion Detection Environment) erstellt einen Snapshot des Dateisystems und meldet bei jeder Prüfung Änderungen in /etc, /bin, /usr. Nach der Installation ist die Initialisierung der Datenbank zwingend (aideinit).

sudo apt install aide # Datenbank initialisieren (5–15 Minuten): sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04: Verzeichnis /var/lib/aide wird mit Modus 700 angelegt (Besitzer _aide), # und das Dashboard (www-data) sieht die Datenbank nicht → zeigt „Nicht initialisiert“. # Verzeichnis zum Durchlaufen öffnen (DB-Dateien bleiben 600): sudo chmod 755 /var/lib/aide # Erste Prüfung MIT SCHREIBEN in das Log, das der Monitor liest. # Unter Ubuntu/Debian benötigt aide ein explizites --config (sonst „missing configuration“; # das Binary aide.wrapper wird in neueren Versionen nicht mehr mitgeliefert): sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Während aideinit bleibt das Terminal 5–15 Minuten bei der Zeile Running aide --init... stehen — das ist normal (Hashing des gesamten Dateisystems, Festplattenlast). Nicht mit Ctrl+C abbrechen. Wenn der Prozess „hängt“, aber nichts ausgibt — möglicherweise wartet er auf eine Antwort zu einer versteckten Abfrage Overwrite existing aide.db.new [Yn]? (drücken Sie Y). Aktivität aus einer anderen Sitzung prüfen: pgrep -af aide.
Fehler bei aideinit: „21_aide_spamassassin … printf: invalid number“ (return code 20) — ein bekannter Bug des AIDE-Konfig-Snippets in Ubuntu 22.04. Die Datenbank wird nicht erstellt. Verschieben Sie das defekte Snippet und wiederholen Sie:
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
Status im Dashboard. „Nicht initialisiert“ = das Dashboard sieht die Datenbankdatei nicht: entweder wurde aideinit nicht ausgeführt, oder (Ubuntu 24.04) das Verzeichnis /var/lib/aide wurde mit Modus 700 angelegt und ist für www-data nicht zugänglich — behoben mit sudo chmod 755 /var/lib/aide (siehe Block oben). „Keine Prüfungen“ = die Datenbank ist vorhanden, aber es wurde noch keine Prüfung ausgeführt — das ist kein Fehler. Die Ergebnisse liest der Monitor aus /var/log/aide/aide.log.
Regelmäßige Prüfung → Log für das Dashboard. Das reguläre /etc/cron.daily/aide auf neueren Ubuntu/Debian schreibt /var/log/aide/aide.log möglicherweise nicht im benötigten Format (und aide.wrapper gibt es dort nicht mehr). Zuverlässiger ist ein eigener Cron mit explizitem --config — er schreibt das Log als root im Modus 644, und der Monitor liest es ohne zusätzliche Gruppen:
# sudo crontab -e — tägliche Prüfung um 02:00: 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # Jetzt ausführen, ohne auf den Zeitplan zu warten: sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Die automatische Installation erledigt all das bereits: chmod 755 /var/lib/aide und den Prüf-Cron um 02:00 — manuell ist nichts nötig.
Führen Sie die erste Initialisierung auf einem sauberen Server durch — vor der Installation von Webanwendungen. Nach legitimen Änderungen erstellen Sie die Datenbank neu: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.

15. ClamAV installieren

Antivirus-Scanner für Linux. Besonders nützlich, um /var/www auf PHP-Shells und Schadcode zu prüfen.

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # Signaturdatenbank aktualisieren: sudo freshclam # Ordner manuell scannen: sudo clamscan -r /var/www --infected
Der Daemon clamd zeigt nach enable --now „Inaktiv“? Drei typische Ursachen:

1. In der Konfiguration steht noch die Zeile Example — clamd startet nicht, solange sie vorhanden ist:

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

2. Die Signaturdatenbank ist nicht heruntergeladen — clamd startet ohne sie nicht:

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

3. Es lädt einfach noch — clamd lädt ~8 Mio. Signaturen 30–60 Sek. in den Speicher. Warten und prüfen: systemctl is-active clamav-daemon (Status activating → lädt noch).

Diagnose: sudo journalctl -u clamav-daemon -n 30 --no-pager.
Im Dashboard „Geprüfte Dateien: 0“ / „Letzter Scan: —“? Der Daemon clamd hält nur die Signaturen im Speicher, er scannt selbst nichts nach Zeitplan. Das Dashboard zeigt Ergebnisse des geplanten Scans, daher ist ein Cronjob nötig, der scannt und ins Log schreibt. Die automatische Installation richtet den Wrapper /usr/local/bin/clamav-scan.sh und einen Cronjob um 01:30 ein — nach dem ersten Lauf werden „Geprüfte Dateien“ und „Letzter Scan“ befüllt. Sofort starten, ohne auf den Zeitplan zu warten: sudo /usr/local/bin/clamav-scan.sh.

16. Linux Malware Detect (maldet) installieren

Linux Malware Detect (LMD) — Malware-Scanner für Web-Bedrohungen: PHP-Shells, Web-Backdoors, Downloader. Nutzt die ClamAV-Engine und ergänzt sie um eigene Signaturen.

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 # Signaturen aktualisieren: sudo maldet -u # /var/www scannen: sudo maldet -a /var/www
LMD und ClamAV ergänzen sich gut. Letzter Bericht: maldet --report.
Bei der Installation kann die Zeile update-rc.d: error: unable to read /etc/init.d/maldet erscheinen — sie ist harmlos. maldet nutzt kein init.d, Signatur-Updates und Scans laufen über /etc/cron.daily/maldet. Erscheint darunter installation completed, ist alles installiert.
Auf der Seite steht bei LMD „Nicht installiert“, obwohl es installiert ist? maldet wird nicht über apt installiert, sondern nach /usr/local/maldetect, und bei aktivem open_basedir wird seine Präsenz über die Shell geprüft — siehe Abschnitt „Seite leer, obwohl Daten auf dem Server vorhanden sind“.

17. Suricata installieren

Netzwerkbasiertes Angriffserkennungssystem: analysiert den Datenverkehr auf Paketebene und kennt Tausende von Angriffssignaturen. Ergänzt ModSecurity (das auf HTTP-Ebene arbeitet, Suricata auf TCP/IP-Ebene).

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # Aktuelle Regeln herunterladen: sudo suricata-update sudo systemctl enable --now suricata
Suricata „Aktiv“, aber das Dashboard zeigt keine Alerts / Ereigniszahl 0? Suricata schreibt /var/log/suricata/eve.json als root mit Modus 750 auf das Verzeichnis, und der Webserver (www-data) kann es nicht lesen. Geben Sie das Verzeichnis für den Durchgang frei – die Dateien darin bleiben geschützt:
sudo chmod o+rx /var/log/suricata
Die automatische Installation erledigt das selbst – manuell nicht nötig.

18. Falco installieren

Fängt Systemaufrufe über eBPF/Kernel-Modul ab und erkennt Anomalien in Echtzeit: Shell aus nginx, Lesen von /etc/passwd durch einen Webprozess, Schreiben nach /bin usw.

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
Der Monitor liest Falco-Ereignisse über journalctl -u falco (ohne sudo — über die Gruppe systemd-journal). Stellen Sie sicher, dass www-data in dieser Gruppe ist — siehe „sudo einrichten“ (Punkt 2) auf der Seite zur manuellen Installation.
„0 Ereignisse in 24 Stunden“ ist normal, kein Fehler. Falco ist ereignisgesteuert: Es bleibt still, solange alles in Ordnung ist, und schreibt ein Ereignis nur bei einer Anomalie (Shell aus einem Webprozess, Lesen von /etc/passwd, Schreiben in Systemverzeichnisse). Null kritische Ereignisse an einem Tag auf einem ruhigen Server sind ein gesunder Zustand.
Für das Dashboard ist die Datei-Ausgabe zuverlässiger. Das Lesen über journalctl erfordert Rechte am Journal; damit das Dashboard Ereignisse zuverlässig sieht, aktiviert die automatische Installation bei Falco file_output/var/log/falco/falco.log und setzt für den Dienst UMask=0022 (das Log ist für den Webserver lesbar). Bei einer Neuinstallation muss dies nicht manuell eingerichtet werden.

19. Installation von ModSecurity (WAF)

ModSecurity — eine Web-Firewall (WAF) für Apache oder Nginx. Blockiert Angriffe auf Anwendungsebene: SQL-Injektionen, XSS, Path Traversal, Scanner.

# Apache: sudo apt install libapache2-mod-security2 sudo a2enmod security2 # Regelsatz OWASP Core Rule Set: sudo apt install modsecurity-crs # ZWINGEND: ohne diese Datei ist die Regel-Engine deaktiviert 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 # Prüfung: sollte 403 zurückgeben curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
Die Paketinstallation allein schützt nichts. Apache bindet Konfigurationen mit der Zeile IncludeOptional /etc/modsecurity/*.conf ein, das Paket legt aber nur modsecurity.conf-recommended ab — diese fällt nicht unter die Maske *.conf. Wird sie nicht nach modsecurity.conf kopiert, bleibt SecRuleEngine auf Off: Das Modul ist geladen, die CRS-Regeln sind geladen, aber der Verkehr wird nicht geprüft und kein Audit-Log erstellt. Der Zwischenmodus DetectionOnly schreibt Ereignisse nur ins Log, ohne Anfragen zu blockieren — das Dashboard zeigt ihn gelb an.

Zugriff des Dashboards auf das Audit-Log. Das Log /var/log/apache2/modsec_audit.log gehört root (Rechte 640), der Webbenutzer kann es nicht lesen. Das Dashboard bezieht die Daten über einen Wrapper — legen Sie ihn an:

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 # in /etc/sudoers.d/monitor (Benutzer = der, unter dem PHP-FPM läuft): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
Der Wrapper nimmt die letzte Direktive SecRuleEngine ohne Einrückung: eingerückte Zeilen stehen innerhalb von Blöcken <LocationMatch>/<Directory> (z. B. Deaktivierung der WAF für phpMyAdmin) und legen den globalen Modus nicht fest.
Der Benutzer in sudoers muss mit dem Benutzer des FPM-Pools übereinstimmen: bei normalem Apache/Debian ist das www-data, in HestiaCP läuft der Site-Pool unter dem Site-Eigentümer (z. B. admin) — prüfen Sie grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.
Steht die Site hinter einem Nginx-Proxy (HestiaCP), sieht Apache den Proxy selbst als Client — das Dashboard entnimmt die echte IP-Adresse des Angreifers dem Header X-Forwarded-For. In die Statistik gelangen nur Transaktionen mit ausgelöster Regel: die Direktive SecAuditLogRelevantStatus schreibt alle 4xx/5xx-Antworten ins Audit-Log, daher landen dort auch gewöhnliche 403/500 — als WAF-Ereignisse zählt das Dashboard sie nicht.
Der Block ---RULES--- wird für den Bereich „Alle aktiven Regeln“ benötigt — das Dashboard zeigt nicht nur die ausgelösten, sondern alle geladenen CRS-Regeln + benutzerdefinierte an. Die drei Pfade in der Schleife for f in … sind die typischen Orte für CRS-Regeln und lokale Ergänzungen; bei einer anderen Aufteilung (das Paket legt Dateien in ein eigenes Verzeichnis, oder benutzerdefinierte Regeln liegen nicht in /etc/modsecurity/custom-rules.conf) ermitteln Sie die tatsächlichen Pfade mit sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null und tragen sie in die Liste ein. Ist der Wrapper alt (ohne diesen Abschnitt) — zeigt der Bereich einfach die Warnung „nicht verfügbar“ an, der Rest der Seite funktioniert wie zuvor.

20. Auditd installieren

Auditd (Linux Audit Daemon) protokolliert Systemaufrufe auf Kernel-Ebene: An- und Abmeldungen, sudo-Befehle, fehlgeschlagene Authentifizierungsversuche, Dateiänderungen. Der Monitor zeigt Anmeldungen, fehlgeschlagene Versuche und sudo-Befehle von heute.

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # Status und Ereignisse prüfen: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
Der Monitor liest Ereignisse über ausearch (/usr/sbin/ausearch) und bei Bedarf aus /var/log/audit/audit.log per tail. Beide müssen in sudoers eingetragen sein.

21. Monit installieren

Überwacht Dienste (nginx, php-fpm, mysql usw.) und startet sie bei einem Ausfall neu. Kann Warnmeldungen per E-Mail senden.

sudo apt install monit sudo systemctl enable --now monit # Konfigurationen: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
Der Monitor bezieht die Dienstliste über monit status. In /etc/monit/monitrc muss die HTTP-Schnittstelle aktiviert sein (Block set httpd mit allow localhost), sonst gibt monit status einen Fehler zurück.
Im Dashboard „0 überwachte Dienste“? Zwei Ursachen. (1) Die HTTP-Schnittstelle ist deaktiviert — in monitrc ist die Zeile set httpd auskommentiert (standardmäßig als # set httpd port 2812 …). Kommentieren Sie den Block ein und erlauben Sie localhost. (2) Ein aktivierter httpd allein überwacht nichts — Monit zählt nur das, was in check-Stanzas beschrieben ist; ohne diese bleibt die Liste selbst bei laufender Schnittstelle leer. Minimale funktionierende Konfiguration:
# /etc/monit/conf.d/00-httpd — HTTP-Schnittstelle für localhost: set httpd port 2812 use address localhost allow localhost # Beispiele für check-Stanzas (was überwacht werden soll): 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 # Syntax prüfen (Control file syntax OK) sudo systemctl reload monit sudo monit status
Die automatische Installation legt eine fertige conf.d mit httpd auf 2812 und einer Reihe von Prüfungen an — bei einer Neuinstallation ist keine manuelle Konfiguration nötig.
Dienst im Status „Mit Fehlern“? Der Monitor zeigt nur den Zustand an und startet Dienste bewusst nicht aus dem Webpanel neu (das wäre eine entfernte Ausführung von root-Befehlen in einem Sicherheitspanel). Diagnose und Neustart erfolgen per SSH über Monit:
sudo monit status <service> # Fehlerursache sudo monit restart <service> # Neustart über Monit # wenn Monit den Dienst nicht startet — sehen Sie sich dessen eigene Unit an: sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

22. Installation von PSAD (Erkennung von Portscans)

PSAD analysiert das iptables-Protokoll und erkennt Portscans sowie Netzwerkangriffe, wobei jeder Quelle eine Bedrohungsstufe (1–5) zugewiesen wird. Ergänzt fail2ban und Suricata.

sudo apt install psad # PSAD liest das iptables-Log — die Protokollierung muss aktiviert sein (UFW erledigt das selbst). # Für reines iptables fügen Sie LOG-Regeln zu den Ketten INPUT/FORWARD hinzu. sudo psad --sig-update sudo systemctl enable --now psad
Der Monitor liest die Daten über psad --Status (in sudoers erforderlich). Ohne iptables-Protokollierung bleibt die Seite leer — das ist normal, solange keine Scans stattgefunden haben.

23. AppArmor / SELinux (Zugriffskontrolle)

Mandatory Access Control schränkt ein, auf welche Dateien und Ressourcen ein Programm zugreifen darf – selbst wenn es kompromittiert wurde. Unter Ubuntu/Debian kommt standardmäßig AppArmor zum Einsatz (meist bereits installiert und aktiv).

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # Profile prüfen
Der Monitor liest den Status über aa-status (in sudoers erforderlich). Er zeigt die Anzahl der Profile im Modus enforce/complain sowie Prozesse ohne Profil.

„Geladene Profile“ sind mehr als enforce + complain – das ist normal. In AppArmor 4.x (Ubuntu 24.04 und neuer) gibt es den Modus unconfined: Das Profil ist in den Kernel geladen, schränkt aber nichts ein. Ubuntu markiert so Dutzende Profile für Programme, die user namespaces nutzen (Browser, Torrent-Clients und Ähnliches). Wenn solche Profile vorhanden sind, wird die Karte „Geladene Profile“ bernsteinfarben und zeigt ihre Anzahl – etwa unconfined: 90 bei 120 geladenen und 26 im enforce-Modus. Tatsächlich schützen nur Profile im enforce-Modus; unter Ubuntu 22.04 (AppArmor 3.x) gibt es diesen Modus nicht und die Zahlen stimmen immer überein.

sudo aa-status | grep -E "profiles are" # Aufschlüsselung nach Modus sudo aa-enforce /etc/apparmor.d/profile-name # Profil in enforce-Modus versetzen
Profile, die Ubuntu bewusst im Modus unconfined belassen hat, sollten nur mit Bedacht in den enforce-Modus versetzt werden: Sie sind nicht aus Versehen deaktiviert, sondern weil sonst die Programme selbst nicht mehr funktionieren. Profile im Modus complain sind etwas anderes: Dort sind die Regeln bereits geschrieben und werden nur nicht angewendet.

24. debsums installieren (Paketintegrität)

debsums prüft, ob die Dateien installierter Pakete mit den Prüfsummen aus dem Repository übereinstimmen — erkennt manipulierte System-Binärdateien (ergänzt AIDE). Eine vollständige Prüfung dauert 1–2 Minuten und läuft daher per cron; das Dashboard liest das Ergebnis aus data/debsums/debsums.log und ordnet es selbst in Kategorien ein (wichtig sind nur Binärdateien und Bibliotheken).

Der Job gehört in den root-cron (sudo crontab -e). Der fertige Wrapper debsums-scan.sh wird in /usr/local/bin/ abgelegt (chmod +x; siehe cron-Übersicht) und schreibt den Bericht selbst nach data/debsums/ im Dashboard.

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

Der Wrapper debsums-scan.sh findet data/ im Dashboard selbst — der Pfad muss nicht eingetragen werden.

Änderungen in /etc/ (Konfigurationen) und /usr/share/ (Ressourcen) auf dem Server sind meist normal — das Dashboard markiert sie mit einer eigenen Farbe. Bedenklich sind Änderungen an Binärdateien und Bibliotheken (/bin, /sbin, /usr/lib usw.) — die Karte „Binärdateien / Bibliotheken“ zeigt genau diese an.

25. Lynis-Berichte einrichten

Lynis wird manuell oder per cron gestartet. Der Bericht muss im Ordner data/lynis/ des Projekts abgelegt werden — der Monitor liest die Datei lynis-report.dat.

# Einmaliger Start (eigenen Pfad zum Panel-Stammverzeichnis einsetzen): sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # Täglicher Audit — cron-Zeile (fertiger Wrapper lynis-scan.sh in /usr/local/bin/, siehe Übersicht): 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
Nach dem ersten Start zeigt die Seite „Lynis-Audit“ sofort den Hardening-Index, Warnungen und Empfehlungen an.
Schaltfläche „Audit starten“ auf der Lynis-Seite. Sie startet lynis-scan.sh im Hintergrund direkt aus dem Panel (ohne auf cron zu warten): zeigt „Wird gescannt…“ an und aktualisiert nach Abschluss den Bericht selbst. Dafür braucht der Web-Benutzer eine sudoers-Zeile zum Ausführen des Skripts — das Installationsprogramm fügt sie automatisch in /etc/sudoers.d/monitor ein. Wurde das Panel manuell/früher installiert, ergänzen Sie sie mit demselben Benutzer, der bereits in der Datei angegeben ist:
u=$(sudo awk '/NOPASSWD/ && !/lynis-scan/ {print $1; exit}' /etc/sudoers.d/monitor) [ -n "$u" ] && echo "$u ALL=(ALL) NOPASSWD: /usr/local/bin/lynis-scan.sh" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c && sudo chmod 440 /etc/sudoers.d/monitor

26. Logwatch-Berichte konfigurieren

Logwatch muss die täglichen Berichte im Format .txt im Ordner data/logwatch/ des Projekts speichern. Der Monitor zeigt den letzten Bericht und das Archiv an.

# Täglich (6:00) — cron-Zeile (fertiger Wrapper logwatch_daily.sh in /usr/local/bin/, siehe Übersicht): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

Dashboard-Module

27. Netzwerk-Monitor (integriert)

Der Netzwerk-Monitor erfordert keine Installation — er ist eine integrierte Seite des Dashboards. Sie zeigt den Netzwerkstatus des Servers aus lokalen Quellen:

  • Schnittstellen und Traffic — aus /proc/net/dev;
  • Link-Status (UP/DOWN) und IP — über ip;
  • Verbindungen und lauschende Ports — über ss;
  • Kernel-Netzwerkereignisse der letzten 24 h — über journalctl -k.

Die ersten drei Quellen funktionieren ohne sudo, daher sind Schnittstellen, Traffic, Verbindungen und Ports sofort sichtbar. Der Block „Kernel-Ereignisse“ nutzt journalctl -k — er wird über die Gruppe systemd-journal gelesen („sudo einrichten“, Pkt. 2), sudo ist nicht nötig. So prüfen Sie, dass alles für den Web-Benutzer verfügbar ist:

# Prüfung als www-data (darunter läuft 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
Der Block „Kernel-Netzwerkereignisse“ zeigt Ereignisse des Kernel-Netzwerkstacks (Link-Wechsel up/down, Trägerfehler, „network unreachable“). Firewall-Einträge UFW BLOCK erscheinen hier nicht — sie stehen auf den Seiten „UFW-Firewall“ und „Angriffskarte“. Ein leerer Block mit grünem Haken = in den letzten 24 h gab es keine Netzwerkstörungen.

28. Festplatte und SMART

Die integrierte Seite zeigt drei Dinge:

  • Dateisysteme — Belegung der Partitionen (df); die Skala wird bei ≥90% rot;
  • Datenträger — Liste der Festplatten (lsblk), nur reale (loop/snap ausgeblendet);
  • Zustand (SMART) — Status und Attribute der Festplatte (smartctl).

Speicherplatz und Geräteliste funktionieren sofort, ohne Konfiguration. Für SMART wird das Paket smartmontools benötigt. Der Web-Prozess hat keinen direkten Zugriff auf die Festplattengeräte, daher wird SMART per cron in die Datei data/disk/smart.txt geschrieben, und das Dashboard liest sie aus.

Der Auftrag gehört in den root-cron (sudo crontab -e). Der fertige Wrapper smart-scan.sh wird nach /usr/local/bin/ abgelegt (chmod +x; siehe die Übersicht der cron-Aufträge) und schreibt selbst in data/disk/ des Dashboards.

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

Der Wrapper smart-scan.sh findet data/ des Dashboards selbst — der Pfad muss nicht angegeben werden. Intern schließt lsblk -e7,11 loop/cdrom aus.

Auf virtuellen Festplatten (QEMU/KVM und ähnlichen) ist meist nur der allgemeine Status „Zustand: OK“ verfügbar, während Temperatur, Betriebsstunden und neu zugewiesene Sektoren leer sein können — das ist normal. Auf einem physischen Server werden alle Attribute angezeigt.

29. Leistung (CPU/RAM/Netzwerk/Festplatte)

Die Seite zeigt den Auslastungsverlauf des Servers der letzten 24 Stunden — Load Average, CPU-Auslastung und I/O-Wartezeit, RAM/Swap, Netzwerkverkehr (Empfang/Sendung), Festplatten-I/O (Lesen/Schreiben), Festplatten- und Inode-Belegung, offene Dateideskriptoren und MySQL-Verbindungen sowie die aktuelle Anzahl der TCP-Verbindungen und Prozesse.

Die Daten erfasst cron/collect_metrics.php — alle 5 Minuten wird ein „roher“ Snapshot der Zähler (/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') in die DB-Tabelle system_metrics geschrieben; Prozentwerte und Geschwindigkeiten berechnet die Seite selbst aus der Differenz benachbarter Snapshots (Festplatten-/Inode-/Deskriptor-Belegung und MySQL-Verbindungen sind Momentanwerte, ohne Neuberechnung). Sudo wird nicht benötigt — die Quellen werden ohne Root-Rechte gelesen. Punkte, die älter als 24 Stunden sind, werden bei jeder Schreiboperation automatisch gelöscht.

# Cron-Zeile (alle 5 Minuten): */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

Der Wrapper collect-metrics-all.sh (siehe Übersicht der Cron-Aufgaben) findet selbst alle auf dem Server installierten Panel-Instanzen und startet cron/collect_metrics.php jeder Instanz im Namen des Website-Eigentümers.

Solange der Collector nicht mindestens zweimal gelaufen ist (die ersten ~10 Minuten nach der Installation), zeigt die Seite „Daten werden erfasst“ — die Grafiken benötigen mindestens ein Paar benachbarter Punkte, um Geschwindigkeiten und Prozentwerte zu berechnen.

Auslastungsbenachrichtigungen (Bereich „Einstellungen“ → „Auslastungsbenachrichtigungen“) — bei Überschreiten des Schwellenwerts für CPU/RAM/Festplatte/Inodes sendet das Panel eine Benachrichtigung an Telegram/E-Mail (dieselben Kanäle wie der Tagesbericht — sie müssen für die Alerts nicht separat aktiviert werden) und eine weitere, sobald die Metrik wieder normal ist. Während der Schwellenwert gehalten wird, wird nicht erneut gespammt: Die nächste Benachrichtigung kommt erst nach einem Zyklus „wieder normal → erneut überschritten“.

Die Schwellenwerte prüft dasselbe collect_metrics.php bei jedem Lauf (alle 5 Minuten) — ein separater Cron ist nicht nötig. Der Status „bereits benachrichtigt / noch nicht“ wird in data/alerts_state.json gespeichert, die Schwellenwerte in den Panel-Einstellungen.

30. Angriffskarte (GeoIP)

Die Seite „Angriffskarte“ ermittelt das Land anhand der IP-Adresse mit dem Befehl geoiplookup. Ohne das GeoIP-Paket werden keine Länder erkannt und es erscheinen keine Punkte auf der Karte:

sudo apt install geoip-bin geoip-database # Prüfung: geoiplookup 8.8.8.8
Sudo ist nicht erforderlich – die Datenbank /usr/share/GeoIP/GeoIP.dat ist für alle lesbar, die Ergebnisse werden in tmp/geoip_cache.json zwischengespeichert. Die Karte selbst (Leaflet + OpenStreetMap-Kacheln) wird im Browser geladen – der Rechner, auf dem das Dashboard geöffnet ist, benötigt Internetzugang.

31. Externe Exposition, Updates und automatische Updates

Zwei integrierte Dashboard-Karten, die nicht „ein/aus“ eines Tools zeigen, sondern die tatsächliche Absicherung des Servers. Sie erfordern keine Installation und werden lokal ohne sudo ausgelesen.

Externe Exposition — wie viele Dienste auf allen Schnittstellen lauschen (0.0.0.0/[::]) und von außen erreichbar sind. Wird rot hervorgehoben, wenn eine Datenbank oder ein Cache nach außen offensteht (MySQL, PostgreSQL, Redis, MongoDB, Memcached, Elasticsearch) — das ist ein direktes Sicherheitsleck (−10 auf die Sicherheitsbewertung). Quelle: ss -tuln.

Ist die Karte rot — schließen Sie die Datenbank vor dem externen Zugriff: binden Sie sie an 127.0.0.1 (bind-address in der MySQL-/PostgreSQL-Konfiguration, bind 127.0.0.1 in Redis) oder schließen Sie den Port in UFW.
„Offener Port“ ≠ „von außen erreichbar“. Ein Dienst, der auf 127.0.0.1 (loopback) lauscht, ist nur für den Server selbst sichtbar — von außen nicht erreichbar, selbst wenn der Port „offen“ ist. Deshalb ist Postfix auf Port 25, gebunden an loopback, sicher: Die Autokonfiguration setzt inet_interfaces = loopback-only (dazu ein neutraler smtpd_banner — behebt den Lynis-Hinweis MAIL-8818 zur Preisgabe der Version). Die Karte „Externe Exposition“ zählt nach außen nur, was auf 0.0.0.0/[::] lauscht; Loopback-Dienste fallen nicht darunter.
Lynis MAIL-8818 manuell (falls Sie den Mailserver selbst eingerichtet haben): Setzen Sie in /etc/postfix/main.cf smtpd_banner = $myhostname ESMTP (ohne Version und OS) und inet_interfaces = loopback-only, dann sudo systemctl restart postfix.

Sicherheitsupdates — wie viele Sicherheitspatches zur Installation anstehen und ob nach einem Kernel-Update ein Neustart nötig ist (−5 auf die Sicherheitsbewertung, wenn Patches vorliegen). Quelle: /usr/lib/update-notifier/apt-check, Datei /var/run/reboot-required. Die detaillierte Liste finden Sie auf der Seite „Sicherheitsupdates“.

# Updates installieren: sudo apt update && sudo apt upgrade # Prüfen, was nach außen lauscht: ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
Die Update-Karte funktioniert unter Ubuntu/Debian (update-notifier-common). Fehlt apt-check — zählt der Monitor die Patches über apt-get -s upgrade.

Automatische Sicherheitsupdates (unattended-upgrades) — auf der Seite „Sicherheitsupdates“ zeigt eine eigene Karte, ob die automatische Installation von Sicherheitspatches aktiviert ist und wann sie zuletzt lief. Sudo ist nicht nötig — der Status wird über apt-config dump ausgelesen.

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # aktivieren # Prüfen, was aktiviert ist: apt-config dump | grep Unattended-Upgrade

Wartung

32. Datensicherung

Ein Backup ist die wichtigste Absicherung: Datenverlust ist schlimmer als jeder Einbruch. Zwei Dinge sind nötig — ein Backup des Servers/der Websites und separat ein Backup der Panel-Datenbank (dort liegen Benutzer, WebAuthn-Schlüssel, Einstellungen, Lizenz).

Variante A — HestiaCP: Reiter Backup beim Benutzer → Schaltfläche zum Erstellen eines Backups (oder zeitgesteuert in den Servereinstellungen). Das Backup umfasst Websites und ihre Datenbanken.

Variante B — manuell (cron): Datenbank-Dump + Archiv des Verzeichnisses data/ des Panels:

# root-cron (sudo crontab -e) — tägliches Backup um 2:30 (eigene Namen/Pfade einsetzen): 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 # Archive löschen, die älter als 14 Tage sind: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
Ein Backup auf demselben Server schützt vor Fehlern, aber nicht vor dem Verlust des Servers. Kopieren Sie die Archive auf einen externen Speicher (anderer Server, S3, rclone in die Cloud). Prüfen Sie, dass die Wiederherstellung tatsächlich funktioniert.

33. Aktualisierung und Umzug des Dashboards

Aktualisierung auf eine neue Version. Erstellen Sie zuerst ein Backup. Laden Sie dann die Code-Dateien neu hoch und behalten Sie Ihre Daten:

  • überschreiben (Code): public/, includes/, assets/, cron/, database/ sowie die Root-.htaccess (Front-Controller — das Routing darf nicht von der alten Version stammen), manifest.json, sw.js;
  • nicht anrühren: config.php (DB-Daten), data/ (Berichte), logs/, tmp/ (Sitzungen und Cache).
# Nach dem Hochladen — PHP-Cache leeren (falls opcache aktiv ist): sudo systemctl reload php*-fpm
FileZilla meldet SSH_FX_PERMISSION_DENIEDPermission denied. Die Panel-Dateien gehören www-data (so wurden sie bei der Installation gesetzt), während sich der SFTP-Client unter Ihrem eigenen Benutzer verbindet, der keine Schreibrechte hat. Dem www-data das gesamte Panel „damit es läuft“ zu übergeben — genau das führt zu diesem Fehler; unten drei Wege, jeder löst das Problem.
# Variante A (empfohlen) — Eigentümer trennen: Code gehört Ihnen, Arbeitsverzeichnisse dem Webserver. # Der Webserver erhält überhaupt keine Schreibrechte auf den CODE des Panels: 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 # Variante B — ACL über den aktuellen Eigentümern (nichts wird umgezogen): 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 # Variante C — über die Gruppe www-data. Einfacher, aber Schreibrechte auf die # Panel-Dateien erhält auch der Webserver (bei einer PHP-Schwachstelle austauschbar): 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
Warum Variante A sicher ist. Das Panel schreibt nur in drei Verzeichnisse — data/ (Berichte), tmp/ (Sitzungen und Cache), logs/; diese bleiben bei www-data. Der Rest ist Code, und der Webserver braucht ihn nur zum Lesen, was die Gruppe www-data mit den Rechten 644 ermöglicht. Als Nebeneffekt: Bei einer PHP-Schwachstelle lassen sich die Panel-Dateien nicht mehr überschreiben. Auf Hosting-Panels (HestiaCP und ähnliche) ist Variante A nicht nötig: Dort gehören die Website-Dateien ohnehin dem Konto, mit dem Sie sich per SFTP anmelden, und der Webserver liest sie über die Gruppe.
Falle von Variante B: Jeder spätere chmod auf die Dateien setzt die ACL-Maske zurück, und der Zugriff verschwindet stillschweigend. Wenn nach dem „Aufräumen der Rechte“ das Hochladen erneut an Permission denied scheitert — führen Sie beide setfacl-Befehle noch einmal aus.
Das Bit 2 in Variante C ist setgid: per SFTP hochgeladene Dateien bleiben in der Gruppe www-data, sonst kann das Panel sie nicht überschreiben. Nach Variante C verbinden Sie sich in FileZilla neu — die neue Gruppe wird erst bei einer neuen Anmeldung wirksam. Prüfen: id deploy (die Gruppe www-data muss erscheinen) und ls -ld /path/to/monitor (drwxrwsr-x — das s bedeutet, dass setgid gesetzt ist).

Umzug auf einen anderen Server:

  1. Richten Sie auf dem neuen Server die Website + HTTPS ein (siehe Seite zur manuellen Installation).
  2. Kopieren Sie alle Dateien des Dashboards zusammen mit config.php, data/.
  3. Übertragen Sie die DB: mysqldump auf dem alten → Import auf dem neuen; passen Sie die DB-Daten in config.php an.
  4. Wiederholen Sie auf dem neuen Server: sudoers, Mitgliedschaft in der Gruppe adm, cron-Aufgaben.
  5. Die Lizenz ist an die Domain gebunden — bleibt die Domain gleich, funktioniert der Schlüssel weiterhin.

34. Zugang wiederherstellen (Schlüssel, Passwort oder IP verloren)

Wenn Sie sich nicht anmelden können, lässt sich alles direkt in der Datenbank auf dem Server beheben. Öffnen Sie die Datenbank (Name aus config.php):

sudo mysql MY_DB

WebAuthn-Schlüssel verloren (zweiter Faktor scheitert) — 2FA deaktivieren, per Passwort anmelden, neuen Schlüssel registrieren:

UPDATE users SET webauthn_enabled = 0;

Passwort vergessen — neuen Hash setzen (auf dem Server erzeugen und einsetzen):

# Hash für das neue Passwort erzeugen: php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # In der Datenbank (erhaltenen Hash einsetzen): # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

Selbst per IP-Filter ausgesperrt — Einschränkung deaktivieren:

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
Zugriff auf die Datenbank besteht immer: sudo mysql auf dem Server oder phpMyAdmin / Datenbankbereich im Hosting-Panel. Aktivieren Sie nach der Wiederherstellung WebAuthn und den IP-Filter erneut.

35. Alle cron-Aufgaben an einem Ort

Die Aufgabenübersicht liegt im root-Cron des Servers (hinzugefügt über sudo crontab -e). Behalten Sie nur die Zeilen der Tools, die Sie nutzen; passen Sie die Pfade an Ihren Server an.

# Server-Cron des Monitors (root) — eintragen über: sudo crontab -e # 01:30 — ClamAV-Scan gefährlicher Pfade (web, home, temp) → Kacheln „Dateien geprüft“ und „Letzter Scan“ 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — Dateiintegritätsprüfung mit AIDE (benötigt explizites --config) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # beim Start — Rechte für /var/lib/aide wiederherstellen (die tmpfiles-Datei des Pakets # aide-common.conf setzt sie auf 0700, und das Dashboard sieht die Datenbank nicht mehr) @reboot chmod 755 /var/lib/aide # 03:00 — Sicherheitsaudit mit Lynis 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — Aktualisierung der ipsum-Blockliste (level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # das ipsum-Set wird beim Start vom Dienst ipsum-load.service geladen (VOR der Firewall, sonst # sieht UFW das Set in before.rules nicht) — kein Cron. Hier nur der tägliche Refresh oben. # 06:00 — Logwatch-Bericht 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # alle 30 Min — SMART-Prüfung der Datenträger */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — Paketintegrität mit debsums 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — geplanter Bericht an E-Mail und Telegram 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # stündlich — Aktualisierung der Paketlisten (für die Karte „Sicherheitsupdates“) 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # alle 5 Min — Ressourcen-Snapshot (CPU/RAM/Netz/Datenträger) für die Seite „Leistung“ */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1
Details zu jedem finden Sie in den entsprechenden Abschnitten. Die Backup-Aufgaben (vorheriger Abschnitt) werden in denselben Cron eingetragen. Prüfen Sie nach den Änderungen: sudo crontab -l und ob der cron-Dienst aktiv ist.
Die Cron-Zeit = Zeitzone des Servers, nicht TIMEZONE aus config.php. Die Konstante TIMEZONE wirkt nur auf PHP (wie das Dashboard Datumsangaben anzeigt), aber der cron-Daemon startet Aufgaben nach der Systemzeit des OS. Stimmt die Serverzeitzone nicht mit Ihrer überein, kommt der „08:00“-Bericht zur falschen Zeit. Beispiel: Der Server steht in einer anderen Zone (Europe/London, UTC+1), Sie aber in Berlin (UTC+2) → der „08:00“-Bericht kommt bei Ihnen um 09:00. Prüfen Sie die Systemzone und passen Sie sie bei Bedarf an Ihre an:
# Aktuelle Serverzeitzone prüfen: timedatectl # Eigene Zone setzen (Beispiel) und cron neu starten: sudo timedatectl set-timezone Europe/Berlin sudo systemctl restart cron
Danach greift die Zeile 0 8 * * * um 08:00 Ortszeit. Andernfalls müsste man den Cron selbst verschieben, aber bei der Zeitumstellung Winter/Sommer würde die Verschiebung wieder abweichen — daher ist es korrekter, die Systemzone einzustellen.
Fertige Wrapper-Skripte. Ihre Arbeitskopien und eine Muster-crontab (crontab.txt) liegen im Ordner system/ neben dem Projekt, außerhalb von public_html. Das ist kein Teil der Website — sie müssen nicht ins Web-Root geladen werden; platzieren Sie sie auf dem Server unter den Systempfaden (wie in der crontab oben):
  • lynis-scan.sh/usr/local/bin/ (chmod +x) — startet lynis audit system, setzt während des Scans das Flag /tmp/lynis-running und kopiert lynis-report.dat in data/lynis/ des Dashboards;
  • logwatch_daily.sh/usr/local/bin/ (chmod +x) — erstellt den täglichen Logwatch-Bericht (sshd, fail2ban, sudo, postfix) in data/logwatch/;
  • smart-scan.sh/usr/local/bin/ (chmod +x) — erfasst den Zustand der Datenträger (smartctl) in data/disk/;
  • debsums-scan.sh/usr/local/bin/ (chmod +x) — prüft die Paketintegrität (debsums) in data/debsums/;
  • clamav-scan.sh/usr/local/bin/ (chmod +x) — ClamAV-Virenscan gefährlicher Pfade (web, home, temp); schreibt die Übersicht nach /var/log/clamav/scan.log, von wo sie die ClamAV-Seite liest (Zeile 01:30 in der crontab oben);
  • load-ipsum.sh/usr/local/bin/ (chmod +x) — aktualisiert das ipset-Set ipsum (Level 1) an Ort und Stelle, ohne die aktiven Firewall-Regeln zu unterbrechen (Zeile 04:00 in der crontab oben);
  • daily-report-all.sh/usr/local/bin/ (chmod +x) — startet den Bericht cron/daily_report.php des Dashboards (Zeile 08:00 in der crontab oben);
  • daily_report.php — ist bereits im Dashboard enthalten (cron/daily_report.php), wird über daily-report-all.sh gestartet, muss nicht separat installiert werden;
  • collect-metrics-all.sh/usr/local/bin/ (chmod +x) — startet cron/collect_metrics.php des Dashboards (Seite „Leistung“, Zeile */5 in der crontab oben); collect_metrics.php ist bereits im Dashboard enthalten, muss nicht separat installiert werden;
  • crontab.txt (system/cron/) — Muster-Aufgaben; tragen Sie die benötigten Zeilen über sudo crontab -e ein.
Der Pfad zum Skript in der crontab muss mit dem Ort übereinstimmen, an den Sie es gelegt haben.
So legen Sie ein Skript nach /usr/local/bin/. Direkt aus FileZilla lässt sich dort nichts schreiben — das Verzeichnis gehört root, und der SFTP-Client erhält SSH_FX_PERMISSION_DENIED. Vorgehen: zuerst die Datei nach /tmp hochladen (dorthin dürfen alle schreiben), dann mit einem Befehl an ihren Platz verschieben:
# in FileZilla: im Feld „Server“ /tmp eingeben und das Skript dorthin laden, # dann per SSH (install setzt Eigentümer und Rechte gleich mit, chown/chmod entfallen): sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # Prüfung: Datei am Platz, Rechte rwxr-xr-x, Syntax intakt bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
Verwechseln Sie die Verzeichnisse nicht: gebraucht wird /tmp im Wurzelverzeichnis des Servers — nicht /var/tmp und nicht tmp/ innerhalb des Dashboards selbst (Letzteres gehört www-data und ist für Ihren Benutzer gesperrt). Im FileZilla-Baum ist /tmp ein Zweig der obersten Ebene, neben var, nicht darin.
Server per Autokonfiguration aufgesetzt? Diese Wrapper und ihre cron-Aufgaben sind bereits installiert durch das Skript (in /usr/local/bin/, Log — /var/log/arciveo-cron.log) — manuell ist nichts zu tun.
Wo die Skripte das Dashboard suchen. Die Wrapper sind domänen-neutral: Sie finden Dashboard-Installationen, indem sie /home/*/web/*/public_html und /var/www/* durchgehen, und legen Berichte in deren data/ ab. Liegt das Dashboard unter einem anderen Pfad — fügen Sie ihn in der Zeile for app in … innerhalb der Skripte hinzu, sonst gelangen die Berichte von Lynis/SMART/debsums/Logwatch nicht ins Dashboard.
cron.log und Zugriffsrechte. Die Datei logs/cron.log wird zuerst vom root-Cron erstellt — sie gehört dann root, und der Tab „Cron-Journal“ im Dashboard kann sie weder lesen noch leeren. Erstellen Sie die Datei vorab als Web-Benutzer (Eigentümer des Website-Verzeichnisses; bei HestiaCP das Konto, z. B. admin) — dann schreibt der root-Cron nur an, ohne den Eigentümer zu ändern:
# vorab als Web-Benutzer erstellen (vor dem Hinzufügen der cron-Zeilen): sudo -u OWNER touch /path/to/monitor/logs/cron.log # wenn cron.log schon vom root-Cron erstellt wurde — dem Web-Benutzer übergeben: sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
Eigentümer des Verzeichnisses ermitteln: stat -c %U /path/to/monitor.
Verwaltung aus dem Dashboard. Im Bereich „System“ gibt es die Seite „Crontab“ — hier lassen sich Aufgaben ohne SSH ansehen und hinzufügen. Das Dashboard bearbeitet nur Aufgaben, die über es selbst hinzugefügt wurden (ein eigener Block in der root-crontab, gekennzeichnet durch Dienstkommentare); alles, was bereits in der crontab steht (Liste oben), wird dort schreibgeschützt als Liste „Sonstige Server-Aufgaben“ mit der Schaltfläche „In den Editor kopieren“ angezeigt — sie überträgt nur Zeitplan/Befehl ins Hinzufügen-Formular, die Ursprungszeile bleibt unangetastet. Um eine bestehende Aufgabe unter die Verwaltung des Dashboards zu „überführen“ — kopieren Sie sie in den Editor, speichern Sie, und löschen Sie dann die alte Zeile manuell (sudo crontab -e), sonst wird sie doppelt ausgeführt.
Einmalige Einrichtung auf dem Server. Die Seite benötigt ein privilegiertes Wrapper-Skript — kein nacktes sudo crontab (das wäre eine direkte Eskalation zu root für jeden, der Zugriff auf die Dashboard-Sitzung erhält), sondern ein enges Skript mit zwei Befehlen (list/set), das nur seinen eigenen Block zwischen den Dienstkommentaren berührt. Einmalig installieren:
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
Der Web-Benutzer kann sich von www-data unterscheiden — prüfen Sie, unter wem der PHP-FPM-Pool der Website läuft (ps -o user= -C php-fpm), und setzen Sie ihn in der sudoers-Zeile ein.
Neue Datei mit falschem Eigentümer hochgeladen — die Seite antwortet „Access denied.“. Wenn die Datei public/crontab_monitor.php per FTP/SFTP unter einem anderen Systembenutzer (z. B. root) als die übrigen Website-Dateien hochgeladen wurde, kann der Webserver sie nicht lesen. Gleichen Sie Eigentümer und Rechte mit einer benachbarten Datei ab und passen Sie sie an:
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

Diagnose

36. Werkzeug installiert, meldet aber „Nicht installiert“

Der Monitor erkennt vorhandene Werkzeuge über dpkg-query — die APT-Paketdatenbank. Wenn ein Werkzeug nicht über apt installiert wurde (manuell, per snap oder aus dem Quellcode), sieht dpkg es nicht.

# Über dpkg prüfen: dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # Pfad zur Binärdatei finden: which ufw fail2ban-client auditctl # sudo als www-data testen: sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

37. Fehlerbehebung (500, keine Daten)

Fehler 500 — prüfen Sie die Logs von PHP, Nginx und des Monitors selbst:

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # Monitor-Logs: tail -50 logs/monitor_$(date +%Y-%m-%d).log # Verzeichnisrechte: ls -la data/ tmp/ logs/
Dashboard auf einer Hosting-Panel (HestiaCP, ISPmanager, cPanel)? Dort läuft PHP nicht unter www-data, sondern unter dem Benutzerkonto (z. B. admin — Eigentümer des Website-Verzeichnisses). Alle sudo-Regeln und Gruppenmitgliedschaften (adm, systemd-journal) müssen für diesen Benutzer eingetragen werden, sonst zeigen die Module „Nicht aktiv / 0“ bei laufenden Diensten. Den realen PHP-Benutzer ermitteln: ps -o user= -C php-fpm | sort -u oder den Eigentümer des Website-Verzeichnisses stat -c '%U' /path/to/monitor. Ersetzen Sie in allen folgenden Befehlen www-data durch diesen. Die automatische Installation erkennt den Web-Benutzer selbst und trägt sudoers darauf ein.

Daten werden nicht angezeigt — fast immer fehlen sudo-Rechte. Prüfen Sie den konkreten Befehl als Web-Benutzer (ersetzen Sie www-data durch Ihren). Der Schalter -n = ohne Passwort, wie bei PHP — wenn ein Passwort verlangt wird, fehlt die Regel in 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
Ein Modul zeigt „Nicht aktiv“ / „0“, obwohl das Werkzeug läuft (z. B. sudo aa-status im Terminal zeigt Profile, aber die Seite „AppArmor“ zeigt „Inaktiv“). Ursache: Der Web-Benutzer hat kein sudo-Recht für den Befehl dieses Moduls. Prüfen Sie ihn aus der Liste oben: Wird ein Passwort verlangt — fügen Sie die fehlende Zeile in /etc/sudoers.d/monitor hinzu („sudo-Einrichtung“). Häufige „neue“ Befehle: /usr/sbin/aa-status (MAC), /usr/sbin/psad --Status (PSAD).
Wenn eine bestimmte Seite (Falco, ModSecurity, Auditd, offene UFW-Ports) leer ist — vergleichen Sie mit der Liste im Abschnitt zu sudo: Wahrscheinlich ist apache2ctl, ausearch, aa-status oder ss nicht erlaubt, oder der Web-Benutzer ist nicht in den Gruppen adm/systemd-journal (von dort werden die Logs von fail2ban/auth/modsec und journalctl gelesen — Falco und Kernel-Ereignisse).

38. Seite ist leer, obwohl Daten auf dem Server vorhanden sind

Symptom: Auf dem Server sind Daten vorhanden (per Shell sichtbar), doch die Seite zeigt „keine Daten“ oder einen falschen Status – zum Beispiel meldet AIDE „Nicht initialisiert“, obwohl die Datenbank erstellt wurde.

Ursache ist open_basedir: Viele Panels und Hoster beschränken den PHP-FPM-Pool auf das Domain-Verzeichnis, daher werden die PHP-Funktionen file_exists(), file_get_contents(), filemtime() auf Systempfaden (/var/lib/aide, /var/log, /proc…) blockiert. Der Monitor umgeht dies, indem er solche Pfade mit regulären Systembefehlen (cat, test, stat) liest.

# Ist die Datei per Shell sichtbar (so liest der Monitor): sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # Aktueller open_basedir-Wert für den Domain-Pool: grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
Wenn die Shell die Datei „sieht“ (VISIBLE), die Seite aber nicht – dann liegt es an open_basedir. Die richtige Lösung ist das Lesen über Systembefehle (für AIDE und den Netzwerk-Monitor bereits umgesetzt). open_basedir auf /var, /proc auszuweiten ist unnötig und weniger sicher.

39. SSL-Seite funktioniert nicht

Der Monitor prüft Zertifikate, indem er sich direkt über Port 443 mit den Domains verbindet. Ist eine Domain vom Server selbst nicht erreichbar oder der Port durch die Firewall gesperrt, schlägt die Prüfung fehl.

# Zertifikat manuell prüfen: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # Erreichbarkeit prüfen: curl -I https://monitor.example.com
Die Domains übernimmt der Monitor automatisch aus den Konfigurationen von Nginx (/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) und Apache (/etc/apache2/sites-enabled/) sowie dem aktuellen Host aus HTTP_HOST.
Automatische Subdomain-Erkennung. Subdomains werden automatisch aus öffentlichen Certificate-Transparency-Logs ermittelt und über das Netzwerk geprüft – selbst wenn sie auf anderen Servern liegen. Manuell muss nichts hinzugefügt werden.

40. Nur eine von mehreren Datenbanken sichtbar

Der Monitor verbindet sich mit MySQL unter einem Benutzer aus config.php, der nur Zugriff auf seine eigene Datenbank hat. MySQL zeigt in information_schema nur Datenbanken mit entsprechenden Rechten – die übrigen bleiben daher unsichtbar.

Damit der Monitor alle Datenbanken sieht, erteilen Sie diesem Benutzer nur Leserechte (einmalig als root; setzen Sie den Benutzernamen aus config.php ein):

sudo mysql -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* erteilt nur Leserechte – Ändern, Löschen oder Erstellen ist nicht möglich, für die Überwachung also unbedenklich.
Ohne diesen GRANT sieht das Dashboard nur seine eigene Datenbank – das ist kein Fehler, sondern eine Rechtebeschränkung. Das Dashboard verwendet kein sudo mysql: Die Datenbankliste wird über die eigene PDO-Verbindung abgerufen.

41. PostgreSQL wird auf der Seite „Datenbank“ nicht angezeigt

PostgreSQL erfordert Zugriff auf der Ebene des Benutzers postgres, den der Web-Benutzer des Dashboards nicht hat. Ein weit gefasstes sudo psql aus PHP zu öffnen, ist unsicher — stattdessen ruft das Dashboard einen eng begrenzten Wrapper ohne Parameter auf, der nur die Version, die Anzahl der Verbindungen und die Liste der Datenbanken mit Größen ausgibt. Erstellen Sie ihn:

sudo tee /usr/local/bin/monitor-pgstat >/dev/null <<'EOF' #!/bin/sh # Arciveo Monitor - read-only PostgreSQL version, connections and per-database size sudo -u postgres psql -tAc "SELECT version();" | grep -oE 'PostgreSQL [0-9.]+' echo "---" sudo -u postgres psql -tAc "SELECT count(*) FROM pg_stat_activity;" echo "---" sudo -u postgres psql -tAc "SELECT datname || '|' || pg_size_pretty(pg_database_size(datname)) FROM pg_database WHERE datistemplate = false;" EOF sudo chown root:root /usr/local/bin/monitor-pgstat sudo chmod 755 /usr/local/bin/monitor-pgstat # in /etc/sudoers.d/monitor (Benutzer = derjenige, unter dem PHP-FPM läuft): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
Wenn Sie PostgreSQL nicht verwenden — entfernen Sie die Zeile monitor-pgstat aus sudoers (Schritt 13 der manuellen Installation) und erstellen Sie das Skript nicht: Die PostgreSQL-Karte bleibt einfach inaktiv.

42. Ein Alarm wurde ausgelöst — was tun

Das Dashboard zeigt, was passiert; unten steht, was zu tun ist in typischen Situationen. Grundprinzip: nicht in Panik geraten, mit legitimer Aktivität abgleichen (Ihre Aktionen, Updates, Backups) und je nach Schweregrad reagieren.

  • Angriffskarte / viele Fail2ban-Bans — das ist für jeden Server im Internet normal (Bots probieren ständig SSH/Web durch). Wichtig ist, dass die Bans auslösen. Stellen Sie sicher, dass die SSH-Anmeldung nur per Schlüssel erfolgt (Passwort deaktiviert) und Ihre IP in ignoreip steht.
  • ModSecurity hat Anfragen blockiert — die WAF wehrt Angriffe auf die Website ab, das ist ihre Aufgabe. Wird Ihr legitimer Traffic blockiert (Fehlalarm), suchen Sie die rule id in den Details und tragen Sie eine Ausnahme in die CRS-Konfiguration ein.
  • AIDE: Dateien geändert — gleichen Sie die Liste mit dem ab, was Sie getan haben (Paket-Updates, Konfig-Änderungen sind normal). Änderungen an System-Binaries, die Sie nicht angefasst haben, sind ein Grund zur Vorsicht. Aktualisieren Sie nach legitimen Änderungen die AIDE-Datenbank.
  • debsums: Binaries/Bibliotheken geändert (außerhalb von /etc, außerhalb von /usr/share) — potenziell manipuliert. Prüfen Sie das Paket: debsums PACKAGE_NAME, installieren Sie es im Zweifel neu (apt install --reinstall).
  • ClamAV / maldet: Bedrohung gefunden — prüfen Sie die Datei in der Quarantäne, öffnen Sie sie nicht. Handelt es sich um eine Web-Shell im Website-Verzeichnis, isolieren Sie den Server und suchen Sie den Einstiegspunkt (verwundbares Plugin, geleakte Zugangsdaten).
  • Falco: kritische Ereignisse (Shell-Start im Container, Zugriff auf sensible Dateien) — analysieren Sie das Ereignis: wessen Prozess, wer es gestartet hat. Oft ist das legitime Admin-Aktivität.
  • Externe Exponierung: DBMS/Cache rot — sofort schließen: binden Sie den Dienst an 127.0.0.1 oder schließen Sie den Port in UFW. Das ist ein echtes Loch.
  • SSL läuft ab / abgelaufen — Zertifikat verlängern (Let's Encrypt verlängert sich selbst; falls nicht, prüfen Sie certbot renew oder die Einstellungen im Panel).
  • Sicherheitsupdates ausstehend — installieren: sudo apt update && sudo apt upgrade; nach einem Kernel-Update den Server neu starten.
Anzeichen eines echten Einbruchs (unbekannte Prozesse/Benutzer, geänderte Binaries, ausgehender Spam, unbekannte Cron-Jobs): trennen Sie den Server vom externen Zugriff, ziehen Sie ein Backup zur Analyse und richten Sie, falls die Daten kritisch sind, einen sauberen Server aus einem vertrauenswürdigen Backup ein — ein Rootkit zuverlässig zu entfernen ist schwierig.
Arcivéo - Security Monitor © 2026