From 8e66d9e7bc75c911f7a99fee2bc1068a4ad97ab3 Mon Sep 17 00:00:00 2001 From: Tim Eertmoed Date: Wed, 2 Sep 2026 21:49:56 +0200 Subject: [PATCH] apt-Installation laeuft ausserhalb des Dienst-Sandkastens Der Sandkasten wird an dpkg und dessen Maintainer-Skripte weitervererbt; mit ProtectKernelLogs=yes liefert "dmesg" ein EPERM und ein Postinst-Skript bricht daran ab -- die Installation scheitert mit "dpkg returned an error code (1)", obwohl mit dem Paket alles in Ordnung ist. apt laeuft jetzt ueber systemd-run, also von PID 1 gestartet und damit ohne unsere Haertung. Der zuvor eingebaute nsenter-Umweg beim Einbinden von Freigaben ist zurueckgenommen. Diese Verben ruft der Lizenzserver nicht auf -- der Helfer bleibt aber in beiden Repositorien dieselbe Datei, weil eine zweite Fassung zwei Stellen waere, die auseinanderlaufen koennen. Co-Authored-By: Claude Opus 5 --- deploy/tesm-helper | 59 +++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/deploy/tesm-helper b/deploy/tesm-helper index cd7dfc1..70aee1d 100644 --- a/deploy/tesm-helper +++ b/deploy/tesm-helper @@ -258,19 +258,17 @@ case "$verb" in # Kommandozeile (Prozessliste) und nie ueber die Umgebung # (/proc//environ). read_stdin_limited > "$creds" - # Der aufrufende Dienst laeuft in einem eigenen Mount-Namensraum - # (ProtectSystem, PrivateTmp, ReadWritePaths). Ein Einbinden *darin* - # scheitert mit "mount error(1): Operation not permitted" -- gemessen: mit - # denselben Optionen und denselben Zugangsdaten gelingt es aus einer - # normalen Root-Shell sofort. Deshalb wird im Namensraum des Wirts - # eingebunden; von dort wird der Mount in den Namensraum des Dienstes - # weitergegeben (er haengt als "slave" darunter), die Anwendung sieht ihn - # also. Die Zugangsdatei unter /run ist ueber die Grenze lesbar, /run ist - # nicht privatisiert. - im_wirt=(nsenter --mount=/proc/1/ns/mnt --) - command -v nsenter >/dev/null 2>&1 || im_wirt=() - "${im_wirt[@]}" mkdir -p "$target" - if "${im_wirt[@]}" mount -t cifs "$share" "$target" \ + # Eingebunden wird im Mount-Namensraum des aufrufenden Dienstes -- absichtlich: + # nur dieser Dienst liest die Dateien, und mit ihm verschwinden die + # Einbindungen wieder. Damit der Syscall durchkommt, braucht die Unit + # "@mount" im SystemCallFilter. Ohne das scheitert es mit + # "mount error(1): Operation not permitted"; gemessen war der Syscall-Filter + # die Ursache, nicht der Namensraum. Ein Umweg ueber nsenter in den + # Namensraum des Wirts ist keine Option -- "RestrictNamespaces=yes" + # verbietet setns, und das Recht dafuer wieder aufzugeben waere teurer als + # der Gewinn. + mkdir -p "$target" + if mount -t cifs "$share" "$target" \ -o "credentials=$creds,uid=$(id -u tesm 2>/dev/null || echo 0),gid=$(id -g tesm 2>/dev/null || echo 0),file_mode=0640,dir_mode=0750,vers=3.1.1,seal" then shred -u "$creds" 2>/dev/null || rm -f "$creds" @@ -282,13 +280,12 @@ case "$verb" in umount) check_mount_target "${1:?Ziel fehlt}" - # Gegenstueck zu mount-cifs: der Mount liegt im Namensraum des Wirts, also - # muss er auch dort geloest werden. Ein umount im Dienst-Namensraum wuerde - # ihn nur dort ausblenden und die Freigabe bliebe eingebunden. - im_wirt=(nsenter --mount=/proc/1/ns/mnt --) - command -v nsenter >/dev/null 2>&1 || im_wirt=() - "${im_wirt[@]}" umount -l "$1" || true - "${im_wirt[@]}" rmdir "$1" 2>/dev/null || true + # Gegenstueck zu mount-cifs, und deshalb im selben Namensraum: loesen kann + # nur, wer die Einbindung auch sieht. Ein Aufruf aus einem anderen Dienst + # greift ins Leere -- genau deshalb laeuft die Aufraeumaufgabe fuer + # abgelaufene Einbindungen im Webprozess und nicht im Ueberwachungsdienst. + umount -l "$1" || true + rmdir "$1" 2>/dev/null || true ;; apt-install) @@ -296,9 +293,27 @@ case "$verb" in for package in "$@"; do check_in_list "$package" "${PACKAGES_ALLOWED[@]}" || fail "Paket nicht erlaubt: $package" done + # apt laeuft ausserhalb des Sandkastens des aufrufenden Dienstes. Der wird + # sonst an dpkg und dessen Maintainer-Skripte weitervererbt, und dort + # scheitert etwas voellig Beilaeufiges: mit "ProtectKernelLogs=yes" gibt + # "dmesg" ein EPERM zurueck, ein Postinst-Skript bricht daran ab, und dpkg + # endet mit Code 1 -- die Installation schlaegt fehl, obwohl mit dem Paket + # alles in Ordnung ist. Gemessen und mit systemd-run reproduzierbar + # behoben: PID 1 startet den Prozess, also ohne unsere Haertung. export DEBIAN_FRONTEND=noninteractive - apt-get update -qq - apt-get install -y -qq "$@" + if command -v systemd-run >/dev/null 2>&1 && [[ -S /run/dbus/system_bus_socket ]]; then + # Zwei getrennte Aufrufe, damit keine Shell dazwischen steht: die + # Paketnamen gehen als eigene Argumente an apt-get, nicht durch eine + # Kommandozeile, die sie erst wieder zerlegen muesste. + ausserhalb=(systemd-run --wait --pipe --collect --quiet + --setenv=DEBIAN_FRONTEND=noninteractive) + "${ausserhalb[@]}" apt-get update -qq + "${ausserhalb[@]}" apt-get install -y -qq "$@" + else + # Kein systemd (Container, Entwicklungsrechner): direkt, wie bisher. + apt-get update -qq + apt-get install -y -qq "$@" + fi ;; certbot-issue)