#!/bin/bash # x22-log-backfill.sh — Alte Apache-Logs von x22 messen, parsen, als Archiv # bereitstellen. DIESES SKRIPT AENDERT NICHTS AM LAUFENDEN DIENST. # # SICHERHEIT (die drei Wege, die schon schiefgingen): # 1. Es importiert NICHTS in die Produktions-TSDB. Der Import laeuft # ausschliesslich ueber prom-archive-import.sh --file auf dem imac, # der nur in die getrennte Archiv-Instanz (:9091) schreibt. # 2. Es stoppt nichts und loescht nichts. Ausgabe geht nach /root/apache-archive. # 3. Die Ausgabe wird VOR der Uebergabe geprueft (Einheiten, Zukunft, Inhalt). # # AUFRUF auf x22 (als root): # curl -fsSL https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main/scripts/x22-log-backfill.sh -o /root/x22-backfill.sh # bash /root/x22-backfill.sh # MESSEN (nichts wird veraendert) # bash /root/x22-backfill.sh --backfill # parsen und Tarball bauen # # Danach steht am Ende der genaue Befehl fuer den imac. set -eu REPO_RAW="${REPO_RAW:-https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main}" OUT_DIR="${OUT_DIR:-/root/apache-archive}" NODE="${NODE:-x22.idx.wf-keyhelp}" BACKFILL=0 [ "${1:-}" = "--backfill" ] && BACKFILL=1 log() { printf '\033[1;34m[backfill]\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m[backfill] WARNUNG:\033[0m %s\n' "$*" >&2; } die() { printf '\033[1;31m[backfill] FEHLER:\033[0m %s\n' "$*" >&2; exit 1; } [ "$(id -u)" -eq 0 ] || die "Bitte als root ausfuehren." # -------------------------------------------------------------------------- # 1) Bestand MESSEN — nicht raten # -------------------------------------------------------------------------- log "Bestandsaufnahme der Apache-Logs (aendert nichts) …" mkdir -p /tmp/x22-archiv curl -fsSL -m 30 "$REPO_RAW/scripts/apache-log-survey.py" -o /tmp/x22-archiv/survey.py \ || die "survey.py konnte nicht geladen werden." # ACHTUNG: der Survey kennt KEIN --node (er beschreibt nur den lokalen Host). # Der Fortschritt wird nach stdout UND in die Datei geschrieben; das Skript # erzwingt Zeilenpufferung, sonst bleibt bestand.txt bei einer Pipe bis zum # Prozessende leer und der laufende Scan sieht wie ein Haenger aus. python3 /tmp/x22-archiv/survey.py 2>&1 | tee /tmp/x22-archiv/bestand.txt # Wie viel Historie liegt wirklich vor? Das entscheidet ueber den Nutzen. FILES=$(find /var/log/apache2 /home/users -name 'access.log*' -o -name 'error.log*' 2>/dev/null | wc -l) log "Gefundene Logdateien (inkl. rotierte/.gz): $FILES" if [ "$BACKFILL" -eq 0 ]; then echo log "Das war NUR die Messung — es wurde nichts veraendert." echo " Weiter mit: bash $0 --backfill" echo " Bestand: /tmp/x22-archiv/bestand.txt" exit 0 fi # -------------------------------------------------------------------------- # 2) Archiv bauen # -------------------------------------------------------------------------- log "Archiver laden …" curl -fsSL -m 30 "$REPO_RAW/scripts/apache-log-archive.py" -o /tmp/x22-archiv/archive.py \ || die "apache-log-archive.py konnte nicht geladen werden." log "Logs parsen (kann bei viel Historie dauern) …" python3 /tmp/x22-archiv/archive.py --node "$NODE" --out "$OUT_DIR" --include-raw 2>&1 | tail -20 # -------------------------------------------------------------------------- # 3) Selbst pruefen, BEVOR etwas weitergereicht wird # -------------------------------------------------------------------------- OM="$OUT_DIR/apache-archive.openmetrics" [ -s "$OM" ] || die "Keine Ausgabe erzeugt: $OM fehlt oder ist leer." log "Selbstpruefung der Ausgabe" python3 - "$OM" <<'PY' || exit 1 import sys, time, collections path = sys.argv[1] pts = [l.rstrip("\n") for l in open(path) if l and not l.startswith("#") and l.strip()] if not pts: print(" FEHLER: keine Datenpunkte"); sys.exit(1) ts = sorted(int(l.rsplit(" ", 1)[1]) for l in pts) now = int(time.time()) f = lambda t: time.strftime("%d.%m.%Y %H:%M", time.gmtime(t)) print(f" Datenpunkte: {len(pts):,}") print(f" Zeitraum: {f(ts[0])} .. {f(ts[-1])} UTC") domains = collections.Counter() for l in pts: i = l.find('domain="') if i >= 0: j = l.find('"', i + 8) domains[l[i+8:j]] += 1 print(f" Domains: {len(domains)}") for d, n in domains.most_common(8): print(f" {d}: {n} Punkte") # Einheiten-Wache: alles nach 2100 ist kein Sekundenwert if ts[-1] > 4102444800: print(" FEHLER: Zeitstempel nach dem Jahr 2100 -> Millisekunden statt Sekunden") print(" promtool wuerde Bloecke im Jahr 58.660 erzeugen. Abbruch.") sys.exit(1) print(" Einheit: OK (Sekunden)") # Zukunft: der Fehler, der die Produktion kosten kann z = [t for t in ts if t > now + 300] if z: print(f" FEHLER: {len(z)} Punkte in der ZUKUNFT (max {f(z[-1])}).") print(" Ursache: falsche Systemuhr oder Log-Zeitzone. Abbruch.") sys.exit(1) print(" Zukunft: OK") # Plausibilitaet: sind die Zahlen aus echten Logs? (nicht alle identisch) if len(domains) < 2: print(f" WARNUNG: nur {len(domains)} Domain(s) erkannt — Logs vollstaendig?") PY TAR="$OUT_DIR/apache-archive.tar.gz" [ -s "$TAR" ] && log "Tarball: $TAR ($(du -h "$TAR" | cut -f1))" # -------------------------------------------------------------------------- # 4) Uebergabe — der sichere Weg # -------------------------------------------------------------------------- cat <= 1h wegen Stunden-Buckets!) ssh mm@192.168.188.177 'curl -s -G http://127.0.0.1:9091/api/v1/query_range \\ --data-urlencode "query=apache_archive_requests_total" \\ --data-urlencode "start=\$(date -d "-30 days" +%s)" \\ --data-urlencode "end=\$(date +%s)" --data-urlencode "step=3600" | head -c 400' Die Produktions-TSDB bleibt bei diesem Weg unberuehrt. Der Tarball mit den Rohlogs liegt unter $TAR — aufheben, er ist die Quelle fuer Einzelfaelle. EOF