
🌐 Auch auf: English · Français · Español
Version v3.6.0 des Ansible-Codes hinter diesem Blog und zwei Nextcloud-Servern ist da. Für eine Weile ist es das letzte Release mit neuen Funktionen – ab jetzt steht die Pflege im Vordergrund. Der rote Faden der letzten Tage: Fast jeder Fehler der vergangenen Wochen war durchgerutscht, weil die Prüfungen fragten „ist es eingerichtet und läuft es?“ statt „funktioniert es wirklich?“. Dieses Release ändert das.
📦 Der Download, der bei 1 GB abbrach
Der jüngste Fehler kam heute Morgen ans Licht, und zwar nicht durch einen Test: Eine Alarm-Mail meldete, dass ein Container in mehr als der Hälfte der Zeit bei der CPU gebremst wurde. Betroffen war der nginx vor PHP auf einem der Nextcloud-Server – eine Komponente, die sonst kaum etwas tut.
Dahinter steckte ein Nutzer, der ein 13 GB großes Archiv herunterladen wollte. Jeder Versuch brach bei rund 1,08 GB ab. Zwei Standardwerte trafen aufeinander: Der Ingress legt eine Antwort in einer temporären Datei von höchstens 1 GB ab und liest danach nichts mehr vom Pod, bis der Client aufgeholt hat. Der nginx im Pod wartet 60 Sekunden auf Fortschritt und schließt dann die Verbindung. Der Client erhält einen kaputten Download, der Ingress protokolliert „upstream prematurely closed“. Die Zeitstempel passten bei drei Versuchen hintereinander: 1 GB in wenigen Sekunden, dann genau 67 Sekunden Stille.
Die Korrektur ist eine einzige Annotation, mit der der Ingress im Tempo des Clients weiterreicht, statt eine Zwischendatei zu füllen:
nginx.org/proxy-max-temp-file-size: "0"Der CPU-Alarm war nur ein Nebeneffekt: 1 GB in wenigen Sekunden zu kopieren, brachte einen Container mit 0,5 Kernen Limit kurz an seine Grenze.
Was man mitnimmt: Zwei vernünftige Standardwerte können zusammen einen Fehler ergeben. Einem Alarm auf ein Symptom nachzugehen lohnt sich, auch wenn das Symptom selbst harmlos wirkt.
🧪 Tests, die die Wirkung prüfen
Die Verify-Rollen prüfen jetzt, was eine Komponente bewirken soll, und nicht nur, ob sie vorhanden ist:
- Sperrkette: Für jedes fail2ban-Jail wird eine Dokumentationsadresse (TEST-NET) gesperrt, muss im richtigen Firewall-Set auftauchen, wird entsperrt und muss wieder verschwinden. Zwei Jails sperren dieselbe Adresse, eines hebt die Sperre auf – die andere muss bestehen bleiben.
- Filter: Für jedes Jail läuft eine vorbereitete Log-Zeile im aktuellen Format durch
fail2ban-regex. Ein geändertes Log-Format kann ein Jail nicht mehr unbemerkt blind machen. - Daten vorhanden: Wichtige Metriken müssen existieren, und keine Grafana-Alarmregel darf im Fehlerzustand hängen oder dauerhaft keine Daten haben.
- SELinux-Domänen: Jeder Dienst auf dem Host muss in seiner eigenen Domäne laufen. „Active (running)“ in der falschen Domäne gilt als Fehler.
Was man mitnimmt: „Eingerichtet und läuft“ ist ein Zustand. „Funktioniert es“ ist ein Test mit einer Eingabe und einem erwarteten Ergebnis.
💓 Lebenszeichen und der Mailweg
Manche Ausfälle sind still: Ein nächtlicher Job, der nicht mehr läuft, meldet nie einen Fehler. Jeder regelmäßige Job – AIDE, rkhunter, ClamAV, automatische Updates, die Login- und Sperr-Benachrichtigungen – schreibt jetzt beim Abschluss einen Zeitstempel als Metrik. Ist ein Job überfällig, alarmiert Grafana.
Das hilft nur, wenn Alarm-Mails auch ankommen. Deshalb löst eine Regel jede Woche absichtlich aus; bleibt diese Mail aus, ist der Mailweg kaputt. Zusätzlich überwacht Monit das Mail-Log auf fehlgeschlagene Zustellungen.
Was man mitnimmt: Stille ist kein Signal. Ein Job muss melden, dass er gelaufen ist, und die Alarmierung muss beweisen, dass sie dich noch erreicht.
📉 Ein Fehlerbudget für Logs
Zwei Exporter schrieben tagelang Fehler, ohne dass eine Metrik es zeigte. Ein neuer Loki-Alarm zählt Fehler- und Warnzeilen pro Dienst und schlägt bei mehr als 100 pro Stunde an. Bekanntes harmloses Rauschen steht mit Begründung auf einer Ausnahmeliste.
Was man mitnimmt: Ein Dienst, der ständig klagt, ist ein Befund – auch wenn alle seine Metriken gut aussehen.
🔍 Upload-Proben im Zugriffslog – und was die ersten zwei Tage zeigten
Am 21. Oktober wurde eine kritische Lücke in der Verarbeitung von Datei-Uploads (multipart/form-data) in PHP bekannt: Ein präparierter Upload konnte PHP dazu bringen, Code des Absenders auszuführen. Scanner probierten das innerhalb eines Tages aus. Im Zugriffslog sah so ein Versuch aus wie jedes Formular – nur POST /.
Der Ingress schreibt jetzt zusätzlich Content-Type und Größe jeder Anfrage ins Log. Das reicht für ein einfaches Muster: Ein Multipart-Upload von wenigen Kilobyte an eine Seite, die nie Uploads annimmt – etwa die Startseite oder die Login-Seite –, ist ein Angriffsversuch und kein Besucher.
Die ersten zwei Tage mit dem neuen Log: Scanner schickten solche Uploads an alle drei Server, meist von Adressen großer Cloud-Anbieter. Einer feuerte 32 Stück innerhalb einer Sekunde ab, jeweils etwa 6 KB, und wechselte alle paar Anfragen die Browser-Kennung, um wie normale Besucher auszusehen. Im Inhalt stecken präparierte Formulardaten, die den Upload-Parser aus dem Tritt bringen sollen. Hätte das geklappt, würde der Server den Code des Angreifers ausführen – meist eine kleine Web-Shell, also eine Hintertür, die Befehle über HTTP entgegennimmt.
Durchgekommen ist nichts. Jeder Versuch wurde mit 400 (abgewiesen), 404 oder einer Weiterleitung beantwortet, keiner bekam eine Antwort mit Inhalt. Mehrere Absender waren ohnehin schon von fail2ban gesperrt, weil sie auch verbotene Pfade abgeklopft hatten. Normale Uploads – die Nextcloud-Desktop-Clients, Dokumentvorschauen, Medien im Blog-Backend – sehen klar anders aus: andere Pfade, andere Content-Types, angemeldete Nutzer.
Was man mitnimmt: Man kann nur beurteilen, was die Logs aufzeichnen. Zwei zusätzliche Felder machten aus „wahrscheinlich ein Angriff“ ein „sicher ein Versuch, sicher abgewiesen“.
🔧 Der Rest
- Blog auf PHP 8.5.
- Speicherlimit für PHP-FPM auf dem Blog von 1 GiB auf 1,5 GiB – zwölf Worker plus OPcache passten nie in 1 GiB; der alte Kommentar ging von halb so viel Speicher pro Worker aus. Ein eigener Fehler, gefunden durch Messen.
- Rolling Updates für die App-Pods: Der neue Pod startet, bevor der alte stoppt.
- Nextcloud-Server auf Redis 7 aus dem Modul-Stream der Distribution.
- Zugriffslog des Ingress mit Content-Type und Anfragelänge – verdächtige Multipart-Uploads lassen sich jetzt von normalen Formularen unterscheiden.
- Pressure Stall Information (PSI) beim Booten aktiviert, damit das Pressure-Panel Daten zeigt.
🧭 Wie es weitergeht: nichts Neues – mit Absicht
Für die nächsten Wochen und Monate habe ich keine weiteren Funktionen oder Änderungen geplant. Das Setup tut, was es soll, und jede neue Komponente brächte neue Fehlerquellen mit. Die Zeit fließt jetzt ins Verstehen dessen, was schon da ist – die Konfiguration mit frischem Blick lesen, messen statt raten – und ins Dokumentieren: die Deep Dives, die Betriebshandbücher und die Lehren in den Code-Kommentaren.
Die Pflege läuft wie gewohnt weiter: Updates jeden Montag, Zertifikate, Sicherheitskorrekturen. Fehlerbehebungen ja, neue Funktionen nur bei echtem Bedarf.
Was man mitnimmt: Ein Setup, das man versteht, ist mehr wert als eine weitere Funktion. Auch Stabilität ist eine Entscheidung.
🎯 Die Lehren
- Die Wirkung testen, nicht die Konfiguration.
- Jeder regelmäßige Job braucht ein Lebenszeichen; Stille heißt nicht „alles gut“.
- Den Alarmweg regelmäßig beweisen, mit einem Alarm, der immer auslöst.
- Symptomen nachgehen – ein harmlos wirkender CPU-Alarm führte zu kaputten Downloads.
- Standardwerte sind Entscheidungen anderer; sie gehören gegen die eigenen Größenordnungen geprüft.
Die vollständige Liste der Änderungen steht in der Tag-Nachricht von v3.6.0.





