diff --git a/install.sh b/install.sh index e058888..9ac3a7d 100755 --- a/install.sh +++ b/install.sh @@ -131,7 +131,9 @@ if [ "$DIAGNOSE" -eq 1 ]; then printf '\n-- Logpfade aus der aktiven Conf --\n' CF="/etc/telegraf/telegraf.d/apache.conf" [ -f "$CF" ] || CF="/etc/telegraf/telegraf.conf" - _files="$(sed -n 's/^[[:space:]]*files = \[\(.*\)\].*/\1/p' "$CF" | tr -d '"' | tr ',' ' ')" + # 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 @@ -230,91 +232,77 @@ 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 -# Apache-Logpfad ermitteln: erst explizit gesetzter Wert, dann uebliche Orte, -# dann die von Apache selbst konfigurierte CustomLog-Direktive. if [ "$HAS_APACHE" -eq 1 ] && [ -z "$APACHE_LOG" ]; then - # Erst die von Apache selbst konfigurierten Pfade — das ist die einzige - # verlaessliche Quelle. Viele Panels (KeyHelp, Plesk) setzen pro VHost eigene - # CustomLog-Pfade; der "globale" Standardpfad existiert dann oft leer, und - # ein Input darauf liefert still nichts. - _cl="$(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)" - if [ -n "$_cl" ]; then - # Alle konfigurierten Pfade als Glob-Liste (Telegraf akzeptiert Listen). - _found="" - for _p in $_cl; do - # Variablen wie ${APACHE_LOG_DIR} aufloesen - _p="$(printf '%s' "$_p" | sed "s#\${APACHE_LOG_DIR}#/var/log/apache2#g; s#\${APACHE_LOG_DIR:-/var/log/apache2}#/var/log/apache2#g")" - [ -r "$_p" ] && _found="$_found $_p" - done - [ -n "$_found" ] && APACHE_LOG="$(printf '%s' "$_found" | sed 's/^ //')" - fi -fi - -if [ "$HAS_APACHE" -eq 1 ] && [ -z "$APACHE_LOG" ]; then - for _p in /var/log/apache2/access.log /var/log/httpd/access_log \ - /var/log/apache2/other_vhosts_access.log /var/log/httpd/access.log; do - [ -r "$_p" ] && { APACHE_LOG="$_p"; break; } - done - # Panel-Hoster legen PRO DOMAIN eigene Logs an, oft ZWEISTUFIG - # (/logs//access.log). Ein einstufiges Glob trifft das nicht. - # Muster je Panel — die reale Struktur wurde auf x22 (KeyHelp) verifiziert: - # KeyHelp : /home/users//logs//access.log + # --------------------------------------------------------------------------- + # 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/ (bzw. /home//logs) - # ALLE passenden Muster sammeln, nicht nur das erste: auf einem KeyHelp-Host - # existieren oft BEIDE Strukturen — /var/log/apache2/keyhelp/access.log UND - # /home/users//logs//access.log. Nur eines zu waehlen verliert - # die Haelfte der Requests. - if [ -z "$APACHE_LOG" ]; then - _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"; do - # shellcheck disable=SC2086 - if ls $_pat >/dev/null 2>&1; then - _pats="$_pats $_pat" - fi - done - # Globale Standarddatei nur, wenn sie NICHT leer ist (auf KeyHelp ist - # /var/log/apache2/access.log oft vorhanden, aber 0 Bytes). - for _p in /var/log/apache2/access.log /var/log/httpd/access_log; do - [ -s "$_p" ] && _pats="$_pats $_p" - done - [ -n "$_pats" ] && APACHE_LOG="$(printf '%s' "$_pats" | sed 's/^ //')" - fi - # Einzelne, flache Faelle weiterhin abdecken - if [ -z "$APACHE_LOG" ]; then - for _d in /var/log/apache2 /var/log/httpd; do - [ -d "$_d" ] || continue - if ls "$_d"/*access*.log "$_d"/access_log >/dev/null 2>&1; then - APACHE_LOG="$_d/*access*.log" - break - fi + # 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"; 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; 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 - if [ -z "$APACHE_LOG" ]; then - # Aus der Config lesen (KeyHelp/Plesk setzen eigene Pfade) - _cl="$(grep -rhoE '^\s*CustomLog\s+\S+' /etc/apache2/ /etc/httpd/ 2>/dev/null \ - | awk '{print $2}' | grep -v '^|' | head -1)" - [ -n "$_cl" ] && [ -r "$_cl" ] && APACHE_LOG="$_cl" + # 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' 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. if [ "$HAS_APACHE" -eq 1 ]; then APACHE_FILES="" + # 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. + set -f for _p in $APACHE_LOG; do APACHE_FILES="${APACHE_FILES:+$APACHE_FILES, }\"$_p\"" done + set +f [ -n "$APACHE_FILES" ] || APACHE_FILES='"/var/log/apache2/access.log"' APACHE_FILES="[$APACHE_FILES]" export APACHE_FILES @@ -437,6 +425,87 @@ else 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. + if command -v apt-get >/dev/null; then + 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 # -------------------------------------------------------------------------- @@ -530,11 +599,17 @@ case "$ROLE" in 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 ;; + 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; } +[ "$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