Was created under docs/intent/, which only ever reaches the Operator (direct_builder/split/needs_human -- no "send to Researcher" option exists there at all). The Operator picked direct_builder for a read-only audit needing systemd/journal/network access; Builder correctly refused (scoped git-only Bash, no shell/journal/network access) and recommended the Researcher role by name. Moved to docs/research-intent/, re-ran through Request Research, and committing its now-completed docs/research/full-system-aufit.md alongside it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016w25covboHfcYuLXBAvX3g
164 lines
24 KiB
Markdown
164 lines
24 KiB
Markdown
# Full-System-Audit: NixOS-Plattform (blunix / pinix / tnix)
|
||
|
||
**Datum:** 2026-09-11
|
||
**Scope:** Read-only Audit von Repository (`/home/drone/nixos-config`, Branch `dev`, HEAD detached), Runtime-Zustand auf `blunix` (lokale Shell, systemd, Journal, Netzwerk, HTTP), sowie extern erreichbare Endpunkte unter `*.0ff.space`.
|
||
**Methode:** Repository-Lektüre (Nix-Konfiguration, Git-Historie), lokale Diagnosebefehle auf blunix (systemctl, journalctl, ss, df/du, tailscale status), sowie HTTP-Prüfungen gegen öffentlich exponierte Endpunkte über einen bereits im Nix-Store vorhandenen `curl`-Build (kein neuer Build/keine Zustandsänderung). Kein SSH auf pinix/tnix, kein `nixos-rebuild`, keine schreibenden Aktionen.
|
||
|
||
## Executive Summary
|
||
|
||
1. **Kritischer Vorfall bestätigt:** blunix ist mit 96 % Diskbelegung (1,5 GB frei von 37 GB) faktisch am Limit. Am 10.09. führte das tatsächlich zu Ausfällen (Forgejo-Dump, Prosody Push-Notifications, DHCP-Lease-Schreibvorgang schlugen mit „No space left on device" fehl). Ursache: ~208 `nixos-rebuild switch`-Läufe und 34 System-Generationen in weniger als 48 Stunden (automatisierte Builder-Pipeline), während GC nur wöchentlich mit 30-Tage-Retention läuft.
|
||
2. **pinix ist seit ca. 1 Tag über Tailscale nicht erreichbar** (`tailscale status`: „offline, last seen 1d ago"). Das erklärt gleichzeitig mehrere scheinbar unabhängige Symptome: Timeouts auf `board.0ff.space`, `social.0ff.space`, `phanpy.0ff.space`, `sound.0ff.space`, `calendar.0ff.space` sowie wiederkehrende Forgejo-Webhook-Fehler gegen `100.64.0.3:9000` (pinix' Tailnet-IP).
|
||
3. Der **Codeberg-Push-Mirror schlägt seit Tagen zyklisch alle paar Stunden fehl**, weil der Mirror versucht, den Branch `master` auf Codeberg zu löschen (lokal existiert kein `master` mehr, nur `dev`/`production`), Codeberg das aber als aktuellen Default-Branch ablehnt.
|
||
4. Auf blunix liegt ein **verwaistes 117 MB großes `/var/lib/gotosocial`-Verzeichnis**, obwohl GoToSocial laut Repo ausschließlich auf pinix läuft — vermutlich Rückstand einer Host-Migration, verschärft den Diskdruck unnötig.
|
||
5. **LLDAP** (Admin-Verzeichnisdienst) ist öffentlich unter `lldap.0ff.space` ohne zusätzliche Traefik-Middleware (kein `basicAuth`, anders als z. B. das Traefik-Dashboard) erreichbar (HTTP 200 auf Login-Seite) — vergrößert die Angriffsfläche für den zentralen Auth-Verzeichnisdienst.
|
||
|
||
## Audit Coverage
|
||
|
||
**Untersucht:**
|
||
- Repository-Struktur, `flake.nix`, Host-Configs (`hosts/blunix`, `hosts/pinix`, `hosts/tnix`, `hosts/globalServices`), Firewall-/Traefik-/GC-Konfiguration
|
||
- Git-Historie (letzte ~30 Commits), Branch-/Remote-Zustand
|
||
- systemd: alle Units, Failed-Units (keine), Restart-Zähler, laufende vs. nicht-laufende Services, Timer (restic, forgejo-dump, nix-gc)
|
||
- Journal: `journalctl -p err` über die letzten Tage, gezielte Suche nach wiederkehrenden Mustern (Forgejo, Prosody, SSH, dhcpcd)
|
||
- Disk-/Speicherzustand (`df -h`, `du` auf `/var/lib/*`, `/nix/store`, `/home`, journal disk usage), Nix-Generationenverlauf
|
||
- Netzwerk: offene Ports (`ss -tlnp`), Firewall-Regeln aus dem Repo (`allowedTCPPorts`, `trustedInterfaces`), Tailscale-Peer-Status
|
||
- HTTP: Live-Check mehrerer extern gerouteter Endpunkte (`git`, `lldap`, `traefik`, `board`, `social`, `phanpy`, `sound`, `data`, `hdm`, `head`, `calendar`) via bereits gebautem `curl`
|
||
- Backup-Kette: restic-Timer und letzte drei Läufe, Forgejo-Dump-Timer und letzter Lauf
|
||
|
||
**Nicht untersucht / eingeschränkt:**
|
||
- **Kein Zugriff auf pinix oder tnix** (weder SSH noch Shell) — alle Aussagen zu diesen Hosts stammen ausschließlich aus Repo-Konfiguration und indirekten Beobachtungen von blunix aus (Traefik-Timeouts, Tailscale-Status, Webhook-Fehler). Der tatsächliche Zustand von pinix (läuft es noch? Netzwerkproblem oder Host down?) bleibt ungeklärt.
|
||
- Keine Web-UI-Funktionsprüfung mit echtem Login (Board, Kanboard, Mastodon-Frontend etc.), da das die Sandbox-Vorgabe „keine Formulareingaben/Aktionen" verletzen würde und ohnehin die Backends über pinix nicht erreichbar waren.
|
||
- Detailinhalt von `/var/lib/forgejo`, `/var/lib/mysql/*`, `/var/log/fail2ban`, `/var/lib/tailscale` etc. war aus Permission-Gründen (kein sudo im Runtime-Pfad dieser Rolle) nicht lesbar; Aussagen zu diesen Komponenten stützen sich auf öffentlich lesbare Metadaten (Verzeichnisgröße von außen, systemctl-Status).
|
||
- Kein `nix flake check` / `nix eval` durchgeführt (für diesen Auftrag nicht notwendig gewesen); keine Bewertung der Nix-Ausdrucks-Korrektheit über die gelesenen Dateien hinaus.
|
||
- Keine tiefere Analyse von `agents/` (Board-Automatisierung, Codex/Claude-Runner) über das hier relevante Maß hinaus — der Job, der diesen Audit ausführt, läuft selbst als `board-agent-worker.service`-Kindprozess; das wurde nur am Rande beobachtet, nicht separat auditiert.
|
||
- Die genannten 5 SSH-`kex_exchange_identification`-Fehler/Tag wurden nicht tiefer untersucht (typisches Internet-Hintergrundrauschen von Portscannern); fail2ban-Jail-Details konnten mangels `fail2ban-client` im PATH nicht abgefragt werden, nur `systemctl is-active fail2ban` (aktiv).
|
||
|
||
## Findings
|
||
|
||
### F1 — Disk-Auslastung auf blunix kritisch, bereits mit realem Ausfall (Critical)
|
||
**Komponenten:** blunix Root-Filesystem, Nix-Store, `nix.gc` (`hosts/nix-gc.nix`), automatisierte Rebuild-Pipeline (`agent-codex/deploy_runner.py`), Forgejo, Prosody, dhcpcd
|
||
**Beobachtung:** `df -h` zeigt 96 % Belegung (34G/37G, 1,5G frei). `/nix/store` allein belegt 25G. Zwischen dem 09.09. 22:49 und dem 11.09. 16:40 wurden 34 System-Generationen erzeugt (`system-471-link` … `system-504-link`), `journalctl` zählt 208 Aktivierungen von `nixos-rebuild-switch-to-configuration.service` in 3 Tagen.
|
||
**Evidenz:**
|
||
```
|
||
Filesystem Size Used Avail Use% Mounted on
|
||
/dev/mapper/pool-root 37G 34G 1.5G 96% /
|
||
Sep 10 04:31:02 blunix forgejo[1636407]: Failed to create temporary directory: ... no space left on device
|
||
Sep 10 04:31:02 blunix systemd[1]: Failed to start forgejo dump.
|
||
Sep 10 00:59:42 blunix prosody[683999]: datamanager: Unable to write to cloud_notify storage ('No space left on device')
|
||
Sep 10 05:00:07 blunix dhcpcd[450984]: dhcp_writefile: ... No space left on device
|
||
```
|
||
**Erwarteter Zustand:** Ausreichend Puffer entsprechend der in `hosts/nix-gc.nix` dokumentierten Analyse (`docs/intent/nix-gc-plattform.md`, min-free 1GiB/max-free 3GiB), keine anwendungsseitigen Fehler durch Plattenmangel.
|
||
**Tatsächlicher Zustand:** Trotz aktivierter `min-free`/`max-free`-Settings (verifiziert in `/etc/nix/nix.conf`) reicht die inkrementelle Garbage Collection nicht aus, weil `nix.gc` nur wöchentlich mit `--delete-older-than 30d` läuft — bei 200+ Rebuilds/3 Tage bleiben praktisch alle Generationen (und ihre GC-Roots) für Wochen erhalten.
|
||
**Vermutete Ursache:** Hohe Rebuild-Kadenz der Board-/Builder-Automatisierung kombiniert mit einer GC-Policy, die auf gelegentliche manuelle Rebuilds ausgelegt war, nicht auf mehrfach-stündliche automatisierte Deploys.
|
||
**Mögliche Auswirkungen:** Wiederkehrende Datenverlust-nahe Situationen (fehlgeschlagene Backups/Dumps), Instabilität weiterer schreibender Dienste, im Extremfall Root-FS voll → Totalausfall.
|
||
**Empfohlene nächste Schritte:** GC-Strategie an tatsächliche Rebuild-Frequenz anpassen (z. B. `nix.gc.dates` täglich statt wöchentlich, `--delete-older-than` deutlich verkürzen, oder Generationen explizit auf eine kleine Anzahl begrenzen statt nur Boot-Menu-Einträge via `configurationLimit`); alternativ Rebuild-Frequenz der Automatisierung selbst hinterfragen (muss jeder einzelne Board-Job sofort `switch` auslösen?).
|
||
**Konfidenz:** Hoch (direkt aus `df`, `journalctl`, Generationenliste und Rebuild-Zähler belegt).
|
||
|
||
### F2 — pinix seit ~1 Tag über Tailscale nicht erreichbar, mehrere Public-Routes tot (Critical)
|
||
**Komponenten:** pinix (Tailnet-Konnektivität), Traefik-Routing (`routes-external.nix`), Forgejo-Webhooks, Kanboard/„board", GoToSocial/„social", „phanpy", „sound", Radicale/„calendar"
|
||
**Beobachtung:** `tailscale status` zeigt pinix als „offline, last seen 1d ago" (tx>0/rx=0 — einseitig tot). Live-Checks gegen alle Traefik-Routen, die auf `tailnetIPs.pinix` zeigen, laufen in einen TLS-Handshake-Erfolg gefolgt von vollständigem Timeout; rein lokale Routen (`data`, `hdm`, `head`, alle `127.0.0.1`) liefern HTTP 200.
|
||
**Evidenz:**
|
||
```
|
||
100.64.0.3 pinix worker@ linux active; relay "fra"; offline, last seen 1d ago, tx 6240 rx 0
|
||
board.0ff.space -> Operation timed out after 15002 milliseconds
|
||
social/phanpy/sound/calendar.0ff.space -> http_code 000 (Timeout)
|
||
data/hdm/head.0ff.space -> http_code 200
|
||
Sep 11 16:33:16 blunix forgejo[...]: Unable to deliver webhook task[1374] ... http://100.64.0.3:9000/hooks/wiki-update ... context deadline exceeded
|
||
```
|
||
**Erwarteter Zustand:** pinix als Tailnet-Peer aktiv, alle darüber gerouteten Public-Services erreichbar.
|
||
**Tatsächlicher Zustand:** pinix vom Tailnet aus nicht erreichbar; dadurch mindestens 5 nach außen beworbene Dienste (Board/Kanboard, Mastodon-Frontend, Phanpy, Sound, Calendar) faktisch down, plus tägliche Webhook-Fehlversuche gegen pinix.
|
||
**Vermutete Ursache:** Entweder pinix selbst ist down/hängt, oder ein Tailscale-/Netzwerkproblem auf pinix-Seite (z. B. IP-Wechsel, Stromausfall, Kernel-Update-Hänger). Ohne Zugriff auf pinix nicht abschließend klärbar.
|
||
**Mögliche Auswirkungen:** Nutzer-sichtbarer Totalausfall mehrerer Dienste seit ~24h, ohne dass dies (soweit im Audit sichtbar) bereits gemeldet/alarmiert wurde — deutet auf eine **Observability-Lücke** hin (kein Alerting für Tailnet-Peer-Verfügbarkeit oder Backend-Health der Traefik-Routen).
|
||
**Empfohlene nächste Schritte:** Sofortige manuelle Prüfung von pinix (Konsolen-/Out-of-Band-Zugriff, da SSH über Tailnet ja gerade das Problem sein könnte); danach Monitoring/Alerting für Tailnet-Peer-Status und Traefik-Backend-Health ergänzen.
|
||
**Konfidenz:** Hoch für „pinix nicht erreichbar" und „dadurch Route-Timeouts" (direkt gemessen); die eigentliche Ursache auf pinix selbst ist unbelegte Vermutung.
|
||
|
||
### F3 — Codeberg-Push-Mirror scheitert zyklisch seit Tagen (Medium)
|
||
**Komponenten:** Forgejo Push-Mirror (`lukas/nixos-config` → `codeberg.org/pijukka/nixos-config`)
|
||
**Beobachtung:** Wiederkehrender Fehler alle paar Stunden über mehrere Tage hinweg.
|
||
**Evidenz:**
|
||
```
|
||
Sep 11 13:54:49 blunix forgejo[608639]: Error pushing .../lukas/nixos-config.git mirror[2] ...: PushRejected
|
||
Sep 11 13:54:49 blunix forgejo[608639]: remote: Forgejo: branch master is the default branch and cannot be deleted
|
||
```
|
||
Lokal existiert kein `master`-Branch mehr (`git branch -a` zeigt nur `dev`/`production`), `origin/HEAD` zeigt aber noch auf `refs/remotes/origin/master`.
|
||
**Erwarteter Zustand:** Push-Mirror synchronisiert erfolgreich, kein wiederkehrender Fehler im Journal.
|
||
**Tatsächlicher Zustand:** Jeder Sync-Versuch versucht, den auf Codeberg als Default gesetzten `master`-Branch zu löschen, was Codeberg ablehnt — seit Tagen ununterbrochen wiederholt.
|
||
**Vermutete Ursache:** `master` wurde im Quell-Repo entfernt/umbenannt (vermutlich im Zuge der `dev`/`production`-Umstellung), ohne den Default-Branch auf dem Mirror-Ziel entsprechend nachzuziehen.
|
||
**Mögliche Auswirkungen:** Kein Datenverlust, aber dauerhaftes Fehlerrauschen im Journal, unnötige Last, und ein nicht mehr aktueller Codeberg-Mirror (bleibt vermutlich auf dem Stand vor der Branch-Umstellung).
|
||
**Empfohlene nächste Schritte:** Default-Branch auf Codeberg-Seite auf `production` oder `dev` umstellen (oder Mirror-Konfiguration entsprechend anpassen), danach Journal auf Wiederholung prüfen.
|
||
**Konfidenz:** Hoch (Fehlermeldung ist eindeutig, lokale Branch-Situation bestätigt die Diskrepanz).
|
||
|
||
### F4 — Verwaistes GoToSocial-Datenverzeichnis auf blunix (Low, Cleanup)
|
||
**Komponenten:** `/var/lib/gotosocial` auf blunix, GoToSocial-Service (laut Repo nur auf pinix definiert)
|
||
**Beobachtung:** `du -sh /var/lib/gotosocial` liefert 117M auf blunix, obwohl `hosts/pinix/localServices/gotosocial.nix` der einzige Ort im Repo ist, der den Service definiert; `systemctl status gotosocial` auf blunix meldet „could not be found".
|
||
**Erwarteter Zustand:** Keine GoToSocial-Daten auf einem Host, der den Service gar nicht betreibt.
|
||
**Tatsächlicher Zustand:** 117 MB toter Daten-Altlast auf einer Platte, die gerade bei 96 % liegt (F1).
|
||
**Vermutete Ursache:** Migration von GoToSocial von blunix nach pinix, Altdaten wurden nicht bereinigt.
|
||
**Mögliche Auswirkungen:** Unnötiger Verbrauch von knappem Speicherplatz; potenzielle Verwechslungsgefahr bei zukünftigen Debugging-Sessions.
|
||
**Empfohlene nächste Schritte:** Verifizieren, dass die Daten wirklich nicht mehr gebraucht werden (z. B. Vergleich mit dem aktiven Datenstand auf pinix, sobald erreichbar), dann manuell entfernen.
|
||
**Konfidenz:** Mittel — Verzeichnisinhalt selbst war wegen fehlender Leserechte nicht einsehbar, nur die Größe und der Servicename sind belegt.
|
||
|
||
### F5 — LLDAP-Admin-UI öffentlich ohne zusätzliche Auth-Schicht exponiert (Medium, Security)
|
||
**Komponenten:** `hosts/blunix/localServices/lldap.nix`, `hosts/blunix/localServices/traefik/routes-external.nix` (Route „lldap")
|
||
**Beobachtung:** `https://lldap.0ff.space/` liefert HTTP 200 (Login-Seite) ohne Traefik-`basicAuth`-Middleware, im Unterschied zur Traefik-API-Route, die per `middlewares = ["auth"]` geschützt ist (HTTP 401 bestätigt).
|
||
**Erwarteter Zustand / Vergleich:** Andere sensible interne Tools (`intent-board`) sind bewusst mit `middlewares = ["auth"]` versehen; LLDAP als zentraler Identitäts-/Admin-Dienst hat dieses zusätzliche Netzwerk-Gate nicht.
|
||
**Vermutete Ursache:** LLDAP verlässt sich allein auf sein eigenes Login (Passwort-Auth), vergleichbar mit der bewussten Design-Entscheidung bei „calendar" (Kommentar: „Radicale's own per-request LDAP auth is the real security boundary"). Bei LLDAP ist das aber der Verzeichnisdienst selbst, dessen Kompromittierung (z. B. Brute-Force, ungepatchte CVE) alle nachgelagerten Auth-Flows (Dex, oauth2-proxy) betrifft.
|
||
**Mögliche Auswirkungen:** Größere Angriffsfläche für den zentralen Identity-Provider als nötig; kein Brute-Force-Schutz auf HTTP-Ebene sichtbar (nur generisches fail2ban, Jail-Details nicht einsehbar).
|
||
**Empfohlene nächste Schritte:** Prüfen, ob `basicAuth`- oder IP-/Tailnet-Beschränkung für die LLDAP-Web-UI sinnvoll ergänzt werden kann, ohne den beabsichtigten Nicht-Tailnet-Zugriff (falls gewollt) zu brechen; alternativ gezielte fail2ban-Jail für LLDAP-Login-Fehlversuche verifizieren/ergänzen.
|
||
**Konfidenz:** Mittel — die Exposition ist belegt, ob das eine bewusste Design-Entscheidung war (wie bei „calendar" dokumentiert) oder ein Versehen, ist aus dem Repo nicht eindeutig ersichtlich (kein entsprechender Kommentar wie bei anderen Routen).
|
||
|
||
### F6 — Wiederkehrende Forgejo „bad object"-Fehler bei PR-Diff-Stats (Low)
|
||
**Komponenten:** Forgejo, `GetPullDiffStats`
|
||
**Beobachtung:** Drei distincte Objekt-Hashes (`059210b7…`, `13f74c33…`, `45db7d4d…`) tauchen über mehrere Tage hinweg wiederholt als „fatal: bad object" auf.
|
||
**Erwarteter Zustand:** Diff-Stats-Berechnung für alle offenen PRs funktioniert fehlerfrei.
|
||
**Tatsächlicher Zustand:** Für (vermutlich) einzelne PRs mit inzwischen nicht mehr existenten Commits (z. B. nach Force-Push/Rebase eines Fork-Branches) schlägt die Berechnung dauerhaft fehl und wird periodisch erneut versucht.
|
||
**Vermutete Ursache:** Stale PR-Referenz auf gelöschten/umgeschriebenen Branch-Kopf, kein Repo-Korruptionsproblem des Hauptrepos.
|
||
**Mögliche Auswirkungen:** Kosmetisch (fehlende Diff-Stats-Anzeige für betroffene PR(s)), plus unnötige wiederkehrende Last/Log-Rauschen.
|
||
**Empfohlene nächste Schritte:** Betroffene(n) PR(s) identifizieren (über Forgejo-UI/DB) und schließen oder neu synchronisieren.
|
||
**Konfidenz:** Mittel (Fehlermuster eindeutig, genaue betroffene PR nicht identifiziert).
|
||
|
||
### F7 — MySQL/MariaDB lauscht auf `0.0.0.0:3306` statt `127.0.0.1` (Informational)
|
||
**Komponenten:** MySQL/MariaDB (WordPress-Backend), Firewall
|
||
**Beobachtung:** `ss -tlnp` zeigt `0.0.0.0:3306` und `[::]:3306`; die Firewall-Konfiguration (`allowedTCPPorts = [22 80 443 5222 5269]`, keine `trustedInterfaces`, kein `tailscale0`-Sonderfall für 3306) lässt aber keinen externen oder Tailnet-Zugriff zu.
|
||
**Erwarteter/tatsächlicher Zustand:** Kein akutes Risiko, da die OS-Firewall den Port blockt; sauberer wäre dennoch ein `bind-address = 127.0.0.1`, um Defense-in-Depth nicht allein von der Firewall-Regel abhängig zu machen.
|
||
**Mögliche Auswirkungen:** Gering, solange Firewall-Konfiguration unverändert bleibt; Risiko steigt bei künftigen Firewall-Änderungen (z. B. neuer `trustedInterfaces`-Eintrag).
|
||
**Empfohlene nächste Schritte:** `bind-address`/`http_host`-Äquivalent für MySQL auf `127.0.0.1` setzen, falls kein externer DB-Zugriff je benötigt wird.
|
||
**Konfidenz:** Hoch für die Beobachtung, mittel für die Risikoeinschätzung (abhängig von künftigen Konfigurationsänderungen).
|
||
|
||
## Positive Findings
|
||
|
||
- **Backups laufen zuverlässig:** `restic-backups-vpsBackup` hat an allen drei geprüften Tagen (09.–11.09.) erfolgreich Snapshots erzeugt, auch am Tag des Disk-Vorfalls — die Backup-Kette selbst war nicht betroffen.
|
||
- **Selbstheilung des Forgejo-Dumps:** Nach dem akuten Space-Vorfall am 10.09. lief der Forgejo-Dump am 11.09. wieder sauber durch (`Finished dumping in file forgejo-dump-…`), sobald wieder Platz frei war — kein manuelles Eingreifen nötig.
|
||
- **Durchdachte, dokumentierte GC-Tuning-Historie:** `hosts/nix-gc.nix` zeigt bereits eine bewusste, aus einem Preflight abgeleitete Anpassung (`docs/intent/nix-gc-plattform.md`, 2026-09-05) mit klar begründeten Host-spezifischen Schwellenwerten — das Problem in F1 ist also bereits erkannt und teilweise adressiert worden, reicht aber (siehe F1) angesichts der Rebuild-Frequenz nicht aus.
|
||
- **Saubere Trennung von Auth-Boundaries dort, wo dokumentiert:** Die `calendar`-Route hat eine explizit begründete Design-Entscheidung („Radicale's own per-request LDAP auth is the real security boundary"), die `intent-board`-Route bekam bewusst `basicAuth` ergänzt, nachdem ein Review das als Lücke markiert hatte — die Codebase zeigt an mehreren Stellen nachvollziehbare, kommentierte Sicherheitsentscheidungen statt Zufall.
|
||
- **Firewall-Policy minimal und explizit:** Nur 5 TCP-Ports global offen, alle weiteren Dienste laufen entweder rein lokal oder über gezielte `tailscale0`-Ausnahmen — kein Anzeichen einer „alles offen"-Konfiguration.
|
||
- **Keine failed systemd-Units zum Prüfzeitpunkt**, kein Service mit auffälliger Restart-Historie außer dem hier selbst laufenden Board-Worker-Job.
|
||
|
||
## Cross-System Observations
|
||
|
||
- **F1 (Disk voll) und die 34 Generationen/208 Rebuilds sind direkt kausal verknüpft:** Die hohe Automatisierungs-Kadenz der Board-/Builder-Pipeline (jeder abgeschlossene Job löst offenbar einen `nixos-rebuild switch` aus) unterläuft die am 2026-09-05 bewusst eingeführte GC-Politik. Das ist kein Konfigurationsfehler in `nix-gc.nix` selbst, sondern ein Mismatch zwischen einer für gelegentliche Rebuilds ausgelegten Politik und einer inzwischen viel höherfrequenten Realität.
|
||
- **F2 ist die gemeinsame Ursache für mindestens 5 scheinbar unabhängige Symptome:** Timeout auf `board.0ff.space`, `social.0ff.space`, `phanpy.0ff.space`, `sound.0ff.space`, `calendar.0ff.space` sowie die tagelang wiederkehrenden Forgejo-Webhook-Fehler gegen `100.64.0.3:9000` — alle laufen über denselben Tailnet-Pfad zu pinix. Ohne Korrelation hätte man das leicht als 6 getrennte Bugs behandelt.
|
||
- **Dokumentation vs. Runtime:** `docs/intent/nix-gc-plattform.md` beschreibt einen Zustand („blunix hat kaum Reserve, 29G Store auf 37G-Platte"), der sich seit dem 2026-09-05 durch die gestiegene Rebuild-Frequenz weiter verschärft hat (Store jetzt 25G, Gesamtbelegung 34G/37G) — die Doku ist also nicht falsch, aber die zugrunde liegende Annahme (Rebuild-Häufigkeit) hat sich seither verändert, ohne dass die Gegenmaßnahme nachgezogen wurde.
|
||
- **Kein sichtbares Alerting für beide Critical-Findings:** Weder der Disk-Vorfall vom 10.09. noch der seit ~24h laufende pinix-Ausfall scheinen ein aktives Monitoring/Alerting ausgelöst zu haben (soweit aus Journal/Config ersichtlich) — beides wäre ohne diesen Audit vermutlich erst durch Nutzerbeschwerden aufgefallen.
|
||
|
||
## Empfohlene Folge-Intents (priorisiert)
|
||
|
||
1. **[Reliability/Bugfix] GC-Policy an tatsächliche Rebuild-Frequenz von blunix anpassen.** Ziel: `nix.gc` täglich statt wöchentlich, kürzere Retention und/oder Begrenzung der Anzahl gehaltener System-Generationen, damit die min-free/max-free-Schwellen nicht durch Dutzende frischer Generationen unterlaufen werden. Bezug: F1, `hosts/nix-gc.nix`.
|
||
2. **[Reliability] pinix-Erreichbarkeit klären und wiederherstellen.** Ziel: Root Cause des Tailnet-Ausfalls seit ca. 24h ermitteln (Host down? Tailscale-Client hängt? Netzwerkproblem?) und Board/Social/Phanpy/Sound/Calendar wieder erreichbar machen. Bezug: F2. Höchste Nutzerrelevanz.
|
||
3. **[Observability] Alerting für Tailnet-Peer-Status und Disk-Füllstand ergänzen.** Ziel: Beide Critical-Vorfälle (F1, F2) hätten proaktiv erkannt werden können; ein einfacher Schwellenwert-Check (Diskbelegung, `tailscale status` Peer-Offline-Dauer) mit Benachrichtigung wäre risikoarm und hochwirksam.
|
||
4. **[Bugfix] Codeberg-Push-Mirror-Konfiguration an aktuellen Default-Branch (`production`/`dev`) anpassen.** Bezug: F3. Geringer Aufwand, beseitigt tagelanges Fehlerrauschen.
|
||
5. **[Cleanup] Verwaiste GoToSocial-Altdaten auf blunix identifizieren und entfernen**, nachdem pinix wieder erreichbar ist und der aktive Datenstand verifiziert werden kann. Bezug: F4.
|
||
6. **[Security] LLDAP-Web-UI-Exposition bewerten und ggf. zusätzliche Schutzschicht (Middleware/IP-Beschränkung/Rate-Limiting) ergänzen.** Bezug: F5.
|
||
7. **[Cleanup] Betroffene PR(s) mit „bad object"-Referenzen in Forgejo identifizieren und bereinigen.** Bezug: F6.
|
||
8. **[Security/Cleanup] MySQL `bind-address` auf `127.0.0.1` setzen als Defense-in-Depth**, unabhängig von der Firewall-Regel. Bezug: F7.
|
||
9. **[Architecture]** Grundsätzliche Diskussion, ob jeder einzelne Board-/Builder-Job wirklich einen sofortigen `nixos-rebuild switch` auslösen muss, oder ob Batching/Throttling der Deploys (z. B. max. 1 Switch pro X Minuten) die Systembelastung strukturell reduzieren würde — würde F1 nicht nur symptomatisch (GC), sondern ursächlich adressieren.
|
||
|
||
## Unsicherheiten / offene Punkte
|
||
|
||
- Die tatsächliche Ursache der pinix-Nichterreichbarkeit (F2) ist von blunix aus nicht abschließend diagnostizierbar (Host down vs. reines Netzwerkproblem).
|
||
- Ob die fehlende Middleware bei LLDAP (F5) eine bewusste Entscheidung oder ein Versehen war, ist aus dem Repo nicht mit Sicherheit ablesbar.
|
||
- Genaue Größe/Inhalt von `/var/lib/forgejo`, `/var/lib/mysql/*`, `/var/lib/traefik`, `/var/lib/postgresql` etc. blieb wegen fehlender Leserechte im Runtime-Pfad dieser Rolle unzugänglich; die Diskbilanz aus F1 könnte daher noch weitere, hier nicht sichtbare Verbraucher enthalten.
|
||
- Fail2ban-Jail-Konfiguration/-Wirksamkeit im Detail nicht prüfbar (`fail2ban-client` nicht im PATH der Sandbox).
|
||
|
||
## Fazit
|
||
|
||
Die Plattform ist grundsätzlich solide und mit erkennbarer Sorgfalt gepflegt (dokumentierte Design-Entscheidungen, funktionierende Backups, minimale Firewall-Policy). Der Audit deckt jedoch zwei bereits real eingetretene bzw. laufende Incidents auf, die ohne diesen Blick von außen leicht als mehrere unabhängige Kleinprobleme behandelt worden wären: ein selbstverschärfender Diskplatz-Engpass durch die hohe Automatisierungs-Kadenz der Deploy-Pipeline (F1), und ein seit rund einem Tag bestehender Totalausfall mehrerer öffentlicher Dienste durch einen nicht erreichbaren pinix-Host (F2). Beide sollten vorrangig behandelt werden; die übrigen Findings sind überwiegend kostengünstig zu beheben und primär Hygiene-/Cleanup-Charakter.
|