#!/usr/bin/env sh # ============================================================================ # SELBSTSCHUTZ BEI "curl | sh" # # Problem (real passiert, x14): Bei `curl … | sh` liest die Shell das Skript aus # STDIN. Jeder Unterbefehl, der stdin genauso liest, frisst den Rest des # Skripts — die Shell setzt dann mitten im Quelltext fort und bricht ab. # Ausloeser ist apt/dpkg: auf Ubuntu laeuft dabei `needrestart`, und das # meldet sich mit "Scanning processes... / Scanning linux images...". # Symptom in der Praxis: Der Installer gibt seinen EIGENEN Quelltext aus # ("; then", "fi", Kommentarzeilen) und bleibt mitten in der Installation stehen. # Auf Nodes mit bereits installiertem Telegraf tritt das NICHT auf (kein apt). # # Loesung: Laeuft das Skript ueber eine Pipe, kopiert es sich EINMAL auf die # Platte und startet sich von dort neu. Ab da ist stdin frei und apt kann # nichts mehr verschlucken. Idempotent: der Neustart setzt die Marker-Variable. # ============================================================================ if [ -z "${NETBIRD_TG_REEXEC:-}" ] && [ ! -t 0 ]; then # Das Skript wird NICHT per `cat` von stdin kopiert: die Shell liest stdin # blockweise und hat den Anfang bereits gepuffert — `cat` bekaeme nur den # Rest und die Kopie waere unvollstaendig. Stattdessen frisch von der Quelle # laden (dieselbe URL, die der Aufrufer benutzt hat). _src="${REPO_RAW:-https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main}" _self="$(mktemp /tmp/netbird-telegraf-install.XXXXXX)" if command -v curl >/dev/null 2>&1 && \ curl -fsSL "$_src/install.sh" -o "$_self" 2>/dev/null && [ -s "$_self" ]; then chmod 0700 "$_self" # stdin ausdruecklich auf /dev/null: der Prozess erbt sonst die Pipe, und # apt/needrestart koennte weiter daraus lesen. NETBIRD_TG_REEXEC=1 REPO_RAW="$_src" sh "$_self" "$@" < /dev/null _rc=$? rm -f "$_self" exit "$_rc" fi # Kein curl oder Download fehlgeschlagen: weiter wie bisher. Dann bleibt das # Risiko, aber der Installer laeuft wenigstens (z. B. in einer Umgebung ohne # Netz beim zweiten Aufruf). rm -f "$_self" fi # ============================================================================ # NetBird Observability — Telegraf Node Installer (Linux) # # Installiert Telegraf als systemd-Dienst und verbindet die Node fest mit dem # zentralen Collector auf dem imac (Prometheus Remote Write + Loki) im NetBird. # # Unterstützt: Debian/Ubuntu/Proxmox VE (apt), RHEL/Rocky/Alma/Fedora (dnf/yum), # Alpine (apk, OpenRC), sonstige Linux via statischem Binary. # Architekturen: amd64 / x86_64, arm64 / aarch64, armv7. # Windows: install.ps1 verwenden. # # Nutzung (als root): # curl -fsSL https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main/install.sh | sh -s -- --name # Optionen: # --name Node-Name (Default: hostname -s) # --token Bearer-Token des Reverse-Proxys (Pflicht) # --collector Direktweg im NetBird statt Reverse Proxy # --role Zusatz-Profil: proxmox | apache | docker | none (Default: auto) # --pve-token Proxmox API-Token 'user@pve!name=secret' (nur --role proxmox) # --pve-node Name des PVE-Knotens im PVE-Baum (Default: per pvesh erkannt) # --pve-url PVE-API-URL (Default: https://127.0.0.1:8006/api2/json) # --apache-log

Apache-Access-Log (Default: /var/log/apache2/access.log) # --uninstall Telegraf + Konfiguration entfernen # --check Nur Versionsvergleich, nichts aendern (Exit 0 = aktuell) # --version Installierte und verfuegbare Version ausgeben # --force-update Confs/Scripts IMMER neu schreiben + Dienst neu starten # --update-only Nur Confs/Scripts aktualisieren (kein Paket, kein Restart # ohne Aenderung) — wird vom Auto-Updater-Timer genutzt # --install-update-timer [daily|hourly|weekly|min] # systemd-Timer fuer den Auto-Updater einrichten # --remove-update-timer Timer wieder entfernen # ============================================================================ set -eu # Ausgang ueber den NetBird-Reverse-Proxy: funktioniert auch fuer Hosts, die # KEIN NetBird-Peer sind (Büro-LAN, Kundenlaptop, CGNAT). Token erforderlich. PROM_URL_DEFAULT="https://imac-prometheus.x05.idx.wf/api/v1/write" LOKI_URL_DEFAULT="https://imac-loki.x05.idx.wf" COLLECTOR="${COLLECTOR:-}" # nur setzen fuer den Direktweg im NetBird TELEGRAF_TOKEN="${TELEGRAF_TOKEN:-}" NODE_NAME="${NODE_NAME:-$(hostname -s 2>/dev/null || hostname)}" ROLE="auto" # Wurden --name/--role auf der Kommandozeile gesetzt? Nur dann darf ein # Update-Lauf die in /etc/default/telegraf hinterlegten Werte ueberschreiben. NAME_GIVEN=0 ROLE_GIVEN=0 PVE_TOKEN="${PVE_TOKEN:-}" PVE_NODE="${PVE_NODE:-}" PVE_URL="${PVE_URL:-}" # Apache-Logpfad: Distributionen unterscheiden sich — Debian/Ubuntu/KeyHelp # nutzen /var/log/apache2/, RHEL/Rocky /var/log/httpd/. Der Installer sucht beim # ersten Lauf; --apache-log ueberschreibt. APACHE_LOG="" TELEGRAF_VERSION="${TELEGRAF_VERSION:-1.40.0}" # Ueberschreibbar (Tests/Mirror): REPO_RAW=... sh install.sh REPO_RAW="${REPO_RAW:-https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main}" UNINSTALL=0 CHECK=0 SHOW_VERSION=0 DIAGNOSE=0 FORCE_UPDATE=0 UPDATE_ONLY=0 TIMER_INTERVAL="" REMOVE_TIMER=0 while [ $# -gt 0 ]; do case "$1" in --name) NODE_NAME="$2"; NAME_GIVEN=1; shift 2 ;; --collector) COLLECTOR="${2:-}"; shift 2 ;; --token) TELEGRAF_TOKEN="$2"; shift 2 ;; --role) ROLE="$2"; ROLE_GIVEN=1; shift 2 ;; --pve-token) PVE_TOKEN="$2"; shift 2 ;; --pve-node) PVE_NODE="$2"; shift 2 ;; --pve-url) PVE_URL="$2"; shift 2 ;; --apache-log) APACHE_LOG="$2"; shift 2 ;; --uninstall) UNINSTALL=1; shift ;; --check) CHECK=1; shift ;; --version) SHOW_VERSION=1; shift ;; --diagnose) DIAGNOSE=1; shift ;; --force-update) FORCE_UPDATE=1; shift ;; --update-only) UPDATE_ONLY=1; shift ;; --install-update-timer) TIMER_INTERVAL="${2:-daily}" case "$TIMER_INTERVAL" in daily|hourly|weekly|[0-9]*min|[0-9]*) shift 2 ;; *) TIMER_INTERVAL="daily"; shift ;; esac ;; --remove-update-timer) REMOVE_TIMER=1; shift ;; -h|--help) sed -n '2,24p' "$0"; exit 0 ;; *) echo "Unbekannte Option: $1" >&2; exit 2 ;; esac done log() { printf '\033[1;34m[netbird-telegraf]\033[0m %s\n' "$*"; } # -------------------------------------------------------------------------- # Bestehende Installation: Werte aus /etc/default/telegraf nachladen # # WARUM: Der Auto-Updater liest Token, NODE_NAME und ROLE aus dieser Datei und # gibt sie dem Unterlauf mit. Ein MANUELLER `--update-only`-Lauf tat das nicht # und brach an der Token-Pruefung ab ("Kein Token"), obwohl der Token auf der # Maschine liegt. Die Datei ist die Quelle der Wahrheit fuer eine bereits # eingerichtete Node — die Kommandozeile ueberschreibt sie nur, wenn sie # ausdruecklich etwas anderes sagt. # # Zweiter, stiller Fehler derselben Ursache: ohne --name fiel NODE_NAME auf den # Hostnamen zurueck. Auf einem KeyHelp-Host heisst der Host aber nicht wie die # Node ('x22' vs 'x22.idx.wf-keyhelp') — ein Update-Lauf haette die Node unter # falschem Label neu registriert. Ebenso haette ein PVE-Knoten ohne --role seine # Rolle auf 'auto' verloren (kein pvesh-Drop-in mehr). # -------------------------------------------------------------------------- _rd_default() { sed -n "s/^$1=//p" /etc/default/telegraf 2>/dev/null | head -1 \ | sed "s/^['\"]//; s/['\"]$//" } if [ -r /etc/default/telegraf ]; then if [ -z "$TELEGRAF_TOKEN" ]; then TELEGRAF_TOKEN="$(_rd_default TELEGRAF_TOKEN)" [ -n "$TELEGRAF_TOKEN" ] && log "Token aus /etc/default/telegraf uebernommen." fi [ -z "$COLLECTOR" ] && COLLECTOR="$(_rd_default COLLECTOR)" if [ "$NAME_GIVEN" -eq 0 ]; then _n="$(_rd_default NODE_NAME)" if [ -n "$_n" ] && [ "$_n" != "$NODE_NAME" ]; then log "Node-Name aus /etc/default/telegraf uebernommen: $_n" NODE_NAME="$_n" fi fi if [ "$ROLE_GIVEN" -eq 0 ]; then _r="$(_rd_default ROLE)" if [ -n "$_r" ] && [ "$_r" != "none" ]; then log "Rolle aus /etc/default/telegraf uebernommen: $_r" ROLE="$_r" fi fi [ -z "$PVE_NODE" ] && PVE_NODE="$(_rd_default PVE_NODE)" [ -z "$PVE_URL" ] && PVE_URL="$(_rd_default PVE_URL)" # APACHE_LOG kommt als gequotete Liste — die Quotes entfernt _rd_default. fi # /etc/default/telegraf robust einlesen. Ein direktes "." bricht die ganze # Installation ab, wenn die Datei einen unquoted Wert mit Leerzeichen oder # Sonderzeichen enthaelt (alte APACHE_LOG-Zeile mit zwei Pfaden: # "sh: /etc/default/telegraf: /pfad/zwei.log: Permission denied" — genau das # passierte auf x22 und blockierte jedes weitere Update). # Deshalb: nur syntaktisch heile Dateien auswerten, sonst sichern und warnen. load_defaults() { [ -r /etc/default/telegraf ] || return 0 if sh -n /etc/default/telegraf >/dev/null 2>&1; then set -a; . /etc/default/telegraf; set +a return 0 fi printf '\033[1;33m[netbird-telegraf] WARNUNG:\033[0m /etc/default/telegraf ist nicht auswertbar — wird neu geschrieben.\n' _bad="/etc/default/telegraf.unlesbar.$(date +%s)" cp -a /etc/default/telegraf "$_bad" 2>/dev/null || true printf '\033[1;33m[netbird-telegraf]\033[0m Sicherung: %s\n' "$_bad" return 0 } die() { printf '\033[1;31m[netbird-telegraf] FEHLER:\033[0m %s\n' "$*" >&2; exit 1; } [ "$(id -u)" -eq 0 ] || die "Bitte als root ausführen (sudo)." # -------------------------------------------------------------------------- # --diagnose: prueft in EINEM Aufruf, warum ein Input nichts liefert. # Prueft genau die Stellen, die schon still versagt haben: # Rechte des Dienst-Benutzers, leere Dateien, Offset-Zustand, Parser-Fehler. # -------------------------------------------------------------------------- if [ "$DIAGNOSE" -eq 1 ]; then printf '=== Telegraf-Installer Diagnose ===\n' TG="$(command -v telegraf || echo /usr/local/bin/telegraf)" printf '\n-- Dienst --\n' if command -v systemctl >/dev/null 2>&1; then printf 'Status: %s\n' "$(systemctl is-active telegraf 2>/dev/null || echo unbekannt)" printf 'Benutzer: %s\n' "$(systemctl show telegraf -p User --value 2>/dev/null || echo unbekannt)" printf 'Gruppen: %s\n' "$(systemctl show telegraf -p SupplementaryGroups --value 2>/dev/null || echo -)" fi DBU="$(systemctl show telegraf -p User --value 2>/dev/null || echo telegraf)" [ -n "$DBU" ] || DBU=telegraf printf '\n-- Logpfade aus der aktiven Conf --\n' CF="/etc/telegraf/telegraf.d/apache.conf" [ -f "$CF" ] || CF="/etc/telegraf/telegraf.conf" # Beide files-Zeilen (journal + apache_access) lesen, aber entdoppeln — # sonst erscheint jeder Pfad zweimal in der Ausgabe. _files="$(sed -n 's/^[[:space:]]*files = \[\(.*\)\].*/\1/p' "$CF" | tr -d '"' | tr ',' '\n' | sed 's/^ *//; s/ *$//' | grep -v '^$' | sort -u | tr '\n' ' ')" [ -n "$_files" ] || { printf ' (keine files-Liste gefunden)\n'; } _n=0; _ok=0; _empty=0; _noread=0 for _f in $_files; do _n=$((_n+1)) if [ ! -e "$_f" ]; then printf ' FEHLT %s\n' "$_f"; continue; fi _size="$(stat -c %s "$_f" 2>/dev/null || echo 0)" if [ "$_size" -eq 0 ]; then printf ' LEER (0 B) %s\n' "$_f"; _empty=$((_empty+1)); continue; fi # Lesbarkeit aus Sicht des Dienst-Benutzers — das war die stille Fehlerquelle. if [ "$(id -u)" -eq 0 ] && command -v su >/dev/null 2>&1 && id "$DBU" >/dev/null 2>&1; then # Dateiname fuer die Shell quoten (Pfade koennen Sonderzeichen haben) _q="$(printf '%s' "$_f" | sed "s/'/'\\\\''/g")" if su -s /bin/sh "$DBU" -c "head -c 1 '$_q' >/dev/null 2>&1"; then printf ' OK %s (%s Bytes)\n' "$_f" "$_size"; _ok=$((_ok+1)) else printf ' NICHT LESBAR %s (Benutzer %s) ← URSACHE\n' "$_f" "$DBU"; _noread=$((_noread+1)) fi else printf ' vorhanden %s (%s Bytes)\n' "$_f" "$_size" fi done printf ' -> %s Pfade, %s lesbar, %s leer, %s nicht lesbar\n' "$_n" "$_ok" "$_empty" "$_noread" printf '\n-- Parser-Test (ohne Offset-Zwang) --\n' _out="$("$TG" --config /etc/telegraf/telegraf.conf --config-directory /etc/telegraf/telegraf.d --test --test-wait 2 2>&1)" printf '%s\n' "$_out" | grep -c 'E!' | sed 's/^/ Fehlerzeilen: /' printf '%s\n' "$_out" | grep -E 'E!' | head -3 | sed 's/^/ /' printf '%s\n' "$_out" | grep -cE '^[>] ' | sed 's/^/ erzeugte Messungen: /' printf '\n-- Offset-Zustand (erste Messung je Datei erst nach neuem Zugriff) --\n' ls -la /var/lib/telegraf/ 2>/dev/null | tail -5 || printf ' (kein State-Verzeichnis)\n' printf '\nHinweis: initial_read_offset = "saved-or-end" bedeutet, dass beim ERSTEN\n' printf 'Lauf nur NEUES gelesen wird. Bestehende Zeilen kommen erst mit neuen\n' printf 'Zugriffen. Fuer einen sofortigen Test: eine Datei anfordern und warten.\n' exit 0 fi # Reine Abfragen brauchen keinen Token und kein root (Timer-Verwaltung schon). if [ "$SHOW_VERSION" -eq 1 ] || [ "$CHECK" -eq 1 ]; then LOCAL_V="$(cat /etc/telegraf/CONFIG_VERSION 2>/dev/null || echo 'nicht installiert')" REMOTE_V="$(curl -fsS -m 15 "$REPO_RAW/VERSION" 2>/dev/null | tr -d ' \r\n')" [ -n "$REMOTE_V" ] || REMOTE_V="nicht erreichbar" printf 'installiert: %s\nverfuegbar: %s\n' "$LOCAL_V" "$REMOTE_V" [ "$CHECK" -eq 1 ] || exit 0 if [ "$LOCAL_V" = "$REMOTE_V" ] && [ -n "$LOCAL_V" ] && [ "$LOCAL_V" != "nicht installiert" ]; then echo "Status: aktuell"; exit 0 fi echo "Status: Update verfuegbar"; exit 10 fi [ -n "$TELEGRAF_TOKEN" ] || die "Kein Token: --token angeben (siehe README)." if [ -n "${COLLECTOR:-}" ]; then PROM_URL="http://$COLLECTOR:9090/api/v1/write"; LOKI_URL="http://$COLLECTOR:3100" log "Direktweg im NetBird: Collector $COLLECTOR" else PROM_URL="$PROM_URL_DEFAULT"; LOKI_URL="$LOKI_URL_DEFAULT" fi # -------------------------------------------------------------------------- # Uninstall # -------------------------------------------------------------------------- if [ "$UNINSTALL" -eq 1 ]; then log "Entferne Telegraf …" systemctl disable --now telegraf 2>/dev/null || rc-service telegraf stop 2>/dev/null || true if command -v apt-get >/dev/null; then apt-get remove -y telegraf >/dev/null 2>&1 || true elif command -v dnf >/dev/null; then dnf remove -y telegraf >/dev/null 2>&1 || true elif command -v yum >/dev/null; then yum remove -y telegraf >/dev/null 2>&1 || true elif command -v apk >/dev/null; then apk del telegraf >/dev/null 2>&1 || true fi rm -rf /etc/telegraf /usr/local/bin/telegraf /etc/systemd/system/telegraf.service systemctl daemon-reload 2>/dev/null || true log "Fertig." exit 0 fi # -------------------------------------------------------------------------- # Erkennung: OS, Arch, Rolle # -------------------------------------------------------------------------- OS_ID="unknown"; [ -r /etc/os-release ] && . /etc/os-release && OS_ID="${ID:-unknown}" ARCH_RAW="$(uname -m)" case "$ARCH_RAW" in x86_64|amd64) ARCH="amd64" ;; aarch64|arm64) ARCH="arm64" ;; armv7l|armhf) ARCH="armhf" ;; *) die "Architektur $ARCH_RAW wird nicht unterstützt." ;; esac IS_PVE=0; command -v pveversion >/dev/null 2>&1 && IS_PVE=1 HAS_DOCKER=0; [ -S /var/run/docker.sock ] && HAS_DOCKER=1 GPU="none" if command -v nvidia-smi >/dev/null 2>&1 && nvidia-smi -L >/dev/null 2>&1; then GPU="nvidia" elif command -v rocm-smi >/dev/null 2>&1; then GPU="amd" elif [ -e /dev/dri/renderD128 ]; then GPU="intel" fi HAS_APACHE=0; { command -v apache2ctl >/dev/null 2>&1 || command -v httpd >/dev/null 2>&1; } && HAS_APACHE=1 if [ "$HAS_APACHE" -eq 1 ] && [ -z "$APACHE_LOG" ]; then # --------------------------------------------------------------------------- # Apache-Logpfade ermitteln — MUSTER, nicht expandierte Einzelpfade. # # Warum Muster: KeyHelp/Plesk legen pro Domain eigene Logs an, und es kommen # laufend neue dazu. Eine Conf mit 43 festen Pfaden verliert jede neue Domain, # bis der Installer erneut laeuft. Ein Glob deckt sie sofort mit ab. # # Verifizierte Strukturen (auf x22 real vorgefunden): # KeyHelp : /home/users//logs//access.log (zweistufig!) # /var/log/apache2//access.log (z.B. keyhelp/) # Plesk : /var/www/vhosts//logs/access_log # cPanel : /usr/local/apache/domlogs/ # klassisch: /var/log/apache2/access.log, /var/log/httpd/access_log # --------------------------------------------------------------------------- _pats="" for _pat in \ "/var/log/apache2/*/access.log" \ "/home/users/*/logs/*/access.log" \ "/home/*/logs/*/access.log" \ "/var/www/vhosts/*/logs/access_log" \ "/usr/local/apache/domlogs/*" \ "/var/log/httpd/*access*.log" \ "/var/log/apache2/*/error.log" \ "/home/users/*/logs/*/error.log" \ "/home/*/logs/*/error.log" \ "/var/log/apache2/error.log" \ "/var/log/httpd/*error*.log"; do # shellcheck disable=SC2086 if ls $_pat >/dev/null 2>&1; then _pats="$_pats $_pat" fi done # Klassische Einzeldateien nur, wenn sie NICHT leer sind: auf KeyHelp ist # /var/log/apache2/access.log vorhanden, aber 0 Bytes — ein Input darauf # liefert still nichts. for _p in /var/log/apache2/access.log /var/log/httpd/access_log \ /var/log/apache2/other_vhosts_access.log \ /var/log/apache2/error.log /var/log/httpd/error_log; do [ -s "$_p" ] && _pats="$_pats $_p" done # Nichts gefunden? Dann die CustomLog-Direktive befragen (verlaesslichste # Quelle bei exotischen Setups) — ${APACHE_LOG_DIR} wird aufgeloest. if [ -z "$_pats" ]; then _cl_dir="$(grep -rhoE '^[[:space:]]*CustomLog[[:space:]]+(\S+|"[^"]+")' \ /etc/apache2/ /etc/httpd/ /etc/apache2/sites-enabled/ 2>/dev/null \ | sed -E 's/^[[:space:]]*CustomLog[[:space:]]+//; s/^"//; s/"$//' \ | grep -v '^|' | grep -v '^/dev/' | sort -u)" for _p in $_cl_dir; do _p="$(printf '%s' "$_p" | sed "s#\${APACHE_LOG_DIR}#/var/log/apache2#g")" [ -r "$_p" ] && _pats="$_pats $_p" done fi # Letzter Ausweg: breiter Scan der ueblichen Wurzelverzeichnisse. if [ -z "$_pats" ]; then _pats="$(find /var/log/apache2 /var/log/httpd /home -maxdepth 4 \ \( -name 'access.log' -o -name 'access_log' \ -o -name 'error.log' -o -name 'error_log' \) 2>/dev/null \ | head -200 | tr '\n' ' ')" fi [ -n "$_pats" ] && APACHE_LOG="$(printf '%s' "$_pats" | sed 's/^ *//; s/ *$//')" [ -n "$APACHE_LOG" ] || APACHE_LOG="/var/log/apache2/access.log" fi # APACHE_LOG kann mehrere, leerzeichengetrennte Pfade enthalten -> als # TOML-Liste rendern. Achtung: Pfade mit Leerzeichen sind in Apache-Logs # ungewoehnlich; wir quoten jeden Eintrag einzeln. # APACHE_FILES IMMER berechnen, NICHT nur bei erkanntem Apache. # Realer Fehler: bei ROLE=apache ohne Apache-Erkennung (z. B. httpd auf RHEL, # oder Apache wurde entfernt) blieb die Variable leer. Die Conf rendert dann zu # "files = " — ungueltiges TOML, Telegraf startet nicht mehr, ALLE Metriken aus. # Der Guard war die Ursache; die Berechnung selbst ist harmlos. APACHE_FILES="" if [ "$HAS_APACHE" -eq 1 ] || [ "$ROLE" = "apache" ]; then # set -f: Pfadnamen-Expansion abschalten! Sonst ersetzt die Shell die # Glob-Muster durch die aktuell vorhandenen Dateien — die Conf enthielte # feste Einzelpfade und jede NEUE Domain fiele heraus, bis der Installer # erneut laeuft. Wir wollen die Muster in der Conf, Telegraf loest sie bei # jedem Lauf selbst auf. # TRENNUNG access/error — wichtig fuer die Metriken: # Der Grok-Parser versteht nur das ACCESS-Format # IP ident auth [HTTPDATE] "VERB REQ" CODE BYTES # Eine Error-Zeile sieht anders aus: # [Mon Sep 14 00:10:01.123456 2026] [core:error] [pid 9] AH00124: ... # Laeuft sie durch den Access-Parser, entstehen keine Metriken (Muster passt # nicht) — schlimmer: der Aggregator zaehlt sie in Gruppen mit, die es nicht # gibt. Deshalb bekommt der Access-Input nur Access-Pfade, und die Error-Logs # gehen ueber einen eigenen Input roh nach Loki. set -f for _p in $APACHE_LOG; do case "$_p" in *error*|*Error*) APACHE_ERROR_FILES="${APACHE_ERROR_FILES:+$APACHE_ERROR_FILES, }\"$_p\"" ;; *) APACHE_FILES="${APACHE_FILES:+$APACHE_FILES, }\"$_p\"" ;; esac done set +f [ -n "$APACHE_FILES" ] || APACHE_FILES='"/var/log/apache2/access.log"' APACHE_ERROR_FILES="[${APACHE_ERROR_FILES:-\"/var/log/apache2/error.log\"}]" export APACHE_ERROR_FILES APACHE_FILES="[$APACHE_FILES]" fi # Sicherheitsnetz: die Variable MUSS nicht-leer sein, bevor apache.conf gerendert # wird. Ein leerer Wert erzeugt ungueltiges TOML und kostet die ganze Node. [ -n "${APACHE_FILES:-}" ] || APACHE_FILES='["/var/log/apache2/access.log"]' export APACHE_FILES if [ "$ROLE" = "auto" ]; then ROLE="none" [ "$IS_PVE" -eq 1 ] && ROLE="proxmox" fi log "Node=$NODE_NAME OS=$OS_ID Arch=$ARCH Rolle=$ROLE Docker=$HAS_DOCKER Apache=$HAS_APACHE GPU=$GPU Collector=$COLLECTOR" # -------------------------------------------------------------------------- # Erreichbarkeit des Collectors (NetBird Lazy Connection: erst anklopfen) # -------------------------------------------------------------------------- if command -v curl >/dev/null; then HOST_OF_URL="$(printf '%s' "$PROM_URL" | sed -E 's#^https?://([^/:]+).*#\1#')" ping -c1 -W2 "$HOST_OF_URL" >/dev/null 2>&1 || true # NetBird Lazy Connection wecken if curl -fsS -m 10 -H "Authorization: Bearer $TELEGRAF_TOKEN" "${PROM_URL%/api/v1/write}/-/ready" >/dev/null 2>&1; then log "Collector erreichbar (auth ok): $PROM_URL" else log "WARNUNG: $PROM_URL nicht erreichbar oder Token falsch — Installation läuft weiter, Telegraf puffert und retried." fi fi # -------------------------------------------------------------------------- # Update-Only: nur Konfiguration und Hilfsscripts aktualisieren. # Kein Paket installieren, /etc/default/telegraf unangetastet lassen (Token!), # Dienst nur neu starten, wenn sich wirklich etwas geaendert hat. # -------------------------------------------------------------------------- SNAPSHOT_BEFORE="" if [ "$UPDATE_ONLY" -eq 1 ]; then SNAPSHOT_BEFORE="$(mktemp)" cat /etc/telegraf/telegraf.d/*.conf /etc/telegraf/telegraf.conf 2>/dev/null > "$SNAPSHOT_BEFORE" || true for s in journal-tail syslog-tail node-info proxmox-ve gpu-rocm; do [ -f "/etc/telegraf/$s.sh" ] && cat "/etc/telegraf/$s.sh" >> "$SNAPSHOT_BEFORE" 2>/dev/null done fi # -------------------------------------------------------------------------- # Installation # -------------------------------------------------------------------------- install_apt() { # ZWEITE SCHUTZSCHICHT gegen den stdin-Fraß: # `needrestart` (Ubuntu-Standard) liest stdin und meldet # "Scanning processes... / Scanning linux images...". Mit NEEDRESTART_MODE=a # laeuft es nicht-interaktiv, und die apt-Optionen halten dpkg davon ab, # Prompts zu stellen. Der Re-Exec oben schuetzt die Shell — diese Zeile # schuetzt zusaetzlich den Installer-Ablauf selbst. export NEEDRESTART_MODE=a export DEBIAN_FRONTEND=noninteractive APT_OPTS="-y -qq -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold" command -v gpg >/dev/null || { apt-get update -qq; apt-get install $APT_OPTS gnupg >/dev/null; } command -v curl >/dev/null || apt-get install $APT_OPTS curl >/dev/null mkdir -p /etc/apt/keyrings curl -fsSL https://repos.influxdata.com/influxdata-archive.key | gpg --dearmor --yes -o /etc/apt/keyrings/influxdata-archive.gpg echo "deb [signed-by=/etc/apt/keyrings/influxdata-archive.gpg] https://repos.influxdata.com/debian stable main" > /etc/apt/sources.list.d/influxdata.list apt-get update -qq apt-get install $APT_OPTS telegraf >/dev/null } install_rpm() { cat > /etc/yum.repos.d/influxdata.repo <<'EOF' [influxdata] name = InfluxData Repository - Stable baseurl = https://repos.influxdata.com/stable/$basearch/main enabled = 1 gpgcheck = 1 gpgkey = https://repos.influxdata.com/influxdata-archive.key EOF # dnf/yum lesen ebenfalls stdin (Prompts, Plugin-Rueckfragen) -> hier mit # -y und ohne Assumeyes-Rueckfrage, damit nichts aus der Pipe greift. if command -v dnf >/dev/null; then dnf install -y -q telegraf < /dev/null else yum install -y -q telegraf < /dev/null fi } install_static() { command -v curl >/dev/null || die "curl fehlt." TB="telegraf-${TELEGRAF_VERSION}_linux_${ARCH}.tar.gz" log "Statisches Binary $TB …" TMP="$(mktemp -d)" curl -fsSL "https://dl.influxdata.com/telegraf/releases/$TB" -o "$TMP/t.tgz" tar -xzf "$TMP/t.tgz" -C "$TMP" install -m 0755 "$TMP"/telegraf-*/usr/bin/telegraf /usr/local/bin/telegraf rm -rf "$TMP" id telegraf >/dev/null 2>&1 || useradd -r -s /usr/sbin/nologin -d /etc/telegraf telegraf 2>/dev/null || adduser -S -D -H -s /sbin/nologin telegraf if command -v rc-update >/dev/null; then cat > /etc/init.d/telegraf <<'EOF' #!/sbin/openrc-run description="Telegraf" command=/usr/local/bin/telegraf command_args="--config /etc/telegraf/telegraf.conf --config-directory /etc/telegraf/telegraf.d" command_user=telegraf command_background=true pidfile=/run/telegraf.pid start_pre() { # Nur auswerten, wenn die Datei syntaktisch heil ist — sonst startet der # Dienst gar nicht (unquoted Werte mit Leerzeichen brechen das Sourcing). if [ -r /etc/default/telegraf ] && sh -n /etc/default/telegraf 2>/dev/null; then set -a; . /etc/default/telegraf; set +a fi } depend() { need net; } EOF chmod +x /etc/init.d/telegraf return fi mkdir -p /etc/systemd/system cat > /etc/systemd/system/telegraf.service <<'EOF' [Unit] Description=Telegraf After=network-online.target Wants=network-online.target [Service] User=telegraf EnvironmentFile=-/etc/default/telegraf ExecStart=/usr/local/bin/telegraf --config /etc/telegraf/telegraf.conf --config-directory /etc/telegraf/telegraf.d Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target EOF } if command -v telegraf >/dev/null 2>&1 || [ -x /usr/local/bin/telegraf ]; then log "Telegraf bereits installiert ($(telegraf --version 2>/dev/null || /usr/local/bin/telegraf --version))." [ "$UPDATE_ONLY" -eq 1 ] && log "Update-Only: Paketinstallation uebersprungen." else case "$OS_ID" in debian|ubuntu|raspbian|linuxmint|pop) install_apt ;; rhel|centos|rocky|almalinux|fedora|ol) install_rpm ;; alpine) apk add --no-cache -q curl ca-certificates < /dev/null; install_static ;; *) if command -v apt-get >/dev/null; then install_apt elif command -v dnf >/dev/null || command -v yum >/dev/null; then install_rpm else install_static; fi ;; esac fi # -------------------------------------------------------------------------- # Log-Leserechte fuer den Dienst-Benutzer # -------------------------------------------------------------------------- # Zwei verschiedene Rechte-Welten bei Apache-Logs: # /var/log/apache2/... root:adm 0640 -> usermod -aG adm genuegt # /home/users//logs/... : 0640 -> adm hilft NICHT # KeyHelp/Plesk legen die VHost-Logs in den User-Homes ab (auf x22: 64 von 84 # Pfaden waren fuer 'telegraf' unlesbar). inputs.tail meldet das NICHT — es # liefert einfach nichts, und der Node sieht "aktiv" aus. # Loesung: ein ACL fuer den Dienst-Benutzer, rekursiv und vererbbar. Damit # bleibt der Dienst im eingeschraenkten Konto (kein root noetig) und neue # Domain-Ordner erben das Recht automatisch. grant_apache_log_access() { [ "$HAS_APACHE" -eq 1 ] || return 0 _u="telegraf" id "$_u" >/dev/null 2>&1 || return 0 if ! command -v setfacl >/dev/null 2>&1; then # acl-Paket nachziehen, sonst ist die Rechtevergabe nicht moeglich. # Alle Paketmanager mit /dev/null; then NEEDRESTART_MODE=a DEBIAN_FRONTEND=noninteractive \ apt-get install -y -qq acl /dev/null 2>&1 || true elif command -v dnf >/dev/null; then dnf install -y -q acl /dev/null 2>&1 || true elif command -v yum >/dev/null; then yum install -y -q acl /dev/null 2>&1 || true elif command -v apk >/dev/null; then apk add --no-cache -q acl /dev/null 2>&1 || true fi fi if ! command -v setfacl >/dev/null 2>&1; then log "HINWEIS: setfacl fehlt — Apache-Logs in User-Homes bleiben evtl. unlesbar." log " Pruefen mit: sh install.sh --diagnose" return 0 fi # 1) Verzeichnisse aus der aktiven Conf ermitteln (nur die Wurzel, dann # rekursiv — das haelt die ACL-Zahl klein). _roots="" for _f in $(sed -n 's/^[[:space:]]*files = \[\(.*\)\].*/\1/p' \ /etc/telegraf/telegraf.d/apache.conf 2>/dev/null | tr -d '"' | tr ',' ' '); do case "$_f" in */\**) _f="${_f%%/\**}" ;; # /a/b/** -> /a/b */*) _f="${_f%/*}" ;; # /a/b/*/x -> /a/b/* esac case "$_f" in */\*|*/\*/*) _f="${_f%%\**}" ;; # Glob-Rest abschneiden esac _f="${_f%/}" [ -d "$_f" ] && _roots="$_roots $_f" done # Panel-typische Wurzeln direkt mitnehmen (auch wenn die Conf sie globt) for _d in /home/users /home/*/logs /var/www/vhosts /usr/local/apache/domlogs; do [ -d "$_d" ] && _roots="$_roots $_d" done [ -n "$_roots" ] || return 0 _n=0 for _r in $_roots; do # Durchgangsrecht (x) auf die Elternverzeichnisse, Leserecht (r) auf die # Dateien: rX vergibt x nur fuer Verzeichnisse. setfacl -R -m "u:$_u:rX" "$_r" 2>/dev/null && _n=$((_n+1)) # Neu angelegte Dateien/Ordner erben das Recht (default-ACL nur fuer dirs). if [ -d "$_r" ]; then setfacl -R -d -m "u:$_u:rX" "$_r" 2>/dev/null || true fi done [ "$_n" -gt 0 ] && log "Log-Leserechte gesetzt (ACL fuer '$_u') auf $_n Wurzel(n)." # 2) Gegentest: eine echte Datei als Dienst-Benutzer lesen. _testfile="" for _f in $(sed -n 's/^[[:space:]]*files = \[\(.*\)\].*/\1/p' \ /etc/telegraf/telegraf.d/apache.conf 2>/dev/null | tr -d '"' | tr ',' ' '); do # shellcheck disable=SC2086 _cand="$(ls $_f 2>/dev/null | head -1)" if [ -n "$_cand" ] && [ -s "$_cand" ]; then _testfile="$_cand"; break; fi done if [ -n "$_testfile" ]; then if su -s /bin/sh "$_u" -c "head -c 1 \"$_testfile\" >/dev/null 2>&1"; then log "Gegentest: '$_u' kann Apache-Logs lesen ($_testfile)." else log "WARNUNG: '$_u' kann $_testfile nicht lesen — --diagnose ausfuehren." fi fi } # -------------------------------------------------------------------------- # Konfiguration # -------------------------------------------------------------------------- # Proxmox-Parameter VOR dem Schreiben der Konfiguration ermitteln if [ "$ROLE" = "proxmox" ]; then [ -n "$PVE_NODE" ] || PVE_NODE="$(pvesh get /nodes --output-format json 2>/dev/null | sed -n 's/.*"node":"\([^"]*\)".*/\1/p' | head -1)" [ -n "$PVE_NODE" ] || PVE_NODE="$NODE_NAME" [ -n "$PVE_URL" ] || PVE_URL="https://127.0.0.1:8006/api2/json" log "Proxmox: node_name=$PVE_NODE base_url=$PVE_URL" fi mkdir -p /etc/telegraf/telegraf.d /etc/default umask 077 cat > /etc/default/telegraf < "$_dst.tmp" && mv "$_dst.tmp" "$_dst" done } # Holt eine Conf und ersetzt ${...}-Platzhalter aus /etc/default/telegraf. fetch_conf() { fetch "$1" "$2.tmp" load_defaults if command -v envsubst >/dev/null; then envsubst '${NODE_NAME} ${PROM_URL} ${LOKI_URL} ${TELEGRAF_TOKEN} ${PVE_TOKEN} ${PVE_NODE} ${PVE_URL} ${APACHE_LOG} ${APACHE_FILES} ${APACHE_ERROR_FILES}' else subst_vars "$2.tmp" "$2" NODE_NAME PROM_URL LOKI_URL TELEGRAF_TOKEN PVE_TOKEN PVE_NODE PVE_URL APACHE_LOG APACHE_FILES APACHE_ERROR_FILES fi if grep -q '\${' "$2"; then log "WARNUNG: $2 enthaelt noch unaufgeloeste Platzhalter:" grep -o '\${[A-Z_]*}' "$2" | sort -u >&2 fi rm -f "$2.tmp" } fetch_conf telegraf/base.conf /etc/telegraf/telegraf.conf # Nur Confs entfernen, die DIESER Installer verwaltet — nicht pauschal alles. # Sonst reisst ein Auto-Update fremde oder manuell ergaenzte Confs mit, die # jemand bewusst nach /etc/telegraf/telegraf.d/ gelegt hat. for _c in docker proxmox apache gpu journal nodeinfo syslog; do rm -f "/etc/telegraf/telegraf.d/$_c.conf" done [ "$HAS_DOCKER" -eq 1 ] && { fetch telegraf/docker.conf /etc/telegraf/telegraf.d/docker.conf; usermod -aG docker telegraf 2>/dev/null || true; } case "$ROLE" in proxmox) : # pvesh-Weg braucht keinen Token (siehe unten) fetch scripts/proxmox-ve.sh /etc/telegraf/proxmox-ve.sh chmod 0755 /etc/telegraf/proxmox-ve.sh # pvesh-Weg: braucht keinen Token und keine ACL (laeuft als root) cat > /etc/telegraf/telegraf.d/proxmox.conf <<'EOF' [[inputs.exec]] interval = "30s" timeout = "25s" data_format = "prometheus" commands = ["/etc/telegraf/proxmox-ve.sh"] EOF log "Proxmox: pvesh-Input aktiv (VM/CT-Metriken, kein API-Token noetig)." ;; apache) fetch_conf telegraf/apache.conf /etc/telegraf/telegraf.d/apache.conf usermod -aG adm telegraf 2>/dev/null || true grant_apache_log_access ;; docker|none) ;; *) die "Unbekannte Rolle: $ROLE" ;; esac [ "$HAS_APACHE" -eq 1 ] && [ "$ROLE" != "apache" ] && { fetch_conf telegraf/apache.conf /etc/telegraf/telegraf.d/apache.conf usermod -aG adm telegraf 2>/dev/null || true grant_apache_log_access } # --- System-Logs (journald) → Loki — nur auf systemd-Systemen --- if command -v journalctl >/dev/null 2>&1 && [ -d /run/systemd/system ]; then fetch scripts/journal-tail.sh /etc/telegraf/journal-tail.sh chmod 0755 /etc/telegraf/journal-tail.sh fetch_conf telegraf/journal.conf /etc/telegraf/telegraf.d/journal.conf # Zustandsverzeichnis fuer den Journal-Cursor mkdir -p /var/lib/telegraf-journal if [ "$ROLE" = "proxmox" ]; then : # laeuft als root (Drop-in) — voller Journal-Zugriff else chown telegraf:telegraf /var/lib/telegraf-journal 2>/dev/null || true usermod -aG systemd-journal telegraf 2>/dev/null || true fi log "Journald-Logs aktiv (Warnungen+, inkrementell ueber Cursor)." fi # --- Node-Info (LAN-IP, NetBird-IP/FQDN, PVE-URL) — auf jeder Node --- fetch scripts/node-info.sh /etc/telegraf/node-info.sh chmod 0755 /etc/telegraf/node-info.sh cat > /etc/telegraf/telegraf.d/nodeinfo.conf <<'EOF' [[inputs.exec]] interval = "300s" timeout = "25s" data_format = "prometheus" commands = ["/etc/telegraf/node-info.sh"] EOF log "Node-Info aktiv (LAN-IP, NetBird-IP/FQDN, PVE-URL)." # --- GPU (nur wenn erkannt) --- # Jeder Block wird NUR geschrieben, wenn das jeweilige Werkzeug wirklich da ist. # Ein Input ohne Binary laesst Telegraf beim Start hart scheitern -> alle Metriken aus. case "$GPU" in nvidia) cat > /etc/telegraf/telegraf.d/gpu.conf <<'EOF' [[inputs.nvidia_smi]] timeout = "10s" [inputs.nvidia_smi.tagdrop] compute_mode = ["*"] EOF log "NVIDIA-GPU erkannt: inputs.nvidia_smi aktiv." ;; amd) fetch scripts/gpu-rocm.sh /etc/telegraf/gpu-rocm.sh chmod 0755 /etc/telegraf/gpu-rocm.sh # KEIN name_override: das Script liefert die Metriknamen selbst # (gpu_utilization_gpu, gpu_temperature_edge, ...). name_override wuerde # alle Werte zu "gpu" kollabieren -> in Prometheus nur noch gpu_value. # data_format = prometheus: mit "influx" haengt Telegraf "_value" an den Namen. cat > /etc/telegraf/telegraf.d/gpu.conf <<'EOF' [[inputs.exec]] interval = "30s" timeout = "20s" data_format = "prometheus" commands = ["/etc/telegraf/gpu-rocm.sh"] EOF log "AMD-GPU erkannt: rocm-smi-exec-Input aktiv." ;; intel) # Bewusst KEIN GPU-Input: intel_gpu_top liefert kein Prometheus/Influx-Format # und ein nvidia_smi-Block ohne nvidia-smi wuerde Telegraf killen. log "Intel-GPU (/dev/dri) erkannt: kein GPU-Input aktiviert (intel_gpu_top wird nicht erfasst)." ;; *) : ;; esac chown -R telegraf:telegraf /etc/telegraf 2>/dev/null || true # -------------------------------------------------------------------------- # Force-Update: Confs und Hilfsscripts erzwungen neu schreiben. # Laeuft VOR dem Konfig-Test, damit ein Rollback unten greifen kann. # -------------------------------------------------------------------------- write_conf_backups() { # Sicherung fuer den Rollback BK="/var/backups/telegraf-$(date +%Y%m%d-%H%M%S)" mkdir -p "$BK" cp -a /etc/telegraf/telegraf.d "$BK/" 2>/dev/null || true cp -a /etc/telegraf/telegraf.conf "$BK/" 2>/dev/null || true printf '%s' "$BK" > /var/lib/telegraf-last-backup } rollback_conf() { # letzte Sicherung zurueckspielen BK="$(cat /var/lib/telegraf-last-backup 2>/dev/null || true)" [ -n "$BK" ] && [ -d "$BK/telegraf.d" ] || return 1 rm -rf /etc/telegraf/telegraf.d cp -a "$BK/telegraf.d" /etc/telegraf/telegraf.d [ -f "$BK/telegraf.conf" ] && cp -a "$BK/telegraf.conf" /etc/telegraf/telegraf.conf log "Rollback aus $BK" } if [ "$FORCE_UPDATE" -eq 1 ]; then write_conf_backups log "Force-Update: Confs und Scripts werden neu geschrieben." fi # -------------------------------------------------------------------------- # Konfig testen # -------------------------------------------------------------------------- # WICHTIG: NICHT mit --input-filter pruefen! Telegraf validiert dann nur die # gefilterten Plugins und ueberspringt die uebrigen Konfigdateien. Ein Fehler in # telegraf.d/apache.conf (z.B. ein ungueltiges Feld) lief damit durch, der # Installer meldete "OK", und erst der Dienststart scheiterte: # Job for telegraf.service failed # Ohne Filter werden ALLE Dateien geparst — genau wie beim Dienststart. Das ist # der einzige Test, der den Startfehler wirklich verhindert. TG="$(command -v telegraf || echo /usr/local/bin/telegraf)" load_defaults conf_test() { _out="$("$TG" --config /etc/telegraf/telegraf.conf \ --config-directory /etc/telegraf/telegraf.d --test --test-wait 1 2>&1)" _rc=$? # Nur echte Konfigurationsfehler zaehlen. Ein fehlendes Input-Binary # (nvidia-smi) erscheint erst beim Start als E!, nicht beim Parsen — und # Sammel-Warnungen wie die DeprecationNotice sind harmlos. if [ "$_rc" -ne 0 ] || printf '%s' "$_out" | grep -q '^.*E!.*loading config file'; then printf '%s\n' "$_out" | grep -E 'E!' | head -5 >&2 return 1 fi return 0 } if ! conf_test; then # Bei Force-/Auto-Update zuerst zurueckrollen, dann erst aufgeben. if [ "$FORCE_UPDATE" -eq 1 ] || [ "$UPDATE_ONLY" -eq 1 ]; then log "Konfiguration ungültig — Rollback auf die letzte Sicherung." rollback_conf || true conf_test || die "Konfiguration auch nach Rollback ungültig — telegraf --test prüfen." log "Rollback erfolgreich, alte Konfiguration aktiv." else die "Konfiguration ungültig — siehe Meldung oben. Einzelne Datei pruefen mit: telegraf --config /etc/telegraf/telegraf.conf --config-directory /etc/telegraf/telegraf.d --test" fi fi # -------------------------------------------------------------------------- # Dienst # -------------------------------------------------------------------------- # Proxmox-Rolle: pvesh verlangt root. Der InfluxData-Dienst laeuft als User # "telegraf" -> das Script liefert dann still nichts. Drop-in auf root. if [ "$ROLE" = "proxmox" ] && [ -d /etc/systemd/system ]; then mkdir -p /etc/systemd/system/telegraf.service.d cat > /etc/systemd/system/telegraf.service.d/10-proxmox-root.conf <<'EOF' # pvesh (VM/CT-Metriken) erfordert root-Rechte. [Service] User=root Group=root EOF log "Proxmox: systemd-Drop-in gesetzt — Dienst laeuft als root (pvesh braucht root)." fi CHANGED=1 if [ "$UPDATE_ONLY" -eq 1 ] && [ -n "$SNAPSHOT_BEFORE" ]; then SNAPSHOT_AFTER="$(mktemp)" cat /etc/telegraf/telegraf.d/*.conf /etc/telegraf/telegraf.conf 2>/dev/null > "$SNAPSHOT_AFTER" || true for s in journal-tail syslog-tail node-info proxmox-ve gpu-rocm; do [ -f "/etc/telegraf/$s.sh" ] && cat "/etc/telegraf/$s.sh" >> "$SNAPSHOT_AFTER" 2>/dev/null done if cmp -s "$SNAPSHOT_BEFORE" "$SNAPSHOT_AFTER"; then CHANGED=0; fi rm -f "$SNAPSHOT_BEFORE" "$SNAPSHOT_AFTER" if [ "$CHANGED" -eq 0 ]; then log "Update-Only: keine Aenderung an Confs/Scripts — kein Neustart noetig." # Versionsmarker TROTZDEM auf den Repo-Stand ziehen: sonst meldet der # naechste Timer-Lauf erneut ein Update und laeuft endlos im Kreis. _RV="$(curl -fsS -m 15 "$REPO_RAW/VERSION" 2>/dev/null | tr -d ' \r\n' || true)" [ -n "$_RV" ] && printf '%s\n' "$_RV" > /etc/telegraf/CONFIG_VERSION _RS="$(curl -fsS -m 20 "$REPO_RAW/install.sh" 2>/dev/null | { command -v sha256sum >/dev/null && sha256sum || shasum -a 256; } | cut -d' ' -f1 || true)" [ -n "$_RS" ] && printf '%s\n' "$_RS" > /etc/telegraf/INSTALLER_SHA printf '%s\n' "$REPO_RAW" > /etc/telegraf/REPO_RAW exit 0 fi log "Update-Only: Aenderungen erkannt — Dienst wird neu gestartet." fi if command -v systemctl >/dev/null && [ -d /run/systemd/system ]; then systemctl daemon-reload if [ "$UPDATE_ONLY" -eq 1 ]; then systemctl restart telegraf else systemctl enable --now telegraf >/dev/null systemctl restart telegraf fi sleep 3 if ! systemctl is-active --quiet telegraf; then journalctl -u telegraf -n 20 --no-pager # Haeufigste Ursache: ein aktivierter Input, dessen Binary fehlt # (z.B. nvidia_smi ohne nvidia-smi). GPU-Conf verwerfen und erneut versuchen. if [ -f /etc/telegraf/telegraf.d/gpu.conf ]; then log "Start fehlgeschlagen — entferne GPU-Conf und versuche erneut." rm -f /etc/telegraf/telegraf.d/gpu.conf systemctl reset-failed telegraf 2>/dev/null || true systemctl restart telegraf sleep 3 fi systemctl is-active --quiet telegraf || die "telegraf läuft nicht. journalctl -u telegraf -n 30 prüfen." log "WARNUNG: Telegraf läuft, aber ohne GPU-Metriken (Input-Binary fehlte)." fi elif command -v systemctl >/dev/null; then log "HINWEIS: systemd nicht aktiv (Container/Chroot) — Dienst wird beim nächsten Boot gestartet (enabled)." systemctl enable telegraf >/dev/null 2>&1 || true elif command -v rc-update >/dev/null; then rc-update add telegraf default >/dev/null 2>&1 || true rc-service telegraf restart >/dev/null 2>&1 || log "HINWEIS: OpenRC nicht aktiv (Container?) — Dienst beim nächsten Boot." fi PROM_BASE="$(printf '%s' "$PROM_URL" | sed -E 's#/api/v1/write/?$##')" [ "$GPU" != "none" ] && log "GPU=$GPU erfasst. Hinweis: im Docker-Betrieb muss der GPU-Treiber im Container verfuegbar sein (nvidia-container-toolkit bzw. /dev/dri-Mount); im systemd-Betrieb ist das automatisch der Fall." # -------------------------------------------------------------------------- # Versionsmarker fuer den Auto-Updater # -------------------------------------------------------------------------- REMOTE_V="$(curl -fsS -m 15 "$REPO_RAW/VERSION" 2>/dev/null | tr -d ' \r\n' || true)" printf '%s\n' "${REMOTE_V:-unbekannt}" > /etc/telegraf/CONFIG_VERSION # Aktive Repo-Quelle festhalten, damit der Auto-Updater denselben Weg nutzt. printf '%s\n' "$REPO_RAW" > /etc/telegraf/REPO_RAW # Hash der Installer-Logik: erkennt auch Aenderungen ohne Versionssprung # (z.B. eine Korrektur an journal-tail.sh). if curl -fsS -m 20 "$REPO_RAW/install.sh" -o /tmp/.tg-installer.sha 2>/dev/null; then if command -v sha256sum >/dev/null; then sha256sum /tmp/.tg-installer.sha | cut -d' ' -f1 > /etc/telegraf/INSTALLER_SHA elif command -v shasum >/dev/null; then shasum -a 256 /tmp/.tg-installer.sha | cut -d' ' -f1 > /etc/telegraf/INSTALLER_SHA fi rm -f /tmp/.tg-installer.sha fi chmod 0644 /etc/telegraf/CONFIG_VERSION /etc/telegraf/INSTALLER_SHA 2>/dev/null || true # -------------------------------------------------------------------------- # Auto-Updater-Timer # -------------------------------------------------------------------------- install_update_timer() { IV="${1:-daily}" case "$IV" in daily) ONCAL="*-*-* 04:30:00" ;; hourly) ONCAL="*-*-* *:30:00" ;; weekly) ONCAL="Mon *-*-* 04:30:00" ;; *min) ONCAL="" ;; # OnUnitActiveSec statt OnCalendar *) ONCAL="*-*-* 04:30:00" ;; esac # Der Updater holt sich das Script frisch und ruft sich selbst mit # --update-only auf. Token kommt aus /etc/default/telegraf (nicht im Repo). cat > /usr/local/bin/telegraf-installer-update <<'UPD' #!/bin/sh # Auto-Updater: nur bei echter Aenderung etwas tun. set -u # Repo-Quelle: aus der Umgebung, sonst aus der beim Install hinterlegten # Datei, sonst Default. So funktioniert der Updater auch mit Mirror/Testserver. REPO_RAW="${REPO_RAW:-$(cat /etc/telegraf/REPO_RAW 2>/dev/null || echo https://gitea.m-m.bio/mm/netbird-telegraf-installer/raw/branch/main)}" LOG="/var/log/telegraf-installer-update.log" say() { printf '%s %s\n' "$(date -Is)" "$*" >> "$LOG"; } LOCAL_V="$(cat /etc/telegraf/CONFIG_VERSION 2>/dev/null || echo '')" REMOTE_V="$(curl -fsS -m 20 "$REPO_RAW/VERSION" 2>/dev/null | tr -d ' \r\n' || true)" [ -n "$REMOTE_V" ] || { say "Repo nicht erreichbar (NetBird down?) — kein Update."; exit 0; } NEED=0 [ "$LOCAL_V" != "$REMOTE_V" ] && NEED=1 # Hash-Vergleich: erkennt Script-Fixes ohne Versionssprung if [ "$NEED" -eq 0 ] && command -v sha256sum >/dev/null; then TMP="$(mktemp)"; curl -fsS -m 25 "$REPO_RAW/install.sh" -o "$TMP" 2>/dev/null || true if [ -s "$TMP" ]; then R_SHA="$(sha256sum "$TMP" | cut -d' ' -f1)" L_SHA="$(cat /etc/telegraf/INSTALLER_SHA 2>/dev/null || echo '')" [ "$R_SHA" != "$L_SHA" ] && NEED=1 fi rm -f "$TMP" fi if [ "$NEED" -eq 0 ]; then say "aktuell ($LOCAL_V) — nichts zu tun." exit 0 fi say "Update $LOCAL_V -> $REMOTE_V" TMPI="$(mktemp)" if ! curl -fsS -m 60 "$REPO_RAW/install.sh" -o "$TMPI" 2>/dev/null; then say "Download fehlgeschlagen."; rm -f "$TMPI"; exit 1 fi # --update-only: nur Confs/Scripts, kein Paket, kein Tokenwechsel. # WICHTIG: REPO_RAW und die Node-Identitaet muessen mitgegeben werden, sonst # faellt NODE_NAME auf den Hostnamen der AUFRUFENDEN Maschine zurueck und der # Unterlauf holt die Version von der falschen Quelle. UPD_NAME="$(sed -n 's/^NODE_NAME=//p' /etc/default/telegraf 2>/dev/null | head -1)" UPD_ROLE="$(sed -n 's/^ROLE=//p' /etc/default/telegraf 2>/dev/null | head -1)" UPD_TOKEN="$(sed -n 's/^TELEGRAF_TOKEN=//p' /etc/default/telegraf 2>/dev/null | head -1)" UPD_COLL="$(sed -n 's/^COLLECTOR=//p' /etc/default/telegraf 2>/dev/null | head -1)" [ -n "$UPD_NAME" ] || UPD_NAME="$(hostname -s)" set -- --update-only --name "$UPD_NAME" --token "$UPD_TOKEN" [ -n "$UPD_ROLE" ] && set -- "$@" --role "$UPD_ROLE" [ -n "$UPD_COLL" ] && set -- "$@" --collector "$UPD_COLL" if REPO_RAW="$REPO_RAW" sh "$TMPI" "$@" >> "$LOG" 2>&1; then say "Update erfolgreich." else say "Update FEHLGESCHLAGEN — siehe oben. Alte Konfiguration bleibt aktiv." fi rm -f "$TMPI" UPD chmod 0755 /usr/local/bin/telegraf-installer-update if [ -d /etc/systemd/system ]; then cat > /etc/systemd/system/telegraf-installer-update.service <<'EOF' [Unit] Description=Telegraf-Installer Auto-Update (nur bei Aenderung) After=network-online.target Wants=network-online.target [Service] Type=oneshot ExecStart=/usr/local/bin/telegraf-installer-update EOF if [ -n "$ONCAL" ]; then cat > /etc/systemd/system/telegraf-installer-update.timer < /etc/systemd/system/telegraf-installer-update.timer </dev/null 2>&1 \ && log "Auto-Updater aktiv ($IV) — Log: /var/log/telegraf-installer-update.log" \ || log "WARNUNG: Timer konnte nicht aktiviert werden (systemd nicht aktiv?)." else log "HINWEIS: kein systemd — Auto-Updater nur manuell (telegraf-installer-update)." fi } remove_update_timer() { command -v systemctl >/dev/null 2>&1 || { log "Auto-Updater: systemctl fehlt — Dateien werden entfernt."; rm -f /etc/systemd/system/telegraf-installer-update.timer /etc/systemd/system/telegraf-installer-update.service /usr/local/bin/telegraf-installer-update; return 0; } systemctl disable --now telegraf-installer-update.timer >/dev/null 2>&1 || true rm -f /etc/systemd/system/telegraf-installer-update.timer \ /etc/systemd/system/telegraf-installer-update.service \ /usr/local/bin/telegraf-installer-update systemctl daemon-reload 2>/dev/null || true log "Auto-Updater entfernt." } if [ "$REMOVE_TIMER" -eq 1 ]; then if command -v systemctl >/dev/null 2>&1; then remove_update_timer else log "Auto-Updater: kein systemd — nichts zu entfernen."; fi fi if [ -n "$TIMER_INTERVAL" ]; then install_update_timer "$TIMER_INTERVAL"; fi log "OK — Node '$NODE_NAME' liefert an $PROM_URL (Metriken) und $LOKI_URL (Logs)." log "Prüfen: curl -s -H 'Authorization: Bearer ' '$PROM_BASE/api/v1/query?query=system_uptime{node=\"$NODE_NAME\"}'" log "Grafana: http://100.83.134.46:3000 (nur im NetBird)"