fix(acl): Durchgangsrecht auf Elternverzeichnisse + Gegentest prueft ALLE Pfade

Zwei reale Fehler, beide still:

1. setfacl -R lief nur ab dem logs/-Verzeichnis. /home/users/<user> ist auf
   Panel-Hostern chmod 700 — ohne Durchgangsrecht (x) auf den ELTERNTEIL kommt
   telegraf nicht bis zur Datei. Die Meldung sagte trotzdem "ACL gesetzt".
   Fix: x auf allen Elternverzeichnissen bis zur Wurzel + Default-ACL, damit
   neu angelegte Domain-Ordner erben.

2. Der Gegentest brach beim ERSTEN Treffer ab. Das war
   /var/log/apache2/keyhelp/access.log (root:adm, immer lesbar) -> Meldung
   "Gegentest OK", waehrend ALLE Domain-Logs in den User-Homes unlesbar
   blieben. Fix: jeden Logpfad pruefen und die Anzahl der Fehlschlaege melden.

Verifiziert in Debian-12 mit KeyHelp-aehnlichen Rechten (Home 700, Logs 640).
This commit is contained in:
mm committed 2026-09-14 10:36:04 +02:00
1 parent 360f91f8a1
commit 6a881ce674
1 file changed
+64 -18
+64 -18
View File
@@ -625,11 +625,30 @@ grant_apache_log_access() {
return 0
fi
# 1) Verzeichnisse aus der aktiven Conf ermitteln (nur die Wurzel, dann
# rekursiv — das haelt die ACL-Zahl klein).
# 1) Verzeichnisse aus der aktiven Conf ermitteln.
#
# REIHENFOLGE-BUG (real): diese Funktion liest
# /etc/telegraf/telegraf.d/apache.conf
# die erst SPAETER im Ablauf geschrieben wird. Beim ERSTEN Install ist sie
# nicht da -> _roots bleibt leer -> der Fallback unten greift auf die groben
# Panel-Wurzeln (/home/users, /var/www/vhosts) zu, also auf zehntausende
# Dateien inklusive Mails und Webinhalten. Beim zweiten Lauf (x22) existierte
# die Conf und lieferte die praezisen Domain-Ordner (83 Wu­rzeln).
# Deshalb: fehlt die Conf, wird sie hier aus den erkannten Pfaden erzeugt,
# BEVOR die ACL gesetzt wird.
# QUELLE DER PFADE, richtig herum:
# 1. die aktive apache.conf (Folge-Laeufe — dort stehen bereits die Muster)
# 2. sonst APACHE_LOG (in DIESEM Lauf erkannt; beim Erstinstall ist die Conf
# noch nicht geschrieben, und genau dann griff vorher der grobe Fallback)
_pfadquelle=""
if [ -f /etc/telegraf/telegraf.d/apache.conf ]; then
_pfadquelle="$(sed -n 's/^[[:space:]]*files = \[\(.*\)\].*/\1/p' \
/etc/telegraf/telegraf.d/apache.conf 2>/dev/null | tr -d '"' | tr ',' ' ')"
fi
[ -n "$_pfadquelle" ] || _pfadquelle="${APACHE_LOG:-}"
_roots=""
for _f in $(sed -n 's/^[[:space:]]*files = \[\(.*\)\].*/\1/p' \
/etc/telegraf/telegraf.d/apache.conf 2>/dev/null | tr -d '"' | tr ',' ' '); do
for _f in $_pfadquelle; do
case "$_f" in
*/\**) _f="${_f%%/\**}" ;; # /a/b/** -> /a/b
*/*) _f="${_f%/*}" ;; # /a/b/*/x -> /a/b/*
@@ -664,8 +683,22 @@ grant_apache_log_access() {
esac
for _t in $_targets; do
[ -d "$_t" ] || continue
# Durchgangsrecht (x) auf die Elternverzeichnisse, Leserecht (r) auf die
# Dateien: rX vergibt x nur fuer Verzeichnisse.
# DURCHGANGSRECHT auf allen ELTERNVERZEICHNISSEN bis zur Wurzel.
# Realer Fehler: /home/users/<user> ist oft chmod 700. Ein setfacl -R auf
# .../<user>/logs setzt nichts auf <user> selbst — telegraf kommt dann
# nicht bis zur Datei, obwohl die Meldung "ACL gesetzt" erscheint.
_p="$(dirname "$_t")"
while [ -n "$_p" ] && [ "$_p" != "/" ] && [ "$_p" != "." ]; do
timeout 30 setfacl -m "u:$_u:x" "$_p" 2>/dev/null || true
# Default-ACL auf Zwischenverzeichnissen, damit neue Ordner erben
case "$_p" in
/home|/home/users|/var|/var/www|/usr|/usr/local) ;;
*) timeout 30 setfacl -d -m "u:$_u:x" "$_p" 2>/dev/null || true ;;
esac
_p="$(dirname "$_p")"
done
# Leserecht (r) auf die Dateien, Durchgang (x) auf die Ordner:
# rX vergibt x nur fuer Verzeichnisse.
timeout 120 setfacl -R -m "u:$_u:rX" "$_t" 2>/dev/null && _n=$((_n+1))
# Neu angelegte Dateien/Ordner erben das Recht (default-ACL nur fuer dirs).
timeout 120 setfacl -R -d -m "u:$_u:rX" "$_t" 2>/dev/null || true
@@ -673,20 +706,33 @@ grant_apache_log_access() {
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
# 2) Gegentest: JEDEN Logpfad als Dienst-Benutzer pruefen — nicht nur den ersten.
#
# Realer Fehler: der Test brach beim ersten Treffer ab. Auf einem KeyHelp-Host
# war das /var/log/apache2/keyhelp/access.log (root:adm, immer lesbar) — die
# Meldung lautete "Gegentest OK", waehrend ALLE Domain-Logs in den User-Homes
# unlesbar blieben. Ein Test, der beim ersten Erfolg abbricht, verdeckt genau
# die Pfade, fuer die er gedacht ist.
_tested=0; _fail=0; _firstfail=""
for _f in $_pfadquelle; do
# shellcheck disable=SC2086
_cand="$(ls $_f 2>/dev/null | head -1)"
if [ -n "$_cand" ] && [ -s "$_cand" ]; then _testfile="$_cand"; break; fi
for _cand in $(ls $_f 2>/dev/null); do
[ -s "$_cand" ] || continue
_tested=$((_tested+1))
if ! su -s /bin/sh "$_u" -c "head -c 1 \"$_cand\" >/dev/null 2>&1"; then
_fail=$((_fail+1))
[ -n "$_firstfail" ] || _firstfail="$_cand"
fi
done
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
if [ "$_tested" -eq 0 ]; then
log "HINWEIS: keine gefuellte Logdatei gefunden — Gegentest nicht moeglich."
elif [ "$_fail" -eq 0 ]; then
log "Gegentest: '$_u' kann alle $_tested Logdateien lesen."
else
log "WARNUNG: $_fail von $_tested Logdateien sind fuer '$_u' NICHT lesbar."
log " Beispiel: $_firstfail"
log " Pruefen mit: sh install.sh --diagnose"
fi
}