From 5cb401797d0540a9d31afd24cdfdda1349eb2951 Mon Sep 17 00:00:00 2001 From: Micha Date: Tue, 26 May 2026 14:55:49 +0200 Subject: [PATCH] Bind AdGuard admin to Tailscale --- HOMELAB_ARCHITECTURE_MASTER_V2.md | 10 +- README.md | 10 + docs/AI_CONTEXT.md | 2 +- docs/AUDIT_2026-05-25.md | 895 +++++++++++++++++++++++ docs/AUDIT_2026-05-25_TODO.md | 86 +++ docs/CAPACITY_AND_LIFECYCLE.md | 63 ++ docs/DISASTER_RECOVERY.md | 5 + docs/EXTERNAL_DEPENDENCIES.md | 64 ++ docs/FAMILY_ONBOARDING.md | 38 + docs/HARDWARE_INVENTORY.md | 177 +++++ docs/MIGRATION_LOG.md | 14 + docs/NETWORK_INVENTORY.md | 106 +++ docs/REPO_MAP.md | 11 +- docs/SERVICES_RECOVERY.md | 98 +++ docs/SERVICE_CATALOG.md | 2 +- docs/WORKFLOW.md | 3 + host-services/Adguard/docker-compose.yml | 2 +- ops/policy-checks/exceptions.json | 2 +- ops/policy-checks/last-report.md | 16 +- 19 files changed, 1588 insertions(+), 16 deletions(-) create mode 100644 docs/AUDIT_2026-05-25.md create mode 100644 docs/AUDIT_2026-05-25_TODO.md create mode 100644 docs/CAPACITY_AND_LIFECYCLE.md create mode 100644 docs/EXTERNAL_DEPENDENCIES.md create mode 100644 docs/FAMILY_ONBOARDING.md create mode 100644 docs/HARDWARE_INVENTORY.md create mode 100644 docs/NETWORK_INVENTORY.md create mode 100644 docs/SERVICES_RECOVERY.md diff --git a/HOMELAB_ARCHITECTURE_MASTER_V2.md b/HOMELAB_ARCHITECTURE_MASTER_V2.md index c7ba9c4..d53a286 100644 --- a/HOMELAB_ARCHITECTURE_MASTER_V2.md +++ b/HOMELAB_ARCHITECTURE_MASTER_V2.md @@ -47,7 +47,7 @@ ## 2. Architektur-Prinzipien ### P1 — Traefik ist der einzige öffentliche HTTP(S)-Einstiegspunkt -Kein Webdienst veröffentlicht finale direkte Host-Ports außer `traefik` selbst. Begründete Ausnahmen: `gitea`-SSH (Port 222), `AdGuard Home` (Port 53/DNS + 8082/Admin als bewusste LAN-Operator-Entscheidung), `Tailscale`, `Plex-Media-Server` und `monitoring-influxdb3-core` Port 8181 als LAN-only Writer-Endpunkt fuer Home Assistant. +Kein Webdienst veröffentlicht finale direkte Host-Ports außer `traefik` selbst. Begründete Ausnahmen: `gitea`-SSH (Port 222), `AdGuard Home` (Port 53/DNS direkt; Admin 8082 nur auf Tailscale-IP `100.80.98.33`), `Tailscale`, `Plex-Media-Server` und `monitoring-influxdb3-core` Port 8181 als LAN-only Writer-Endpunkt fuer Home Assistant. ### P2 — Das Setup bleibt bewusst einfach: `frontend_net` + `backend_net` + app-interne Netze - `frontend_net` = Proxy-/Web-Netz @@ -167,7 +167,7 @@ Diese Dienste sind **keine Public Apps**: - `monitoring-grafana` — monitoring.kaleschke.info (Middleware) - `hermes-dashboard` — hermes.kaleschke.info (Middleware) - `Traefik-Dashboard` -- `AdGuard Home` — Port 8082 direkt auf die Admin-UI (`80` im Container), kein Traefik, nur LAN-Zugang; 2026-05-25 bewusst keine 2FA-/Traefik-Umstellung +- `AdGuard Home` — Admin-UI auf Port 8082 (`80` im Container), kein Traefik, nur Tailscale-IP `100.80.98.33`; 2026-05-26 bewusst keine 2FA-/Traefik-Umstellung ### 4.3 Regel Wenn ein Dienst im `frontend_net` hängt, heißt das **nicht automatisch öffentlich**. Admin-Dienste dürfen im `frontend_net` liegen, wenn: @@ -239,7 +239,7 @@ Legende Status: | Container | Status | Soll-Netz(e) | Finaler Zugang | Finaler Sollzustand | Offene Punkte | |---|---|---|---|---|---| | `traefik` | ✅ | `frontend_net`, `backend_net` | öffentlich 80/443 | zentraler Ingress, Service-Routing via Docker-Labels | — | -| `AdGuard Home` | ✅ | `dns_net` (172.23.0.3), `frontend_net` | Port 53 DNS direkt, Port 8082 Admin (LAN) | DNS-Server + Upstream zu unbound; kein Traefik fuer Admin-UI | Admin-Port bleibt bewusst LAN-direkt; Operator-Entscheidung 2026-05-25 | +| `AdGuard Home` | ✅ | `dns_net` (172.23.0.3), `frontend_net` | Port 53 DNS direkt, Port 8082 Admin nur auf Tailscale-IP `100.80.98.33` | DNS-Server + Upstream zu unbound; kein Traefik fuer Admin-UI | Admin-Port bleibt bewusst ohne Traefik/2FA, aber nicht mehr auf allen LAN-Interfaces | | `unbound` | ✅ | `dns_net` | intern | Upstream-Resolver für AdGuard, isoliert | — | | `ddns-updater` | ✅ | `frontend_net` | intern | Cloudflare DNS API; bleibt in `frontend_net` | Dokumentierte Ausnahme | | `tailscale` | ✅ | `host` | VPN-Zugang | Git-Stack (`host-services/tailscale/`) | nutzt `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` als dokumentierte VPN-Ausnahme | @@ -393,7 +393,7 @@ Für den laufenden Betrieb gilt stattdessen: |---|---|---| | `traefik` | Host-Ports 80/443 | zentraler Reverse Proxy | | `tailscale` | `host`, `NET_ADMIN`, `NET_RAW`, `/dev/net/tun` | VPN-Zugang benoetigt Kernel-Netzwerkfunktionen; Umstellung nur kontrolliert moeglich | -| `AdGuard Home` | Port 53 (TCP/UDP) direkt + Port 8082 auf Container-Port 80 | DNS benoetigt direkten Port 53; Admin-Port 8082 bleibt bewusst LAN-direkt ohne Traefik/2FA | +| `AdGuard Home` | Port 53 (TCP/UDP) direkt + `100.80.98.33:8082` auf Container-Port 80 | DNS benoetigt direkten Port 53; Admin-Port 8082 bleibt bewusst ohne Traefik/2FA, aber nur via Tailscale | | `Plex-Media-Server` | `host` | Discovery / mDNS / Plex GDM | | `scrutiny` | `privileged: true` | SMART-Datenzugriff auf Laufwerke | | `Komodo` | Docker-Socket Zugriff | Stack-Deployments benötigen Socket | @@ -495,7 +495,7 @@ Komodo ist nun der primäre GitOps-Stack-Manager: - AdGuard läuft als Git-Stack (`host-services/Adguard/docker-compose.yml`) - Netzwerke: `dns_net` (feste IP 172.23.0.3) + `frontend_net` - Port 53 (DNS) direkt gebunden — dokumentierte Ausnahme -- Admin-UI direkt gebunden via Host-Port 8082 auf Container-Port 80 — 2026-05-25 bewusst als einfache LAN-Operator-Entscheidung beibehalten, keine Traefik-/2FA-Umstellung +- Admin-UI direkt gebunden via Tailscale-IP `100.80.98.33:8082` auf Container-Port 80 — 2026-05-26 bewusst als einfache Operator-Entscheidung ohne Traefik-/2FA-Umstellung - `unbound` läuft weiterhin als Upstream-Resolver in `dns_net` ### diun — Entfernung (2026-03-28) diff --git a/README.md b/README.md index aeeee43..c0561bb 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,14 @@ Bei Restore-, Host-Ausfall- oder Wiederanlauf-Fragen zusaetzlich: 3. `docs/DISASTER_RECOVERY.md` 4. `docs/RESTORE_MATRIX.md` +5. `docs/SERVICES_RECOVERY.md` + +Bei Hardware-, Netzwerk-, Provider- oder Kapazitaetsfragen zusaetzlich: + +6. `docs/HARDWARE_INVENTORY.md` +7. `docs/NETWORK_INVENTORY.md` +8. `docs/EXTERNAL_DEPENDENCIES.md` +9. `docs/CAPACITY_AND_LIFECYCLE.md` ## Architektur @@ -63,5 +71,7 @@ Bei Restore-, Host-Ausfall- oder Wiederanlauf-Fragen zusaetzlich: - Traefik `dynamic/` bleibt eine dokumentierte manuelle Host-Sync-Ausnahme ausserhalb des normalen Komodo-Deployments. - Mutable Image-Tags sind auf die aktuell laufenden Digests eingefroren; echte Versions-Upgrades erfolgen bewusst separat. - Disaster-Recovery und dienstspezifische Restore-Quellen sind in `docs/DISASTER_RECOVERY.md` und `docs/RESTORE_MATRIX.md` beschrieben. +- Recovery-kritische Services-Pfade wie Gitea-Repositories, Komodo-Workspaces und Host-Automation sind in `docs/SERVICES_RECOVERY.md` beschrieben. +- Hardware-, Netzwerk-, Provider- und Capacity-Inventare sind als operative Audit-Dokumente unter `docs/HARDWARE_INVENTORY.md`, `docs/NETWORK_INVENTORY.md`, `docs/EXTERNAL_DEPENDENCIES.md` und `docs/CAPACITY_AND_LIFECYCLE.md` vorbereitet. - Der verbindliche Detailablauf steht in `docs/WORKFLOW.md`. - `nextcloud`, `bentopdf` und `monitoring` folgen dem dokumentierten Netz-/Secret-/Traefik-Modell; der zentrale Monitoring-Stack buendelt Prometheus, Loki, Promtail, Grafana und InfluxDB 3 Core. diff --git a/docs/AI_CONTEXT.md b/docs/AI_CONTEXT.md index 369b455..2a32019 100644 --- a/docs/AI_CONTEXT.md +++ b/docs/AI_CONTEXT.md @@ -135,7 +135,7 @@ Bekannte Ausnahmen: - Traefik: 80/443 - Gitea: SSH 222 -- AdGuard: DNS 53 und Admin 8082; Admin 8082 bleibt bewusst LAN-direkt ohne Traefik/2FA +- AdGuard: DNS 53 direkt; Admin 8082 ist bewusst ohne Traefik/2FA, aber auf Tailscale-IP `100.80.98.33` begrenzt - Tailscale: Host-Netz, `NET_ADMIN`, `NET_RAW`, `/dev/net/tun` - Scrutiny: privileged - Komodo: Docker-Socket, native Auth diff --git a/docs/AUDIT_2026-05-25.md b/docs/AUDIT_2026-05-25.md new file mode 100644 index 0000000..9dc5ede --- /dev/null +++ b/docs/AUDIT_2026-05-25.md @@ -0,0 +1,895 @@ +# Homelab-Audit KalliLab CORE + +Stand: 2026-05-25 +Methode: Repo-basierter Audit auf `master` (lokaler Clone). Keine Live-Messung gegen den Host. Querverweise auf Audit-Live-Daten aus `docs/AUDIT_2026-05-23_LIVE.md`, wo verfuegbar. +Auftrag: externer, kritischer Audit-Blick zusaetzlich zur internen `docs/STRATEGISCHE_BEWERTUNG_2026-05-23.md`. + +## Wichtige Vorbemerkung + +Es gibt seit dem 23.05. eine fundierte interne Bewertung (`docs/STRATEGISCHE_BEWERTUNG_2026-05-23.md`) und eine konsolidierte Hausaufgabenliste (`docs/CODEX_KONSOLIDIERUNG_2026-05-23.md`). Davon wurden seit dem 25.05. bereits umgesetzt: + +- Jellyfin entfernt (MASTER 7.8) +- Homepage entfernt (MASTER 7.8) +- Uptime-Kuma entfernt (MASTER 7.8, SECRETS_MAP 29) +- Monitoring-Stack produktiv (AUDIT_FINAL 9), Altstaende-Container down +- Disk1 NTFS -> XFS Phase 2 abgeschlossen am 2026-05-25 (STORAGE_LAYOUT 3) +- Unraid-Flash-Backup live (`unraid-flash-config.tar.gz` im Borg-Lauf) +- GitHub-Push-Mirror `michaelkaleschke-spec/homelab-infra` aktiv (DR 10, MASTER 7.1) + +Diese geloesten Punkte werden hier nicht wiederholt. Dieser Audit konzentriert sich auf das, was nach dem 23.05.-Sprint **noch offen ist** und auf das, was die strategische Bewertung **nicht oder nur kurz angerissen** hat. + +--- + +# Phase 1: Repo-Inventar + +## Ordnerstruktur (Ist-Zustand) + +``` +homelab-infra/ +├── apps/ 9 Stacks (bentopdf, immich, mail-archiver, mealie, nextcloud, ntfy, paperless, paperless-gpt, unbound) +├── core/ 1 Stack (gitea) +├── docs/ 28 Markdown-Dokumente +├── env/ 2 *.example +├── host-services/ 3 Stacks (Adguard, plex, tailscale) +├── infra/ 3 Stacks (ddns-updater, postgresql17, redis) +├── monitoring/ 1 Compose mit 10 Services + Provisioning +├── ops/ 17 Verzeichnisse (Semaphore, borg-ui, code-server, filebrowser, glance, glances, grafana-influxdb [Altstand], hermes-agent, komodo, loki [Altstand], policy-checks, restore-tests, scrutiny, speedtest, uptime-kuma [Altrest], windows-reinstall) +├── security/ 2 Stacks (authelia, vaultwarden) +├── services/ 1 posture-check (Host-Skripte) +└── traefik/ 1 Compose + dynamic/ (3 Files) +``` + +**Inventar-Befund:** + +- ~30 Compose-Dateien, 1 zentraler Compose-Multi-Service (`monitoring/`). +- 29 Composes wurden vom Policy-Checker validiert (`ops/policy-checks/last-report.md`): **0 Critical, 4 Warnings, 9 Info**. +- Doku-Dichte ist hoch (REPO_MAP, SERVICE_CATALOG, RESTORE_MATRIX, DISASTER_RECOVERY, SECRETS_MAP, WORKFLOW, STORAGE_LAYOUT, GITOPS_DRIFT_RUNBOOK, ALERTING_MAP). +- Restore-Tests sind als echte Scripts versioniert (`ops/restore-tests/`). Ueberdurchschnittlich. + +## Gut dokumentierte Bereiche (Belegt) + +| Bereich | Quelle | +|---|---| +| Architektur, Netze, Ausnahmen | `HOMELAB_ARCHITECTURE_MASTER_V2.md` | +| GitOps-Workflow, Drift | `docs/WORKFLOW.md`, `docs/GITOPS_DRIFT_RUNBOOK.md` | +| Backup-Scope, Restore-Wege, Tier-Modell | `docs/RESTORE_MATRIX.md`, `docs/DISASTER_RECOVERY.md`, `ops/borg-ui/BACKUP_SCOPE.md` | +| Storage / Cache-Policy / FS / Posture | `docs/STORAGE_LAYOUT.draft.md` | +| Secret-Inventur | `docs/SECRETS_MAP.md` | +| Alert-Pfade | `docs/ALERTING_MAP.md` | + +## Luecken / Unklar (vermutet bzw. Rueckfrage noetig) + +| Luecke | Warum es ein Audit-Loch ist | +|---|---| +| `docs/STORAGE_LAYOUT.draft.md` ist `Draft 1.3`, nicht `Active` | Mehrere Hard Rules (12 Constitution) gelten formal noch nicht. Hard Rule 11 (kein Stack ohne Restore-Pfad in RESTORE_MATRIX) wird heute schon eingehalten — also nur Formal-Luecke. | +| `docs/SERVICES_RECOVERY.md` ist als verbindlich angekuendigt (STORAGE_LAYOUT 4), aber nicht im Repo | Konkrete Mirror-Mechanik fuer `services/gitea/git/repositories/` ≤ 6 h ist nirgends spezifiziert. | +| Hardware-Inventar: kein zentrales Dokument | Keine Stelle im Repo nennt CPU-Modell, RAM-Groesse, NIC-Speed, Mainboard, Parity-Disk-Groessen — nur "Samsung 970 EVO Plus 2 TB" steht in STORAGE_LAYOUT 3. | +| USV: keine Erwaehnung | Keine Datei nennt eine USV. Unklar, ob vorhanden. | +| Familien-/User-Onboarding-Doku | Keine Doku, die deine Frau/Kinder lesen muessten ("So loggst du dich in Nextcloud ein"). Aktuell ist alles Operator-Doku. | + +## Fuer den Audit besonders wichtige Dateien (verwendet) + +- `HOMELAB_ARCHITECTURE_MASTER_V2.md` — komplett +- `docs/WORKFLOW.md`, `docs/REPO_MAP.md`, `docs/SERVICE_CATALOG.md` — komplett +- `docs/DISASTER_RECOVERY.md`, `docs/RESTORE_MATRIX.md`, `docs/SECRETS_MAP.md` — komplett +- `docs/STORAGE_LAYOUT.draft.md`, `docs/STRATEGISCHE_BEWERTUNG_2026-05-23.md` — komplett +- `docs/AUDIT_2026-05-23_LIVE.md`, `docs/AUDIT_2026-05-23_FINAL.md` +- `ops/policy-checks/last-report.md` +- `monitoring/docker-compose.yml`, `monitoring/prometheus/alerts.yml` +- `traefik/docker-compose.yml`, `traefik/dynamic/middlewares.yml` +- `security/authelia/configuration.yml`, `security/authelia/docker-compose.yml` +- `apps/paperless/docker-compose.yml`, `apps/immich/docker-compose.yml`, `apps/nextcloud/docker-compose.yml` +- `host-services/Adguard/docker-compose.yml` + +--- + +# A. Executive Summary + +**Was schon stark ist:** + +- GitOps-Disziplin: Gitea als Sollzustand, Komodo als Consumer, dokumentierter Drift-Runbook, Stop-Regel ("zwei Fehlversuche -> Pflichtmatrix"). Seltene Reife. +- Backup-Architektur: Pre-Backup-Dumps + Borg + Restore-Tests mit echtem Smoke-Test-Kriterium ("Container startet ≠ Erfolg"). 15 frische Dumps < 24 h alt (`AUDIT_LIVE 9.4`). +- Architektur-Klarheit: `frontend_net` / `backend_net` / app-interne Netze, keine Sammelnetze, dokumentierte Ausnahmen. +- Image-Pinning: Tier-1-Stateful mit `@sha256:...`. Konsequent durchgezogen. +- Secrets-Hygiene: Keine Secret-Werte im Repo, `_FILE`-Mounts + Komodo Stack ENV, explizit dokumentierte Ausnahmen. +- Policy-as-Code: `check_repo.ps1` mit 0 Critical und sauber dokumentierten Exceptions. + +**Was kritisch ist:** + +- **AdGuard Admin-Port 8082 ohne Authelia/2FA am LAN gebunden** (`host-services/Adguard/docker-compose.yml:16`) — dokumentierte "Operator-Entscheidung" 2026-05-25. Im Heim-LAN tolerierbar, mit IoT/Gaeste-WLAN potenziell ein Pfad zur DNS-Manipulation. Niedrigster Aufwand: Bind nur auf Tailscale-Interface. +- **Authelia ACL: 2FA nur fuer `files.kaleschke.info` und `scrutiny.kaleschke.info`** (`security/authelia/configuration.yml:44-48`). Borg-UI, Code-Server, Filebrowser, Glance — alles nur `one_factor`. Bei Pwd-Kompromittierung des Operator-Accounts ist Borg-UI + Code-Server der direkteste Pfad zur Datenexfiltration. +- **Authelia-Repo-Baseline ↔ Host-Config-Drift "by design"** (`docs/REPO_MAP.md:48`, `SERVICE_CATALOG 23`). User-DB, OIDC-Clients und Secrets sind hostseitig, Manual-Merge-Pflicht. Stelle, an der Drift mit Anlauf passiert. +- **Komodo Self-Bootstrap-Problem ist nur dokumentiert, nicht geloest** (MASTER 13: Self-Stack Drift-Recovery 2026-05-04). Bei Recovery vom kalten Host musst du Komodo aus `compose.yaml` neu erzeugen — dafuer brauchst du die `.env` mit `KOMODO_*`-Secrets, die nur auf Host und ggf. Vaultwarden liegen. +- **Backup-Off-Site-Diversitaet:** BorgBase Hetzner ist Single-Provider; Borg-Passphrase analog gesichert ist als TODO markiert (`docs/DISASTER_RECOVERY.md:64,401`). Wenn Hetzner-Account verloren geht, ist das halbe DR-Versprechen weg. + +**Was unnoetig komplex ist:** + +- Drei dokumentierte Monitoring-/Logging-Pfade gleichzeitig im Repo: `ops/grafana-influxdb` (Altstand), `ops/loki` (Altstand), `monitoring/` (Ziel). Die Altstaende sind als Container down, aber **Verzeichnisse noch im Repo** — Doppelpflege-Risiko. Der versprochene Repo-Cleanup (`git rm`) fehlt. +- Hermes-Agent: NAS-Stack bewusst deaktiviert ("VM-seitig offen"), aber Stack-Verzeichnis und Compose mit Dashboard-Domain bleiben im Repo. Mehr "Schwebezustand" als operativer Wert. +- BentoPDF: "vorbereitet", noch nie produktiv abgenommen (`SERVICE_CATALOG 52`, `MASTER 7.5`). +- `infra/redis` ist als shared Cache deklariert, wird de facto nur von Paperless genutzt (Authelia nicht, Immich/Nextcloud/Mealie haben eigene Redis). Das "shared" stimmt im Repo nicht mit der Realitaet ueberein. + +**Groesster Hebel:** + +**Authelia OIDC-Provider aktivieren** — wenn Nextcloud, Immich, Grafana (und perspektivisch Mealie via OIDC-Bridge) per SSO laufen, gewinnst du gleichzeitig: + +- (a) Familien-Onboarding-Komfort (ein Login), +- (b) zentrale Brute-Force-Regulation und Audit, +- (c) Voraussetzung fuer sinnvolles CrowdSec/Fail2Ban, +- (d) zentrale 2FA-Pflicht statt App-by-App. + +Das ist ein Sprint, nicht ein Quartal, und macht aus deinem "Admin-Authelia" ein echtes Identity-System. + +**Was ein erfahrener Homelabber sofort aendern wuerde:** + +1. AdGuard-Admin-Port nur auf Tailscale-Interface binden (5 Min, Compose-Edit). +2. Borg-Passphrase auf Papier in Bankschliessfach (15 Min, off-system). +3. `scrutiny` und `ddns-updater` `no-new-privileges` Warning aufraeumen (10 Min) — kosmetisch, aber Policy-Check sollte clean sein. +4. Altstaende `ops/grafana-influxdb/` und `ops/loki/` aus Repo entfernen (Backup-Branch dann `git rm`). +5. Renovate-Bot gegen Gitea einrichten — beendet manuelle Digest-Pflicht. + +--- + +# B. Scorecard (1 = exzellent, 10 = ungenuegend) + +| Bereich | Note | Begruendung | +|---|---:|---| +| Hardware | **nicht bewertbar** | Keine Inventar-Doku im Repo. Nur Cache-NVMe genannt. Siehe Phase H. | +| Ordnerstruktur | **2** | Klare Trennung apps/infra/ops/security/core; konsistente Namenskonvention (mit Migrationsplan in STORAGE_LAYOUT 6). Kleinerer Haenger: `host-services/Adguard/` mit Grossbuchstabe. | +| Storage | **3** (Repo-Stand) / **2** (mit STORAGE_LAYOUT Active) | Cache-XFS, Disk1-XFS jetzt erreicht. Pfad-Disziplin via `/mnt/user/...`. Posture-Check etabliert. Note durch Draft-Status und fehlendes `SERVICES_RECOVERY.md` gedrueckt. | +| Docker-Architektur | **2** | Netzmodell klar, Healthchecks fehlen grossflaechig, `latest@sha256` als bewusster Kompromiss bei einigen Images dokumentiert. Keine Memory-Limits. | +| Reverse Proxy / Zugriff | **2** | DNS-Challenge, Wildcard-faehig, Authelia ForwardAuth, dynamic config sauber getrennt. Manuelle Host-Sync-Ausnahme ist pragmatisch. | +| Security | **3** | Solides Fundament (Authelia Argon2id, no-new-privileges-Standard, Webhook-Allowlist, `cloudflare_dns_api_token` als Docker Secret), aber: nur 2 Domains mit 2FA, AdGuard-Admin direkt am LAN, kein WAF/Bouncer, Authelia Regulation 5-Min-Ban ist gentil. | +| Netzwerk / DNS | **2** | AdGuard + Unbound + Tailscale-Trias ist Best-of-Class-Homelab. FritzBox als Router nicht im Repo dokumentiert, daher Note nicht 1. | +| Backup | **2** | Borg, Pre-Dumps, Tier-Modell, dokumentierter Scope. Punkt-Abzug: Single-Provider Off-Site, Passphrase nicht analog, Komodo-Mongo-Dump-Verifikation nicht im Auto-Cron. | +| Restore-Faehigkeit | **2** | RESTORE_MATRIX mit Smoke-Test je Dienst, Restore-Test-Schedule + Validierungen fuer Vaultwarden/Gitea/Paperless dokumentiert. Punkt-Abzug: Immich-Restore noch nie geuebt — groesster Datentopf. | +| GitOps | **1-2** | Webhook-Pflicht fuer neue Stacks, Source-of-Truth-Hierarchie, Drift-Runbook. Self-Bootstrap-Problem von Komodo zieht von 1 auf 2. | +| Monitoring | **3** | Stack produktiv, aber Altstaende noch im Repo, Family-View-Dashboard fehlt, Alerts (`alerts.yml`) sehr knapp (5 Regeln), keine Cert-Expiry-Alert auf Prometheus-Ebene (Cert-Token-Check laeuft separat). | +| Dokumentation | **1** | Aussergewoehnlich. SERVICE_CATALOG ist Gold. Einziger Punkt: kein End-User-/Familien-Onboarding. | +| Automatisierung | **3** | Borg-Dumps automatisiert, posture-check Host-Cron, Alert->ntfy-Pipe. Aber: keine CI gegen Repo, kein Renovate, kein automatisches Image-Update-Tracking. | +| Wartbarkeit | **2** | Doku traegt; Sprintpflege-Disziplin sichtbar (MIGRATION_LOG, AUDIT_FINAL). Risiko: Authelia-Drift, Hermes-Schwebezustand. | +| Nerd-Faktor | **2-** | Komodo + Borg-UI + ntfy-Bridge + Posture-Check + Restore-Lab + Hermes-Experiment + Push-Mirror + Digest-Pinning. Liegt zwischen "Solider Senior" und "Spielwiese halten lernen". | + +**Gesamteindruck: 2 (gut).** Strukturell weit ueber durchschnittlichem Homelab; konkrete Luecken sind klar benennbar und nicht systemisch. + +--- + +# C. Top-20 Findings + +> Format: priorisiert nach Risiko-zu-Aufwand-Hebel. Jeder Eintrag hat Fundstelle, Empfehlung und Prio. + +### F-01 · AdGuard-Admin am LAN ohne Auth + +- **Kategorie:** Security / Zugriff +- **Fundstelle:** `host-services/Adguard/docker-compose.yml:16`, `MASTER 10`, `docs/SERVICE_CATALOG.md:14` +- **Beobachtung:** Port `8082:80` direkt auf alle Interfaces. Bewusste Operator-Entscheidung 2026-05-25. +- **Risiko:** Jedes Geraet im LAN kann DNS-Filterregeln, Upstream und Logging manipulieren. IoT-Kompromittierung oder Gast-WLAN -> DNS-Hijack moeglich. +- **Best Practice:** Admin-UIs nicht im LAN ohne Auth. Entweder hinter Traefik+Authelia mit `two_factor` oder Bind auf Tailscale-Interface (z. B. `100.x.y.z:8082:80`). +- **Empfehlung:** Schritt 1 — Bind auf Tailscale-IP (S, 5 Min). Schritt 2 — optional spaeter Traefik-Route hinter Authelia. +- **Prioritaet:** Sollte zeitnah +- **Aufwand:** S +- **Validierung:** `ss -ltnp | grep :8082` zeigt nur Tailscale-IP; LAN-Browser-Zugriff schlaegt fehl. +- **Rollback:** Compose-Diff zurueck, Komodo redeploy. + +### F-02 · Borg-Passphrase nicht analog gesichert + +- **Kategorie:** Backup / DR +- **Fundstelle:** `docs/DISASTER_RECOVERY.md:64,401`, `docs/SECRETS_MAP.md:48` +- **Beobachtung:** `borg_repo_passphrase.txt` liegt im Host-Filesystem unter `/mnt/user/appdata/secrets/`. Doku weist explizit darauf hin, dass eine externe analoge Sicherung Operator-Aufgabe ist. +- **Risiko:** Wenn Unraid-Host und ggf. Vaultwarden gleichzeitig defekt sind, ist das verschluesselte Borg-Repo bei Hetzner nutzlos. +- **Empfehlung:** Auf Papier ausdrucken, in Bankschliessfach oder bei vertrauter Person versiegelt. Zusaetzlich in Vaultwarden hinterlegen (aber Vaultwarden hilft nicht, wenn es selbst restauriert werden muss). +- **Prioritaet:** Muss sofort +- **Aufwand:** S +- **Validierung:** Du kannst den Wert ohne Host wiederherstellen. + +### F-03 · Single-Provider Off-Site Backup + +- **Kategorie:** Backup +- **Fundstelle:** `ops/borg-ui/BACKUP_SCOPE.md`, `docs/RESTORE_MATRIX.md:77-96`, `STORAGE_LAYOUT 8.1` +- **Beobachtung:** Hetzner Storage Box als alleiniges Off-Site-Borg-Ziel. STORAGE_LAYOUT 8.1 sieht zusaetzlich lokales Borg-Repo auf `/mnt/user/backups/borg/` vor (gleicher Host) und eine externe Wechselplatte (manuell rotiert). +- **Risiko:** Hetzner-Account-Verlust (Payment-Issue, Account-Hack, Provider-Outage) = halbes 3-2-1. +- **Best Practice:** Zweites Off-Site-Ziel mit unterschiedlichem Provider oder Cold-Wechselplatte mit fester Rotationskadenz. +- **Empfehlung:** (a) Wechselplatten-Rotation in fester Kadenz dokumentieren (zwei Platten, monatlicher Tausch). Oder (b) zweites Borg-Repo bei rsync.net / BorgBase EU2 / privatem 2. Standort. +- **Prioritaet:** Sollte zeitnah +- **Aufwand:** M +- **Validierung:** `borg list` gegen beide Repos, beide < 7 Tage alt. + +### F-04 · Authelia 2FA-Pflicht zu schmal + +- **Kategorie:** Security +- **Fundstelle:** `security/authelia/configuration.yml:44-53` +- **Beobachtung:** Nur `files.kaleschke.info` und `scrutiny.kaleschke.info` sind `two_factor`. Tier-1-Operator-UIs wie Borg-UI, Code-Server, Filebrowser (zweite Route?), Komodo (eigene Auth), Glance, Grafana laufen mit `one_factor`. +- **Risiko:** Operator-Passwort-Kompromittierung (Phishing, Keylogger, Browser-Save-Leak) gibt ohne 2FA Vollzugriff auf Code-Server (Repo + Workspace), Borg-UI (Restore-Pfade), Filebrowser (Documents/Photos). +- **Empfehlung:** ACL erweitern: `two_factor` fuer `borg.kaleschke.info`, `code.kaleschke.info`, `files.kaleschke.info` (schon), `glance.kaleschke.info` (debattierbar), `traefik.kaleschke.info`. Komodo bleibt Ausnahme. +- **Prioritaet:** Muss sofort +- **Aufwand:** S +- **Validierung:** Nach Login auf `borg.kaleschke.info` wird 2FA-Prompt erzwungen. +- **Rollback:** ACL-Block zurueck. + +### F-05 · Repo-Altstaende `ops/grafana-influxdb/` und `ops/loki/` nicht entfernt + +- **Kategorie:** Wartbarkeit / GitOps +- **Fundstelle:** Repo-Wurzeln `ops/grafana-influxdb/`, `ops/loki/`; `MASTER 7.6`, `SERVICE_CATALOG 68-70,80-81`, `AUDIT_FINAL 9` +- **Beobachtung:** Container down, aber Compose-Dateien + Provisioning bleiben im Repo. Doku referenziert beide gleichzeitig. Risiko: jemand (zukuenftiges Ich, KI) deployt versehentlich den Altstand. +- **Empfehlung:** `git rm` der beiden Verzeichnisse, Tag `pre-monitoring-cleanup` fuer Rollback, MIGRATION_LOG-Eintrag. +- **Prioritaet:** Sollte zeitnah +- **Aufwand:** S +- **Validierung:** `policy-checks` laeuft clean, Repo enthaelt nur noch `monitoring/`. + +### F-06 · Hermes-Agent im Schwebezustand + +- **Kategorie:** App-Landschaft / Wartbarkeit +- **Fundstelle:** `ops/hermes-agent/docker-compose.yml`, `MASTER 7.5`, `SERVICE_CATALOG 82-83` +- **Beobachtung:** "NAS-Stack bewusst deaktiviert" wegen offener VM-Seite. Dashboard-Domain (`hermes.kaleschke.info`) + Authelia-ACL + Secret-Pfade dokumentiert. +- **Risiko:** Schleichender Verfall — in 6 Monaten verstehst du Model-C nicht mehr ohne `ops/hermes-agent/README.md`. Bei jeder Authelia-/Compose-Aenderung musst du Hermes mitpruefen, obwohl es nichts tut. +- **Empfehlung:** Operator-Entscheidung mit 60-Tage-Deadline ehrlich treffen. Wenn nicht produktiv bis 2026-07-25: `git rm ops/hermes-agent/`, Domain aus DNS, ACL-Eintrag raus. +- **Prioritaet:** Sollte zeitnah (Entscheidung) +- **Aufwand:** S (Entfernen) / L (echte Produktiv-Aktivierung) +- **Validierung:** Entweder Smoke-Test auf `hermes.kaleschke.info` mit funktionalem Use-Case-Beleg, oder Repo-clean. + +### F-07 · Monitoring-Stack ohne Digest-Pin + +- **Kategorie:** Reproduzierbarkeit / GitOps +- **Fundstelle:** `monitoring/docker-compose.yml:3,28,66,84,100,118,276,296` +- **Beobachtung:** Prometheus, Alertmanager, Blackbox, Loki, Promtail, Grafana, node-exporter, cAdvisor sind alle nur per Tag gepinnt (`prom/prometheus:v3.7.3`, `grafana/grafana:12.4.3`, ...). Nur `influxdb3-core` hat `@sha256:`. Das widerspricht der Image-Versionierungs-Disziplin der Tier-1-Stateful-Dienste. +- **Risiko:** Wenn upstream einen Tag erneut pushed (Versionsdrift, Supply Chain), wird beim Rebuild ein anderer Container deployed — gerade Monitoring sollte stabil sein. +- **Empfehlung:** Beim naechsten Komodo-Redeploy aktuellen Digest auslesen und einpinnen. Vorbereitung fuer Renovate (F-12). +- **Prioritaet:** Nice to have +- **Aufwand:** S +- **Validierung:** `grep '@sha256' monitoring/docker-compose.yml` listet alle 10 Services. + +### F-08 · Alert-Regeln zu duenn + +- **Kategorie:** Monitoring +- **Fundstelle:** `monitoring/prometheus/alerts.yml` +- **Beobachtung:** Exakt 5 Regeln: ExternalConnectivityDown, EndpointDown, EndpointSlow, DiskAlmostFull, MemoryHighUsage, Traefik5xx. Es fehlen: + - Borg-Lauf-Frische (ueber `node_exporter` textfile collector oder Pushgateway). + - Zertifikatslaufzeit (Blackbox kann `probe_ssl_earliest_cert_expiry`, aber keine Alert-Regel dafuer). + - Container-down-Alert (cAdvisor liefert `container_last_seen`). + - PostgreSQL-Connection-Saturation. + - Loki ingestion-rate / log-volume spike. + - InfluxDB-Disk-Pressure. + - Backup-Job-Failure. +- **Risiko:** Du siehst Probleme nicht, bevor sie weh tun. Cert-Expiry und Borg-Stale sind die schmerzhaftesten Blind-Spots. +- **Empfehlung:** Mindestens zwei Regeln nachziehen: `BorgArchiveStale` (>30 h, Pushgateway oder textfile) und `TLSCertExpiryNear` (<14 Tage). Rest als Folge-Sprint. +- **Prioritaet:** Sollte zeitnah +- **Aufwand:** M +- **Validierung:** Alerts feuern in Test-Bedingung (Borg-Dump-File touch -d backwards). + +### F-09 · Komodo-Self-Bootstrap-Problem + +- **Kategorie:** GitOps / DR +- **Fundstelle:** `MASTER 13: Komodo Self-Stack Drift-Recovery 2026-05-04` +- **Beobachtung:** Du hattest schon einen Drift-Vorfall (Komodo-Core ran aus `/tmp/*repair.yml`, Mongo-Pfad fehlte). Recovery-ENV liegt als "temporaeres Tier-1-Secret-Material" unter `/mnt/user/appdata/secrets/_komodo_stack_env_recovery_2026-05-04.env` (Doku-Stand). +- **Risiko:** Bei Totalausfall musst du Komodo aus Compose-Datei wiederbeleben, dafuer brauchst du die Stack-ENV mit `KOMODO_SECRET_KEY`, `KOMODO_MONGO_PASSWORD`, `KOMODO_PERIPHERY_PASSKEY` etc., die nur als Komodo Stack ENV existieren. Klassisches Henne-Ei. +- **Empfehlung:** Komodo-Self-Stack aus Komodos eigener Verwaltung herausnehmen und als handgepflegten `docker compose`-Service in `services/komodo-bootstrap/` halten. Stack-ENV als versiegelte Datei unter `/mnt/user/appdata/secrets/` mit deterministischem Restore-Pfad in RESTORE_MATRIX. +- **Prioritaet:** Sollte zeitnah +- **Aufwand:** M +- **Validierung:** Komodo-Stack-Datei lebt im Repo unter `services/`, nicht in Komodos eigener Workspace-Sicht. + +### F-10 · Authelia Repo↔Host Drift "by design" + +- **Kategorie:** GitOps / Security +- **Fundstelle:** `docs/REPO_MAP.md:48`, `security/authelia/configuration.yml`, `SERVICE_CATALOG 23` +- **Beobachtung:** Repo enthaelt Baseline ohne Secrets, OIDC, Users-DB. Manuelles Merge auf den Host noetig. Es gibt keine automatische Konsistenz-Pruefung. +- **Risiko:** Repo-Aenderung (z. B. neue ACL-Regel) wird gepusht, aber nie auf den Host gemerged -> Drift, Authelia hinkt der Wahrheit hinterher. +- **Empfehlung:** Symmetrisch zum Traefik-Dynamic-Workflow (manueller Sync explizit als Pflicht in WORKFLOW.md). Zusaetzlich ein einfaches Diff-Script `services/authelia-diff.sh`, das `diff` zwischen Repo-Baseline und Host-Datei zeigt, und das im posture-check als Warning auftaucht, wenn die ACL-Sektion differiert. +- **Prioritaet:** Sollte zeitnah +- **Aufwand:** S +- **Validierung:** Script laeuft, posture-check kennt einen neuen Check `authelia_config_drift`. + +### F-11 · Immich-Restore noch nie geuebt + +- **Kategorie:** Backup / Restore +- **Fundstelle:** `docs/RESTORE_MATRIX.md:49`, Restore-Test-Schedule +- **Beobachtung:** Vaultwarden / Gitea / Paperless haben Mini-Restore-Tests (2026-05-07). Immich nicht. Immich ist der groesste Datentopf (Familien-Fotos). +- **Risiko:** Silent Corruption in Postgres-pgvecto-rs-Daten bemerkst du erst beim Restore-Versuch. +- **Empfehlung:** Eigener Sprint: Immich-Restore-Test gegen `/mnt/user/backups/restore-lab/immich/` mit Sub-Set der `immich.dump` und einem Foto-Sample. Smoke-Test = "10 Fotos im Browser sichtbar nach Restore". +- **Prioritaet:** Sollte zeitnah +- **Aufwand:** M +- **Validierung:** Report unter `/mnt/user/backups/restore-reports/immich-.json`. + +### F-12 · Keine Image-Update-Automatik (Renovate o. ae.) + +- **Kategorie:** Wartbarkeit +- **Fundstelle:** Repo-weit; `docs/WORKFLOW.md:282-288` (Image-Versionierung) +- **Beobachtung:** Digest-Pinning ist konsequent, aber rein manuell. Bei ~30 Images bedeutet das, du musst monatlich fuer Patch-Updates manuell Digests auslesen — oder es bleibt liegen. +- **Risiko:** CVE-Patches werden nicht eingespielt, weil "der laufende Stand ist stabil". +- **Empfehlung:** Renovate Bot (self-hosted, gegen Gitea), Gitea-Actions-Runner. Renovate oeffnet PRs fuer Patch-/Minor-Updates; Major-Updates werden mit Labels separiert. +- **Prioritaet:** Sollte zeitnah (oder Nice to have, je nach Schmerz) +- **Aufwand:** M (Initial-Setup ist substantiell) +- **Validierung:** Renovate hat erste PRs in Gitea geoeffnet, du mergst eines davon kontrolliert. + +### F-13 · Keine OIDC-SSO fuer User-Apps + +- **Kategorie:** Security / UX +- **Fundstelle:** `security/authelia/configuration.yml`, `docs/SECRETS_MAP.md` +- **Beobachtung:** Authelia kann OIDC, ist aber nur als ForwardAuth konfiguriert. Nextcloud, Immich, Grafana, Mealie laufen mit eigenen User-DBs. +- **Risiko:** N getrennte Passwortspeicher, N getrennte 2FA-Setups, keine zentrale Sperrung bei Account-Kompromittierung. Familie hat keinen einfachen Onboarding-Pfad. +- **Empfehlung:** OIDC-Provider in Authelia aktivieren, Nextcloud (via Plugin), Immich (nativer OIDC-Support), Grafana (nativer OIDC-Support) als Clients konfigurieren. Vaultwarden via OIDC-Bridge nur, wenn der Aufwand klar mehrwertig ist — sonst bewusst auslassen. +- **Prioritaet:** Sollte zeitnah (groesster Hebel laut Executive) +- **Aufwand:** L +- **Validierung:** Familienkonto kann sich mit einem Login bei Nextcloud + Immich + Grafana anmelden. + +### F-14 · Kein WAF / Bouncer vor oeffentlichen Apps + +- **Kategorie:** Security +- **Fundstelle:** `traefik/docker-compose.yml`, oeffentliche Hosts in `docs/REPO_MAP.md:127-152` +- **Beobachtung:** Sechs oeffentliche Apps mit nativer Auth (vault, paperless, mealie, ntfy, git, immich, cloud) ohne IP-Bouncer. Authelia-Regulation greift nur fuer die ForwardAuth-Pfade; Apps mit eigener Auth bekommen den vollen Traffic. +- **Risiko:** Credential-Stuffing-Bot-Wellen treffen die App selbst (Nextcloud, Immich) — Logs sind im Loki, aber kein Sperr-Mechanismus. +- **Empfehlung:** CrowdSec als Bouncer fuer Traefik (`crowdsecurity/traefik-bouncer`). Nutzt Loki/Logs fuer Erkennung, sperrt IPs auf Traefik-Ebene, bevor sie die Apps treffen. +- **Prioritaet:** Sollte zeitnah +- **Aufwand:** M +- **Validierung:** CrowdSec-Dashboard zeigt erste Sperren; Test-Brute-Force gegen `nextcloud.kaleschke.info` wird bei N Versuchen geblockt. + +### F-15 · Healthchecks fehlen grossflaechig + +- **Kategorie:** Docker / Operations +- **Fundstelle:** Spot-checks: `apps/paperless/docker-compose.yml`, `apps/immich/docker-compose.yml`, `security/authelia/docker-compose.yml`, `traefik/docker-compose.yml` — keiner hat `healthcheck:`-Block. +- **Beobachtung:** Restart-Policy ist ueberall `unless-stopped`, aber ohne Healthcheck kann Docker keinen Crash-Loop bei "Container laeuft, aber App tot" erkennen. +- **Risiko:** Bei Soft-Failure (Postgres-Connection-Pool tot, Authelia haengt im Storage-Connect) merkst du nichts, weil Container "running" bleibt. +- **Empfehlung:** Fuer Tier-1 (Traefik `wget /ping`, Authelia `/api/health`, PostgreSQL `pg_isready`, Komodo `wget /api/healthcheck`) Healthchecks ergaenzen. Fuer Tier-2 schrittweise. +- **Prioritaet:** Sollte zeitnah +- **Aufwand:** M (pro Stack 5–15 Min) +- **Validierung:** `docker ps` zeigt `(healthy)` neben den Tier-1-Containern. + +### F-16 · `infra/redis` als "shared" deklariert, faktisch nur Paperless + +- **Kategorie:** Architektur-Konsistenz +- **Fundstelle:** `infra/redis/docker-compose.yml`, `docs/SERVICE_CATALOG.md:31` ("shared Redis Cache") +- **Beobachtung:** Immich, Nextcloud, Mealie haben jeweils eigene Redis-Instanzen. Authelia nutzt bewusst kein Redis (MASTER 13). Paperless nutzt es laut Compose. Effektiv "Paperless-Redis im Frack des shared-Caches". +- **Risiko:** Niedrig. Aber: Wenn du `infra/redis` fuer etwas anderes wegnimmst, denkst du, es kostet Paperless was — und das waere der Fall. +- **Empfehlung:** Doku-Update: SERVICE_CATALOG 31 praezisieren ("dediziertes Redis fuer Paperless; andere Stacks haben eigene Redis-Instanzen"). Architektur bleibt, nur Etikett ehrlich machen. Alternativ: in `apps/paperless/` als App-internes Netz konsolidieren wie Mealie. +- **Prioritaet:** Nice to have +- **Aufwand:** S (Doku) / M (Architektur) + +### F-17 · Plex bleibt als Host-Net-Stack + +- **Kategorie:** Security / Architektur +- **Fundstelle:** `host-services/plex/docker-compose.yml`, `MASTER 7.4` +- **Beobachtung:** Plex laeuft als Host-Net wegen Discovery/GDM. Dokumentierte Ausnahme. +- **Risiko:** Plex hat hoehere Angriffsoberflaeche als Apps mit Traefik. Plex-Login wurde mehrfach Ziel von Account-Uebernahmen (Plex.tv-Auth-Issues 2024/25). Bei Plex.tv-Kompromittierung greift Authelia nicht — Plex authentifiziert gegen Plex.tv. +- **Empfehlung:** Plex bewusst beibehalten (Doku stuetzt), aber: (a) "Remote Access" in Plex-UI deaktivieren, wenn nur lokal/Tailscale genutzt. (b) Plex-Server nicht in `frontend_net` (waere schaedlich) — bleibt Host-Net, korrekt. +- **Prioritaet:** Nice to have +- **Aufwand:** S +- **Validierung:** Plex `Remote Access` UI zeigt "disabled". + +### F-18 · Nextcloud ohne ForwardAuth, ohne dedizierte Brute-Force-Doku + +- **Kategorie:** Security +- **Fundstelle:** `apps/nextcloud/docker-compose.yml`, `MASTER 13: Nextcloud-Entscheidung` +- **Beobachtung:** Bewusste Ausnahme (WebDAV/CardDAV). In Nextcloud selbst sind Brute-Force-Schutz, 2FA-Pflicht und App-Passwords konfigurierbar, aber nicht im Repo dokumentiert. +- **Risiko:** Familien-Konto mit schwachem Passwort + Nextcloud oeffentlich = direkter Pfad zu Dokumenten/Fotos. +- **Empfehlung:** `apps/nextcloud/POST_INSTALL.md` mit Pflicht-Checkliste: Brute-Force-Plugin aktiv, 2FA-Provider TOTP installiert, Admin-Account hat 2FA, "Enforce 2FA for admin group" gesetzt. Optional: `OCC`-Befehle als Skript in `services/nextcloud-policy/`. +- **Prioritaet:** Sollte zeitnah +- **Aufwand:** S +- **Validierung:** Test-Login ohne 2FA als Admin schlaegt fehl. + +### F-19 · Keine Container-Memory-Limits + +- **Kategorie:** Docker / Hardware-Schutz +- **Fundstelle:** Spot-checks aller Composes +- **Beobachtung:** Keine `mem_limit:` oder `deploy.resources.limits` Sektion in Tier-1- oder Tier-2-Stacks. +- **Risiko:** Bei Image-Bug oder Memory-Leak (z. B. Immich-ML, Paperless-OCR-Loop) kann ein Container den Host in OOM treiben. Posture-Check + Docker-Critical-Events sehen das nachher, aber praeventiver waere Container-Limit + Docker-OOM-Kill fuer den richtigen Prozess. +- **Empfehlung:** Fuer Tier-1 (Postgres, Authelia, Traefik, Komodo) sanfte Limits setzen (z. B. Postgres 2 GB, Authelia 256 MB, Traefik 256 MB). Fuer Immich-ML-Container ein hartes Limit, das Verhungern verhindert. +- **Prioritaet:** Nice to have +- **Aufwand:** M +- **Validierung:** `docker stats` zeigt `MEM USAGE / LIMIT` ungleich `unlimited`. + +### F-20 · Paperless-DBPass weiter als Stack-ENV (dokumentierte Ausnahme) + +- **Kategorie:** Secrets +- **Fundstelle:** `MASTER 13: Secrets in Komodo Stacks`, `docs/SECRETS_MAP.md:25` +- **Beobachtung:** Paperless unterstuetzt `_FILE` nicht fuer DB-Pass. Bewusste Ausnahme. +- **Risiko:** Stack-ENV liegt in Komodo-DB (Mongo), nicht im Repo. Bei Komodo-Mongo-Backup-Luecke fehlt das Passwort beim Restore. +- **Empfehlung:** Erweiterung der Disaster-Recovery-Doku: explizite Liste aller "Stack-ENV-only"-Secrets mit Zeiger, dass `komodo-mongo.archive.gz` fuer Restore zwingend ist, oder die ENV manuell vorgehalten werden muss (in Vaultwarden + externer Notiz). +- **Prioritaet:** Nice to have +- **Aufwand:** S +- **Validierung:** DR-Doc Abschnitt "Stack-ENV-Werte" referenziert konkrete Restore-Pfade. + +--- + +# D. Risiko-Matrix + +| Risiko | Bereich | Wahrscheinlichkeit | Auswirkung | Prioritaet | Massnahme | +|---|---|---|---|---|---| +| Borg-Passphrase weg -> Restore unmoeglich | Backup | niedrig | katastrophal | P0 | F-02 analoge Sicherung | +| Hetzner-Account-Verlust -> halbes 3-2-1 | Backup | niedrig-mittel | hoch | P0/P1 | F-03 Zweitziel | +| AdGuard-Admin-Manipulation aus LAN | Security | niedrig | hoch (DNS-Hijack) | P0 | F-01 Bind auf Tailscale | +| Operator-Pwd-Leak -> 2FA fehlt fuer Borg-UI/Code-Server | Security | mittel | hoch | P0 | F-04 2FA-ACL erweitern | +| Komodo-Self-Bootstrap-Failure nach Totalausfall | DR | niedrig | hoch | P1 | F-09 Bootstrap-Datei in `services/` | +| Authelia Repo↔Host Drift unbemerkt | GitOps/Security | mittel | mittel | P1 | F-10 Diff-Check | +| Immich Silent Corruption -> kein Restore-Test belegt | Backup | niedrig | sehr hoch (Familien-Fotos) | P1 | F-11 Restore-Test | +| Cert-Expiry unbemerkt -> Public Apps down | Operations | niedrig | mittel | P1 | F-08 Alert-Regel | +| Nextcloud Brute-Force ohne Bouncer | Security | mittel | mittel-hoch | P1 | F-14 CrowdSec / F-18 Nextcloud-Haerten | +| Image-Update-Stillstand -> CVE bleibt | Security | mittel | mittel | P1 | F-12 Renovate | +| Hermes-Wartungsschuld | Wartbarkeit | hoch | niedrig | P1 | F-06 Entscheidung | +| Repo-Altstaende ueberleben -> Doppel-Deploy | GitOps | mittel | niedrig | P1 | F-05 Cleanup | +| OOM durch unlimitierte Container | Hardware | niedrig | mittel | P2 | F-19 mem_limit | +| Healthcheck-Luecke -> Soft-Failure stumm | Operations | mittel | niedrig | P2 | F-15 Healthchecks | +| Monitoring-Stack ohne Digest-Pin | Reproduzierbarkeit | niedrig | niedrig | P2 | F-07 Digests + Renovate | +| Hardware-SPOF (kein zweiter Host) | Hardware | niedrig | sehr hoch | P3 | Cold-Standby / 2. Host | + +--- + +# E. Zielarchitektur (realistisch fuer privates Homelab) + +**Hardware** + +- 1× Unraid-Host (bestehend) als Production. CPU mit AVX2/AVX-512 fuer Immich-ML. ≥ 32 GB RAM (fuer 2× Postgres + Immich-ML + Loki/Prometheus + 2 VMs). +- 2× NVMe als BTRFS-RAID1-Cache, sobald Cache-Auslastung > 70 % (STORAGE_LAYOUT 15.3). +- Parity-Disk ≥ groesste Daten-Disk. +- USV mit USB-Steuerung (NUT-faehig: APC Back-UPS RS 700+, Eaton 3S, CyberPower CP1500EPFCLCD). Direkter Shutdown bei Power-Loss. +- Optional: zweiter alter Mini-PC oder NUC als Cold-Standby mit Tailscale, der den letzten Komodo-Bootstrap + Gitea-Mirror tragen kann. + +**Netzwerk** + +- FritzBox (bestehend) als Router + NAT. +- VLANs nur wenn IoT-WLAN dazukommt (FritzBox-Gast-WLAN reicht fuer Anfang). +- DNS: AdGuard -> Unbound (bestehend). Admin-UI nur Tailscale. +- Tailscale (bestehend): Operator-Pfad. Subnet-Router optional fuer LAN-Devices ueber Tailscale. + +**Storage** + +- Cache `only` fuer `appdata`, `system`, `domains` (bestehend STORAGE_LAYOUT 4). +- Disk1 (XFS) fuer `services`, `documents`, `photos`, `backups`, `media`, `finance`, `projekte`. +- Externe Wechselplatte (XFS) fuer Cold-Off-Site mit fester monatlicher Rotation. + +**Ordnerstruktur (Repo)** + +- Beibehalten. Nur Cleanup von Altstaenden (F-05). Naming `kebab-case`-Migration aus STORAGE_LAYOUT 6 schrittweise. + +**Docker** + +- Compose-only via Komodo (bestehend). +- Digest-Pin fuer alle Images (F-07). +- Healthchecks fuer Tier-1 (F-15). +- Mem-Limits fuer Tier-1 + Immich-ML (F-19). +- App-interne Netze fuer Stack-Isolation (bestehend). + +**Reverse Proxy** + +- Traefik v3 (bestehend), DNS-Challenge, Wildcard. +- Dynamic Config nur fuer Middlewares, TLS, Dashboard (bestehend). +- CrowdSec-Bouncer (F-14) fuer oeffentliche Apps. + +**Auth** + +- Authelia als ForwardAuth **und** OIDC-Provider (F-13). +- Nextcloud, Immich, Grafana via OIDC. +- 2FA-Pflicht fuer alle Operator-Dienste (F-04). +- Komodo bewusste Ausnahme (bestehend). + +**Backup** + +- Borg lokal (`/mnt/user/backups/borg/`) + Borg Hetzner + Wechselplatte. +- Pre-Dump-Hooks (bestehend). +- Borg-Passphrase off-system analog (F-02). +- Restore-Tests automatisiert (F-11 Immich, dann andere via CI). + +**Monitoring** + +- `monitoring/`-Stack als alleinige Quelle. Altstaende raus (F-05). +- Family-View-Dashboard in Grafana (alles gruen, Backup-Frische, Cert-Tage). +- Alerts ausgebaut (F-08). +- Posture-Check + Docker-Critical-Events -> ntfy `homelab-alerts` (bestehend). + +**Dokumentation** + +- Aktuelle Doku-Tiefe halten. +- `SERVICES_RECOVERY.md` und `STORAGE_LAYOUT.md` (Active) finalisieren. +- Familien-/User-Onboarding-Doku als eigenes kleines Dokument. + +**GitOps** + +- Gitea + Komodo (bestehend). +- GitHub-Push-Mirror (umgesetzt, bestaetigt durch MASTER 7.1). +- Renovate-Bot gegen Gitea (F-12). +- Optional: Staging-Branch + zweites Komodo-Ziel in Tailscale-VM (Phase 3). + +**Restore** + +- RESTORE_MATRIX bleibt fuehrend. +- Restore-Lab unter `/mnt/user/backups/restore-lab/` (bestehend). +- Immich-Restore als Luecke schliessen (F-11). +- Komodo-Self-Bootstrap raus aus Komodo (F-09). + +--- + +# F. Priorisierte Massnahmenliste + +| # | Aufgabe | Warum | Kategorie | Prio | Aufwand | Risiko (bei Nicht-Tun) | Mehrwert | Abhaengigkeiten | Validierung | +|---|---|---|---|---|---|---|---|---|---| +| 1 | Borg-Passphrase analog sichern | DR-SPOF schliessen | Backup | P0 | S | katastrophal | DR-Sicherheit | — | Wert ohne Host abrufbar | +| 2 | AdGuard-Admin auf Tailscale-IP binden | LAN-Angriffsflaeche | Security | P0 | S | hoch | LAN-IoT-Haertung | — | `ss -ltnp` zeigt nur Tailscale | +| 3 | 2FA-ACL erweitern (borg, code, files, traefik) | Operator-Pwd-Leak | Security | P0 | S | hoch | 2FA-Coverage | Authelia-TOTP-Setup | Login erzwingt 2FA | +| 4 | Altstaende `ops/grafana-influxdb`+`ops/loki` `git rm` | Repo-Hygiene, kein Re-Deploy | GitOps | P0 | S | niedrig | Klarheit | Tag setzen | Policy-Check clean | +| 5 | Hermes 60-Tage-Deadline | Wartungsschuld | App | P1 | S/L | mittel | Komplexitaet raus | Operator-Entscheidung | Entweder produktiv oder weg | +| 6 | Immich-Restore-Test einrichten | Groesster Datentopf ungeprueft | Backup | P1 | M | hoch | Restore-Vertrauen | Restore-Lab-Pfad | Smoke-Test-Report | +| 7 | Renovate-Bot in Gitea | manuelle Digest-Pflege | Wartung | P1 | M | mittel | Update-Hygiene | Gitea-Runner | erste PR offen | +| 8 | Alert-Regeln (Borg-Frische, Cert-Expiry) | Blind-Spot Operations | Monitoring | P1 | M | mittel | echte Alerts | Pushgateway o. textfile | Alert in Test | +| 9 | Family-View-Dashboard Grafana | Morgens 30 s Check | Monitoring | P1 | M | niedrig | Uebersicht | Datasources stehen | Dashboard funktioniert | +| 10 | Komodo-Self-Bootstrap als `services/komodo-bootstrap/` | Henne-Ei-Problem | GitOps/DR | P1 | M | mittel | sauberer Recovery-Pfad | Komodo-Stack-Doku | Bootstrap aus Repo allein moeglich | +| 11 | Authelia-Drift-Diff-Check in posture-check | Repo↔Host Drift | GitOps | P1 | S | mittel | Drift-Detektion | posture-check-Erweiterung | neuer Check sichtbar | +| 12 | Healthchecks Tier-1 | Soft-Failure-Erkennung | Docker | P1 | M | niedrig | Self-Healing-Trigger | — | `docker ps` zeigt `healthy` | +| 13 | CrowdSec-Bouncer vor Traefik | oeffentliche Apps schuetzen | Security | P1 | M | mittel | Brute-Force-Stop | Traefik-Middleware | Test-IP wird geblockt | +| 14 | Nextcloud-Haertung dokumentieren | Public App + native Auth | Security | P1 | S | mittel | App-Haertung | Plugin-Install | 2FA-erzwingt | +| 15 | Authelia OIDC-Provider + Nextcloud/Immich/Grafana | SSO, Familien-Onboarding | Security/UX | P2 | L | niedrig | hoher Mehrwert | Authelia-OIDC-Setup | SSO-Login funktioniert | +| 16 | Immich-Smartphone-Auto-Backup fuer Familie | Killer-App fuer Familie | App | P2 | S | niedrig | hoher Mehrwert | — | Familien-Foto in Immich | +| 17 | Monitoring-Stack Digests + Renovate-Pin | Reproduzierbarkeit | GitOps | P2 | S | niedrig | konsistent | Renovate optional | `@sha256` an allen Images | +| 18 | Mem-Limits Tier-1 + Immich-ML | OOM-Schutz | Hardware/Docker | P2 | M | niedrig | Schutz | — | `docker stats` zeigt Limits | +| 19 | Off-Site-Zweitziel (rsync.net o. Wechselplatte) | Single-Provider | Backup | P2 | M | mittel | 3-2-1 echt | Borg-Config | beide Repos < 7d | +| 20 | Staging-Branch + 2. Komodo-Ziel | Risiko-Aenderung testbar | GitOps | P3 | L | niedrig | Reife | 2. VM/Host | Deploy auf staging klappt | + +--- + +# G. Refactoring-Plan (Sprints) + +## Sprint 0 — Sicherheitsnetz und Ist-Zustand sichern (1 Tag) + +- **Ziel:** Du kannst danach im schlimmsten Fall alles, was du jetzt aenderst, sicher zurueckrollen. +- **Aufgaben:** + - Git-Tag `audit-2026-05-25-baseline` auf `master` setzen und nach Gitea + GitHub-Mirror pushen. + - Borg-Lauf manuell ausloesen ("freshen up"), Erfolg im Log dokumentieren. + - Aktuellen Komodo-Mongo-Dump verifizieren (`mongorestore --dry-run`). + - `docs/MIGRATION_LOG.md` Eintrag "Audit-Sprint-Start". +- **Erfolgskriterium:** Tag pushed, Borg-Lauf gruen, Mongo-Dump verifiziert. +- **Validierung:** `git fetch && git tag | grep audit-2026-05-25-baseline` und `ls /mnt/user/backups/borg/dumps/latest/` zeigt fresh. +- **Rollback:** N/A (rein additiv). +- **Risiko bei Nichtumsetzung:** Keine Notbremse fuer Sprint 1. + +## Sprint 1 — Offensichtliche Risiken entschaerfen (1 Woche) + +- **Ziel:** P0-Risiken weg, Repo-Hygiene wieder gruen. +- **Aufgaben (in dieser Reihenfolge):** + 1. F-02 Borg-Passphrase analog sichern (off-system, kein Code-Change). + 2. F-01 AdGuard-Admin-Port auf Tailscale-IP — Edit `host-services/Adguard/docker-compose.yml:16`. + 3. F-04 Authelia ACL erweitern (`two_factor` fuer borg, code, files, traefik) — Edit `security/authelia/configuration.yml` + Host-Sync. + 4. F-05 Altstaende `ops/grafana-influxdb/`, `ops/loki/` entfernen — `git rm`, MIGRATION_LOG. + 5. Policy-Check-Warnings `SEC001` (ddns-updater, scrutiny) aufraeumen. +- **Erfolgskriterium:** Policy-Check 0 Warnings fuer SEC001, AdGuard-Admin nur via Tailscale, 2FA-Login auf borg.kaleschke.info. +- **Validierung:** Policy-Check-Report; Browser-Test mit/ohne 2FA-Cookie. +- **Rollback:** Commit-Revert pro Block. + +## Sprint 2 — GitOps-Robustheit (1–2 Wochen) + +- **Ziel:** Self-Bootstrap-Problem entschaerft, Drift-Detektion automatisiert. +- **Aufgaben:** + 1. F-09 Komodo-Bootstrap-Compose nach `services/komodo-bootstrap/` extrahieren + dokumentierter Standalone-Restore-Pfad. + 2. F-10 Authelia-Drift-Diff in posture-check ergaenzen. + 3. F-11 Immich-Restore-Test einrichten (analog zu vaultwarden/gitea/paperless). + 4. F-06 Hermes-Entscheidung mit 60-Tage-Deadline schriftlich. +- **Erfolgskriterium:** Komodo laesst sich aus Repo allein wiederherstellen. Posture-Check zeigt `authelia_config_drift: false`. Immich-Restore-Report unter `/mnt/user/backups/restore-reports/`. +- **Validierung:** Trockenversuch (Komodo-Container stoppen, `docker compose up -d` aus `services/komodo-bootstrap/`). +- **Rollback:** Bootstrap-Verzeichnis loeschen, Komodo-Self-Stack wie vorher. + +## Sprint 3 — Backup & Restore belastbar machen (2–3 Wochen) + +- **Ziel:** 3-2-1 echt, Restore-Tests breiter, Stack-ENV im DR-Pfad. +- **Aufgaben:** + 1. F-03 Zweitziel: Wechselplatten-Rotation dokumentieren ODER zweites Borg-Repo (rsync.net / BorgBase EU2). + 2. F-20 Stack-ENV-Liste in DR-Doc explizit machen (Restore-Reihenfolge). + 3. Borg-Verifikation Cron fuer `borg check --repository-only` weekly (STORAGE_LAYOUT 8.4). + 4. Quartalsweise End-to-End-Restore-Drill in Schedule aufnehmen. +- **Erfolgskriterium:** Beide Off-Site-Ziele < 7 Tage alt; DR-Doc enthaelt "ENV-Restore-Reihenfolge". +- **Validierung:** `borg list` gegen beide Repos. + +## Sprint 4 — Monitoring & Alerting ausbauen (2 Wochen) + +- **Ziel:** Sichtbarkeit auf das, was wirklich weh tut. +- **Aufgaben:** + 1. F-08 Alert-Regeln: `BorgArchiveStale`, `TLSCertExpiryNear`, `ContainerDown`, `PostgresConnSaturation`. + 2. F-15 Healthchecks fuer Traefik, Authelia, Postgres, Komodo, Gitea. + 3. F-07 Digest-Pin in `monitoring/docker-compose.yml`. + 4. Family-View-Dashboard in Grafana (1 Panel: Service-Up, 1 Panel: Backup-Frische, 1 Panel: Cert-Tage, 1 Panel: Disk-Fuellung). +- **Erfolgskriterium:** Family-View zeigt alles gruen; Cert-Alert feuert in Test (Datum vorgespult). +- **Validierung:** Dashboard sichtbar unter `monitoring.kaleschke.info/d/family-view`. + +## Sprint 5 — Auth-Konsolidierung & Frontdoor-Haertung (3–4 Wochen) + +- **Ziel:** SSO fuer die Familie, Brute-Force-Bouncer vor oeffentlichen Apps. +- **Aufgaben:** + 1. F-13 Authelia OIDC-Provider aktivieren. + 2. Nextcloud OIDC-Plugin + Test-Login. + 3. Immich OIDC + Test-Login. + 4. Grafana OIDC + Test-Login. + 5. F-14 CrowdSec-Bouncer vor Traefik. + 6. F-18 Nextcloud-Haertung-Dokument + 2FA-Pflicht. +- **Erfolgskriterium:** Familien-Konto loggt sich mit einem Login bei drei Apps ein; CrowdSec sperrt Test-IP nach N fehlerhaften Versuchen. +- **Validierung:** OIDC-Sequenz im Browser ohne Eingabe-Wiederholung; CrowdSec-Dashboard zeigt Sperre. + +## Sprint 6 — Automatisierung und Nerd-Level (laufend) + +- **Ziel:** Image-Update-Pipeline, optional Staging. +- **Aufgaben:** + 1. F-12 Renovate-Bot gegen Gitea. + 2. F-19 Mem-Limits Tier-1. + 3. Restore-Test-CI via Gitea Actions (P3). + 4. Optional: Staging-Branch + zweites Komodo-Ziel in Tailscale-VM (P3). + 5. Optional: Firefly III / Actual Budget fuer `/mnt/user/finance`. +- **Erfolgskriterium:** Renovate-PRs erscheinen woechentlich; mindestens ein automatisches Patch-Update gemerged. + +--- + +# H. Fehlende Informationen + +> Nur, was den Audit konkret schaerfer machen wuerde. + +| Frage | Warum | Bereich | Kommando / Datei | +|---|---|---|---| +| CPU-Modell, RAM-Groesse, Mainboard | Hardware-Bewertung, OOM-Risiko, Immich-ML-Eignung, AVX-Verfuegbarkeit | Hardware | `cat /proc/cpuinfo \| grep -E 'model name\|flags'`, `free -h`, `dmidecode -t baseboard \| head -20` | +| USV vorhanden? Modell? | DR-Beurteilung Power-Loss, Shutdown-Pfad | Hardware/DR | physische Sichtpruefung; `apcaccess` falls APC mit NUT | +| Stromverbrauch idle / Last | Betriebskosten, Sizing | Hardware | Smartmeter / Tibber-API | +| NIC-Speed (1 GbE? 2.5 GbE?) | Backup-Durchsatz, Plex-Streaming | Netzwerk | `ip -br link`, `ethtool eth0` | +| Disk-Inventar (Anzahl, Modelle, Alter) | Storage-Health, Replacement-Plan | Storage | `lsblk -o NAME,SIZE,MODEL,SERIAL`, Scrutiny-UI | +| Aktueller Cache-Fuellstand | Wann zweite NVMe? | Storage | `df -h /mnt/cache` | +| FritzBox-Modell + Firmware | Net-Sicherheit, VLAN-Faehigkeit | Netzwerk | FritzBox-UI / `fritzconnection` | +| Tatsaechlich genutzte Plex- vs. ungenutzte App | Konsolidierungs-Belege | App-Landschaft | Plex-Server-Logs, ggf. Glances-Container-CPU pro Stack | +| Existiert `/mnt/user/finance/`-Share schon? | Ist Firefly-Vorbereitung trivial? | Storage | `ls /mnt/user/finance/` | +| Authelia Live-User-DB-Tiefe (Anzahl User, 2FA-Status) | 2FA-Coverage-Bewertung | Security | `cat /mnt/user/appdata/authelia/config/users_database.yml` (nur Strukturansicht, keine Hashes hier zitieren) | +| Komodo-Mongo-Dump letzter Integrity-Check | F-09-Vorbereitung | Backup | `mongorestore --dry-run --archive=komodo-mongo.archive.gz --gzip` | +| Aktuelle Cert-Restlaufzeit | F-08-Test-Vorbereitung | Operations | `openssl s_client -connect git.kaleschke.info:443 -servername git.kaleschke.info < /dev/null \| openssl x509 -noout -dates` | + +--- + +# I. Pruefkommandos (Linux / Unraid / Docker / Windows) + +> Strukturiert nach Bereich. Sicher zum Ausfuehren am Host. + +### Hardware + +```bash +# CPU + Flags (AVX fuer Immich-ML) +cat /proc/cpuinfo | awk '/model name|flags/ {print; if(/flags/) exit}' + +# RAM +free -h +dmidecode -t memory | grep -E "Size|Speed" | head -20 + +# Mainboard +dmidecode -t baseboard | head -20 + +# PCI / SATA / NVMe +lspci +nvme list +lsblk -o NAME,SIZE,MODEL,SERIAL,FSTYPE,MOUNTPOINT,VENDOR + +# SMART +smartctl -a /dev/nvme0n1 | head -40 +smartctl -a /dev/sdb | head -40 + +# Stromverbrauch (Unraid Plugin oder ipmitool falls IPMI) +sensors | head -30 +``` + +### Filesystem / Storage / Mounts + +```bash +# Filesystem-Typen (Hard Rule 12.1) +findmnt -no FSTYPE /mnt/cache /mnt/disk1 /boot +mount | grep -E "ntfs3|fuseblk" # darf leer sein + +# Share-Settings +ls -la /boot/config/shares/ + +# Cache-Fuellstand +df -h /mnt/cache /mnt/disk1 /mnt/user +du -sh /mnt/user/appdata/* | sort -hr | head -20 +``` + +### Docker + +```bash +# Container-Inventur +docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | sort +docker ps -a --filter "status=exited" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}" + +# Netzwerke +docker network ls +docker network inspect frontend_net | jq '.[0].Containers | keys' +docker network inspect backend_net | jq '.[0].Internal' + +# Volumes ohne Container (Waisen) +docker volume ls -qf dangling=true + +# Effektive Ports +ss -ltnp | sort -k4 + +# Healthchecks +docker ps --format "{{.Names}}\t{{.Status}}" | grep -E "healthy|unhealthy|starting" +``` + +### Security + +```bash +# Privileged-Container und Docker-Socket-Mounts +for c in $(docker ps -q); do + docker inspect "$c" --format '{{.Name}}: priv={{.HostConfig.Privileged}}; sock={{range .HostConfig.Binds}}{{println .}}{{end}}' +done | grep -E "priv=true|docker.sock" + +# Direkte Host-Ports +docker ps --format "{{.Names}}: {{.Ports}}" | grep -v "^[^:]*: $" + +# Secret-Datei-Rechte +ls -la /mnt/user/appdata/secrets/ +stat -c "%a %n" /mnt/user/appdata/secrets/*.txt +``` + +### Backup / Restore + +```bash +# Borg-Frische +ls -lat /mnt/user/backups/borg/dumps/latest/ | head +find /mnt/user/backups/borg/dumps/latest -mmin +1440 -type f # aelter 24h + +# Borg-Repo (Passphrase per File) +export BORG_PASSPHRASE=$(cat /mnt/user/appdata/secrets/borg_repo_passphrase.txt) +borg list ssh://... --short | tail -5 +borg info ssh://... ::Taegliche-Sicherung-2026-05-25T05:52:44.157 + +# Posture-Check +cat /mnt/user/services/posture-check/last.json | jq '.warning_count, .critical_count' +``` + +### Netzwerk / DNS + +```bash +# Tailscale +tailscale status + +# DNS auf AdGuard testen +dig @127.0.0.1 git.kaleschke.info +dig @127.0.0.1 example.com # Unbound-Recursion + +# Cert-Restlaufzeit +for h in vault git immich cloud paperless mealie ntfy; do + echo -n "$h.kaleschke.info: " + openssl s_client -connect ${h}.kaleschke.info:443 -servername ${h}.kaleschke.info /dev/null \ + | openssl x509 -noout -dates 2>/dev/null +done +``` + +### GitOps-Konsistenz + +```bash +# Komodo Stack-Workspace vs. Repo +cd /mnt/user/services/stacks/ +git rev-parse HEAD +git status --short + +# Webhook-Liveness +docker logs komodo-core 2>&1 | grep -i "webhook\|deploy" | tail -20 +``` + +### Windows (lokal) + +```powershell +# Repo-Status +git status --short +git fetch origin +git log origin/master..HEAD --oneline # ungepushte Commits +git log HEAD..origin/master --oneline # ungepullte Commits + +# Policy-Check +.\ops\policy-checks\check_repo.ps1 + +# Restore-Freshness +.\ops\restore-tests\check-restore-freshness.ps1 +``` + +--- + +# J. Community- / Best-Practice-Abgleich + +> Nur fuer die Architekturentscheidungen, bei denen der Markt eindeutig ist oder Gegenpositionen relevant sind. + +| Entscheidung | Markt-Best-Practice | Stuetzende Quellen | Bewertung | +|---|---|---|---| +| Traefik mit Docker-Labels statt File-Provider | Standard in Selfhosted (siehe `awesome-selfhosted-docker`, Smarthome-Beginner Templates) | Traefik-Doc v3 docs.traefik.io/providers/docker | passt zu MASTER 13 (Wechsel 2026-03-28) | +| DNS-Challenge mit Cloudflare statt HTTP-01 | Standard fuer Wildcard und reduzierte Angriffsflaeche | acme.sh / lego docs | passt, korrekt | +| Authelia ForwardAuth statt Authentik | Authelia ist leichtgewichtiger, Authentik maechtiger; beide valide | r/selfhosted-Konsens 2024-25 | Authelia richtig fuer Single-Family-Setup | +| Authelia ohne Redis-Session-Backend | Markt-Standard ist mit Redis; deine Vereinfachung ist begruendet (MASTER 13 2026-05-04) | Authelia-Doc | Trade-off klar; Bewertung: vertretbar fuer Homelab | +| Komodo statt Portainer/Dockge | Komodo ist neuer (2024), Dockge etabliert, Portainer kommerziell | Selfh.st 2025 Comparison | Komodo legitim, mehr GitOps-nativ als Dockge | +| Borg statt Restic/Kopia | Borg ist klassische Wahl fuer Linux-Backup mit Deduplikation; Kopia/Restic gewinnen mit Multi-Backend | r/datahoarder, ServeTheHome 2024 | Borg passt zu Unraid-Stack; bewusste Vereinfachung | +| Glance statt Homepage als Single-Dashboard | beide auf Augenhoehe; Homepage etablierter, Glance moderner, schneller, mit Live-Widgets | github.com/glanceapp/glance vs. github.com/gethomepage/homepage | Glance legitim; Bewertung: deine Wahl ist verteidigbar | +| Immich nicht hinter Authelia ForwardAuth | offizielle Immich-Doku raet bei nativer App-Auth davon ab, weil Sync-Clients OIDC oder direkte Auth brauchen | immich.app/docs/administration/reverse-proxy | korrekt; OIDC spaeter (F-13) ist der Weg | +| Nextcloud klassisch statt AIO | NC-AIO ist offizielle Empfehlung fuer Neuaufbau, klassisch hat mehr Flexibilitaet fuer GitOps | NC-Blog 2024 | bewusste Ausnahme MASTER 13; vertretbar, da GitOps-Anbindung wichtiger | +| Single-Host + Borg statt Proxmox-Cluster + ZFS-Send | fuer Familien-Homelab ist Cluster Overkill | r/homelab, LTT-Forum | Single-Host korrekt; Cold-Standby (Sprint 6) ist die richtige naechste Stufe | +| AdGuard + Unbound statt Pi-hole | aequivalent; AdGuard hat moderne UI, Unbound recursion | gowri/networkchuck Tutorials 2024 | passt | +| Posture-Check als Skript statt Goss/InSpec | fuer Single-Host pragmatisch | Goss ist maechtiger, aber Overkill | Skript-Loesung legitim | +| Renovate gegen Gitea | mehrere Erfahrungsberichte in Gitea-Issues + Renovate-Docs | docs.renovatebot.com/modules/platform/gitea/ | Standard-Pfad | +| CrowdSec vor Traefik | starker Trend 2024-25 in Selfhosted-Community | crowdsec.net/blog, Marius-Hosting-Tutorials | sinnvolle Haertung | + +**Gegenpositionen, die du kennen solltest:** + +- **"Authelia OIDC ist kompliziert, lieber Authentik."** Korrekt, wenn du auch B2B-SAML brauchst. Fuer reine Familien-OIDC ist Authelia leichter wartbar. +- **"CrowdSec laedt zentrale Reputation-Listen -> Privacy-Bedenken."** Stimmt, du kannst Local-Only-Mode fahren. Fuer Homelab egal. +- **"Renovate-Bot erzeugt Laerm."** Mit Group/Schedule-Rules zaehmbar. Wert > Laerm. +- **"Komodo ist zu jung."** Gegenargument: du benutzt es seit Q1/2026 produktiv, Major-Inzidenz war beherrschbar. Der Wechsel zurueck zu Portainer/Dockge waere hoehere Kosten als der Reifegrad-Nachteil. + +--- + +# K. Endziel — "Nerd-Level Homelab" + +So sieht dein Homelab aus, wenn es wirklich auf Senior-Level ist: + +**Betrieb im Alltag** + +- Morgens 30 Sekunden auf `monitoring.kaleschke.info`: Family-View zeigt 7 Tier-1-Services gruen, Backup-Job in der Nacht hat 100 % Files erfasst, alle Certs > 30 Tage, Disk < 80 %. +- Push-Benachrichtigung auf dem Handy nur, wenn wirklich etwas brennt (Posture-Check critical, Borg > 30 h, Endpoint down ≥ 8 Min, Cert < 14 Tage). +- Familienmitglieder loggen sich mit einem Login bei Nextcloud, Immich, Mealie ein. 2FA per TOTP-App, kein App-by-App-Passwortzettel mehr. + +**Updates** + +- Renovate oeffnet woechentlich 5–10 Pull-Requests in Gitea fuer Patch-Versionen. Du siehst sie im Web-UI, pruefst Release Notes, klickst Merge. Komodo deployt automatisch via Webhook. Smoke-Test in der naechsten Glance-Seite. +- Major-Updates kommen separat mit Label `major`, behandelst du in einem geplanten Slot mit Restore-Snapshot davor. + +**Backups** + +- Borg lokal alle 6 h, Borg Hetzner taeglich, Wechselplatte monatlich. Borg-Passphrase auf Papier im Bankschliessfach. Alle drei Ziele juenger als 36 h Alert-Schwelle. +- Pre-Dump-Hooks erzeugen 15 konsistente Dump-Artefakte pro Lauf, automatische Posture-Check-Vor-Hook bricht Backup ab, wenn FS oder Mount sich veraendert haben. + +**Restore** + +- Monatlicher Mini-Restore-Test fuer Vaultwarden/Gitea/Paperless/Immich nach `/mnt/user/backups/restore-lab//` laeuft automatisiert per Gitea Actions, Erfolgs-Report landet als Datei + ntfy-Info. +- Quartalsweise End-to-End-Drill: ein kompletter Stack restauriert, App startet, Smoke-Test passt, Doku validiert. Komodo-Bootstrap-Pfad ist getestet. +- Im Ernstfall folgst du `docs/DISASTER_RECOVERY.md` Phase 0–5 und bist nach < 8 h wieder im Vollbetrieb. Repo-Bootstrap aus GitHub-Mirror, Stacks in Stufen 1–5, Verifikation pro Stufe. + +**Monitoring** + +- Prometheus + Loki + Grafana + Alertmanager-ntfy-Bridge. ~15 Alert-Regeln, alle in `alerts.yml` versioniert. +- Family-View, Host-Overview, Containers+Logs, Traefik-Standalone, Backup-Frische, Cert-Tage. Sechs Dashboards mehr braucht keine Familie. +- Loki sammelt 30 Tage Logs aus allen Containern via Promtail. cAdvisor + node-exporter liefern Container- und Host-Metriken. Blackbox testet oeffentliche Endpoints alle 60 s. + +**Security** + +- Authelia OIDC fuer Nextcloud, Immich, Grafana, Mealie. ForwardAuth fuer Operator-UIs mit 2FA-Pflicht ab Tier-2. +- CrowdSec sperrt Brute-Force-IPs auf Traefik-Ebene bevor sie die Apps treffen. +- AdGuard-Admin nur via Tailscale. Operator-Pfad ausschliesslich Tailscale. +- Authelia-Repo-Baseline und Host-Config sind per Diff-Check im posture-check abgesichert. +- Secrets-Mounts mode 600, Borg-Passphrase analog. + +**Dokumentation** + +- SERVICE_CATALOG, RESTORE_MATRIX, DISASTER_RECOVERY, STORAGE_LAYOUT, SECRETS_MAP, WORKFLOW, GITOPS_DRIFT_RUNBOOK, ALERTING_MAP, HOMELAB_ARCHITECTURE_MASTER bleiben aktuelle Single-Source-of-Truth. +- `services/komodo-bootstrap/` loest das Henne-Ei. `SERVICES_RECOVERY.md` ist final, nicht Draft. +- Familien-Onboarding-Doku als Markdown: "So nutzt du Nextcloud-Web", "So aktivierst du Immich-Foto-Backup auf dem Handy", "So loggst du dich neu per 2FA ein". + +**Taegliche Nutzung** + +- Familie scannt Briefe per ASN-Barcode in `scans_inbox/`, Paperless tagged via paperless-gpt automatisch, alles durchsuchbar. +- Immich erfasst Smartphone-Fotos aller Familienmitglieder automatisch, Familie blaettert per Web/App, Tagging per ML. +- Nextcloud traegt Kalender, Kontakte, geteilte Familienordner per WebDAV/CardDAV — kein Google/Apple-Lock-In. +- Mealie speichert Rezepte, Einkaufsliste auf dem Handy. +- Vaultwarden ist der einzige Passwort-Tresor der Familie; Familien-Organisation aktiv. +- Plex streamt Heim-Medien an alle Endgeraete. +- ntfy schickt dir Vorfaelle aufs Handy — und sonst nichts. +- Optional: Firefly III fuer die Familien-Finanzen, Ecowitt-Wetter-Dashboard, Home-Assistant-Automationen fuer Strom-Eigenverbrauch. + +**Was bewusst weggelassen ist** + +- Kein Kubernetes. Komodo + Compose reicht. +- Kein zweiter Medienserver neben Plex (Jellyfin-Entscheidung 2026-05-25). +- Kein zweites Dashboard neben Glance (Homepage-Entscheidung 2026-05-25). +- Kein Uptime-Kuma neben Blackbox (Entscheidung 2026-05-25). +- Kein Hermes-Agent, wenn er bis 2026-07-25 keinen klaren Alltagsnutzen liefert. +- Kein BentoPDF/paperless-gpt 24/7, wenn nicht aktiv genutzt. +- Kein Self-Stack-Komodo (durch `services/komodo-bootstrap/` ersetzt). + +--- + +## Schlussbemerkung + +Das Setup ist naeher an Senior-Reife als an Bastel-Niveau. Der groesste Hebel der naechsten drei Monate ist **Konsolidieren statt erweitern** (Hermes-Entscheidung, Altstaende raus, Auth-SSO, Off-Site-Diversitaet), kombiniert mit der einen Aktivierung, die das Setup vom Operator-Tool zum **Familien-Tool** macht: Immich-Smartphone-Backup fuer alle. + +Das vorhandene 2026-05-23-Audit hat die richtigen Sprintziele bereits identifiziert. Diese externe Audit-Sicht ergaenzt: + +- **2FA-Pflicht auf Tier-1-Operator-UIs** (F-04) — fehlt in der Bewertung 2026-05-23 in dieser Klarheit. +- **Healthcheck-Luecke** (F-15) und **fehlende Mem-Limits** (F-19) — operative Detail-Findings, die in der strategischen Bewertung nicht auftauchen. +- **Komodo-Self-Bootstrap als konkreter Code-Vorschlag** (F-09) statt nur als Risiko-Erwaehnung. +- **Authelia-Drift-Detection automatisieren** (F-10) statt nur "manuell merge". +- **Monitoring-Stack ohne Digest-Pin** (F-07) — Inkonsistenz mit der eigenen Image-Pinning-Disziplin. +- **`infra/redis` ist faktisch nicht shared** (F-16) — Etikett-Realitaet-Drift. +- **Alert-Regeln deutlich zu duenn** (F-08) — Sichtbarkeitsluecken bei Cert/Borg/Container-Down. + +Wenn Sprint 1–3 in 4–6 Wochen sitzen, bist du auf einer 1-Note. Wenn dann Sprint 4–5 in weiteren 6–8 Wochen kommen, hat die Familie ein echtes Self-Hosting-System, kein "Container-Sammlung im Keller". Das ist der Unterschied, den der Audit-Auftrag adressiert. diff --git a/docs/AUDIT_2026-05-25_TODO.md b/docs/AUDIT_2026-05-25_TODO.md new file mode 100644 index 0000000..4b7bb74 --- /dev/null +++ b/docs/AUDIT_2026-05-25_TODO.md @@ -0,0 +1,86 @@ +# Audit TODO 2026-05-25 + +Quelle: `docs/AUDIT_2026-05-25.md` + +Status: Arbeitsliste fuer die Umsetzung. Authelia-2FA/OIDC bleibt bewusst spaet, weil die Ziel-Policy noch nicht final entschieden ist. + +## Leitplanken + +- Keine Authelia-2FA-ACL-Aenderungen in den ersten Sprints. +- Keine Live-riskanten Bind-/Port-Aenderungen ohne vorher erfasste Host-Werte, insbesondere Tailscale-IP. +- Erst Inventar und Baseline, dann Aenderungen. +- Jede produktive Aenderung bekommt Validierung und Rollback-Hinweis. + +## Sprint 0 - Inventar und Baseline + +| Status | Aufgabe | Ergebnis | +|---|---|---| +| in Arbeit | Hardware-Inventar ausfuellen | CPU, RAM, Mainboard, NIC, Disks und SMART erfasst; USV/Strom/BIOS offen | +| in Arbeit | Netzwerk-Inventar ausfuellen | Host-IP, Gateway, Tailscale-IP und AdGuard-Bind erfasst; Router-/VLAN-Details offen | +| offen | Externe Abhaengigkeiten dokumentieren | `docs/EXTERNAL_DEPENDENCIES.md` enthaelt Provider, Kritikalitaet, Ausfallplan | +| offen | Services-Recovery-Pfade beschreiben | `docs/SERVICES_RECOVERY.md` enthaelt Gitea-/Komodo-/Secrets-Sonderpfade | +| offen | Baseline-Tag setzen | `audit-2026-05-25-baseline` ist lokal und remote vorhanden | +| erledigt | Policy-Check neu ausfuehren | SEC001-Warnings aus altem Report sind nicht mehr aktuell | + +## Sprint 1 - Nicht-kontroverse Sicherheits- und Repo-Hygiene + +| Status | Aufgabe | Ergebnis | +|---|---|---| +| offen | Borg-Passphrase analog sichern | Passphrase ist ohne Host/Vaultwarden wiederherstellbar | +| erledigt (repo) | AdGuard Admin-Bind vorbereiten | Tailscale-IP `100.80.98.33` erfasst, Compose-Soll geaendert | +| offen (deploy) | AdGuard Admin-Port auf Tailscale-IP binden | Nach Deploy muss `ss -ltnp` `100.80.98.33:8082` zeigen | +| offen | Alte Monitoring-Verzeichnisse entfernen | `ops/grafana-influxdb/` und `ops/loki/` sind aus Repo/Doku entfernt oder als expliziter Rollback-Archive-Pfad markiert | +| offen | Policy-Warnings triagieren | Jede Warning ist behoben oder bewusst dokumentiert | + +## Sprint 2 - Storage und Recovery verbindlich machen + +| Status | Aufgabe | Ergebnis | +|---|---|---| +| offen | `docs/STORAGE_LAYOUT.draft.md` finalisieren | Datei wird als `docs/STORAGE_LAYOUT.md` Active gefuehrt | +| offen | Disk- und Share-TBDs eintragen | Modelle, Groessen, Seriennummern, Filesysteme und Cache-Settings sind dokumentiert | +| offen | Gitea-Repo-Mirror-Mechanik definieren | Mirror fuer `/mnt/user/services/gitea/git/repositories/` mit Frequenz <= 6 h ist spezifiziert | +| offen | Komodo-Bootstrap-Pfad beschreiben | Kaltstart ohne laufendes Komodo ist dokumentiert | +| offen | Immich-Restore-Test planen | Testumfang, Datenpfade und Smoke-Test-Kriterium stehen fest | + +## Sprint 3 - Restore und Monitoring + +| Status | Aufgabe | Ergebnis | +|---|---|---| +| offen | Immich-Restore-Test implementieren | Restore-Report landet unter `/mnt/user/backups/restore-reports/` | +| offen | Borg-Stale-Alert bauen | Alarm feuert, wenn Borg-Archiv zu alt ist | +| offen | TLS-Cert-Expiry-Alert bauen | Alarm feuert bei Restlaufzeit unter Schwellwert | +| offen | Container-Down-Alert bauen | Unerwartet fehlende Container werden sichtbar | +| offen | Family-View Dashboard definieren | Uptime, Backup-Frische, Cert-Tage, Disk-Fuellung auf einer Seite | + +## Sprint 4 - Familien- und Betriebsdoku + +| Status | Aufgabe | Ergebnis | +|---|---|---| +| offen | Familien-Onboarding schreiben | Nextcloud, Immich, Vaultwarden, 2FA-Verlust, Ausfallverhalten kurz erklaert | +| offen | Capacity-/Lifecycle-Review erstellen | Wachstum, Schwellenwerte, Upgrade-Trigger und Disk-Replacement-Plan dokumentiert | +| offen | USV-Test oder USV-Entscheidung | Power-Loss-Verhalten ist bekannt und dokumentiert | + +## Sprint 5 - Auth und Frontdoor, bewusst zuletzt + +| Status | Aufgabe | Ergebnis | +|---|---|---| +| geparkt | Authelia 2FA fuer Operator-UIs erweitern | Erst nach finaler Policy-Entscheidung | +| geparkt | Authelia OIDC fuer Apps pruefen | Erst nach Familien-/Client-Auswirkungsanalyse | +| geparkt | CrowdSec vor Traefik pruefen | Nach stabiler Auth-/Monitoring-Basis | + +## Offene Host-Werte + +Diese Werte muessen am Unraid-Host erhoben werden, bevor die entsprechenden Aenderungen sauber umgesetzt werden: + +```bash +hostname +cat /proc/cpuinfo | awk '/model name|flags/ {print; if(/flags/) exit}' +free -h +dmidecode -t baseboard | head -30 +ip -br link +tailscale ip -4 +lsblk -o NAME,SIZE,MODEL,SERIAL,FSTYPE,MOUNTPOINT,VENDOR +df -h /mnt/cache /mnt/disk1 /mnt/user +smartctl -a /dev/nvme0n1 | head -80 +smartctl -a /dev/sdb | head -80 +``` diff --git a/docs/CAPACITY_AND_LIFECYCLE.md b/docs/CAPACITY_AND_LIFECYCLE.md new file mode 100644 index 0000000..4533972 --- /dev/null +++ b/docs/CAPACITY_AND_LIFECYCLE.md @@ -0,0 +1,63 @@ +# Capacity and Lifecycle - KalliLab CORE + +Status: Template, auszufuellen nach Hardware-/Storage-Audit. + +## Zweck + +Dieses Dokument haelt Wachstum, Schwellenwerte und Upgrade-Trigger fest. Es verhindert, dass Storage-, RAM- oder Backup-Entscheidungen erst dann getroffen werden, wenn der Host bereits unter Druck steht. + +## Aktuelle Kapazitaet + +| Bereich | Groesse | Belegt | Frei | Schwellwert | Bewertung | +|---|---:|---:|---:|---:|---| +| Cache | TBD | TBD | TBD | 70 % Planung / 85 % Aktion | TBD | +| Disk1 | TBD | TBD | TBD | 80 % Planung / 90 % Aktion | TBD | +| Backups lokal | TBD | TBD | TBD | TBD | TBD | +| Hetzner Borg | TBD | TBD | TBD | TBD | TBD | +| Externe Cold-Platte | TBD | TBD | TBD | TBD | TBD | + +Pruefkommando: + +```bash +df -h /mnt/cache /mnt/disk1 /mnt/user +du -sh /mnt/user/appdata/* | sort -hr | head -30 +du -sh /mnt/user/documents /mnt/user/photos /mnt/user/media /mnt/user/backups 2>/dev/null +``` + +## Wachstumsbereiche + +| Bereich | Erwartetes Wachstum | Risiko | Naechste Aktion | +|---|---|---|---| +| Immich Fotos/Videos | TBD | hoechster privater Datentopf | Restore-Test priorisieren | +| Paperless Dokumente | TBD | wichtig, moderates Wachstum | Restore-Test existiert | +| Nextcloud | TBD | Familiennutzung kann stark wachsen | Quota/Backup pruefen | +| Monitoring/Loki | TBD | Retention kann Disk fuellen | Retention dokumentieren | +| Borg Dumps | TBD | Retention und Excludes pruefen | Borg-Stale + Groessenprofil | + +## Upgrade-Trigger + +| Trigger | Massnahme | +|---|---| +| Cache dauerhaft >70 % | Zweite NVMe oder Appdata-Verteilung planen | +| Cache >85 % | Sofortmassnahme, keine grossen Deployments | +| Disk1 >80 % | Array-Erweiterung planen | +| Disk1 >90 % | Keine neuen grossen Datenimporte, Erweiterung priorisieren | +| RAM >90 % ueber 10 Minuten regelmaessig | RAM-Ausbau oder Service-Limits pruefen | +| Borg-Laufzeit deutlich steigend | Scope, Netzwerk und Ziel pruefen | +| SMART-Warnung | Ersatz planen, Restore-/Backup-Frische pruefen | + +## Restore-Zeitziele + +| Tier | Beispiel | Zielzeit | Status | +|---|---|---:|---| +| Tier 0 | Repo, Secrets, Traefik, DNS | TBD | offen | +| Tier 1 | Gitea, Vaultwarden, Paperless, Immich | TBD | offen | +| Tier 2 | Nextcloud, Mealie, Monitoring | TBD | offen | +| Tier 3 | Komfort-/Ops-Tools | TBD | offen | + +## Review-Log + +| Datum | Befund | Entscheidung | +|---|---|---| +| TBD | Initial ausfuellen | TBD | + diff --git a/docs/DISASTER_RECOVERY.md b/docs/DISASTER_RECOVERY.md index ae3f123..aab03f2 100644 --- a/docs/DISASTER_RECOVERY.md +++ b/docs/DISASTER_RECOVERY.md @@ -9,6 +9,8 @@ Verwandte Dokumente: - `docs/ROLLBACK.md` - Rueckweg bei Fehlern im laufenden GitOps-Betrieb - `docs/RESTORE_MATRIX.md` - Restore-Quellen und Verifikationsregeln pro Dienst - `docs/RESTORE_HANDBOOK.md` - praktische Restore-Betriebsanleitung +- `docs/SERVICES_RECOVERY.md` - Recovery-kritische `/mnt/user/services`-Pfade, Gitea-Mirror und Komodo-Bootstrap +- `docs/EXTERNAL_DEPENDENCIES.md` - externe Provider/Konten und Ausfall-Szenarien - `ops/borg-ui/BACKUP_SCOPE.md` - Zielbild des Borg-Scopes --- @@ -66,6 +68,8 @@ Diese Punkte sollten **vor** einem echten Ausfall geklaert sein: | Borg-Passphrase | Host-Secret-Datei vorhanden und fuer Borg-Zugriff verifiziert; externe analoge Hinterlegung bleibt Operator-Aufgabe | | Secrets-Dateien | ueber Borg bzw. Restore-Quellen abgedeckt | | Komodo Stack ENV-Werte | extern dokumentiert, z. B. Vaultwarden | +| Services-Recovery | `docs/SERVICES_RECOVERY.md` gepflegt, insbesondere Gitea-Repo-Mirror und Komodo-Bootstrap | +| Hardware-/Netzwerkdaten | `docs/HARDWARE_INVENTORY.md` und `docs/NETWORK_INVENTORY.md` mit echten Werten gefuellt | | Restore-Smoke-Tests | fuer mindestens 1-2 kritische Dienste nachgewiesen | **Wichtig:** Dieses Dokument ist nur so gut wie die Vorbereitung ausserhalb des Repos. @@ -204,6 +208,7 @@ Besonders kritisch: - `/mnt/user/services/homelab-infra` - `/mnt/user/services/stacks` - `/mnt/user/services/posture-check` +- Details zu `/mnt/user/services/` und Komodo/Gitea-Bootstrap stehen in `docs/SERVICES_RECOVERY.md` - `/mnt/user/services/gitea/data` - `/mnt/user/appdata/authelia/config` - `/mnt/user/appdata/komodo/core` diff --git a/docs/EXTERNAL_DEPENDENCIES.md b/docs/EXTERNAL_DEPENDENCIES.md new file mode 100644 index 0000000..839da51 --- /dev/null +++ b/docs/EXTERNAL_DEPENDENCIES.md @@ -0,0 +1,64 @@ +# External Dependencies - KalliLab CORE + +Status: Template, auszufuellen und quartalsweise zu pruefen. + +## Zweck + +Dieses Dokument beschreibt externe Anbieter und Konten, von denen Betrieb, Recovery oder Zugriff abhaengen. Ziel ist, im Ausfallfall nicht erst suchen zu muessen, welcher Provider welches Teilproblem verursacht. + +## Abhaengigkeiten + +| Anbieter / System | Zweck | Kritikalitaet | Recovery-Auswirkung | Zugang / Besitz | Notfallplan | +|---|---|---:|---|---|---| +| Domain-Registrar | Besitz `kaleschke.info` | hoch | Ohne Domain brechen Public URLs/TLS-Erneuerung | TBD | Registrar-Zugang und 2FA-Recovery sichern | +| Cloudflare DNS | Authoritative DNS, ACME DNS-Challenge | hoch | Neue Zertifikate/DNS-Aenderungen blockiert | TBD | API-Token rotierbar, Account-Recovery dokumentieren | +| Hetzner Storage Box | Off-site Borg Backup | kritisch | Restore aus Off-site ggf. nicht moeglich | TBD | Zweites Off-site-Ziel oder Cold-Platte etablieren | +| GitHub Mirror | Externer Repo-Mirror | mittel/hoch | Gitea-Verlust abfederbar | TBD | Mirror-URL und Token-Recovery dokumentieren | +| Tailscale | Remote-/Operator-Zugang | hoch | Remote-Zugriff erschwert, lokale Bedienung bleibt | TBD | Break-glass LAN-Zugang dokumentieren | +| GMX SMTP | Authelia Notifier | mittel | Mail-Notifier faellt aus, Login selbst nicht zwingend | TBD | ntfy/zweiter SMTP als Fallback pruefen | +| Let's Encrypt | TLS-Zertifikate | hoch | Cert-Erneuerung faellt aus | via Traefik/Cloudflare | Cert-Expiry Alert einrichten | +| Container Registries | Image Pulls | mittel | Redeploy/Update blockiert | oeffentlich/Token TBD | Gepinnte Digests und lokale Runtime helfen kurzfristig | + +## Kritische Secrets ausserhalb des Repos + +Authoritativ ist `docs/SECRETS_MAP.md`. Diese Liste markiert nur externe Abhaengigkeiten. + +| Secret | Zweck | Recovery-Hinweis | +|---|---|---| +| Borg Passphrase | Entschluesselung Borg-Repos | Muss analog/off-system vorhanden sein | +| Cloudflare DNS API Token | ACME DNS-Challenge | Token-Rotation und Scope pruefen | +| GitHub Mirror Token | Push-Mirror | In Gitea/GitHub verwaltet, nicht im Repo | +| Tailscale Account Recovery | Tailnet-Zugang | Account-2FA/Recovery Codes sichern | +| SMTP Passwort | Authelia Mail | In Host-Secret, Fallback pruefen | + +## Ausfall-Szenarien + +### Hetzner Storage Box nicht erreichbar + +- Lokales Borg-Repo und aktuelle Dumps pruefen. +- Keine destruktiven Host-Aenderungen starten, solange Off-site unklar ist. +- Zweites Off-site-Ziel oder Cold-Platte als Folgeaufgabe umsetzen. + +### Cloudflare Account/DNS gestoert + +- Bestehende Zertifikate laufen bis Ablauf weiter. +- Keine Domain-/ACME-Aenderungen moeglich. +- Tailscale/LAN-Zugang als Break-glass nutzen. + +### Tailscale gestoert + +- Lokalen LAN-Zugang nutzen. +- Direkte Admin-Ports nur gemaess dokumentierten Ausnahmen verwenden. +- AdGuard-Admin-Bind muss so geplant werden, dass ein lokaler Break-glass-Weg bekannt ist. + +### Domain verloren oder Registrar-Zugriff verloren + +- Gitea/GitHub Mirror und lokale IP/Tailscale-Pfade fuer Recovery nutzen. +- Neue Domain waere separater Migrationsfall fuer Traefik, Authelia, App-URLs und Clients. + +## Review + +| Datum | Ergebnis | Naechste Aktion | +|---|---|---| +| TBD | Initial ausfuellen | Provider-Zugaenge und Recovery-Codes pruefen | + diff --git a/docs/FAMILY_ONBOARDING.md b/docs/FAMILY_ONBOARDING.md new file mode 100644 index 0000000..d49efb6 --- /dev/null +++ b/docs/FAMILY_ONBOARDING.md @@ -0,0 +1,38 @@ +# Family Onboarding - KalliLab CORE + +Status: Entwurf. Zielgruppe sind Familienmitglieder, nicht Operatoren. + +## Zweck + +Diese Datei soll spaeter kurz und alltagstauglich erklaeren, wie die wichtigsten Dienste genutzt werden und was bei Problemen zu tun ist. Keine Restore-Matrix, keine Docker-Begriffe. + +## Dienste + +| Dienst | URL | Zweck | Konto / Login | Notiz | +|---|---|---|---|---| +| Nextcloud | `https://cloud.kaleschke.info` | Dateien, Kalender, Kontakte | TBD | Mobile App/WebDAV/CardDAV | +| Immich | `https://immich.kaleschke.info` | Fotos und Smartphone-Backup | TBD | Backup-App pro Handy | +| Vaultwarden | `https://vault.kaleschke.info` | Passwoerter | TBD | Familien-Organisation pruefen | +| Mealie | `https://mealie.kaleschke.info` | Rezepte und Einkauf | TBD | TBD | +| Paperless | `https://paperless.kaleschke.info` | Dokumente | TBD | Scan-/Inbox-Prozess beschreiben | +| Plex | intern/App | Medien | TBD | TBD | + +## Was tun bei Problemen? + +| Situation | Verhalten | +|---|---| +| Webseite nicht erreichbar | 10 Minuten warten, dann Operator informieren | +| Passwort vergessen | Operator informieren, nicht selbst neue Konten anlegen | +| Handy-Foto-Backup stoppt | App oeffnen, WLAN/Batteriesparmodus pruefen, Operator informieren | +| 2FA verloren | Operator informieren; Recovery-Prozess wird separat festgelegt | +| Warnmeldung vom Browser | Nicht weiterklicken, Screenshot machen, Operator informieren | + +## Offene Inhalte + +| Status | Aufgabe | +|---|---| +| offen | Pro Dienst kurze Schritt-fuer-Schritt-Anleitung schreiben | +| offen | Konto-/2FA-Policy final entscheiden | +| offen | Immich Mobile Backup fuer alle Geraete testen | +| offen | Vaultwarden Familienorganisation pruefen | + diff --git a/docs/HARDWARE_INVENTORY.md b/docs/HARDWARE_INVENTORY.md new file mode 100644 index 0000000..429375a --- /dev/null +++ b/docs/HARDWARE_INVENTORY.md @@ -0,0 +1,177 @@ +# Hardware Inventory - KalliLab CORE + +Status: Initialer Host-Audit erfasst, offene Punkte markiert. +Host: `Kallilabcore` +Letzte Pruefung: 2026-05-26 +Naechster Review: 2026-08-26 + +## Zweck + +Dieses Dokument beschreibt die physische Basis des Homelabs. Es ist die Grundlage fuer Capacity Planning, Restore-Zeit, Ersatzteilplanung, USV-Verhalten und Entscheidungen wie Immich-ML, Plex-Transcoding oder Storage-Erweiterung. + +## Host + +| Feld | Wert | +|---|---| +| Hostname | Kallilabcore | +| Standort | Heim-LAN, physischer Standort TBD | +| Betriebssystem | Unraid | +| Unraid-Version | 7.2.4 | +| Rolle | Single-Host Homelab, Docker Compose via Komodo | +| Boot-Medium | Samsung Flash Drive, 59.8G, FAT32 | +| Flash-Backup | In Borg-Scope aufgenommen, siehe `docs/MIGRATION_LOG.md` | + +## CPU + +| Feld | Wert | +|---|---| +| Modell | 12th Gen Intel(R) Core(TM) i5-12400F | +| Kerne / Threads | 6 Kerne / 12 Threads | +| Architektur | x86_64 | +| Relevante Flags | AVX, AVX2, FMA, AES, VT-x vorhanden; kein AVX-512 | +| iGPU / Quick Sync | Nein, `F`-CPU ohne iGPU | + +Pruefkommando: + +```bash +cat /proc/cpuinfo | awk '/model name|flags/ {print; if(/flags/) exit}' +lscpu +``` + +## RAM + +| Feld | Wert | +|---|---| +| Gesamt | 31 GiB | +| Belegt im Normalbetrieb | ca. 7.8 GiB genutzt, ca. 23 GiB verfuegbar | +| Slots / Ausbau | 4x 8 GB DDR4 belegt | +| ECC | Nein | + +Pruefkommando: + +```bash +free -h +dmidecode -t memory | grep -E "Size|Speed|Locator|Type" | head -40 +``` + +## Mainboard und Controller + +| Feld | Wert | +|---|---| +| Mainboard | Gigabyte Technology Co., Ltd. B760M DS3H DDR4 | +| BIOS/Firmware | TBD | +| SATA/HBA Controller | Onboard, Details TBD | +| NVMe Slots | mindestens 1 belegt | + +Pruefkommando: + +```bash +dmidecode -t baseboard | head -30 +lspci +``` + +## Netzwerk-Hardware + +| Interface | Speed | Rolle | Bemerkung | +|---|---:|---|---| +| eth0 / bond0 / br0 | 1 Gbit/s full duplex | LAN | Host-IP `192.168.178.58/24`, Gateway `192.168.178.1` | +| tailscale1 | virtuell | VPN | Tailscale IPv4 `100.80.98.33` | + +Pruefkommando: + +```bash +ip -br link +ethtool +tailscale ip -4 +``` + +## Storage + +| Slot | Device | Modell | Seriennummer | Groesse | Filesystem | Rolle | Health | +|---|---|---|---|---:|---|---|---| +| Cache | `nvme0n1p1` | Samsung SSD 970 EVO Plus 2TB | `S4J4NM0W609649H` | 1.8T | XFS | Appdata/system/domains | SMART passed | +| Disk1 | `md1p1` / physisch `sdc` | WDC WD60EFAX-68JH4N1 | `WD-WX32D90PC0V0` | 5.5T | XFS auf md1p1 | Array-Daten | SMART passed | +| Parity | physisch `sdb` | TOSHIBA HDWG480 | `2460A03VFA3H` | 7.3T | n/a | Parity | SMART passed | +| Boot | `sda1` | Samsung Flash Drive | `0375125090000587` | 59.8G | FAT32 | Unraid Boot | aktiv | +| Cold Backup | TBD | TBD | TBD | TBD | TBD | Externe Rotation | offen | + +Pruefkommando: + +```bash +lsblk -o NAME,SIZE,MODEL,SERIAL,FSTYPE,MOUNTPOINT,VENDOR +findmnt -no FSTYPE /mnt/cache /mnt/disk1 /boot +df -h /mnt/cache /mnt/disk1 /mnt/user +``` + +## SMART / Health + +| Device | Letzter Check | Kritische Werte | Bewertung | +|---|---|---|---| +| /dev/nvme0n1 | 2026-05-26 | Critical Warning `0x00`, Percentage Used `0%`, Media Errors `0`, Power On Hours `370`, Written `5.87 TB` | gut | +| /dev/sdb | 2026-05-26 | Reallocated `0`, Pending `0`, Uncorrectable `0`, CRC `1`, Power On Hours `8971` | gut, CRC-Wert beobachten | +| /dev/sdc | 2026-05-26 | Reallocated `0`, Pending `0`, Uncorrectable `0`, CRC `0`, Power On Hours `14174` | gut | + +Pruefkommando: + +```bash +smartctl -a /dev/nvme0n1 +smartctl -a /dev/sdb +smartctl -a /dev/sdc +``` + +## USV / Power Loss + +| Feld | Wert | +|---|---| +| USV vorhanden | Unklar | +| Modell | TBD | +| Verbindung | TBD | +| Software | `apcaccess` vorhanden, aber `apcupsd` auf `localhost:3551` antwortet nicht | +| Laufzeit im Idle | TBD | +| Shutdown-Schwelle | TBD | +| Letzter Shutdown-Test | TBD | + +Bewertung: + +- Wenn keine USV vorhanden ist: Risiko fuer Docker-/DB-State und laufende Writes bleibt offen. +- Wenn USV vorhanden ist: Shutdown-Pfad muss mindestens einmal getestet und dokumentiert werden. +- Aktueller Befund 2026-05-26: USV-Status ist nicht validiert; `apcaccess status` liefert `Connection refused`. + +## Stromverbrauch + +| Zustand | Verbrauch | Messmethode | Datum | +|---|---:|---|---| +| Idle | TBD | TBD | TBD | +| Normalbetrieb | TBD | TBD | TBD | +| Backup-Lauf | TBD | TBD | TBD | +| Last | TBD | TBD | TBD | + +## Ersatzteil- und Lifecycle-Plan + +| Komponente | Trigger | Massnahme | +|---|---|---| +| Cache-NVMe | >70 % Fuellstand oder SMART-Warnung | Zweite NVMe / Pool-Entscheidung; aktuell 6 % belegt | +| Disk1 | >80 % Fuellstand oder SMART-Warnung | Array-Erweiterung / Ersatz; aktuell 33 % belegt | +| Parity | Kleiner als neue groesste Datenplatte | Parity-Upgrade vor Datenplatten-Upgrade | +| Boot-USB | Lesefehler oder Alter TBD | Flash-Backup verifizieren, Ersatzstick vorbereiten | +| RAM | Swap/OOM oder Immich/Nextcloud-Druck | Ausbau planen | + +## Audit-Kommandos + +```bash +hostname +uname -a +cat /etc/unraid-version 2>/dev/null || true +lscpu +free -h +dmidecode -t baseboard | head -30 +dmidecode -t memory | grep -E "Size|Speed|Locator|Type" | head -40 +ip -br link +tailscale ip -4 +lsblk -o NAME,SIZE,MODEL,SERIAL,FSTYPE,MOUNTPOINT,VENDOR +findmnt -no FSTYPE /mnt/cache /mnt/disk1 /boot +df -h /mnt/cache /mnt/disk1 /mnt/user +smartctl -a /dev/nvme0n1 | head -100 +smartctl -a /dev/sdb | head -100 +smartctl -a /dev/sdc | head -100 +``` diff --git a/docs/MIGRATION_LOG.md b/docs/MIGRATION_LOG.md index b94ceba..be6a45e 100644 --- a/docs/MIGRATION_LOG.md +++ b/docs/MIGRATION_LOG.md @@ -16,6 +16,20 @@ Dieses Dokument ist nur noch ein historischer Verlauf. Der aktuelle operative Ab ## Historische Meilensteine +### 2026-05-26 - AdGuard Admin-Port auf Tailscale-Soll begrenzt + +- Host-Audit per SSH gegen `Kallilabcore` durchgefuehrt: Tailscale IPv4 ist `100.80.98.33`, LAN-IP ist `192.168.178.58/24`, Gateway `192.168.178.1`. +- Repo-Soll fuer `host-services/Adguard/docker-compose.yml` geaendert: DNS `53/tcp+udp` bleibt unveraendert, die Admin-UI bindet nun auf `100.80.98.33:8082:80`. +- Architektur, Service-Katalog, Repo-Map, Netzwerk-Inventar und AI-Kontext wurden an das neue Modell angepasst: keine Traefik-/Authelia-2FA-Umstellung, aber keine LAN-weite Admin-Bindung mehr. +- Live-Deploy/Validierung bleibt nach GitOps-Commit/Push zu pruefen: `ss -ltnp | grep :8082` muss nur `100.80.98.33:8082` zeigen, DNS auf Port 53 muss weiter funktionieren. + +### 2026-05-26 - Audit-Umsetzung vorbereitet + +- Aus `docs/AUDIT_2026-05-25.md` wurde `docs/AUDIT_2026-05-25_TODO.md` als operative Arbeitsliste abgeleitet. Authelia-2FA/OIDC bleibt bewusst geparkt und wird erst nach finaler Policy-Entscheidung umgesetzt. +- Neue Inventar- und Betriebsdokumente angelegt: `docs/HARDWARE_INVENTORY.md`, `docs/NETWORK_INVENTORY.md`, `docs/EXTERNAL_DEPENDENCIES.md`, `docs/CAPACITY_AND_LIFECYCLE.md` und `docs/FAMILY_ONBOARDING.md`. +- `docs/SERVICES_RECOVERY.md` beschreibt initial die recovery-kritischen `/mnt/user/services`-Pfade, Gitea-Repo-Mirror-Optionen, Komodo-Bootstrap und Secret-Recovery-Reihenfolge. +- Policy-Check lokal erneut ausgefuehrt: die alten SEC001-Warnings fuer `ddns-updater` und `scrutiny` sind nicht mehr aktuell; verbleibende Warnings betreffen Host-Netz-/User-/Image-Tag-Themen und Altstaende. + ### 2026-05-25 - Unraid Flash-Backup in Borg-Scope aufgenommen - `pre-backup-dumps.sh` erzeugt zusaetzlich zu den DB-Dumps ein sensibles `unraid-flash-config.tar.gz` aus `/boot/config` inklusive SHA256 und Manifest unter `/mnt/user/backups/borg/dumps/latest`. diff --git a/docs/NETWORK_INVENTORY.md b/docs/NETWORK_INVENTORY.md new file mode 100644 index 0000000..36438dd --- /dev/null +++ b/docs/NETWORK_INVENTORY.md @@ -0,0 +1,106 @@ +# Network Inventory - KalliLab CORE + +Status: Initialer Host-Audit erfasst, Router-/VLAN-Details offen. +Letzte Pruefung: 2026-05-26 + +## Zweck + +Dieses Dokument beschreibt Router, DNS, Tailscale, Portfreigaben und Netztrennung. Es ergaenzt das Architektur-Zielbild in `HOMELAB_ARCHITECTURE_MASTER_V2.md` um konkrete Hardware- und Betriebswerte. + +## Internet und Router + +| Feld | Wert | +|---|---| +| Anschluss / Provider | TBD | +| Router-Modell | TBD | +| Firmware | TBD | +| Router-IP | 192.168.178.1 | +| DHCP-Server | vermutlich Router, zu pruefen | +| Lokales Subnetz | 192.168.178.0/24 | +| IPv6 aktiv | TBD | +| DynDNS / DDNS | Cloudflare via `ddns-updater`, Details TBD | + +## DNS + +| Komponente | Rolle | Adresse | Bemerkung | +|---|---|---|---| +| AdGuard Home | LAN DNS / Filter | Host `192.168.178.58`, Docker `172.23.0.3` | DNS auf Port 53; Admin soll nur via Tailscale-IP `100.80.98.33:8082` erreichbar sein | +| Unbound | Rekursiver Resolver | Docker `dns_net` | Upstream fuer AdGuard | +| Cloudflare | Authoritative DNS | extern | DNS-Challenge fuer TLS | +| Router | DHCP DNS-Verteilung | TBD | Muss auf AdGuard zeigen, falls so betrieben | + +## Tailscale + +| Feld | Wert | +|---|---| +| Node-Name | Kallilabcore | +| Tailscale IPv4 | 100.80.98.33 | +| Tailscale IPv6 | TBD | +| Exit Node | TBD | +| Subnet Router | TBD | +| ACL-Policy extern dokumentiert | TBD | + +Pruefkommando: + +```bash +tailscale status +tailscale ip -4 +tailscale ip -6 +``` + +## Portfreigaben und Exposure + +| Port | Ziel | Zweck | Bewertung | +|---:|---|---|---| +| 80/tcp | Traefik | HTTP->HTTPS / ACME | erwartet | +| 443/tcp | Traefik | HTTPS | erwartet | +| 222/tcp | Gitea SSH | Git SSH | dokumentierte Ausnahme | +| 53/tcp+udp | AdGuard | DNS | dokumentierte Ausnahme | +| 8082/tcp | AdGuard Admin | Admin UI | Repo-Soll: nur `100.80.98.33:8082`, DNS-Port 53 unveraendert | +| 8181/tcp | InfluxDB 3 Core | LAN Writer fuer Home Assistant | LAN-only, Bind-IP pruefen | + +Pruefkommando: + +```bash +ss -ltnp | sort -k4 +docker ps --format "{{.Names}}: {{.Ports}}" | sort +``` + +## Netztrennung + +| Netz | Status | Bemerkung | +|---|---|---| +| LAN | 192.168.178.0/24 | Hauptnetz, Host `192.168.178.58` | +| Gast-WLAN | TBD | Zugriff auf AdGuard Admin muss ausgeschlossen sein | +| IoT-Netz | TBD | Zugriff auf AdGuard Admin muss ausgeschlossen sein | +| Tailscale | aktiv | Operator-Zugang, Host-IP `100.80.98.33` | +| VLANs | TBD | Router-/Switch-Faehigkeit pruefen | + +## Docker-Netze + +Authoritativ ist `HOMELAB_ARCHITECTURE_MASTER_V2.md`. Dieses Inventar haelt nur den Laufzeit-Snapshot fest. + +| Docker-Netz | Zweck | Erwartung | +|---|---|---| +| frontend_net | Traefik/Web | external bridge | +| backend_net | DB/Cache intern | internal bridge | +| dns_net | AdGuard/Unbound | bridge | +| monitoring_net | Observability | compose-intern | +| app-interne Netze | Stack-isoliert | nur wenn technisch noetig | + +Pruefkommando: + +```bash +docker network ls +docker network inspect frontend_net | jq '.[0].Containers | keys' +docker network inspect backend_net | jq '.[0].Internal' +``` + +## Offene Entscheidungen + +| Thema | Status | Naechster Schritt | +|---|---|---| +| AdGuard Admin nur via Tailscale | repo-seitig vorbereitet | Compose bindet Admin-Port auf `100.80.98.33:8082`; Live-Validierung nach Deploy | +| Gast-/IoT-Zugriff auf Admin-Ports | offen | Router-Regeln pruefen | +| IPv6 Exposure | offen | Router und Traefik/Cloudflare pruefen | +| Home Assistant InfluxDB Bind | offen | Effektive Listener-Adresse pruefen | diff --git a/docs/REPO_MAP.md b/docs/REPO_MAP.md index 107f8cf..7eb8334 100644 --- a/docs/REPO_MAP.md +++ b/docs/REPO_MAP.md @@ -31,6 +31,13 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam | `docs/GITOPS_DRIFT_RUNBOOK.md` | Pflichtmatrix fuer Git/Gitea/Komodo/Docker/Host-Drift | | `docs/DISASTER_RECOVERY.md` | Wiederanlauf nach Host-/Systemausfall | | `docs/RESTORE_MATRIX.md` | Restore-Quellen, Dump-Artefakte und Smoke-Tests je Dienst | +| `docs/SERVICES_RECOVERY.md` | Recovery-kritische `/mnt/user/services`-Pfade, Gitea-Mirror und Komodo-Bootstrap | +| `docs/HARDWARE_INVENTORY.md` | Hardware-, Disk-, SMART-, USV- und Strom-Inventar | +| `docs/NETWORK_INVENTORY.md` | Router, DNS, Tailscale, Portfreigaben und Netztrennung | +| `docs/EXTERNAL_DEPENDENCIES.md` | Externe Provider, Konten, Ausfall-Szenarien und kritische Off-Repo-Abhaengigkeiten | +| `docs/CAPACITY_AND_LIFECYCLE.md` | Capacity-Schwellen, Wachstum, Upgrade-Trigger und Restore-Zeitziele | +| `docs/FAMILY_ONBOARDING.md` | Familienorientierte Nutzungsdoku ohne Operator-Details | +| `docs/AUDIT_2026-05-25_TODO.md` | Operative Arbeitsliste aus dem Audit vom 2026-05-25; Authelia-2FA bewusst geparkt | | `docs/ALERTING_MAP.md` | ntfy Topic-Konvention und Sender-Mapping fuer Homelab-Alerts | | `docs/ROLLBACK.md` | Rueckweg bei Fehlern im GitOps-Betrieb | | `docs/SECRETS_MAP.md` | Secret-Namen, Pfade und Einbindungsarten ohne Werte | @@ -97,7 +104,7 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam | Stack | Compose | Services / Images | Hosts | Networks | Ports / Mode | Abhaengigkeiten | |---|---|---|---|---|---|---| -| AdGuard Home | `host-services/Adguard/docker-compose.yml` | `adguard` -> `adguard/adguardhome:v0.107.52` | keine Traefik-Route | `dns_net`, `frontend_net` | `53/tcp`, `53/udp`, `8082:80/tcp` | Unbound in `dns_net`; direkte Ports sind dokumentierte Ausnahme; Admin 8082 bleibt bewusst LAN-direkt | +| AdGuard Home | `host-services/Adguard/docker-compose.yml` | `adguard` -> `adguard/adguardhome:v0.107.52` | keine Traefik-Route | `dns_net`, `frontend_net` | `53/tcp`, `53/udp`, `100.80.98.33:8082:80/tcp` | Unbound in `dns_net`; DNS-Port 53 ist direkte Ausnahme; Admin 8082 ist auf Tailscale-IP begrenzt | | Plex | `host-services/plex/docker-compose.yml` | `plex` -> `plexinc/pms-docker:1.43.1.10611-1e34174b1@sha256:...` | keine Traefik-Route | `network_mode: host` | host network | Medienserver; Host-Netz bleibt fuer Discovery / Plex GDM dokumentierte Ausnahme | | Tailscale | `host-services/tailscale/docker-compose.yml` | `Tailscale-Docker` -> `tailscale/tailscale:stable@sha256:...` | keine | `network_mode: host` | host network | VPN/Remote-Zugang | @@ -238,6 +245,8 @@ Das Skript liest Secret-Dateien auf dem Host und schreibt Dump-Artefakte. Bei An ## Unsicherheiten / TODOs aus Repo-Sicht - Echte `stack.env`- und `.env`-Dateien sind per `.gitignore` ausgeschlossen; nur `*.example`-Dateien gehoeren ins Repo. +- Hardware-, Netzwerk- und Provider-Inventare sind initiale Templates und muessen nach einem Host-Audit mit echten Werten gefuellt werden. +- Authelia-2FA-/OIDC-Aenderungen sind nach Audit 2026-05-25 bewusst geparkt und werden nicht als Sofortmassnahme behandelt. - Authelia `configuration.yml` ist Repo-Baseline fuer nicht geheime Einstellungen, wird aber nicht automatisch von Komodo auf den Host kopiert. Die produktive Host-Datei kann zusaetzliche OIDC-/Secret-Konfiguration enthalten; Aenderungen muessen manuell gemerged und validiert werden. - `backend_net` ist in der Architektur als `internal: true` beschrieben; einzelne Compose-Dateien referenzieren es external. Live-Netz-Attribute bei Drift-Fragen pruefen. - Einige Images bleiben trotz Digest-Pin semantisch auf mutable Tags (`latest@sha256`, `release@sha256`). Das ist bewusst dokumentiert, aber bei Updates gesondert pruefen. diff --git a/docs/SERVICES_RECOVERY.md b/docs/SERVICES_RECOVERY.md new file mode 100644 index 0000000..2f9f917 --- /dev/null +++ b/docs/SERVICES_RECOVERY.md @@ -0,0 +1,98 @@ +# Services Recovery - KalliLab CORE + +Status: Initiale Spezifikation, aus dem Audit 2026-05-25 abgeleitet. +Verwandte Docs: `docs/DISASTER_RECOVERY.md`, `docs/RESTORE_MATRIX.md`, `docs/STORAGE_LAYOUT.draft.md`, `docs/SECRETS_MAP.md` + +## Zweck + +Der Share `/mnt/user/services/` ist recovery-kritisch, weil dort GitOps- und Host-Automation-Pfade liegen. Dieses Dokument beschreibt, welche Services-Pfade gesichert werden muessen und wie ein Kaltstart ohne laufendes Komodo gedacht ist. + +## Kritische Pfade + +| Pfad | Zweck | Kritikalitaet | Backup-Anforderung | +|---|---|---:|---| +| `/mnt/user/services/homelab-infra` | Host-Repo-Clone fuer Automation/Posture | hoch | Borg + GitHub/Gitea Mirror | +| `/mnt/user/services/stacks` | Komodo Stack Workspaces | hoch | Borg, vor strukturellen Aenderungen extra sichern | +| `/mnt/user/services/gitea/git/repositories` | Gitea Repository-Inhalte | kritisch | Borg + separater Mirror <= 6 h | +| `/mnt/user/services/posture-check` | Hostseitig ausgefuehrte Checks | hoch | Borg + Repo-Abgleich | +| `/mnt/user/appdata/secrets` | Runtime Secrets | kritisch | Borg + ausgewählte analoge/off-system Kopien | + +## Gitea Repository Mirror + +Ziel: Verlustfenster fuer `/mnt/user/services/gitea/git/repositories/` auf maximal 6 Stunden begrenzen. + +Optionen: + +| Option | Bewertung | +|---|---| +| `git bundle` je Repository auf zweites Medium | Sehr gut fuer Git-Recovery, transparent, gut pruefbar | +| `rsync` auf externe Platte oder zweiten Host | Einfach, aber Ziel muss regelmaessig erreichbar und geprueft sein | +| Separates Borg-Repo mit kurzem Schedule | Konsistent zum bestehenden Backup, aber wieder Borg-/Passphrase-abhaengig | + +Empfohlener Start: + +1. `git bundle`-Job fuer alle Gitea-Repositories definieren. +2. Ziel auf zweitem physischen Medium oder separatem Off-site-Ziel ablegen. +3. Job alle 6 Stunden ausfuehren. +4. Stichprobe: ein Bundle in Wegwerfpfad klonen. + +Erfolgskriterium: + +```bash +git clone /path/to/repo.bundle /tmp/repo-restore-test +git -C /tmp/repo-restore-test fsck +``` + +## Komodo Bootstrap + +Problem: Komodo verwaltet Stacks, ist aber selbst Teil des Recovery-Pfads. Ein kalter Host darf nicht voraussetzen, dass Komodo schon laeuft. + +Minimaler Wiederanlauf: + +1. Docker und externe Netze herstellen (`frontend_net`, `backend_net`, ggf. weitere dokumentierte Netze). +2. Repo aus Gitea/GitHub Mirror klonen. +3. Komodo Compose aus `ops/komodo/docker-compose.yml` oder einem spaeteren Bootstrap-Pfad starten. +4. Erforderliche `.env`/Secrets aus Host-Secret-Backup wiederherstellen. +5. Komodo-Core, Periphery und Mongo starten. +6. Web-UI und Periphery-Verbindung pruefen. + +Offene Aufgabe: + +- Entscheiden, ob ein eigener Pfad `services/komodo-bootstrap/` ins Repo kommt oder `ops/komodo/docker-compose.yml` die verbindliche Bootstrap-Quelle bleibt. + +Validierung: + +```bash +docker compose -f ops/komodo/docker-compose.yml config +docker compose -f ops/komodo/docker-compose.yml up -d +docker ps --filter "name=komodo" +``` + +## Secrets Recovery Reihenfolge + +Authoritativ ist `docs/SECRETS_MAP.md`. Fuer den Kaltstart ist diese Reihenfolge praktisch: + +1. Borg-Passphrase analog/off-system beschaffen. +2. Borg-Repo-Zugang/SSH-Key wiederherstellen. +3. `/mnt/user/appdata/secrets/` aus Borg wiederherstellen. +4. Komodo Stack ENV / Recovery ENV wiederherstellen. +5. Gitea Secrets und SSH-Material wiederherstellen. +6. Traefik/Cloudflare Secret wiederherstellen. +7. App-spezifische Secrets nach Tier-Reihenfolge wiederherstellen. + +## Break-glass Regeln + +- Keine Secret-Werte in Git oder Tickets kopieren. +- Restore-Tests laufen in Wegwerfpfaden, nie direkt gegen produktive Pfade. +- Wenn Gitea und Komodo beide down sind, gewinnt der externe GitHub-Mirror als Repo-Quelle. +- Wenn Borg ohne Passphrase nicht entschluesselbar ist, ist Recovery blockiert. Deshalb ist die analoge Passphrase-Sicherung P0. + +## Naechste Aufgaben + +| Status | Aufgabe | +|---|---| +| offen | Gitea-Bundle- oder Mirror-Mechanik final entscheiden | +| offen | Komodo-Bootstrap-Quelle finalisieren | +| offen | Restore-Kommandos nach erstem Trockenlauf mit echten Pfaden ergaenzen | +| offen | Services-Recovery in `docs/DISASTER_RECOVERY.md` verlinken | + diff --git a/docs/SERVICE_CATALOG.md b/docs/SERVICE_CATALOG.md index cc182e7..464c8e8 100644 --- a/docs/SERVICE_CATALOG.md +++ b/docs/SERVICE_CATALOG.md @@ -11,7 +11,7 @@ Secret-Werte sind nicht enthalten. Es werden nur Secret-Namen, Env-Key-Namen und | Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs | |---|---|---|---|---|---|---|---|---| | `traefik` | zentraler Reverse Proxy, TLS, Docker-Label-Routing | `traefik/docker-compose.yml`, `traefik/dynamic/*` | `https://traefik.kaleschke.info` | Docker socket, Cloudflare DNS API, `frontend_net`, `backend_net` | `/mnt/user/appdata/traefik/dynamic`, `/mnt/user/appdata/traefik/letsencrypt` | Tier 1, Share/Borg | ja, eigene Dashboard-Route mit Authelia | Host-Ports 80/443 sind zentrale Ausnahme; dynamic configs werden nicht automatisch von Komodo deployed | -| `adguard` | DNS-Server / LAN DNS | `host-services/Adguard/docker-compose.yml` | LAN-Port `53`, Admin `8082` | `dns_net`, `frontend_net`, Unbound | `/mnt/user/appdata/adguard/conf`, `/mnt/user/appdata/adguard/work` | Tier 1, config relevant | nein | Direkte Ports 53 und 8082 dokumentierte Ausnahme; Admin-Port bleibt bewusst LAN-direkt ohne Traefik/2FA (Operator-Entscheidung 2026-05-25) | +| `adguard` | DNS-Server / LAN DNS | `host-services/Adguard/docker-compose.yml` | LAN-Port `53`, Admin `100.80.98.33:8082` | `dns_net`, `frontend_net`, Unbound | `/mnt/user/appdata/adguard/conf`, `/mnt/user/appdata/adguard/work` | Tier 1, config relevant | nein | Direkter DNS-Port 53 bleibt; Admin-Port ist bewusst ohne Traefik/2FA, aber auf Tailscale-IP begrenzt (Operator-Entscheidung 2026-05-26) | | `unbound` | Upstream DNS Resolver fuer AdGuard | `apps/unbound/docker-compose.yml` | intern | `dns_net` | `/mnt/user/appdata/unbound/config` | rebuildbar / config relevant | nein | intern isoliert | | `tailscale` | VPN/Remote-Zugang | `host-services/tailscale/docker-compose.yml` | Tailscale | Host-Netz | `/mnt/user/appdata/tailscale` | Tier 1, State relevant | nein | `network_mode: host`, `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` sind dokumentierte VPN-Ausnahmen | | `gitea` | Git-Server / origin fuer GitOps | `core/gitea/docker-compose.yml` | `https://git.kaleschke.info`, SSH `222` | Traefik, `frontend_net`, externe DNS-Resolver fuer GitHub-Push-Mirror | `/mnt/user/services/gitea/data` | Tier 1, `gitea.sqlite.dump` + Share; privater GitHub-Push-Mirror fuer Repo-Bootstrap | ja | SSH-Port 222 direkte Host-Port-Ausnahme; Push-Mirror nach `michaelkaleschke-spec/homelab-infra` reduziert das DR-Bootstrap-Risiko | diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md index 883f89e..6465e4f 100644 --- a/docs/WORKFLOW.md +++ b/docs/WORKFLOW.md @@ -318,6 +318,9 @@ Nach jeder erfolgreichen Migration oder relevanten Aenderung muessen diese Datei - `docs/MIGRATION_LOG.md` - `docs/SECRETS_MAP.md` - `docs/ROLLBACK.md` +- `docs/SERVICES_RECOVERY.md` falls `/mnt/user/services`, Gitea, Komodo oder Host-Automation betroffen sind +- `docs/HARDWARE_INVENTORY.md` und `docs/CAPACITY_AND_LIFECYCLE.md` falls Hardware, Disks, Cache, RAM oder USV betroffen sind +- `docs/NETWORK_INVENTORY.md` und `docs/EXTERNAL_DEPENDENCIES.md` falls Router, DNS, Tailscale, Portfreigaben oder Provider betroffen sind - `HOMELAB_ARCHITECTURE_MASTER_V2.md` falls Architektur betroffen ist - `docs/GITOPS_DRIFT_RUNBOOK.md` falls GitOps-/Komodo-/Runtime-Drift betroffen ist diff --git a/host-services/Adguard/docker-compose.yml b/host-services/Adguard/docker-compose.yml index adbe51c..b26f4b3 100644 --- a/host-services/Adguard/docker-compose.yml +++ b/host-services/Adguard/docker-compose.yml @@ -13,7 +13,7 @@ services: ports: - "53:53/tcp" - "53:53/udp" - - "8082:80" + - "100.80.98.33:8082:80" security_opt: - no-new-privileges:true diff --git a/ops/policy-checks/exceptions.json b/ops/policy-checks/exceptions.json index 9d08177..02e54f3 100644 --- a/ops/policy-checks/exceptions.json +++ b/ops/policy-checks/exceptions.json @@ -16,7 +16,7 @@ "adguard": [ "53:53/tcp", "53:53/udp", - "8082:80" + "100.80.98.33:8082:80" ], "gitea": [ "222:22" diff --git a/ops/policy-checks/last-report.md b/ops/policy-checks/last-report.md index 22e5db1..6b14ce4 100644 --- a/ops/policy-checks/last-report.md +++ b/ops/policy-checks/last-report.md @@ -1,26 +1,30 @@ # Policy Check Report ## Summary -- Compose files checked: 29 +- Compose files checked: 31 - Critical findings: 0 -- Warnings: 4 -- Info findings: 9 +- Warnings: 7 +- Info findings: 10 ## Critical - none ## Warnings -- [SEC001] infra\ddns-updater\docker-compose.yml :: ddns-updater: Missing security_opt no-new-privileges:true. +- [HOSTNET002] host-services\plex\docker-compose.yml :: plex: network_mode: host is enabled. +- [IMAGE001] infra\ddns-updater\docker-compose.yml :: ddns-updater: Image uses a latest tag. Prefer a concrete version tag, even when a digest is present. +- [USER001] monitoring\docker-compose.yml :: influxdb3-core: Runs as user 0. Documented exception, keep visible for hardening. +- [IMAGE001] ops\glances\docker-compose.yml :: glances: Image uses a latest tag. Prefer a concrete version tag, even when a digest is present. - [USER001] ops\grafana-influxdb\docker-compose.yml :: grafana: Runs as user 0. Documented exception, keep visible for hardening. - [USER001] ops\grafana-influxdb\docker-compose.yml :: influxdb3-core: Runs as user 0. Documented exception, keep visible for hardening. -- [SEC001] ops\scrutiny\docker-compose.yml :: scrutiny: Missing security_opt no-new-privileges:true. +- [IMAGE001] ops\scrutiny\docker-compose.yml :: scrutiny: Image uses a latest tag. Prefer a concrete version tag, even when a digest is present. ## Info - [PORT001] core\gitea\docker-compose.yml :: gitea: Allowed host port mapping: 222:22 - [PORT001] host-services\Adguard\docker-compose.yml :: adguard: Allowed host port mapping: 53:53/tcp - [PORT001] host-services\Adguard\docker-compose.yml :: adguard: Allowed host port mapping: 53:53/udp -- [PORT001] host-services\Adguard\docker-compose.yml :: adguard: Allowed host port mapping: 8082:80 +- [PORT001] host-services\Adguard\docker-compose.yml :: adguard: Allowed host port mapping: 100.80.98.33:8082:80 - [HOSTNET001] host-services\tailscale\docker-compose.yml :: tailscale: network_mode: host is a documented exception. +- [PORT001] monitoring\docker-compose.yml :: influxdb3-core: Allowed host port mapping: ${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181 - [PORT001] ops\grafana-influxdb\docker-compose.yml :: influxdb3-core: Allowed host port mapping: ${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181 - [PRIV001] ops\scrutiny\docker-compose.yml :: scrutiny: Privileged mode is a documented exception. - [PORT001] traefik\docker-compose.yml :: traefik: Allowed host port mapping: 80:80