Semaphore: Von Handarbeit zu automatisierten Updates

Ausgangslage

Updates auf den Homelab-Hosts liefen bisher manuell: per SSH einloggen, apt update, apt upgrade, hoffen dass nichts kaputt geht. Funktioniert, aber skaliert nicht und wird schnell vergessen, wenn gerade anderes ansteht. Zeit für eine Ansible-UI-Lösung: Semaphore als zentrale Oberfläche, Gitea als Backend für die Playbooks.

Ein erster Semaphore-Versuch existierte bereits auf einem LXC, war aber kaum konfiguriert. Also Neuanfang auf docker02, sauber nach dem etablierten Compose-Schema.

Problem

Kaum lief der Container, schon die erste Überraschung: Absturz mit „Unknown database dialect: bolt“. Der bisher überall dokumentierte Wert für das Datenbank-Backend existiert schlicht nicht mehr — Semaphore hat BoltDB in einer neueren Version ersatzlos entfernt und durch SQLite ersetzt. Ein guter Reminder, dass man bei Community-Software vor jedem Deployment die aktuelle Doku prüfen sollte statt auf altbekannte Werte zu vertrauen.

Nach dem Dialect-Fix der nächste Absturz: ein Panic wegen eines ungültigen Verschlüsselungsschlüssels. Der Access-Key für Semaphore muss Base64-kodiert sein, nicht Hex wie sonst bei allen anderen Secrets im Homelab üblich — eine Ausnahme von der eigenen Konvention, aber technisch bedingt.

Container lief, aber das Repository liess sich nicht klonen: „Could not connect to server“. Der interne Gitea-Hostname zeigt direkt auf die Backend-IP ohne TLS-Terminierung, während Semaphore aber HTTPS erwartet. Lösung: den öffentlich geroutete Namen mit NPM-Terminierung verwenden statt des internen Kurzschlusses.

Grösste Zeitfalle war aber ein reines Netzwerkproblem: Ein Testhost war plötzlich über SSH nicht mehr erreichbar — TCP-Verbindung baute sich auf, aber danach kam nichts mehr zurück. Stundenlanges Debugging über ARP-Tabellen, Traceroutes und MTU-Vergleiche, bis sich herausstellte: Die eigene pfSense-Firewall hatte eine „Block local Networks“-Regel, die noch vor der eigentlichen Freigabe-Regel für Semaphore stand. Die Regel-Reihenfolge entscheidet in pfSense über alles — eine weiter unten stehende Allow-Regel nützt nichts, wenn eine Block-Regel weiter oben zuerst greift. Und weil das Logging auf der Block-Regel nicht aktiv war, blieb das in den Firewall-Logs komplett unsichtbar.

Auch bei den Ansible-Playbooks selbst gab es Stolpersteine: Eine Sudoers-Konfiguration mit exakter Command-Whitelist (nur apt-get, dpkg etc. erlaubt) funktionierte nicht, weil Ansible seine Module über eine Python-Zwischenschicht ausführt statt das Ziel-Programm direkt aufzurufen — sudo sah dann immer nur „sh“ als Kommando, nicht „apt-get“, und verlangte ein Passwort.

Auflösung

Die Datenbank-Dialect wurde auf sqlite umgestellt, der Access-Key mit dem korrekten Base64-Format neu generiert, die Repository-URL auf den NPM-terminierten Namen geändert. Für den Ansible-Zugriff wurde die Sudoers-Konfiguration von einer Command-Whitelist auf ein generelles NOPASSWD für den dedizierten Service-User umgestellt — pragmatisch, aber im Kontext eines Homelabs mit einem einzigen, nur per SSH-Key zugänglichen Service-Account vertretbar.

Die Firewall-Regel wurde in die richtige Reihenfolge gebracht (Freigabe vor Sperrung), danach lief die Verbindung sofort wieder sauber.

Für die Update-Playbooks kam noch eine zweite Erkenntnis dazu: Auf Raspberry Pi OS wird die Standard-Reboot-Markierungsdatei nie geschrieben, selbst nach einem Kernel-Update — anders als bei Ubuntu. Ein zusätzlicher Vergleich zwischen laufendem und installiertem Kernel macht die Reboot-Erkennung distributionsunabhängig zuverlässig.

Vor einem automatisierten Reboot prüft das Playbook inzwischen zusätzlich: genug Speicherplatz auf /boot, eine gültige fstab-Konfiguration, keine kaputten Pakete. Alles Dinge, die einen Neustart sonst zum Absturz in den Emergency-Mode führen könnten.

Lessons Learned

Community-Software-Dokumentation veraltet schneller als man denkt — vor jedem Deployment die aktuelle Version gegen die offizielle Doku prüfen, nicht auf bekanntes Wissen verlassen.

Firewall-Regel-Reihenfolge ist keine Kleinigkeit. Eine Block-Regel oberhalb einer Allow-Regel gewinnt immer, unabhängig davon wie spezifisch die Allow-Regel weiter unten ist. Logging auf Block-Regeln aktivieren, sonst bleiben solche Probleme unsichtbar.

Ansible mit sudo-Command-Whitelists ist trügerisch: Was nach granularer Sicherheit aussieht, funktioniert in der Praxis oft nicht, weil Ansible über Python-Module läuft statt direkt das gewünschte Programm aufzurufen.

Nicht jeder Reboot-Indikator gilt für jede Distribution. Was auf Ubuntu zuverlässig funktioniert, kann auf Raspberry Pi OS komplett fehlen.

Fazit

Was als einfaches „Semaphore installieren“ begann, wurde zu einer kleinen Tour durch Datenbank-Migrationen, DNS-Eigenheiten, Firewall-Regel-Logik und Ansible-Berechtigungsmodelle. Am Ende steht aber ein sauber automatisiertes Update-System: täglich geplante Updates, gefolgt von einem geprüften, kontrollierten Reboot nur wenn wirklich nötig — inklusive Sicherheitschecks, die einen fehlgeschlagenen Neustart verhindern sollen, bevor er passiert.

Als nächstes fehlt noch eine Benachrichtigung, wenn ein Reboot ansteht oder durchgeführt wurde — aktuell läuft das noch stumm im Hintergrund. Das kommt, sobald die geplante ntfy-Integration steht.

Schreibe einen Kommentar