fix(apache): Glob-Muster in der Conf behalten (set -f statt Expansion) + ACL fuer User-Home-Logs (KeyHelp) + Diagnose entdoppelt

This commit is contained in:
mm committed 2026-09-14 00:43:39 +02:00
1 parent 911823514d
commit 3c0c800f90
1 file changed
+148 -73
+148 -73
View File
@@ -131,7 +131,9 @@ if [ "$DIAGNOSE" -eq 1 ]; then
printf '\n-- Logpfade aus der aktiven Conf --\n' printf '\n-- Logpfade aus der aktiven Conf --\n'
CF="/etc/telegraf/telegraf.d/apache.conf" CF="/etc/telegraf/telegraf.d/apache.conf"
[ -f "$CF" ] || CF="/etc/telegraf/telegraf.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 "$_files" ] || { printf ' (keine files-Liste gefunden)\n'; }
_n=0; _ok=0; _empty=0; _noread=0 _n=0; _ok=0; _empty=0; _noread=0
for _f in $_files; do for _f in $_files; do
@@ -230,91 +232,77 @@ elif [ -e /dev/dri/renderD128 ]; then
GPU="intel" GPU="intel"
fi fi
HAS_APACHE=0; { command -v apache2ctl >/dev/null 2>&1 || command -v httpd >/dev/null 2>&1; } && HAS_APACHE=1 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 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 # Apache-Logpfade ermitteln — MUSTER, nicht expandierte Einzelpfade.
# CustomLog-Pfade; der "globale" Standardpfad existiert dann oft leer, und #
# ein Input darauf liefert still nichts. # Warum Muster: KeyHelp/Plesk legen pro Domain eigene Logs an, und es kommen
_cl="$(grep -rhoE '^[[:space:]]*CustomLog[[:space:]]+(\S+|"[^"]+")' \ # laufend neue dazu. Eine Conf mit 43 festen Pfaden verliert jede neue Domain,
/etc/apache2/ /etc/httpd/ /etc/apache2/sites-enabled/ 2>/dev/null \ # bis der Installer erneut laeuft. Ein Glob deckt sie sofort mit ab.
| sed -E 's/^[[:space:]]*CustomLog[[:space:]]+//; s/^"//; s/"$//' \ #
| grep -v '^|' | grep -v '^/dev/' | sort -u)" # Verifizierte Strukturen (auf x22 real vorgefunden):
if [ -n "$_cl" ]; then # KeyHelp : /home/users/<user>/logs/<domain>/access.log (zweistufig!)
# Alle konfigurierten Pfade als Glob-Liste (Telegraf akzeptiert Listen). # /var/log/apache2/<vhost>/access.log (z.B. keyhelp/)
_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
# (<user>/logs/<domain>/access.log). Ein einstufiges Glob trifft das nicht.
# Muster je Panel — die reale Struktur wurde auf x22 (KeyHelp) verifiziert:
# KeyHelp : /home/users/<user>/logs/<domain>/access.log
# Plesk : /var/www/vhosts/<domain>/logs/access_log # Plesk : /var/www/vhosts/<domain>/logs/access_log
# cPanel : /usr/local/apache/domlogs/<domain> (bzw. /home/<user>/logs) # cPanel : /usr/local/apache/domlogs/<domain>
# ALLE passenden Muster sammeln, nicht nur das erste: auf einem KeyHelp-Host # klassisch: /var/log/apache2/access.log, /var/log/httpd/access_log
# existieren oft BEIDE Strukturen — /var/log/apache2/keyhelp/access.log UND # ---------------------------------------------------------------------------
# /home/users/<user>/logs/<domain>/access.log. Nur eines zu waehlen verliert _pats=""
# die Haelfte der Requests. for _pat in \
if [ -z "$APACHE_LOG" ]; then "/var/log/apache2/*/access.log" \
_pats="" "/home/users/*/logs/*/access.log" \
for _pat in \ "/home/*/logs/*/access.log" \
"/var/log/apache2/*/access.log" \ "/var/www/vhosts/*/logs/access_log" \
"/home/users/*/logs/*/access.log" \ "/usr/local/apache/domlogs/*" \
"/home/*/logs/*/access.log" \ "/var/log/httpd/*access*.log"; do
"/var/www/vhosts/*/logs/access_log" \ # shellcheck disable=SC2086
"/usr/local/apache/domlogs/*" \ if ls $_pat >/dev/null 2>&1; then
"/var/log/httpd/*access*.log"; do _pats="$_pats $_pat"
# shellcheck disable=SC2086 fi
if ls $_pat >/dev/null 2>&1; then done
_pats="$_pats $_pat" # Klassische Einzeldateien nur, wenn sie NICHT leer sind: auf KeyHelp ist
fi # /var/log/apache2/access.log vorhanden, aber 0 Bytes — ein Input darauf
done # liefert still nichts.
# Globale Standarddatei nur, wenn sie NICHT leer ist (auf KeyHelp ist for _p in /var/log/apache2/access.log /var/log/httpd/access_log \
# /var/log/apache2/access.log oft vorhanden, aber 0 Bytes). /var/log/apache2/other_vhosts_access.log; do
for _p in /var/log/apache2/access.log /var/log/httpd/access_log; do [ -s "$_p" ] && _pats="$_pats $_p"
[ -s "$_p" ] && _pats="$_pats $_p" done
done # Nichts gefunden? Dann die CustomLog-Direktive befragen (verlaesslichste
[ -n "$_pats" ] && APACHE_LOG="$(printf '%s' "$_pats" | sed 's/^ //')" # Quelle bei exotischen Setups) — ${APACHE_LOG_DIR} wird aufgeloest.
fi if [ -z "$_pats" ]; then
# Einzelne, flache Faelle weiterhin abdecken _cl_dir="$(grep -rhoE '^[[:space:]]*CustomLog[[:space:]]+(\S+|"[^"]+")' \
if [ -z "$APACHE_LOG" ]; then /etc/apache2/ /etc/httpd/ /etc/apache2/sites-enabled/ 2>/dev/null \
for _d in /var/log/apache2 /var/log/httpd; do | sed -E 's/^[[:space:]]*CustomLog[[:space:]]+//; s/^"//; s/"$//' \
[ -d "$_d" ] || continue | grep -v '^|' | grep -v '^/dev/' | sort -u)"
if ls "$_d"/*access*.log "$_d"/access_log >/dev/null 2>&1; then for _p in $_cl_dir; do
APACHE_LOG="$_d/*access*.log" _p="$(printf '%s' "$_p" | sed "s#\${APACHE_LOG_DIR}#/var/log/apache2#g")"
break [ -r "$_p" ] && _pats="$_pats $_p"
fi
done done
fi fi
if [ -z "$APACHE_LOG" ]; then # Letzter Ausweg: breiter Scan der ueblichen Wurzelverzeichnisse.
# Aus der Config lesen (KeyHelp/Plesk setzen eigene Pfade) if [ -z "$_pats" ]; then
_cl="$(grep -rhoE '^\s*CustomLog\s+\S+' /etc/apache2/ /etc/httpd/ 2>/dev/null \ _pats="$(find /var/log/apache2 /var/log/httpd /home -maxdepth 4 \
| awk '{print $2}' | grep -v '^|' | head -1)" -name 'access.log' -o -name 'access_log' 2>/dev/null | head -200 | tr '\n' ' ')"
[ -n "$_cl" ] && [ -r "$_cl" ] && APACHE_LOG="$_cl"
fi fi
[ -n "$_pats" ] && APACHE_LOG="$(printf '%s' "$_pats" | sed 's/^ *//; s/ *$//')"
[ -n "$APACHE_LOG" ] || APACHE_LOG="/var/log/apache2/access.log" [ -n "$APACHE_LOG" ] || APACHE_LOG="/var/log/apache2/access.log"
fi fi
# APACHE_LOG kann mehrere, leerzeichengetrennte Pfade enthalten -> als # APACHE_LOG kann mehrere, leerzeichengetrennte Pfade enthalten -> als
# TOML-Liste rendern. Achtung: Pfade mit Leerzeichen sind in Apache-Logs # TOML-Liste rendern. Achtung: Pfade mit Leerzeichen sind in Apache-Logs
# ungewoehnlich; wir quoten jeden Eintrag einzeln. # ungewoehnlich; wir quoten jeden Eintrag einzeln.
if [ "$HAS_APACHE" -eq 1 ]; then if [ "$HAS_APACHE" -eq 1 ]; then
APACHE_FILES="" 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 for _p in $APACHE_LOG; do
APACHE_FILES="${APACHE_FILES:+$APACHE_FILES, }\"$_p\"" APACHE_FILES="${APACHE_FILES:+$APACHE_FILES, }\"$_p\""
done done
set +f
[ -n "$APACHE_FILES" ] || APACHE_FILES='"/var/log/apache2/access.log"' [ -n "$APACHE_FILES" ] || APACHE_FILES='"/var/log/apache2/access.log"'
APACHE_FILES="[$APACHE_FILES]" APACHE_FILES="[$APACHE_FILES]"
export APACHE_FILES export APACHE_FILES
@@ -437,6 +425,87 @@ else
esac esac
fi 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/<u>/logs/... <u>:<u> 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 # Konfiguration
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
@@ -530,11 +599,17 @@ case "$ROLE" in
commands = ["/etc/telegraf/proxmox-ve.sh"] commands = ["/etc/telegraf/proxmox-ve.sh"]
EOF EOF
log "Proxmox: pvesh-Input aktiv (VM/CT-Metriken, kein API-Token noetig)." ;; 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) ;; docker|none) ;;
*) die "Unbekannte Rolle: $ROLE" ;; *) die "Unbekannte Rolle: $ROLE" ;;
esac 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 --- # --- System-Logs (journald) → Loki — nur auf systemd-Systemen ---
if command -v journalctl >/dev/null 2>&1 && [ -d /run/systemd/system ]; then if command -v journalctl >/dev/null 2>&1 && [ -d /run/systemd/system ]; then