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>
150 lines
8.3 KiB
Markdown
150 lines
8.3 KiB
Markdown
# 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](06-moderation-content-scanning.md)):
|
|
```bash
|
|
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:
|
|
```bash
|
|
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):
|
|
```bash
|
|
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**:
|
|
```bash
|
|
sudo systemctl enable --now maintenance-notify.timer
|
|
```
|
|
|
|
## Verifikation
|
|
|
|
```bash
|
|
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
|