Der 35-Watt-Mitbewohner (Teil 5): Monitoring oder „Warum will das Internet ständig meine .php-Files?“

Was, du hast noch kein eigenes Grafana-Dashboard? Du denkst, das ist nur was für Enterprise-Umgebungen und für das private Homelab viel zu umständlich?

Dachte ich auch immer. Wenn man Begriffe wie Prometheus, Exporter, Loki und Promtail hört, klingt das erst einmal nach massivem Overhead und einer steilen Lernkurve. Aber ich habe mal “aus Spaß” damit angefangen und muss sagen: Dank Docker ist das Setup schneller hochgezogen, als man „Monitoring“ buchstabieren kann.

In diesem Post zeige ich euch, wie ich mein Setup überwache und wie ich fiese Bots direkt an der Haustür abweise.

Was möchte ich eigentlich wissen?

Ihr kennt mein Setup noch aus dem Artikel über Cloudflare Tunnel und Docker Stacks. Ich möchte Transparenz für meine Nginx-Instanzen, den Proxmox-Host, den Ubuntu-Server (inklusive SSH-Logs) und natürlich meine Docker-Container.

Das sind eine ganze Menge Endpunkte, aber zum Glück gibt es für fast alles einen passenden Exporter, der die Daten mundgerecht serviert:

  • pve-exporter: Holt die Metriken direkt aus der Proxmox-API.
  • node-exporter: Der Klassiker für Linux-Host-Metriken (CPU, RAM, Disk).
  • cAdvisor: Schaut tief in die Docker-Container und cgroups.
  • promtail: Der „Log-Staubsauger“, der alles zu Loki schickt.

Als Backend dienen Loki (für die Logs) und Prometheus (für die zeitbasierten Metriken). Das Ganze sieht architektonisch so aus:

Die Konfiguration ist unter Docker meistens nur ein Copy-Paste-Job. Lediglich beim Proxmox-Exporter muss man kurz Hand anlegen und einen API-Token mit den richtigen Berechtigungen im PVE-Interface erstellen.

Dashboards: Das Auge isst mit

Man muss das Rad nicht neu erfinden. Auf grafana.com/dashboards gibt es eine riesige Auswahl an Community-Dashboards, die man per ID einfach importieren kann. Meine Favoriten-Liste:

Ich habe das Ganze per Infrastructure as Code (IaC) gelöst. Die Provisionierung erfolgt über YAML-Files, damit bei einem Neuaufsetzen alles sofort wieder da ist. Die Details dazu findet ihr in meinem GitHub-Repo.

Erste Entdeckungen: “Hallo, Bot-Netz!”

Kaum waren die ersten Nginx-Logs in Grafana sichtbar, kam das große Staunen: „Wow, hier ist ja richtig Traffic!“. Ein zweiter Blick verriet jedoch schnell: Das sind keine Fans, sondern Bots.

Es hagelte 404-Fehler für Pfade wie /tool.php, /admin-footer.php oder /abc.php. Da meine Seiten auf Astro basieren (statisches HTML), gibt es hier für PHP-Exploits absolut nichts zu holen aber die Bots probieren es trotzdem im Sekundentakt.

Log-Auszug (Anonymisiert): > request for /tool.php with HTTP status: 404 from 172.22.0.8 located in IT request for /jga.php with HTTP status: 404 from 172.22.0.8 located in IT

Die Lösung: Traefik + CrowdSec

Zuerst dachte ich, Cloudflare würde im Free-Plan alles wegbügeln. Pustekuchen. Viele dieser Scans kommen trotzdem durch. Da Traefik hinter dem Cloudflare-Proxy sitzt, sieht man standardmäßig nur die Cloudflare-IP ein Bann würde also den Proxy blockieren.

Die Lösung: CrowdSec als moderner Nachfolger von fail2ban, kombiniert mit einem Traefik-Plugin. Der Clou ist das Label forwardedHeadersCustomName=CF-Connecting-IP. Damit weiß das CrowdSec-Plugin, dass es die echte Besucher-IP aus dem Cloudflare-Header nehmen muss.

Hier ist mein docker-compose.yml Ausschnitt für das dynamische Duo:

version: "3.8"

services:
  crowdsec:
    image: crowdsecurity/crowdsec:latest
    container_name: crowdsec
    restart: unless-stopped
    entrypoint: ["/bin/sh", "-c", "export BOUNCER_KEY_traefik=$(cat /run/secrets/crowdsec_bouncer_key) && exec /docker_start.sh"]
    environment:
      COLLECTIONS: "crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/base-http-scenarios"
    volumes:
      - crowdsec-config:/etc/crowdsec
      - crowdsec-data:/var/lib/crowdsec/data
      - traefik-access-logs:/var/log/traefik:ro
    networks:
      - apps
      - monitoring_net
    secrets:
      - crowdsec_bouncer_key

  traefik:
    image: traefik:v3.6.6
    container_name: traefik
    restart: unless-stopped
    depends_on:
      - crowdsec
    command:
      - "--accesslog=true"
      - "--accesslog.filepath=/var/log/traefik/access.log"
      - "--experimental.plugins.crowdsec-bouncer.modulename=[github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin](https://github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin)"
      - "--experimental.plugins.crowdsec-bouncer.version=v1.4.1"
    labels:
      - "traefik.enable=true"
      - "traefik.http.middlewares.crowdsec.plugin.crowdsec-bouncer.crowdseclapihost=crowdsec:8080"
      - "traefik.http.middlewares.crowdsec.plugin.crowdsec-bouncer.forwardedHeadersCustomName=CF-Connecting-IP"
    networks:
      - cloudflare
      - apps

Die “Middleware-Polizei” im Einsatz

Jetzt muss ich bei jedem Web-Service (wie meinem Portfolio oder dem Blog) nur noch das entsprechende Middleware-Label hinzufügen. Jeder Request wird gegen die CrowdSec-Datenbank geprüft. Ist die IP als “böse” markiert, blockt Traefik den Zugriff sofort.

blog-astro-web-prd:
  image: nginx:alpine
  labels:
    - "traefik.enable=true"
    - "traefik.http.routers.blog-astro-prd.rule=Host(`www.slashgordon.link`)"
    - "traefik.http.routers.blog-astro-prd.middlewares=crowdsec@docker"

Fazit: Lohnt sich der Aufwand?

Definitiv. Es ist ein beruhigendes Gefühl, in Grafana zu sehen, wie die “Banned IPs” nach oben gehen, während die CPU-Last meines 35-Watt-Mitbewohners stabil bleibt. Monitoring ist nicht nur Spielerei es ist das Immunsystem des Homelabs.

Seit CrowdSec aktiv ist, sieht man im Dashboard sehr schön die Korrelation: Sobald ein Bot anfängt, meine .php-Pfade zu scannen, wird er nach wenigen Versuchen komplett “versenkt”.