Files
axion1337.chat-gitops/docs/deployment-guides/07-host-maintenance-notifications.md
T
Thore CimbalandClaude Sonnet 5 1b1fa2b719 feat: pre-update maintenance notifications via mail + matrix (Issue #24)
unattended-upgrades was already active on the host, just never documented
or closed. Adds a generic, reusable systemd timer + script that fires
before the daily update window and notifies via email and a Matrix thread
reply if any packages are actually pending - reusing the mas-cli bot
account pattern established for Draupnir.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 23:28:09 +02:00

8.3 KiB

Host-Wartungsbenachrichtigungen (Pre-Update Mail & Matrix)

Status: Deployed + live getestet (2026-07-29/30, Closes Issue #24) Konfiguration: host-config/maintenance-notify/ (nicht via Flux/GitOps deployt - siehe unten warum)

Überblick

Der Host läuft bereits mit aktivem unattended-upgrades (APT::Periodic::Update-Package-Lists/Unattended-Upgrade in /etc/apt/apt.conf.d/20auto-upgrades, Standard-Origins-Pattern deckt Debian+Debian-Security ab). Das ist unabhängig von diesem Dokument und war schon vor Issue #24 aktiv - nur nie dokumentiert.

Was hier ergänzt wird: eine Benachrichtigung vor dem täglichen Update-Lauf, per E-Mail und Matrix, damit man weiß "gleich läuft ein Update" und im Störungsfall danach sofort den Zusammenhang sieht. Unattended-Upgrade::Mail (auskommentiert in 50unattended-upgrades) wäre keine Alternative gewesen: die feuert nur nach dem Lauf und braucht ohnehin ein lokales mailx-Setup.

Diese Anleitung ist bewusst generisch gehalten - sie funktioniert für jeden Fork dieses Homeserver-Stacks, nicht nur für axion1337.chat. Alle instanzspezifischen Werte (Domain, Matrix-Raum, Mail-Adressen) stecken in einer separaten Config-Datei, nicht im Skript selbst. Ein konkretes, reales Beispiel (axion1337.chat) steht am Ende.

Warum nicht via Flux/GitOps?

Alles andere in diesem Repo landet via Flux im Cluster. Diese Automatisierung läuft aber auf dem nackten Host (systemd-Timer, kein Kubernetes-Pod) - dafür existiert in diesem Repo (noch) kein Deployment-Mechanismus (kein Ansible, kein SOPS-Agent auf dem Host). Das Skript selbst ist trotzdem hier versioniert (host-config/maintenance-notify/), das Deployment auf den Host erfolgt aber manuell per scp/SSH.

Architektur

  • Timing: apt-daily-upgrade.timer führt den echten Update-Lauf aus (OnCalendar=*-*-* 6:00, RandomizedDelaySec=60m → tatsächlicher Start irgendwann zwischen 06:00-07:00, je nach eurer eigenen Konfiguration ggf. abweichend - mit systemctl cat apt-daily-upgrade.timer prüfen). Der neue maintenance-notify.timer feuert fest vor diesem Fenster (Default 05:00, kein Randomize).
  • Prüfung: maintenance-notify.sh ruft apt-get update + unattended-upgrade --dry-run -v auf und liest dessen eigene, im Quellcode verifizierte Log-Zeilen (/usr/bin/unattended-upgrade):
    • "No packages found that can be upgraded unattended..." → nichts ansteht, Skript beendet sich ohne jede Benachrichtigung (kein täglicher Alarm-Spam).
    • "Packages that will be upgraded: <liste>" → genau die Pakete, die der echte Lauf gleich anfassen wird.
  • Zustellung (nur wenn Pakete anstehen):
    • Mail via msmtp, Passwort kommt aus /etc/maintenance-notify/mail-password (chmod 600, nie im Repo).
    • Matrix via curl gegen die Client-Server-API, als Reply in einem bestehenden Thread (m.relates_to: {rel_type: "m.thread", event_id: ...}), Bot-Token aus /etc/maintenance-notify/matrix-token (chmod 600, nie im Repo).

Voraussetzungen

  • Ein Mail-Provider mit SMTP-Auth (eigenes Postfach zum Versenden, nicht zwingend zum Empfangen - der Empfänger kann eine ganz andere, bereits bestehende Adresse sein).
  • Ein Matrix-Raum (und optional ein bestehender Thread darin), in den ein eigener Bot-Account eingeladen wird.
  • Auf dem Host: msmtp, jq, uuid-runtime (apt-get install -y msmtp jq uuid-runtime).

Deployment

  1. Bot-Account anlegen (identisches Muster wie für Draupnir/den Content-Scanner in 06-moderation-content-scanning.md):

    kubectl exec -it -n matrix deploy/matrix-stack-matrix-authentication-service -- \
      mas-cli manage register-user maintenance-notify --yes
    kubectl exec -it -n matrix deploy/matrix-stack-matrix-authentication-service -- \
      mas-cli manage issue-compatibility-token maintenance-notify
    

    Der ausgegebene Token wird manuell in /etc/maintenance-notify/matrix-token auf dem Host eingetragen (chmod 600) - kein automatisierter Schritt, der Token darf nirgends im Klartext im Repo landen.

  2. Bot in den Zielraum einladen UND joinen lassen. Eine Einladung allein reicht nicht - der Account muss aktiv beitreten, sonst kann er nicht senden:

    curl -s -X POST -H "Authorization: Bearer $(cat /etc/maintenance-notify/matrix-token)" \
      "https://<euer-homeserver>/_matrix/client/v3/join/<room-id>"
    
  3. Skript + systemd-Units auf den Host kopieren (aus host-config/maintenance-notify/ in diesem Repo):

    scp host-config/maintenance-notify/maintenance-notify.sh <host>:/tmp/
    scp host-config/maintenance-notify/maintenance-notify.{service,timer} <host>:/tmp/
    ssh <host> "sudo install -m 755 /tmp/maintenance-notify.sh /usr/local/bin/maintenance-notify.sh && \
      sudo install -m 644 /tmp/maintenance-notify.service /etc/systemd/system/ && \
      sudo install -m 644 /tmp/maintenance-notify.timer /etc/systemd/system/ && \
      sudo mkdir -p /etc/maintenance-notify && sudo systemctl daemon-reload"
    
  4. Config-Datei anlegen (config.example in diesem Verzeichnis als Vorlage nach /etc/maintenance-notify/config kopieren, alle Werte für eure Instanz anpassen). Wichtig: Matrix-Event-IDs beginnen mit $ - der MATRIX_THREAD_EVENT_ID-Wert muss single-quoted sein, sonst versucht bash ihn als Variable zu expandieren und schneidet ihn auf einen leeren String zusammen.

  5. msmtprc.template nach /etc/msmtprc kopieren, Platzhalter ausfüllen, chmod 600. Passwort selbst kommt nicht hier rein, sondern separat in /etc/maintenance-notify/mail-password (chmod 600, eine Zeile, kein SMTP-Passwort ohne vorheriges eigenes Testen der Zugangsdaten übernehmen - siehe Stolpersteine unten).

  6. Timer aktivieren:

    sudo systemctl enable --now maintenance-notify.timer
    

Verifikation

sudo systemctl start maintenance-notify.service
sudo journalctl -u maintenance-notify.service --no-pager -n 40
sudo systemctl list-timers maintenance-notify.timer

Bei nichts anstehenden Updates loggt das Skript nur "No pending upgrades - nothing to notify." und beendet sich sauber (kein Fehlerfall). Für einen echten Zustellungstest (Mail + Matrix) unabhängig vom tatsächlichen Update-Status können die send_mail/send_matrix-Bausteine aus dem Skript manuell mit einer Testnachricht nachgestellt werden.

Stolpersteine (live gefunden, nicht aus der Doku ableitbar)

  • Port 465 kann ausgehend blockiert sein, obwohl 587 durchgeht. Bei axion1337.chat war ausgehendes SMTPS (465) sowohl zu IONOS als auch testweise zu Gmail dicht (stiller Timeout, kein aktives Reject - typisch für eine Firewall-Regel auf Cloud-Provider-Ebene), während 587/STARTTLS problemlos funktionierte. Vor dem Debuggen von Auth-Fehlern erst die reine TCP-Erreichbarkeit prüfen: timeout 8 bash -c 'echo > /dev/tcp/<host>/<port>'.
  • msmtp's passwordeval nimmt die Ausgabe wörtlich, inklusive eines eventuellen Trailing-Newlines aus der Passwort-Datei. printf %s "$(cat datei)" > datei entfernt das zuverlässig.
  • Absender-Domain ≠ Matrix-Server-Domain. Es ist nicht garantiert, dass das Mail-Postfach unter derselben Domain läuft wie der Matrix-Homeserver (bei axion1337 z.B. Mail unter .de, Matrix unter .chat) - MAIL_FROM und der user/from in msmtprc müssen zur tatsächlichen Mail-Domain passen, nicht zur Matrix-Domain.
  • MATRIX_HOMESERVER ist oft eine eigene Subdomain, nicht die Apex-Domain. Vor dem Eintragen die eigene .well-known/matrix/client-Delegation prüfen (curl https://<apex-domain>/.well-known/matrix/client, Feld m.homeserver.base_url).
  • 535 "Authentication credentials invalid" trotz korrektem Passwort? Manche Mail-Provider trennen Postfach-Login und SMTP/IMAP-Zugriff als separaten Schalter in den Postfach-Einstellungen - vor weiterem Debugging prüfen, ob dieser aktiviert ist.

Beispiel: axion1337.chat

  • Homeserver: https://matrix.axion1337.chat (nicht die Apex-Domain)
  • Matrix-Ziel: Space "operating" → Raum wartung, Reply in einem vorab angelegten Thread
  • Mail: Absender wartung@axion1337.de (eigene Mail-Domain, getrennt von axion1337.chat) über IONOS SMTP (smtp.ionos.de:587, STARTTLS), Empfänger die private Hauptadresse des Betreibers
  • Timer: OnCalendar=*-*-* 05:00 (fest), reales Update-Fenster 06:00-07:00