Document review matrix and drift checks

This commit is contained in:
2026-06-26 08:29:32 +02:00
parent ad9bb40b95
commit 5fbda4989d
15 changed files with 282 additions and 11 deletions
+1
View File
@@ -21,6 +21,7 @@ Zusaetzlich je nach Thema:
- Rollback: `docs/ROLLBACK.md` - Rollback: `docs/ROLLBACK.md`
- Secrets: `docs/SECRETS_MAP.md` - Secrets: `docs/SECRETS_MAP.md`
- GitOps-/Komodo-/Runtime-Drift: `docs/GITOPS_DRIFT_RUNBOOK.md` - GitOps-/Komodo-/Runtime-Drift: `docs/GITOPS_DRIFT_RUNBOOK.md`
- Periodische Homelab-Reviews / Abdeckungslandkarte: `docs/HOMELAB_REVIEW_MATRIX.md`
- Gesamtbild fuer KI-Agenten: `docs/AI_CONTEXT.md` - Gesamtbild fuer KI-Agenten: `docs/AI_CONTEXT.md`
- Architektur-/Betriebsentscheidungen mit Begruendung: `docs/DECISIONS.md` - Architektur-/Betriebsentscheidungen mit Begruendung: `docs/DECISIONS.md`
+4 -1
View File
@@ -171,6 +171,7 @@ Diese Dienste sind **keine Public Apps**:
- `super-productivity` — sp.kaleschke.info (Middleware) - `super-productivity` — sp.kaleschke.info (Middleware)
- `n8n` — n8n.kaleschke.info (Traefik ohne pauschale Middleware, native Auth + Webhook-Ausnahme analog Komodo) - `n8n` — n8n.kaleschke.info (Traefik ohne pauschale Middleware, native Auth + Webhook-Ausnahme analog Komodo)
- `healthchecks` — hc.kaleschke.info (Traefik, native Healthchecks-Auth; Ping-/API-Endpunkte ohne ForwardAuth analog n8n) - `healthchecks` — hc.kaleschke.info (Traefik, native Healthchecks-Auth; Ping-/API-Endpunkte ohne ForwardAuth analog n8n)
- `dawarich` — dawarich.kaleschke.info (UI hinter Authelia-Middleware; API-Key-Tracking-Pfade fuer OwnTracks/Overland/Traccar bewusst ohne ForwardAuth, siehe §10)
- `Traefik-Dashboard` - `Traefik-Dashboard`
- `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 - `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
@@ -285,6 +286,7 @@ Legende Status:
| `plex` | ✅ | `host` | Traefik via `plex.kaleschke.info` + Plex native Auth; LAN direkt `:32400` | Compose-Stack unter `host-services/plex/`; Host-Netz bleibt fuer Discovery / Plex GDM dokumentierte Ausnahme; Traefik routet per File-Provider-Ausnahme auf `http://192.168.178.58:32400`, weil Docker-Labels Host-Netz-Container aus Traefik heraus auf `127.0.0.1` routen wuerden; kein direkter WAN-Port 32400 und Plex Remote Access bleibt aus; Server geclaimt von `Xeridos`; Smart-TVs (Schlafzimmer, Wohnzimmer) ueber WLAN-LAN per mDNS | — | | `plex` | ✅ | `host` | Traefik via `plex.kaleschke.info` + Plex native Auth; LAN direkt `:32400` | Compose-Stack unter `host-services/plex/`; Host-Netz bleibt fuer Discovery / Plex GDM dokumentierte Ausnahme; Traefik routet per File-Provider-Ausnahme auf `http://192.168.178.58:32400`, weil Docker-Labels Host-Netz-Container aus Traefik heraus auf `127.0.0.1` routen wuerden; kein direkter WAN-Port 32400 und Plex Remote Access bleibt aus; Server geclaimt von `Xeridos`; Smart-TVs (Schlafzimmer, Wohnzimmer) ueber WLAN-LAN per mDNS | — |
| `super-productivity` | ✅ vorbereitet | `frontend_net` | Traefik + Middleware | Persoenliche Task-PWA des Operators; Issues kommen aus Gitea `Micha/mails` via n8n-Mail-Workflow | Deploy + Webhook + DNS-Eintrag offen | | `super-productivity` | ✅ vorbereitet | `frontend_net` | Traefik + Middleware | Persoenliche Task-PWA des Operators; Issues kommen aus Gitea `Micha/mails` via n8n-Mail-Workflow | Deploy + Webhook + DNS-Eintrag offen |
| `n8n` | ✅ vorbereitet | `frontend_net` | Traefik, native Auth (keine pauschale Authelia) | Workflow-Automation; erster Workflow: GMX-Mail -> OpenAI-Extraktion -> Gitea-Issue in `Micha/mails`; `N8N_ENCRYPTION_KEY` ist Stack-ENV-Pflichtsecret | Deploy + Webhook + Owner-Setup offen | | `n8n` | ✅ vorbereitet | `frontend_net` | Traefik, native Auth (keine pauschale Authelia) | Workflow-Automation; erster Workflow: GMX-Mail -> OpenAI-Extraktion -> Gitea-Issue in `Micha/mails`; `N8N_ENCRYPTION_KEY` ist Stack-ENV-Pflichtsecret | Deploy + Webhook + Owner-Setup offen |
| `dawarich_app` | ✅ | `frontend_net`, `backend_net` | Traefik via `dawarich.kaleschke.info`; UI hinter Authelia, API-Key-Pfade ohne ForwardAuth | Standort-Historie (Google-Timeline-Ersatz) mit eigener PostGIS-DB + Redis; `SELF_HOSTED=true`, `STORE_GEODATA=true`, keine externe Geocoding-Egress in der Compose gesetzt; Sidekiq-Worker im `backend_net` | Backup-Scope (`dawarich.dump` + Appdata) noch nicht verdrahtet, siehe `docs/MASTER_TODO.md` |
### 7.5 Admin / Operations ### 7.5 Admin / Operations
@@ -311,7 +313,7 @@ Legende Status:
| `monitoring-influxdb3-core` | ✅ | `monitoring_net`, `monitoring_influx_lan` + LAN-Bind | LAN-Port nur fuer interne Writer | InfluxDB 3 Core fuer Home-Assistant-/Ecowitt-Langzeitdaten; keine Traefik-/Public-Freigabe; Port 8181 nur via `INFLUXDB_BIND_IP` | HA-Write-Token und Sensor-Export finalisieren | | `monitoring-influxdb3-core` | ✅ | `monitoring_net`, `monitoring_influx_lan` + LAN-Bind | LAN-Port nur fuer interne Writer | InfluxDB 3 Core fuer Home-Assistant-/Ecowitt-Langzeitdaten; keine Traefik-/Public-Freigabe; Port 8181 nur via `INFLUXDB_BIND_IP` | HA-Write-Token und Sensor-Export finalisieren |
| `monitoring-loki` | ✅ | `monitoring_net` | intern | interner Container-Logspeicher ohne Public Route; Monitoring-Grafana greift ueber Loki-Datasource zu | Retention/Storage beobachten | | `monitoring-loki` | ✅ | `monitoring_net` | intern | interner Container-Logspeicher ohne Public Route; Monitoring-Grafana greift ueber Loki-Datasource zu | Retention/Storage beobachten |
| `monitoring-promtail` | ✅ | `monitoring_net` | intern | Docker-Log-Collector mit read-only Docker-Socket-Ausnahme; schreibt nach Loki | Socket-Ausnahme regelmaessig pruefen | | `monitoring-promtail` | ✅ | `monitoring_net` | intern | Docker-Log-Collector mit read-only Docker-Socket-Ausnahme; schreibt nach Loki | Socket-Ausnahme regelmaessig pruefen |
| `healthchecks` | ✅ | `frontend_net`, `healthchecks_internal` | Traefik, native Auth | self-hosted Heartbeat-Monitor fuer interne Jobs; Ping-/API ohne ForwardAuth (analog n8n); externe Host-down-/Backup-Waechter bleiben auf healthchecks.io-Cloud; live seit 2026-06-23 | Gitea-Webhook noch manuell anzulegen | | `healthchecks` | ✅ | `frontend_net`, `healthchecks_internal` | Traefik, native Auth | self-hosted Heartbeat-Monitor fuer interne Jobs; Ping-/API ohne ForwardAuth (analog n8n); externe Host-down-/Backup-Waechter bleiben auf healthchecks.io-Cloud; live seit 2026-06-23, Gitea->Komodo-Webhook aktiv | Status in `docs/MASTER_TODO.md` |
| `healthchecks-postgres` | ✅ | `healthchecks_internal` | intern | dedizierte PostgreSQL 18, nie `frontend_net` | — | | `healthchecks-postgres` | ✅ | `healthchecks_internal` | intern | dedizierte PostgreSQL 18, nie `frontend_net` | — |
| `grafana` / `influxdb3-core` / `loki` / `alloy` | entfernt | - | abgeloest | alte Docker-Runtime frei von Altcontainern; Compose-Pfade am 2026-05-26 aus aktivem Repo entfernt | Rollback nur ueber Git-Historie | | `grafana` / `influxdb3-core` / `loki` / `alloy` | entfernt | - | abgeloest | alte Docker-Runtime frei von Altcontainern; Compose-Pfade am 2026-05-26 aus aktivem Repo entfernt | Rollback nur ueber Git-Historie |
@@ -403,6 +405,7 @@ Die Blockmigration aus der Portainer-/Dockerman-Phase ist abgeschlossen: Traefik
| `monitoring-promtail` | Docker-Socket read-only | Docker-Log-Discovery fuer Loki; keine Schreibrechte, keine Appdaten-Persistenz ueber den Socket | | `monitoring-promtail` | Docker-Socket read-only | Docker-Log-Discovery fuer Loki; keine Schreibrechte, keine Appdaten-Persistenz ueber den Socket |
| `n8n` | keine pauschale Authelia-Middleware | Webhook-Endpunkte (`/webhook/*`, `/webhook-test/*`) muessen ohne ForwardAuth erreichbar bleiben; n8n bringt eigene Owner-/Login-Auth mit (analog Komodo/Nextcloud) | | `n8n` | keine pauschale Authelia-Middleware | Webhook-Endpunkte (`/webhook/*`, `/webhook-test/*`) muessen ohne ForwardAuth erreichbar bleiben; n8n bringt eigene Owner-/Login-Auth mit (analog Komodo/Nextcloud) |
| `healthchecks` | keine pauschale Authelia-Middleware | Ping-Endpunkte (`/ping/*`) und API muessen ohne ForwardAuth erreichbar sein, sonst koennen Cron-Jobs nicht melden; Healthchecks bringt eigene Login-Auth fuers Dashboard mit (analog n8n/Komodo). Der self-hosted Dienst deckt bewusst nur INTERNE Job-Checks ab; Host-down-/Backup-Waechter bleiben extern auf healthchecks.io-Cloud | | `healthchecks` | keine pauschale Authelia-Middleware | Ping-Endpunkte (`/ping/*`) und API muessen ohne ForwardAuth erreichbar sein, sonst koennen Cron-Jobs nicht melden; Healthchecks bringt eigene Login-Auth fuers Dashboard mit (analog n8n/Komodo). Der self-hosted Dienst deckt bewusst nur INTERNE Job-Checks ab; Host-down-/Backup-Waechter bleiben extern auf healthchecks.io-Cloud |
| `dawarich` (API-Key-Pfade) | UI hinter Authelia, aber separater Router `dawarich-api` ohne ForwardAuth | Tracking-Ingest-Pfade (`/api/v1/owntracks/points`, `/overland/batches`, `/traccar/points`, `/points`, `/tracks`, `/settings`, `/health`) muessen fuer OwnTracks/Overland/Traccar-Clients ohne ForwardAuth erreichbar sein (analog n8n-Webhook); Schutz ist der Dawarich-App-API-Key. Der UI-Router `dawarich` (`priority=10`) bleibt unter `authelia@file`. Standortdaten sind hochsensibel — keine weiteren Pfade ohne bewusste Entscheidung in die Bypass-Regel aufnehmen. Effektive Auth-Sicht: `docs/AUTH_MATRIX.md` |
| `plex` | Traefik ohne Authelia, File-Provider-Ausnahme trotz Host-Netz | Plex bringt native Konto-/Client-Auth mit; vorgeschaltete ForwardAuth wuerde Plex Web, Apps und Client-Flows stoeren. Docker-Labels sind fuer diesen Host-Netz-Container ungeeignet, weil Traefik sonst `127.0.0.1:32400` nutzt; daher `traefik/dynamic/plex.yml` mit Ziel `192.168.178.58:32400`. Route nur ueber Traefik/443 (`plex.kaleschke.info`), direkter Plex-WAN-Port 32400 und Plex Remote Access bleiben deaktiviert. | | `plex` | Traefik ohne Authelia, File-Provider-Ausnahme trotz Host-Netz | Plex bringt native Konto-/Client-Auth mit; vorgeschaltete ForwardAuth wuerde Plex Web, Apps und Client-Flows stoeren. Docker-Labels sind fuer diesen Host-Netz-Container ungeeignet, weil Traefik sonst `127.0.0.1:32400` nutzt; daher `traefik/dynamic/plex.yml` mit Ziel `192.168.178.58:32400`. Route nur ueber Traefik/443 (`plex.kaleschke.info`), direkter Plex-WAN-Port 32400 und Plex Remote Access bleiben deaktiviert. |
| `homeassistant` | Traefik ohne Authelia, Fach-YAML aus separatem Repo | Home Assistant bringt eigene Auth, mobile Apps, Webhooks und Integrationsfluesse mit. Der Container haengt in `frontend_net` fuer Traefik und in `smarthome_net` fuer MQTT/Zigbee2MQTT/ESPHome. `.storage` und Secrets bleiben in Appdata und werden per Borg gesichert, nicht versioniert. | | `homeassistant` | Traefik ohne Authelia, Fach-YAML aus separatem Repo | Home Assistant bringt eigene Auth, mobile Apps, Webhooks und Integrationsfluesse mit. Der Container haengt in `frontend_net` fuer Traefik und in `smarthome_net` fuer MQTT/Zigbee2MQTT/ESPHome. `.storage` und Secrets bleiben in Appdata und werden per Borg gesichert, nicht versioniert. |
| `homeassistant` (Ecowitt) | LAN-only Host-Port `8123` auf `192.168.178.58` | Ecowitt-GW3000 kann kein HTTPS und pusht per HTTP an den HA-Webhook. HA bekommt einen Host-Bind nur auf der LAN-IP (`192.168.178.58:8123:8123`, nicht `0.0.0.0`/WAN), analog InfluxDB 8181. Kein Traefik-Umbau des globalen HTTP-Redirects noetig, da Ecowitt rein im LAN pusht. Webhook nicht `local_only`, geschuetzt durch 128-bit-Zufalls-ID. Siehe `docs/DECISIONS.md` (2026-06-13). | | `homeassistant` (Ecowitt) | LAN-only Host-Port `8123` auf `192.168.178.58` | Ecowitt-GW3000 kann kein HTTPS und pusht per HTTP an den HA-Webhook. HA bekommt einen Host-Bind nur auf der LAN-IP (`192.168.178.58:8123:8123`, nicht `0.0.0.0`/WAN), analog InfluxDB 8181. Kein Traefik-Umbau des globalen HTTP-Redirects noetig, da Ecowitt rein im LAN pusht. Webhook nicht `local_only`, geschuetzt durch 128-bit-Zufalls-ID. Siehe `docs/DECISIONS.md` (2026-06-13). |
+1
View File
@@ -23,6 +23,7 @@ Diese Datei enthaelt bewusst **keinen** Arbeitsstand mehr — Status nur in
4. bei Service-Fragen `docs/SERVICE_CATALOG.md` 4. bei Service-Fragen `docs/SERVICE_CATALOG.md`
5. bei Restore/DR `docs/DISASTER_RECOVERY.md` und `docs/RESTORE_MATRIX.md` 5. bei Restore/DR `docs/DISASTER_RECOVERY.md` und `docs/RESTORE_MATRIX.md`
6. bei "warum ist das so?"-Fragen `docs/DECISIONS.md` 6. bei "warum ist das so?"-Fragen `docs/DECISIONS.md`
7. bei periodischen Reviews `docs/HOMELAB_REVIEW_MATRIX.md`
## Harte Regeln ## Harte Regeln
+7
View File
@@ -45,6 +45,8 @@ buendelt das an **einem** Ort und **verlinkt** auf die Quellen statt zu kopieren
| `n8n.kaleschke.info` | Keine pauschale Authelia (native) | Webhook-Endpunkte `/webhook/*` | Ausnahmen-Tabelle; ⚠ Middleware-Abweichung lt. policy-check | | `n8n.kaleschke.info` | Keine pauschale Authelia (native) | Webhook-Endpunkte `/webhook/*` | Ausnahmen-Tabelle; ⚠ Middleware-Abweichung lt. policy-check |
| `plex.kaleschke.info` | Keine Authelia (native Plex) | File-Provider-Route; WAN-Port 32400 + Remote Access aus | DECISIONS 2026-05-28 | | `plex.kaleschke.info` | Keine Authelia (native Plex) | File-Provider-Route; WAN-Port 32400 + Remote Access aus | DECISIONS 2026-05-28 |
| `home.kaleschke.info` (homeassistant) | Keine Authelia (native HA) | Traefik + `smarthome_net`; LAN-Port 8123 | Ausnahmen-Tabelle; ⚠ Middleware-Abweichung lt. policy-check | | `home.kaleschke.info` (homeassistant) | Keine Authelia (native HA) | Traefik + `smarthome_net`; LAN-Port 8123 | Ausnahmen-Tabelle; ⚠ Middleware-Abweichung lt. policy-check |
| `dawarich.kaleschke.info` (UI) | **two_factor** | `authelia@file,secure-headers@file` (Router `dawarich`, `priority=10`) | `apps/dawarich/docker-compose.yml` Labels; Standortdaten hinter Authelia |
| `dawarich.kaleschke.info` (API-Key-Ingest-Pfade) | **Bypass/native** (App-API-Key, kein 2FA) | Router `dawarich-api` (`priority=100`) nur `secure-headers@file`, Pfade `/api/v1/{health,settings,points,tracks,owntracks,overland,traccar,...}` | `apps/dawarich/docker-compose.yml` Labels; OwnTracks/Overland/Traccar-Tracking muss ohne ForwardAuth pushen koennen (analog n8n-Webhook) |
| AdGuard-Admin | **Tailscale-only**, nicht oeffentlich | Host-Bind `100.80.98.33:8082`, kein Traefik | DECISIONS 2026-05-26 | | AdGuard-Admin | **Tailscale-only**, nicht oeffentlich | Host-Bind `100.80.98.33:8082`, kein Traefik | DECISIONS 2026-05-26 |
| `influxdb3-core` :8181 | **LAN-only** Writer (HA) | Host-Port, kein Traefik, nicht in `frontend_net` | dokumentierte Ausnahme | | `influxdb3-core` :8181 | **LAN-only** Writer (HA) | Host-Port, kein Traefik, nicht in `frontend_net` | dokumentierte Ausnahme |
@@ -60,6 +62,11 @@ buendelt das an **einem** Ort und **verlinkt** auf die Quellen statt zu kopieren
`homeassistant` sind erwartbar (native Ausnahmen). `grafana` steht **nicht** in `homeassistant` sind erwartbar (native Ausnahmen). `grafana` steht **nicht** in
der Ausnahmen-Tabelle und ist als Catch-all-`two_factor` gefuehrt — die der Ausnahmen-Tabelle und ist als Catch-all-`two_factor` gefuehrt — die
abweichende Middleware ist live zu bestaetigen (offen). abweichende Middleware ist live zu bestaetigen (offen).
- **Dawarich API-Key-Pfade**: Der `dawarich-api`-Router umgeht Authelia bewusst
fuer die Tracking-Ingest-Pfade (hochsensible Standortdaten). Schutz liegt allein
im Dawarich-App-API-Key. Review-Trigger = neue oeffentliche API-Pfade in der
Router-Regel oder veraendertes Risikoprofil der Standortdaten. Begruendung jetzt
auch in `HOMELAB_ARCHITECTURE_MASTER_V2.md` §10.
## Pflege ## Pflege
+3 -2
View File
@@ -18,11 +18,12 @@ Dieses Dokument beschreibt externe Anbieter und Konten, von denen Betrieb, Recov
| GitHub Mirror | Externer Repo-Mirror `michaelkaleschke-spec/homelab-infra` (privat) | mittel/hoch | Gitea-Verlust abfederbar, aber Bare-Metal-Bootstrap braucht Read-Zugang (PAT oder SSH-Deploy-Key); ohne diesen ist der Mirror im DR nicht klonbar | GitHub-Konto; Push-PAT liegt in Gitea-Mirror-Settings; **Read-PAT/Deploy-Key fuer DR muss zusaetzlich offline im DR-Kit liegen** | Mirror-Status regelmaessig pruefen; lokalen Clone als zweite Kopie behalten; Read-PAT mit Scope `repo:read` separat erzeugen und im DR-Kit ablegen | | GitHub Mirror | Externer Repo-Mirror `michaelkaleschke-spec/homelab-infra` (privat) | mittel/hoch | Gitea-Verlust abfederbar, aber Bare-Metal-Bootstrap braucht Read-Zugang (PAT oder SSH-Deploy-Key); ohne diesen ist der Mirror im DR nicht klonbar | GitHub-Konto; Push-PAT liegt in Gitea-Mirror-Settings; **Read-PAT/Deploy-Key fuer DR muss zusaetzlich offline im DR-Kit liegen** | Mirror-Status regelmaessig pruefen; lokalen Clone als zweite Kopie behalten; Read-PAT mit Scope `repo:read` separat erzeugen und im DR-Kit ablegen |
| Tailscale | Remote-/Operator-Zugang | hoch | Remote-Zugriff erschwert, lokale Bedienung bleibt | Tailnet-Konto; Node `Kallilabcore`, IPv4 `100.80.98.33` | Break-glass per LAN und physischem Zugriff; Tailnet-Recovery-Codes sichern | | Tailscale | Remote-/Operator-Zugang | hoch | Remote-Zugriff erschwert, lokale Bedienung bleibt | Tailnet-Konto; Node `Kallilabcore`, IPv4 `100.80.98.33` | Break-glass per LAN und physischem Zugriff; Tailnet-Recovery-Codes sichern |
| GMX SMTP | Authelia Notifier, Vaultwarden-Einladungen, Ops-Report-Mail | mittel | Mail-Notifier und Vaultwarden-Einladungen fallen aus; Login selbst nicht zwingend | GMX-Konto; SMTP-Secrets liegen hostseitig | ntfy/zweiter SMTP als Fallback pruefen | | GMX SMTP | Authelia Notifier, Vaultwarden-Einladungen, Ops-Report-Mail | mittel | Mail-Notifier und Vaultwarden-Einladungen fallen aus; Login selbst nicht zwingend | GMX-Konto; SMTP-Secrets liegen hostseitig | ntfy/zweiter SMTP als Fallback pruefen |
| OpenAI API | Paperless-GPT LLM und Vision-OCR | mittel | Automatische Dokument-Titel, Tags, Korrespondenten und LLM-OCR fallen aus; Paperless selbst laeuft weiter | OpenAI-Projekt/API-Key ausserhalb Repo | Key in Vaultwarden/Komodo sichern, bei Offenlegung rotieren; Kosten/Usage im OpenAI-Projekt beobachten | | OpenAI API | Paperless-GPT LLM/Vision-OCR (Dokumenttext/-bilder) **und** n8n Mail->LLM->Gitea-Workflow (GMX-Mailinhalt bis ~8000 Zeichen) | mittel | Automatische Dokument-Titel/Tags/Korrespondenten/LLM-OCR und die n8n-Mail-Extraktion fallen aus; Paperless und n8n laufen sonst weiter | je eigener OpenAI-API-Key ausserhalb Repo (Paperless-GPT Stack-ENV; n8n Credentials Store) | Keys in Vaultwarden/Komodo sichern, bei Offenlegung rotieren; Kosten/Usage im OpenAI-Projekt beobachten. **Datenklasse/Retention/Loeschpfad je Egress-Pfad noch offen** (`docs/HOMELAB_REVIEW_MATRIX.md` Domaene 12, `docs/MASTER_TODO.md`) |
| Let's Encrypt | TLS-Zertifikate | hoch | Cert-Erneuerung faellt aus | automatisch via Traefik und Cloudflare DNS-Challenge | Cert-Expiry Alert einrichten; Cloudflare-Token und Traefik-Storage pruefen | | Let's Encrypt | TLS-Zertifikate | hoch | Cert-Erneuerung faellt aus | automatisch via Traefik und Cloudflare DNS-Challenge | Cert-Expiry Alert einrichten; Cloudflare-Token und Traefik-Storage pruefen |
| Container Registries | Image Pulls von Docker Hub, GHCR, LSCR, Gitea Registry u. a. | mittel | Redeploy/Update blockiert | ueberwiegend oeffentlich; keine produktiven Registry-Tokens im Repo | Gepinnte Digests und lokale Runtime helfen kurzfristig; Updates geplant und einzeln deployen | | Container Registries | Image Pulls von Docker Hub, GHCR, LSCR, Gitea Registry u. a. | mittel | Redeploy/Update blockiert | ueberwiegend oeffentlich; keine produktiven Registry-Tokens im Repo | Gepinnte Digests und lokale Runtime helfen kurzfristig; Updates geplant und einzeln deployen |
| Plex Konto | Plex native Auth, Claim und Client-Zugriff ueber `plex.kaleschke.info` | mittel | Plex-Web/App-Login und Clients koennen ausfallen; LAN-Medienpfade bleiben lokal | Plex-Konto ausserhalb Repo; `PLEX_CLAIM` nur fuer Setup | Plex Remote Access bleibt aus; externer Zugriff laeuft ueber Traefik/443. Konto-Recovery separat sichern | | Plex Konto | Plex native Auth, Claim und Client-Zugriff ueber `plex.kaleschke.info` | mittel | Plex-Web/App-Login und Clients koennen ausfallen; LAN-Medienpfade bleiben lokal | Plex-Konto ausserhalb Repo; `PLEX_CLAIM` nur fuer Setup | Plex Remote Access bleibt aus; externer Zugriff laeuft ueber Traefik/443. Konto-Recovery separat sichern |
| Mobile Push | ntfy und ggf. mobile Plattform-Pushes | niedrig/mittel | Alerts erreichen Mobilgeraete ggf. nicht | App-/Device-seitig | Kritische Alerts zusaetzlich in Grafana/Glance sichtbar halten | | Mobile Push | ntfy (self-hosted) und ggf. mobile Plattform-Pushes; Egress = Alert-Payloads/Topic-Namen an Upstream-Push | niedrig/mittel | Alerts erreichen Mobilgeraete ggf. nicht | App-/Device-seitig | Kritische Alerts zusaetzlich in Grafana/Glance sichtbar halten; Payloads/Topics ohne PII/Pfade/Secrets halten (Naming-Regel offen, `docs/HOMELAB_REVIEW_MATRIX.md` Domaene 12) |
| healthchecks.io (Cloud) | Externer Dead-Man's-Switch fuer Host-down-/Backup-Stillstand: Borg-Pre-Hook, baerchen-Nearline-Pull, geplanter Monitoring-Watchdog (#8); interne Job-Checks laufen self-hosted | mittel | Stiller Ausfall von Borg-/Nearline-Lauf wird nicht extern sichtbar; Backups selbst laufen weiter | healthchecks.io-Account; Ping-/Capability-URLs als Host-Secrets (`docs/SECRETS_MAP.md`) | Datenklasse = operative Metadaten (Check-Name/Timing); Naming ohne PII/Pfade/Secrets. Skripte sind endpoint-agnostisch -> bei Bedarf auf self-hosted `hc.kaleschke.info` umstellbar. Bewusste Architektur (DECISIONS 2026-06-23): Host-down-Waechter bleiben extern |
| Operator-DR-Workstation | Bare-Metal-Recovery-Arbeitsplatz (Gaming-PC Windows, lokaler Repo-Clone `G:\Gitea_Clone\homelab-infra`) | kritisch | Ohne Workstation kein Borg-Extract, kein Hetzner-Zugriff, kein Repo-Bootstrap; der Unraid-Host ist im Bare-Metal-Fall gerade weg | Operator-PC, WSL2 + Borg-Client, SSH-Key fuer Hetzner Storage Box, Offline-Kopie der Borg-Passphrase | Setup als bewusste DR-Vorbedingung pflegen (siehe Abschnitt "DR-Workstation Bare-Metal-Kit") | | Operator-DR-Workstation | Bare-Metal-Recovery-Arbeitsplatz (Gaming-PC Windows, lokaler Repo-Clone `G:\Gitea_Clone\homelab-infra`) | kritisch | Ohne Workstation kein Borg-Extract, kein Hetzner-Zugriff, kein Repo-Bootstrap; der Unraid-Host ist im Bare-Metal-Fall gerade weg | Operator-PC, WSL2 + Borg-Client, SSH-Key fuer Hetzner Storage Box, Offline-Kopie der Borg-Passphrase | Setup als bewusste DR-Vorbedingung pflegen (siehe Abschnitt "DR-Workstation Bare-Metal-Kit") |
## Kritische Secrets ausserhalb des Repos ## Kritische Secrets ausserhalb des Repos
+141
View File
@@ -0,0 +1,141 @@
# Homelab Review Matrix
Typ: Inventar/Review-Landkarte - Stand: 2026-06-26 - Status: aktiv
Diese Matrix erzwingt Abdeckung, nicht Methode. Jede Domaene wird
threat-model-first und mit echtem Ist-Zustand geprueft: Repo, Compose,
Runbooks, Host-Checks, Docker Inspect, API-Exports und Restore-Belege.
Generische Best-Practice-Listen sind hier nur Hilfsmittel, nicht das Ziel.
## Ritual
1. Aelteste oder sicherheitskritische Domaene aus dem Review-Log waehlen.
2. Genau diese eine Domaene tief pruefen.
3. Repo-Soll und Live-Ist getrennt halten: "belegt", "nicht belegt",
"offene Host-Pruefung".
4. Keine Secret-Werte lesen, zitieren oder ins Repo schreiben.
5. Konkrete Befunde in die passende Heimat ueberfuehren:
`MASTER_TODO.md` fuer offene Arbeit, `DECISIONS.md` fuer Entscheidungen,
Zielbild in Architecture/Inventare/Service Catalog, Ablauf in Runbooks.
Kadenz-Vorschlag: eine Domaene alle zwei Wochen. Sicherheitskritisch haeufiger:
Exposure, Identity, Backup-Restore, Data Privacy/Egress, GitOps-Control-Plane.
## Review-Log
| # | Domaene | Reifegrad (1-5) | Zuletzt geprueft | Befund / offene Punkte |
|---|---|:---:|:---:|---|
| 1 | Exposure & Ingress | ? | - | |
| 2 | Identity & Access | ? | - | Authelia-Ausnahmen und native Auth regelmaessig gegen Live-Routen testen |
| 3 | Secrets & Key-Management | ? | in Umsetzung | SOPS+age-Migration/Rotation offen; keine Secret-Werte in Git |
| 4 | Netzwerk-Segmentierung & Blast Radius | ? | - | |
| 5 | Container- & Host-Haertung | ? | - | |
| 6 | Supply Chain | ? | teilweise | Digest-Pinning belegt; Signatur-Verifikation/SBOM/CVE-Gate offen |
| 7 | Backup != Restore | ? | - | Borg->Hetzner belegt; Restore-Drills/Immutability weiter rotieren |
| 8 | Datenintegritaet | ? | - | |
| 9 | Observability-Luecken | ? | - | Alert-on-Absence/Dead-Man-Switch belegt, weiter beweisen |
| 10 | Exit & Portierbarkeit | ? | - | |
| 11 | Update- & Patch-Hygiene | ? | teilweise | Renovate belegt; Host/Engine/Auth-Layer separat betrachten |
| 12 | Data Privacy / Egress / Datenklassifikation | 2 | 2026-06-26 | OpenAI-/Mail-/Healthchecks-/ntfy-/Borg-Egress kartiert; Retention/Loeschpfade und Dawarich-Abdeckung offen |
| 13 | GitOps-Control-Plane & Drift | 3 | 2026-06-26 | Repo-Soll stark; Live-Abgleich fuer Komodo/Runtime/Traefik/Authelia offen; Self-Stack und manuelle Sync-Ausnahmen sind hoechster Hebel |
## 12 - Data Privacy / Egress / Datenklassifikation
**Bedrohung:** Personenbezogene oder sensible Daten verlassen das Lab
unkontrolliert: falscher Dienst, falscher Token, keine Retention, kein
Loesch-/Exportpfad.
**Leitfragen**
- Welche Dienste verarbeiten personenbezogene oder sensible Daten?
- Welche Daten verlassen das Lab an externe Dritte, ueber welchen Pfad und mit
welchem Token/Account?
- Welche Retention gilt beim Dritten? Gibt es Loeschung, Export und Opt-out?
- Gibt es pro Dienst eine Datenklasse und eine erlaubte Egress-Klasse?
- Driftet die reale Datenverarbeitung von `SERVICE_CATALOG.md`,
`EXTERNAL_DEPENDENCIES.md`, Architecture oder Restore-Doku ab?
**Messpunkte**
```bash
rg -n "OPENAI|api\.openai|ANTHROPIC|api_key|API_KEY|webhook|https?://" -g "docker-compose.y*ml" -g "*.env*"
rg -ni "openai|gpt|external|cloud|telemetry|analytics" apps/ ops/ monitoring/
rg -n "httpRequest|emailReadImap|webhook|openai|Authorization|api/v1" apps/n8n/workflows -g "*.json"
rg -n "Dawarich|dawarich|OpenAI|healthchecks.io|ntfy.sh|GitHub Mirror|Hetzner|GMX" docs HOMELAB_ARCHITECTURE_MASTER_V2.md
```
**Befund 2026-06-26**
| Pfad | Belegt | Risiko |
|---|---|---|
| Paperless-GPT -> OpenAI | `apps/paperless-gpt/docker-compose.yml` nutzt OpenAI fuer LLM/Vision OCR | Dokumenttext/Bilder/Metadaten koennen extern verarbeitet werden; Retention/Loeschpfad nicht im Repo dokumentiert |
| n8n Mail -> OpenAI -> Gitea | Workflow-JSON im Repo, Export war inaktiv; live nicht verifiziert | Mailinhalt bis 8000 Zeichen kann an OpenAI gehen; Live-Status und Retention offen |
| GMX IMAP/SMTP | n8n, Vaultwarden, Authelia/Mailpfade dokumentiert | Mailprovider-Retention und Scope nicht als Matrix erfasst |
| Borg -> Hetzner | `EXTERNAL_DEPENDENCIES.md`, Borg-Scope | Verschluesselte Offsite-Kopie vieler Datenklassen; Immutability/Retention weiter beweisen |
| ntfy/Healthchecks | self-hosted + externe Cloud-Waechter dokumentiert | Alert-/Checknamen koennen operative Metadaten leaken |
| Dawarich | Service Catalog + Compose vorhanden | Standortdaten sind hochsensibel; Architecture/Auth/Restore/Backup-Abdeckung war nicht konsistent sichtbar |
**Naechste Optimierungen**
- OpenAI-Retention, Datenkontrollen, Loesch-/Exportpfad und erlaubte Datenklassen
fuer Paperless-GPT und n8n dokumentieren.
- Paperless-GPT nur ueber Tag-/Allowlist oder klare Ausschlussregel auf
Dokumente anwenden.
- Dawarich in Architecture/Auth/Restore/Backup-Scope konsistent eintragen oder
bewusst als Ausnahme dokumentieren.
- Healthchecks/ntfy-Naming-Regel: keine PII, keine Pfade, keine Secrets, keine
aussagekraeftigen internen Namen in Payloads.
## 13 - GitOps-Control-Plane & Drift
**Bedrohung:** Der Steuerpfad driftet vom deklarierten Sollzustand ab oder wird
kompromittiert. Gitea, Komodo, Webhooks, Traefik dynamic config und Authelia
Host-Config verteilen Fehler auf alle Stacks.
**Leitfragen**
- Stimmen lokaler Clone, Gitea `origin/master`, Komodo Stack Workspace,
Docker Runtime und Host-Dateien ueberein?
- Welche Stacks laufen live ohne Repo-Quelle, und welche Repo-Compose-Dateien
haben keinen Komodo-Stack?
- Welche Stack-ENV-Werte werden live in Komodo gepflegt und sind nicht in Git?
- Wie sind Gitea->Komodo-Webhooks authentifiziert, auf Branch `master`
begrenzt und gegen falsche Ausloeser geschuetzt?
- Wer hat Schreibrechte auf Gitea, Komodo und Bot-/Deploy-Tokens?
- Kann ein Push direkt deployen, oder gibt es technische Branch Protection?
- Ist der Recovery-Pfad ohne laufendes Komodo belegt?
**Messpunkte**
```bash
powershell -ExecutionPolicy Bypass -File .\ops\policy-checks\check_repo.ps1
SEND_NTFY=0 bash /mnt/user/services/homelab-infra/services/posture-check/compose-runtime-drift.sh
SEND_NTFY=0 bash /mnt/user/services/homelab-infra/services/posture-check/komodo-stack-hygiene.sh
bash /mnt/user/services/homelab-infra/services/authelia-diff.sh
rsync -nci --checksum /mnt/user/services/homelab-infra/traefik/dynamic/ /mnt/user/appdata/traefik/dynamic/
```
**Befund 2026-06-26**
| Bereich | Belegt | Risiko |
|---|---|---|
| Repo-Soll | `check_repo.ps1`: 0 Critical; lokaler `HEAD` == `origin/master` | Git-Ebene sauber |
| Runtime-Ist | Aus der Windows-Shell nicht gegen Unraid verifizierbar | Host-Pruefung offen; lokale Docker-Missing-Meldungen nicht als Homelab-Drift werten |
| Komodo Self-Stack | `ops/komodo/docker-compose.yml` ist Spiegel/Recovery-Anker; live inline in Komodo | Repo-Aenderung wirkt nicht live; hoechster Drift-Hebel |
| Stack-ENV | Komodo Stack-ENV bewusst nicht vollstaendig in Git | Git allein ist kein vollstaendiger Sollzustand |
| Traefik dynamic | Manuelle Sync-Ausnahme | Auth-Middlewares/File-Routen koennen vom Repo abweichen |
| Authelia Host-Config | `authelia-diff.sh` vergleicht `access_control` | Live-Host-Datei und OIDC-Clientnamen muessen regelmaessig abgeglichen werden |
| Gitea/Komodo Integritaet | Webhook-Regeln dokumentiert; Branch Protection nicht repo-belegt | Prozess-Gate belegt, technisches Enforcement offen |
**Naechste Optimierungen**
- Auf dem Unraid-Host `compose-runtime-drift.sh` und
`komodo-stack-hygiene.sh` ausfuehren und Ergebnisse in
`/mnt/user/services/posture-check/*.json` bewerten.
- Traefik dynamic Drift-Check analog Authelia-Diff als read-only Check
betreiben (`services/traefik-dynamic-diff.sh` / `traefik_dynamic_drift`).
- Komodo Inline-Compose gegen `ops/komodo/docker-compose.yml` diffen, Secret-
Werte maskieren, nur Variablennamen/Pfade dokumentieren.
- Gitea Branch Protection, aktive Hooks, PATs und Deploy-Keys live
inventarisieren; ungenutzte Zugriffe entfernen.
- Healthchecks-Doku-Widerspruch "Webhook offen" vs "Webhook aktiv" bereinigen.
+2
View File
@@ -25,6 +25,8 @@ Host-Reports (`/mnt/user/backups/restore-reports/`) und in der Git-Historie.
| Restore-Test Tailscale | Operator | State-Validierung + Reconnect nur auf Wegwerf-Host/VM, danach Geraet in Tailscale-Admin entfernen | `ops/restore-tests/tailscale-runbook.md` | | Restore-Test Tailscale | Operator | State-Validierung + Reconnect nur auf Wegwerf-Host/VM, danach Geraet in Tailscale-Admin entfernen | `ops/restore-tests/tailscale-runbook.md` |
| Authelia OIDC fuer Apps | Operator/Codex | Live: Grafana + Mealie login-verifiziert; Paperless Secret verdrahtet und Service-Smoke am 2026-06-17 gruen, finaler Browser-Login mit Operator-Account offen. Immich + Nextcloud bewusst geparkt bis Family-Onboarding (siehe `docs/DECISIONS.md` 2026-06-06) | `docs/AUTHELIA_OIDC_PLAN.md` | | Authelia OIDC fuer Apps | Operator/Codex | Live: Grafana + Mealie login-verifiziert; Paperless Secret verdrahtet und Service-Smoke am 2026-06-17 gruen, finaler Browser-Login mit Operator-Account offen. Immich + Nextcloud bewusst geparkt bis Family-Onboarding (siehe `docs/DECISIONS.md` 2026-06-06) | `docs/AUTHELIA_OIDC_PLAN.md` |
| Home Assistant Tibber | Operator/Codex | Tibber per HA-UI-Config-Flow verbinden. Danach Energy-Dashboard um echte Kosten/Preisquelle ergaenzen; SolarEdge-PV, Netz und Speicher sind bereits konfiguriert und validiert | `docs/runbooks/smart-home-bootstrap.md`, `docs/DECISIONS.md` | | Home Assistant Tibber | Operator/Codex | Tibber per HA-UI-Config-Flow verbinden. Danach Energy-Dashboard um echte Kosten/Preisquelle ergaenzen; SolarEdge-PV, Netz und Speicher sind bereits konfiguriert und validiert | `docs/runbooks/smart-home-bootstrap.md`, `docs/DECISIONS.md` |
| Review-Follow-up Data Privacy/Egress | Operator/Codex | (1) Datenklasse + Retention/Loeschpfad je externem Egress-Pfad dokumentieren (OpenAI fuer Paperless-GPT+n8n, GMX, healthchecks.io, ntfy-Upstream, Hetzner, GitHub-Mirror). (2) **Dawarich-Backup-Scope schliessen** (Standortdaten aktuell nicht DR-gesichert): `dawarich.dump` in `pre-backup-dumps.sh`, Pfad in Quellliste + Borg-UI-Mount + `BACKUP_SCOPE.md`, danach `RESTORE_MATRIX.md` Tier-2-Zeile + Reifegrad. Dawarich in Architecture §4.2/§7.4/§10 + `AUTH_MATRIX.md` ist am 2026-06-26 angeglichen. | `docs/HOMELAB_REVIEW_MATRIX.md` Domaene 12 |
| Review-Follow-up GitOps-Control-Plane | Operator/Codex | Auf Unraid `compose-runtime-drift.sh`, `komodo-stack-hygiene.sh`, `authelia-diff.sh` und `traefik-dynamic-diff.sh` laufen lassen; danach Branch-Protection/Hooks/PATs in Gitea live inventarisieren | `docs/HOMELAB_REVIEW_MATRIX.md` Domaene 13 |
| Nearline-Pull Dead-Man's-Switch | Operator | **S4U-Root-Cause 2026-06-21 behoben + verifiziert:** Task `KalliLab H Drive Nearline Pull` von S4U auf LogonType `Interactive` ("Nur wenn Benutzer angemeldet") umgestellt (kein Passwort noetig, da `michi` Dauer-Konsolen-User) -> per Planer mit `0x0` bestaetigt. Spiegel frisch, Exit-Code-Leak gefixt, Heartbeat-Pings gepusht. **Verbleibt (optional, niedrige Dringlichkeit):** je einen Healthchecks-Check anlegen + Capability-URL hinterlegen (baerchen ENV `HEALTHCHECKS_NEARLINE_URL`/Datei; Unraid `/mnt/user/appdata/secrets/healthchecks_borg_url`) | `ops/h-drive-nearline/README.md` | | Nearline-Pull Dead-Man's-Switch | Operator | **S4U-Root-Cause 2026-06-21 behoben + verifiziert:** Task `KalliLab H Drive Nearline Pull` von S4U auf LogonType `Interactive` ("Nur wenn Benutzer angemeldet") umgestellt (kein Passwort noetig, da `michi` Dauer-Konsolen-User) -> per Planer mit `0x0` bestaetigt. Spiegel frisch, Exit-Code-Leak gefixt, Heartbeat-Pings gepusht. **Verbleibt (optional, niedrige Dringlichkeit):** je einen Healthchecks-Check anlegen + Capability-URL hinterlegen (baerchen ENV `HEALTHCHECKS_NEARLINE_URL`/Datei; Unraid `/mnt/user/appdata/secrets/healthchecks_borg_url`) | `ops/h-drive-nearline/README.md` |
| Healthchecks self-hosted (interne Jobs) | Operator | **Live seit 2026-06-23** auf `https://hc.kaleschke.info` (Komodo-Stack-ID `6a3acf2ca7867a4fbab9bfc1`, beide Container healthy, Superuser angelegt). Gitea->Komodo-Webhook seit 2026-06-23 aktiv. Projekt `KalliLab CORE` + ntfy-Integration (`homelab-alerts`). **7 interne Jobs verdrahtet + verifiziert (Status `up`):** `posture-check` (stuendlich), `cert-token-check`/`compose-runtime-drift`/`daily-status-report` (taeglich), `komodo-stack-hygiene` (woechentlich), `renovate`/`gitea-bundle-mirror` (alle 6h) - je endpoint-agnostischer Ping via EXIT-Trap, Capability-URL als Host-Secret `healthchecks_<job>_url`. **Bewusst NICHT self-hosted:** Borg-Pre-Hook + Nearline bleiben healthchecks.io-Cloud (Host-down-Erkennung); guarded Restore-Jobs (Vaultwarden/Gitea/Paperless/Authelia/Immich) sind wegen Shell-Guard nicht je Cron-Trigger monitorbar. **Verbleibt nur noch optional:** die 2 externen Cloud-Checks scharfschalten. | `ops/healthchecks/README.md` | | Healthchecks self-hosted (interne Jobs) | Operator | **Live seit 2026-06-23** auf `https://hc.kaleschke.info` (Komodo-Stack-ID `6a3acf2ca7867a4fbab9bfc1`, beide Container healthy, Superuser angelegt). Gitea->Komodo-Webhook seit 2026-06-23 aktiv. Projekt `KalliLab CORE` + ntfy-Integration (`homelab-alerts`). **7 interne Jobs verdrahtet + verifiziert (Status `up`):** `posture-check` (stuendlich), `cert-token-check`/`compose-runtime-drift`/`daily-status-report` (taeglich), `komodo-stack-hygiene` (woechentlich), `renovate`/`gitea-bundle-mirror` (alle 6h) - je endpoint-agnostischer Ping via EXIT-Trap, Capability-URL als Host-Secret `healthchecks_<job>_url`. **Bewusst NICHT self-hosted:** Borg-Pre-Hook + Nearline bleiben healthchecks.io-Cloud (Host-down-Erkennung); guarded Restore-Jobs (Vaultwarden/Gitea/Paperless/Authelia/Immich) sind wegen Shell-Guard nicht je Cron-Trigger monitorbar. **Verbleibt nur noch optional:** die 2 externen Cloud-Checks scharfschalten. | `ops/healthchecks/README.md` |
+1
View File
@@ -19,6 +19,7 @@ geloescht (Git-Historie ist das Archiv). Verbindliche Doku-Regeln:
| `WORKFLOW.md` | verbindlicher GitOps-/No-Drift-Ablauf | | `WORKFLOW.md` | verbindlicher GitOps-/No-Drift-Ablauf |
| `REPO_MAP.md` | technische Landkarte des Repositories + Doku-Regeln | | `REPO_MAP.md` | technische Landkarte des Repositories + Doku-Regeln |
| `SERVICE_CATALOG.md` | produktiver Service-Katalog | | `SERVICE_CATALOG.md` | produktiver Service-Katalog |
| `HOMELAB_REVIEW_MATRIX.md` | periodische Review-Landkarte fuer Security, Drift, Restore und Privacy |
| `DECISIONS.md` | Entscheidungs-Register (ADR-light) | | `DECISIONS.md` | Entscheidungs-Register (ADR-light) |
| `MASTER_TODO.md` | einzige operative Statusliste | | `MASTER_TODO.md` | einzige operative Statusliste |
+2
View File
@@ -29,6 +29,7 @@ Details gilt immer die betroffene Compose-Datei oder das jeweilige Runbook.
| `HOMELAB_ARCHITECTURE_MASTER_V2.md` | Architektur, Netzmodell, Ausnahmen | | `HOMELAB_ARCHITECTURE_MASTER_V2.md` | Architektur, Netzmodell, Ausnahmen |
| `docs/WORKFLOW.md` | vor operativen Aenderungen | | `docs/WORKFLOW.md` | vor operativen Aenderungen |
| `docs/SERVICE_CATALOG.md` | Service-Zweck, Pfade, Besonderheiten | | `docs/SERVICE_CATALOG.md` | Service-Zweck, Pfade, Besonderheiten |
| `docs/HOMELAB_REVIEW_MATRIX.md` | periodische Review-Landkarte fuer Abdeckung, Messpunkte und offene Review-Follow-ups |
| `docs/DISASTER_RECOVERY.md` | echter Wiederanlauf | | `docs/DISASTER_RECOVERY.md` | echter Wiederanlauf |
| `docs/RESTORE_MATRIX.md` | Restore-Quelle je Dienst | | `docs/RESTORE_MATRIX.md` | Restore-Quelle je Dienst |
| `docs/SECRETS_MAP.md` | Secret-Namen und Pfade ohne Werte | | `docs/SECRETS_MAP.md` | Secret-Namen und Pfade ohne Werte |
@@ -49,6 +50,7 @@ Details gilt immer die betroffene Compose-Datei oder das jeweilige Runbook.
| `services/posture-check/posture-check.sh` | Host-Posture-Check | | `services/posture-check/posture-check.sh` | Host-Posture-Check |
| `services/posture-check/export-prometheus-textfile.sh` | Borg-/Container-/Drift-Metriken | | `services/posture-check/export-prometheus-textfile.sh` | Borg-/Container-/Drift-Metriken |
| `services/authelia-diff.sh` | Authelia ACL Repo-zu-Host-Vergleich | | `services/authelia-diff.sh` | Authelia ACL Repo-zu-Host-Vergleich |
| `services/traefik-dynamic-diff.sh` | Traefik dynamic Repo-zu-Host-Vergleich |
| `ops/h-drive-nearline/pull-critical-backups.ps1` | H:/ Nearline-Pull | | `ops/h-drive-nearline/pull-critical-backups.ps1` | H:/ Nearline-Pull |
## Doku-Regeln ## Doku-Regeln
+5 -5
View File
@@ -43,8 +43,8 @@ Secret-Werte sind nicht enthalten. Es werden nur Secret-Namen, Env-Key-Namen und
| `immich_machine_learning` | Immich ML | `apps/immich/docker-compose.yml` | intern | `immich_default`, `immich_egress` | `model-cache` | rebuildbar | nein | keine Traefik-Route; `immich_egress` (nicht-internal) nur fuer Modell-Download zu huggingface, sonst scheitert Smart Search/Gesichtserkennung an DNS | | `immich_machine_learning` | Immich ML | `apps/immich/docker-compose.yml` | intern | `immich_default`, `immich_egress` | `model-cache` | rebuildbar | nein | keine Traefik-Route; `immich_egress` (nicht-internal) nur fuer Modell-Download zu huggingface, sonst scheitert Smart Search/Gesichtserkennung an DNS |
| `mealie` | Rezeptverwaltung | `apps/mealie/docker-compose.yml` | `https://mealie.kaleschke.info` | `mealie-postgres`, Traefik | `/mnt/user/appdata/mealie/data` | Tier 2, Borg + `mealie.dump` | ja | App + DB in internem Netz getrennt | | `mealie` | Rezeptverwaltung | `apps/mealie/docker-compose.yml` | `https://mealie.kaleschke.info` | `mealie-postgres`, Traefik | `/mnt/user/appdata/mealie/data` | Tier 2, Borg + `mealie.dump` | ja | App + DB in internem Netz getrennt |
| `mealie-postgres` | Mealie-Datenbank | `apps/mealie/docker-compose.yml` | intern | `mealie_internal` | `/mnt/user/appdata/mealie/postgres18`, archivierter Rollback-Altstand `/mnt/user/appdata/_archive/pg18-immich-rollback-volumes-20260602/mealie-postgres17`, `mealie_postgres_password.txt` | Dump `mealie.dump` | nein | interne DB; PostgreSQL 18 | | `mealie-postgres` | Mealie-Datenbank | `apps/mealie/docker-compose.yml` | intern | `mealie_internal` | `/mnt/user/appdata/mealie/postgres18`, archivierter Rollback-Altstand `/mnt/user/appdata/_archive/pg18-immich-rollback-volumes-20260602/mealie-postgres17`, `mealie_postgres_password.txt` | Dump `mealie.dump` | nein | interne DB; PostgreSQL 18 |
| `dawarich_app` | Standort-Historie / Google-Timeline-Ersatz | `apps/dawarich/docker-compose.yml` | `https://dawarich.kaleschke.info` | eigene PostGIS-DB, eigene Redis, Traefik + Authelia, optional Home Assistant Push | `/mnt/user/appdata/dawarich/{postgres17,redis,shared,public,watched,storage}`, `dawarich_*.txt` Secrets | Tier 2, Borg + `dawarich.dump` | ja + Authelia | UI hinter Authelia; API-Key-Tracking-Endpunkte fuer OwnTracks/Overland/Traccar ohne ForwardAuth priorisiert. App und Sidekiq nutzen `freikin/dawarich:1.8.1`; Prometheus-Scrape nach aktueller Dawarich-Doku ueber `dawarich_app:3000/metrics`, Sidekiq-Metriken intern ueber `:9394`. | | `dawarich_app` | Standort-Historie / Google-Timeline-Ersatz | `apps/dawarich/docker-compose.yml` | `https://dawarich.kaleschke.info` | eigene PostGIS-DB, eigene Redis, Traefik + Authelia, optional Home Assistant Push | `/mnt/user/appdata/dawarich/{postgres17,redis,shared,public,watched,storage}`, `dawarich_*.txt` Secrets | Tier 2 (Ziel); **Backup-Scope noch offen** - `dawarich.dump` + Appdata sind weder in `ops/borg-ui/BACKUP_SCOPE.md` noch in der Quellliste/`pre-backup-dumps.sh`; bis dahin Standortdaten **nicht** DR-gesichert (siehe `docs/MASTER_TODO.md`) | ja + Authelia | UI hinter Authelia; API-Key-Tracking-Endpunkte fuer OwnTracks/Overland/Traccar ohne ForwardAuth priorisiert (Auth-Sicht: `docs/AUTH_MATRIX.md`, Ausnahme: Architecture §10). App und Sidekiq nutzen `freikin/dawarich:1.8.1`; Prometheus-Scrape nach aktueller Dawarich-Doku ueber `dawarich_app:3000/metrics`, Sidekiq-Metriken intern ueber `:9394`. |
| `dawarich_db` | Dawarich PostGIS-Datenbank | `apps/dawarich/docker-compose.yml` | intern | `backend_net` | `/mnt/user/appdata/dawarich/postgres17`, `dawarich_postgres_password.txt`, `dawarich_grafana_ro_password.txt` | Dump `dawarich.dump`; raw DB nur bei gleichem PG/PostGIS und sauberem Shutdown | nein | PostGIS 17-3.5 Alpine; Grafana-Read-only-User `dawarich_grafana_ro` per Init-Script | | `dawarich_db` | Dawarich PostGIS-Datenbank | `apps/dawarich/docker-compose.yml` | intern | `backend_net` | `/mnt/user/appdata/dawarich/postgres17`, `dawarich_postgres_password.txt`, `dawarich_grafana_ro_password.txt` | Dump `dawarich.dump` **geplant** (noch nicht in `pre-backup-dumps.sh`); raw DB nur bei gleichem PG/PostGIS und sauberem Shutdown | nein | PostGIS 17-3.5 Alpine; Grafana-Read-only-User `dawarich_grafana_ro` per Init-Script |
| `dawarich_redis` | Dawarich Cache/Queue-Backend | `apps/dawarich/docker-compose.yml` | intern | `backend_net` | `/mnt/user/appdata/dawarich/redis`, `dawarich_redis_password.txt` | Teil von Dawarich-Restore, aber aus DB/Appdaten rekonstruierbar | nein | Redis 8.8 Alpine, keine Host-Ports | | `dawarich_redis` | Dawarich Cache/Queue-Backend | `apps/dawarich/docker-compose.yml` | intern | `backend_net` | `/mnt/user/appdata/dawarich/redis`, `dawarich_redis_password.txt` | Teil von Dawarich-Restore, aber aus DB/Appdaten rekonstruierbar | nein | Redis 8.8 Alpine, keine Host-Ports |
| `mail-archiver` | Mail-Archivierung | `apps/mail-archiver/docker-compose.yml` | `https://mail.kaleschke.info` | PostgreSQL 18, Internet/IMAP, Traefik, Authelia | `/mnt/user/appdata/mailarchiver/data-protection-keys` | Tier 2, `postgresql17-mailarchiver.dump` | ja + Authelia | Hybrid-Dienst: `frontend_net` fuer Internet, `backend_net` fuer DB; App-eigene Auth bleibt zusaetzliche Schutzschicht; Dump-Dateiname behaelt den historischen Cluster-Namen | | `mail-archiver` | Mail-Archivierung | `apps/mail-archiver/docker-compose.yml` | `https://mail.kaleschke.info` | PostgreSQL 18, Internet/IMAP, Traefik, Authelia | `/mnt/user/appdata/mailarchiver/data-protection-keys` | Tier 2, `postgresql17-mailarchiver.dump` | ja + Authelia | Hybrid-Dienst: `frontend_net` fuer Internet, `backend_net` fuer DB; App-eigene Auth bleibt zusaetzliche Schutzschicht; Dump-Dateiname behaelt den historischen Cluster-Namen |
| `nextcloud` | Datei-/Cloud-Dienst | `apps/nextcloud/docker-compose.yml` | `https://cloud.kaleschke.info` | eigene PostgreSQL, eigene Redis, Traefik | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data` | Tier 2, `nextcloud.dump` + Share | ja | native App-Auth ohne zentrale ForwardAuth; WebDAV/CardDAV beachten | | `nextcloud` | Datei-/Cloud-Dienst | `apps/nextcloud/docker-compose.yml` | `https://cloud.kaleschke.info` | eigene PostgreSQL, eigene Redis, Traefik | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data` | Tier 2, `nextcloud.dump` + Share | ja | native App-Auth ohne zentrale ForwardAuth; WebDAV/CardDAV beachten |
@@ -60,7 +60,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 | | Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs |
|---|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|---|
| `glance` | einziges Homelab-Uebersichts-/Status-Dashboard | `ops/glance/docker-compose.yml`, `ops/glance/config/glance.yml` | `https://glance.kaleschke.info` | Traefik + Authelia, interne HTTP-Checks, Immich API, AdGuard API, Speedtest API, interner Docker-Socket-Proxy | Repo-Konfiguration; keine kritische Persistenz | Tier 3, rebuildbar | ja + Authelia | Zeigt aktive Dienste, Immich-Community-Widget, Internet-/DNS-/VPN-Monitore, AdGuard-DNS-Stats, Speedtest-Livewerte, Docker-Containergruppen, Server-Stats, Zeitfortschritt, Bookmarks und eine Infrastruktur-Seite; Docker-API nur ueber `glance-docker-socket-proxy` auf internem Netz | | `glance` | einziges Homelab-Uebersichts-/Status-Dashboard | `ops/glance/docker-compose.yml`, `ops/glance/config/glance.yml` | `https://glance.kaleschke.info` | Traefik + Authelia, interne HTTP-Checks, Immich API, AdGuard API, Speedtest API, interner Docker-Socket-Proxy | Repo-Konfiguration; keine kritische Persistenz | Tier 3, rebuildbar | ja + Authelia | Zeigt aktive Dienste, Immich-Community-Widget, Internet-/DNS-/VPN-Monitore, AdGuard-DNS-Stats, Speedtest-Livewerte, Docker-Containergruppen, Server-Stats, Zeitfortschritt, Bookmarks und eine Infrastruktur-Seite; Docker-API nur ueber `glance-docker-socket-proxy` auf internem Netz |
| `komodo-core` | GitOps UI/API/Stack-Manager | `ops/komodo/docker-compose.yml` | `https://komodo.kaleschke.info` | Mongo, Gitea, Traefik | `/mnt/user/appdata/komodo/core`, `komodo_keys` | Tier 1 | ja, native Auth | keine pauschale Authelia-ForwardAuth; Gitea DNS override | | `komodo-core` | GitOps UI/API/Stack-Manager | `ops/komodo/docker-compose.yml` | `https://komodo.kaleschke.info` | Mongo, Gitea, Traefik | `/mnt/user/appdata/komodo/core`, `komodo_keys` | Tier 1 | ja, native Auth | keine pauschale Authelia-ForwardAuth; Gitea DNS override; **Self-Stack ist inline in Komodo verwaltet** (`repo=""`, `webhook_enabled=false`, kein GitOps-Push), `ops/komodo/docker-compose.yml` ist nur Spiegel/Recovery-Anker - Repo-Aenderungen wirken nicht automatisch live (DECISIONS 2026-05-04 / 2026-06-23); IP-Allowlist statt public |
| `komodo-mongo` | Komodo Datenbank | `ops/komodo/docker-compose.yml` | intern | `komodo_net` | `/mnt/user/appdata/komodo/mongo`, `komodo_mongo_password.txt` | Tier 1, `komodo-mongo.archive.gz` | nein | Dump am 2026-05-04 bestaetigt; nach Major-Upgrades pruefen | | `komodo-mongo` | Komodo Datenbank | `ops/komodo/docker-compose.yml` | intern | `komodo_net` | `/mnt/user/appdata/komodo/mongo`, `komodo_mongo_password.txt` | Tier 1, `komodo-mongo.archive.gz` | nein | Dump am 2026-05-04 bestaetigt; nach Major-Upgrades pruefen |
| `komodo-periphery` | Komodo Host-Agent | `ops/komodo/docker-compose.yml` | intern Core -> Periphery | Docker socket, `/mnt/user/services`, `frontend_net`, `komodo_net` | `/mnt/user/appdata/komodo/periphery`, `komodo_keys` | Tier 1 | nein | Docker-Socket-Ausnahme; `/mnt/user/services` Mount fuer Stack-Workspaces | | `komodo-periphery` | Komodo Host-Agent | `ops/komodo/docker-compose.yml` | intern Core -> Periphery | Docker socket, `/mnt/user/services`, `frontend_net`, `komodo_net` | `/mnt/user/appdata/komodo/periphery`, `komodo_keys` | Tier 1 | nein | Docker-Socket-Ausnahme; `/mnt/user/services` Mount fuer Stack-Workspaces |
| `borg-ui` | Borg Backup-/Restore UI | `ops/borg-ui/docker-compose.yml` | `https://borg.kaleschke.info` | Traefik + Authelia, Borg repo credentials | `/mnt/user/appdata/borg-ui/data`, `/mnt/user/backups/borg/dumps`, Restore-Ziel | Tier 3 / Backup kritisch, `borg-ui.sqlite` | ja + Authelia | breite Mounts bewusst; `/local/secrets` im DR-Scope; Nextcloud-Daten werden read-only nach `/local/nextcloud/data` eingebunden | | `borg-ui` | Borg Backup-/Restore UI | `ops/borg-ui/docker-compose.yml` | `https://borg.kaleschke.info` | Traefik + Authelia, Borg repo credentials | `/mnt/user/appdata/borg-ui/data`, `/mnt/user/backups/borg/dumps`, Restore-Ziel | Tier 3 / Backup kritisch, `borg-ui.sqlite` | ja + Authelia | breite Mounts bewusst; `/local/secrets` im DR-Scope; Nextcloud-Daten werden read-only nach `/local/nextcloud/data` eingebunden |
@@ -82,7 +82,7 @@ Secret-Werte sind nicht enthalten. Es werden nur Secret-Namen, Env-Key-Namen und
| `hermes-gateway` | Hermes Agent Gateway/API intern | `ops/hermes-agent/docker-compose.yml` | intern `8642` auf `hermes_net` | SSH Runner (VM 192.168.178.143), LLM Provider, optional Home Assistant | `/mnt/user/appdata/hermes-agent/data`, SSH key path | Tier 3, Borg/Share | nein | NAS-Stack bleibt deaktiviert, solange die separate Hermes-VM/Runner-Seite nicht wiederhergestellt ist; kein Docker-Socket | | `hermes-gateway` | Hermes Agent Gateway/API intern | `ops/hermes-agent/docker-compose.yml` | intern `8642` auf `hermes_net` | SSH Runner (VM 192.168.178.143), LLM Provider, optional Home Assistant | `/mnt/user/appdata/hermes-agent/data`, SSH key path | Tier 3, Borg/Share | nein | NAS-Stack bleibt deaktiviert, solange die separate Hermes-VM/Runner-Seite nicht wiederhergestellt ist; kein Docker-Socket |
| `hermes-dashboard` | Hermes Dashboard | `ops/hermes-agent/docker-compose.yml` | `https://hermes.kaleschke.info` via `${HERMES_DASHBOARD_HOST}` | `hermes-gateway`, Traefik + Authelia | shared read-only data mount | Tier 3, Borg/Share | ja + Authelia | Compose-Profil `dashboard`; aktuell VM-seitig offen, nicht Teil des NAS-Finalstarts | | `hermes-dashboard` | Hermes Dashboard | `ops/hermes-agent/docker-compose.yml` | `https://hermes.kaleschke.info` via `${HERMES_DASHBOARD_HOST}` | `hermes-gateway`, Traefik + Authelia | shared read-only data mount | Tier 3, Borg/Share | ja + Authelia | Compose-Profil `dashboard`; aktuell VM-seitig offen, nicht Teil des NAS-Finalstarts |
| `n8n` | Workflow-Automation; aktuell genutzt fuer Mail->LLM->Gitea-Issue (Inbox `Micha/mails`) | `apps/n8n/docker-compose.yml`, `apps/n8n/workflows/*.json` | `https://n8n.kaleschke.info` | Traefik (ohne pauschale Authelia, analog Komodo/Nextcloud), GMX IMAP, OpenAI API, Gitea API | `/mnt/user/appdata/n8n/data` (SQLite, Credentials, Workflows) | Tier 2, Borg + `n8n-data` (Credentials sind nur mit `N8N_ENCRYPTION_KEY` entschluesselbar) | ja, native Auth | Wegen Webhook-Endpunkten (`/webhook/*`) bewusst ohne `authelia@file`; eigene Login-/Owner-Auth bleibt Pflicht; `N8N_ENCRYPTION_KEY` ist Stack-ENV-Pflichtsecret, Verlust macht Credentials unbrauchbar. | | `n8n` | Workflow-Automation; aktuell genutzt fuer Mail->LLM->Gitea-Issue (Inbox `Micha/mails`) | `apps/n8n/docker-compose.yml`, `apps/n8n/workflows/*.json` | `https://n8n.kaleschke.info` | Traefik (ohne pauschale Authelia, analog Komodo/Nextcloud), GMX IMAP, OpenAI API, Gitea API | `/mnt/user/appdata/n8n/data` (SQLite, Credentials, Workflows) | Tier 2, Borg + `n8n-data` (Credentials sind nur mit `N8N_ENCRYPTION_KEY` entschluesselbar) | ja, native Auth | Wegen Webhook-Endpunkten (`/webhook/*`) bewusst ohne `authelia@file`; eigene Login-/Owner-Auth bleibt Pflicht; `N8N_ENCRYPTION_KEY` ist Stack-ENV-Pflichtsecret, Verlust macht Credentials unbrauchbar. |
| `healthchecks` | Self-hosted Cron-/Job-Heartbeat-Monitor (Dead-Man's-Switch) fuer interne Jobs/Scripte | `ops/healthchecks/docker-compose.yml`, `ops/healthchecks/README.md` | `https://hc.kaleschke.info` | Traefik (native Auth, ohne pauschale Authelia), `healthchecks-postgres`, ntfy | keine kritische App-Persistenz (Check-Metadaten in der DB) | Tier 3, rebuildbar | ja, native Auth | Hub fuer INTERNE Checks; die externen Host-down-/Backup-Waechter (Borg-Pre-Hook, Nearline-Pull, Monitoring-Watchdog #8) bleiben bewusst auf healthchecks.io-Cloud (ein On-Host-Waechter kann Host-Down nicht melden). Ping-/API-Endpunkte ohne ForwardAuth (analog n8n). Stack-ENV: `HEALTHCHECKS_SECRET_KEY`, `HEALTHCHECKS_DB_PASSWORD`, `HEALTHCHECKS_SUPERUSER_EMAIL/PASSWORD`. **Live seit 2026-06-23** (Komodo-Stack-ID `6a3acf2ca7867a4fbab9bfc1`, deployt via API; Superuser angelegt). Offen: Gitea->Komodo-Webhook noch manuell anzulegen | | `healthchecks` | Self-hosted Cron-/Job-Heartbeat-Monitor (Dead-Man's-Switch) fuer interne Jobs/Scripte | `ops/healthchecks/docker-compose.yml`, `ops/healthchecks/README.md` | `https://hc.kaleschke.info` | Traefik (native Auth, ohne pauschale Authelia), `healthchecks-postgres`, ntfy | keine kritische App-Persistenz (Check-Metadaten in der DB) | Tier 3, rebuildbar | ja, native Auth | Hub fuer INTERNE Checks; die externen Host-down-/Backup-Waechter (Borg-Pre-Hook, Nearline-Pull, Monitoring-Watchdog #8) bleiben bewusst auf healthchecks.io-Cloud (ein On-Host-Waechter kann Host-Down nicht melden). Ping-/API-Endpunkte ohne ForwardAuth (analog n8n). Stack-ENV: `HEALTHCHECKS_SECRET_KEY`, `HEALTHCHECKS_DB_PASSWORD`, `HEALTHCHECKS_SUPERUSER_EMAIL/PASSWORD`. **Live seit 2026-06-23** (Komodo-Stack-ID `6a3acf2ca7867a4fbab9bfc1`, deployt via API; Superuser angelegt). Gitea->Komodo-Webhook seit 2026-06-23 aktiv (Status: `docs/MASTER_TODO.md`) |
| `healthchecks-postgres` | Healthchecks-Datenbank | `ops/healthchecks/docker-compose.yml` | intern | `healthchecks_internal` | `/mnt/user/appdata/healthchecks/postgres18`, `healthchecks_postgres_password.txt` | Check-Metadaten, rebuildbar | nein | interne DB; PostgreSQL 18; nie `frontend_net` | | `healthchecks-postgres` | Healthchecks-Datenbank | `ops/healthchecks/docker-compose.yml` | intern | `healthchecks_internal` | `/mnt/user/appdata/healthchecks/postgres18`, `healthchecks_postgres_password.txt` | Check-Metadaten, rebuildbar | nein | interne DB; PostgreSQL 18; nie `frontend_net` |
## Smart Home ## Smart Home
@@ -96,7 +96,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 | | Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs |
|---|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|---|
| `posture-check` | Host-Posture-Audit fuer Filesystem, Mover-Drift, NVMe-SMART, Fuellstand und Authelia-Repo<->Host-Drift | `services/posture-check/posture-check.sh` | Unraid User-Script / Cron / Borg Pre-Hook | `findmnt`, `df`, `nvme`, optional `curl` fuer ntfy; ruft `services/authelia-diff.sh` fuer `authelia_config_drift` auf | `/mnt/user/services/posture-check/last.json` | Repo-Skript + letzter JSON-Status | nein | Muss auf dem Unraid-Host bei Boot, stuendlich und vor Borg laufen; Disk1-NTFS ist nach Disk1 Phase 2 nicht mehr erlaubt (`ALLOW_DISK1_NTFS=0` Standard); Warning/Critical alarmieren via ntfy nur bei neuer Ursache oder nach `ALERT_REPEAT_SECONDS`. Authelia-Drift-Check braucht einen Repo-Spiegel unter `/mnt/user/services/homelab-infra/` (siehe `docs/WORKFLOW.md` Sektion "Ausnahme: Authelia configuration.yml") | | `posture-check` | Host-Posture-Audit fuer Filesystem, Mover-Drift, NVMe-SMART, Fuellstand, Authelia-Repo<->Host-Drift und Traefik-dynamic-Repo<->Host-Drift | `services/posture-check/posture-check.sh` | Unraid User-Script / Cron / Borg Pre-Hook | `findmnt`, `df`, `nvme`, `rsync`, optional `curl` fuer ntfy; ruft `services/authelia-diff.sh` fuer `authelia_config_drift` und `services/traefik-dynamic-diff.sh` fuer `traefik_dynamic_drift` auf | `/mnt/user/services/posture-check/last.json` | Repo-Skript + letzter JSON-Status | nein | Muss auf dem Unraid-Host bei Boot, stuendlich und vor Borg laufen; Disk1-NTFS ist nach Disk1 Phase 2 nicht mehr erlaubt (`ALLOW_DISK1_NTFS=0` Standard); Warning/Critical alarmieren via ntfy nur bei neuer Ursache oder nach `ALERT_REPEAT_SECONDS`. Authelia- und Traefik-dynamic-Drift-Checks brauchen einen Repo-Spiegel unter `/mnt/user/services/homelab-infra/` |
| `docker-critical-events` | Live-Alarmierung fuer Docker `die`/`oom`/`kill` Events | `services/posture-check/docker-critical-events.sh`, Supervisor `services/posture-check/docker-critical-events-supervisor.sh` | Unraid User-Script / Hintergrundprozess | Docker CLI, ntfy | `/mnt/user/services/posture-check/docker-critical-events-last.log`, PID/Outfile unter `/mnt/user/services/posture-check/` | Repo-Skript + letzter Event-Log | nein | Optional als Unraid User-Script `at array start` starten; Supervisor kann `start`, `stop`, `status`, `smoke`; sendet nach `homelab-alerts` | | `docker-critical-events` | Live-Alarmierung fuer Docker `die`/`oom`/`kill` Events | `services/posture-check/docker-critical-events.sh`, Supervisor `services/posture-check/docker-critical-events-supervisor.sh` | Unraid User-Script / Hintergrundprozess | Docker CLI, ntfy | `/mnt/user/services/posture-check/docker-critical-events-last.log`, PID/Outfile unter `/mnt/user/services/posture-check/` | Repo-Skript + letzter Event-Log | nein | Optional als Unraid User-Script `at array start` starten; Supervisor kann `start`, `stop`, `status`, `smoke`; sendet nach `homelab-alerts` |
## Backup- und Restore-Hinweise ## Backup- und Restore-Hinweise
+7 -1
View File
@@ -262,6 +262,7 @@ Diese Ausnahme bleibt bewusst bestehen. Der File-Provider wird weiterhin nur fue
| `traefik/dynamic/middlewares.yml` | `/mnt/user/appdata/traefik/dynamic/middlewares.yml` | | `traefik/dynamic/middlewares.yml` | `/mnt/user/appdata/traefik/dynamic/middlewares.yml` |
| `traefik/dynamic/tls.yml` | `/mnt/user/appdata/traefik/dynamic/tls.yml` | | `traefik/dynamic/tls.yml` | `/mnt/user/appdata/traefik/dynamic/tls.yml` |
| `traefik/dynamic/dashboards.yml` | `/mnt/user/appdata/traefik/dynamic/dashboards.yml` | | `traefik/dynamic/dashboards.yml` | `/mnt/user/appdata/traefik/dynamic/dashboards.yml` |
| `traefik/dynamic/plex.yml` | `/mnt/user/appdata/traefik/dynamic/plex.yml` |
### Pflicht-Workflow bei Aenderungen an Traefik Dynamic Config ### Pflicht-Workflow bei Aenderungen an Traefik Dynamic Config
@@ -269,10 +270,15 @@ Diese Ausnahme bleibt bewusst bestehen. Der File-Provider wird weiterhin nur fue
2. Commit + Push 2. Commit + Push
3. Datei manuell auf den Host kopieren 3. Datei manuell auf den Host kopieren
4. Traefik laedt dynamic config automatisch neu 4. Traefik laedt dynamic config automatisch neu
5. Aenderung testen 5. `services/traefik-dynamic-diff.sh` muss `exit 0` liefern
6. Aenderung testen
> **Merksatz:** Git-Commit allein reicht hier nicht. Ohne den manuellen Kopier-Schritt wirkt die Aenderung nicht. > **Merksatz:** Git-Commit allein reicht hier nicht. Ohne den manuellen Kopier-Schritt wirkt die Aenderung nicht.
Der Posture-Check (`services/posture-check/posture-check.sh`) ruft
`services/traefik-dynamic-diff.sh` als Check `traefik_dynamic_drift` auf und
meldet Repo-zu-Host-Drift als Warning via ntfy.
--- ---
## Ausnahme: Authelia configuration.yml ## Ausnahme: Authelia configuration.yml
+17
View File
@@ -79,6 +79,23 @@ Filebrowser serviert `/mnt/user/projekte`, `/mnt/user/documents` und `/mnt/user/
- **`/mnt/user/projekte`** ist aktuell in **keinem** Borg-Scope. Ad-hoc-Dateien, die direkt unter `documents/` oder `photos/` (ausserhalb der genannten App-Ordner) abgelegt werden, ebenfalls nicht. - **`/mnt/user/projekte`** ist aktuell in **keinem** Borg-Scope. Ad-hoc-Dateien, die direkt unter `documents/` oder `photos/` (ausserhalb der genannten App-Ordner) abgelegt werden, ebenfalls nicht.
- Entscheidung Operator offen (Eintrag in `docs/MASTER_TODO.md`): Entweder `projekte` als eigenen read-only Borg-UI-Mount + Quelllisten-Eintrag aufnehmen, oder bewusst als "nur lokal, nicht DR-relevant" bestaetigen. Bis zur Entscheidung gilt: dort liegende Originaldaten sind **nicht** wiederherstellbar. - Entscheidung Operator offen (Eintrag in `docs/MASTER_TODO.md`): Entweder `projekte` als eigenen read-only Borg-UI-Mount + Quelllisten-Eintrag aufnehmen, oder bewusst als "nur lokal, nicht DR-relevant" bestaetigen. Bis zur Entscheidung gilt: dort liegende Originaldaten sind **nicht** wiederherstellbar.
### Dawarich (Standortdaten) - noch nicht im Scope
Dawarich (`apps/dawarich/docker-compose.yml`) speichert hochsensible
Standort-Historie in einer eigenen PostGIS-DB unter
`/mnt/user/appdata/dawarich/{postgres17,redis,shared,public,watched,storage}`.
`docs/SERVICE_CATALOG.md` nennt als Ziel "Tier 2, Borg + `dawarich.dump`", aber:
- Es gibt **keinen** `dawarich`-Eintrag in der Service Inventory oben.
- `pre-backup-dumps.sh` erzeugt **kein** `dawarich.dump` (PostGIS-`pg_dump`).
- Die Quellliste (`all-important-sources.txt`) enthaelt **keinen**
`/local/appdata/dawarich`-Pfad, und Borg-UI mountet ihn nicht.
Konsequenz: Standortdaten sind aktuell **nicht** DR-gesichert. Schliessen
(Operator + Host): `dawarich.dump` in `pre-backup-dumps.sh` (PostGIS), Pfad in
Quellliste + Borg-UI-Mount + diese Tabelle aufnehmen, danach `RESTORE_MATRIX.md`
Tier-2-Zeile + Reifegrad ergaenzen. Tracking: `docs/MASTER_TODO.md`.
### Komodo keys ### Komodo keys
Production still stores Komodo Core/Periphery keys in the Docker named volume `komodo_komodo_keys`. This is a known open migration item and is not fixed by the Borg source list alone. Target state: move the keys to a host path such as `/mnt/user/appdata/komodo/keys` and mount that path into both Komodo containers, then include it in Borg. Do not treat this as solved until the live Compose stack has been migrated and Periphery reconnect has been verified. Production still stores Komodo Core/Periphery keys in the Docker named volume `komodo_komodo_keys`. This is a known open migration item and is not fixed by the Borg source list alone. Target state: move the keys to a host path such as `/mnt/user/appdata/komodo/keys` and mount that path into both Komodo containers, then include it in Borg. Do not treat this as solved until the live Compose stack has been migrated and Periphery reconnect has been verified.
+4 -1
View File
@@ -1,4 +1,4 @@
Typ: Runbook · Stand: 2026-06-23 · Status: live (Komodo-Stack-ID `6a3acf2ca7867a4fbab9bfc1`); offen nur der Gitea->Komodo-Webhook Typ: Runbook · Stand: 2026-06-23 · Status: live (Komodo-Stack-ID `6a3acf2ca7867a4fbab9bfc1`); Gitea->Komodo-Webhook seit 2026-06-23 aktiv (Status: `docs/MASTER_TODO.md`)
# Healthchecks (self-hosted) — Cron-/Job-Heartbeat-Monitor # Healthchecks (self-hosted) — Cron-/Job-Heartbeat-Monitor
@@ -72,6 +72,9 @@ Datei-Secret vor; beide Werte muessen identisch sein.
## Deploy + Pflicht-Webhook ## Deploy + Pflicht-Webhook
> Status 2026-06-23: Deploy erfolgt + Gitea->Komodo-Webhook aktiv. Diese Schritte
> bleiben als Referenz / Rebuild-Anleitung. Laufender Status: `docs/MASTER_TODO.md`.
1. Stack in Komodo aus Gitea `Micha/homelab-infra` anlegen, `webhook_enabled` an. 1. Stack in Komodo aus Gitea `Micha/homelab-infra` anlegen, `webhook_enabled` an.
2. Gitea-Webhook auf die neue Stack-ID anlegen 2. Gitea-Webhook auf die neue Stack-ID anlegen
(`http://komodo-core:9120/listener/github/stack/<stack-id>/deploy`), (`http://komodo-core:9120/listener/github/stack/<stack-id>/deploy`),
+43 -1
View File
@@ -11,7 +11,9 @@ ALLOW_DISK1_NTFS="${ALLOW_DISK1_NTFS:-0}"
ALERT_STATE_PATH="${ALERT_STATE_PATH:-/mnt/user/services/posture-check/last-alert.state}" ALERT_STATE_PATH="${ALERT_STATE_PATH:-/mnt/user/services/posture-check/last-alert.state}"
ALERT_REPEAT_SECONDS="${ALERT_REPEAT_SECONDS:-86400}" ALERT_REPEAT_SECONDS="${ALERT_REPEAT_SECONDS:-86400}"
SKIP_AUTHELIA_DRIFT="${SKIP_AUTHELIA_DRIFT:-0}" SKIP_AUTHELIA_DRIFT="${SKIP_AUTHELIA_DRIFT:-0}"
SKIP_TRAEFIK_DYNAMIC_DRIFT="${SKIP_TRAEFIK_DYNAMIC_DRIFT:-0}"
AUTHELIA_DIFF_SCRIPT="${AUTHELIA_DIFF_SCRIPT:-/mnt/user/services/homelab-infra/services/authelia-diff.sh}" AUTHELIA_DIFF_SCRIPT="${AUTHELIA_DIFF_SCRIPT:-/mnt/user/services/homelab-infra/services/authelia-diff.sh}"
TRAEFIK_DYNAMIC_DIFF_SCRIPT="${TRAEFIK_DYNAMIC_DIFF_SCRIPT:-/mnt/user/services/homelab-infra/services/traefik-dynamic-diff.sh}"
mkdir -p "$TMP_DIR" mkdir -p "$TMP_DIR"
RESULTS_FILE="$TMP_DIR/results.$$" RESULTS_FILE="$TMP_DIR/results.$$"
@@ -232,10 +234,12 @@ check_authelia_config_drift() {
return return
fi fi
local output local output=""
local rc local rc
set +e
output="$(bash "$AUTHELIA_DIFF_SCRIPT" 2>&1)" output="$(bash "$AUTHELIA_DIFF_SCRIPT" 2>&1)"
rc=$? rc=$?
set -e
case "$rc" in case "$rc" in
0) 0)
@@ -256,6 +260,43 @@ check_authelia_config_drift() {
esac esac
} }
check_traefik_dynamic_drift() {
if [ "$SKIP_TRAEFIK_DYNAMIC_DRIFT" = "1" ]; then
add_result "ok" "traefik_dynamic_drift" "Traefik dynamic drift check skipped via SKIP_TRAEFIK_DYNAMIC_DRIFT=1"
return
fi
if [ ! -x "$TRAEFIK_DYNAMIC_DIFF_SCRIPT" ] && [ ! -f "$TRAEFIK_DYNAMIC_DIFF_SCRIPT" ]; then
add_result "warning" "traefik_dynamic_drift" "Traefik dynamic diff script missing: $TRAEFIK_DYNAMIC_DIFF_SCRIPT"
return
fi
local output=""
local rc
set +e
output="$(bash "$TRAEFIK_DYNAMIC_DIFF_SCRIPT" 2>&1)"
rc=$?
set -e
case "$rc" in
0)
add_result "ok" "traefik_dynamic_drift" "Traefik dynamic repo baseline matches host directory"
;;
1)
add_result "warning" "traefik_dynamic_drift" "Traefik dynamic repo<->host drift; run traefik-dynamic-diff.sh for details"
;;
2)
add_result "warning" "traefik_dynamic_drift" "Traefik dynamic diff aborted: $output"
;;
4)
add_result "warning" "traefik_dynamic_drift" "Traefik dynamic diff missing tool: $output"
;;
*)
add_result "warning" "traefik_dynamic_drift" "Traefik dynamic diff returned unexpected rc=$rc: $output"
;;
esac
}
send_ntfy() { send_ntfy() {
local severity="$1" local severity="$1"
local topic="$2" local topic="$2"
@@ -426,6 +467,7 @@ main() {
check_nvme_smart check_nvme_smart
check_authelia_config_drift check_authelia_config_drift
check_traefik_dynamic_drift
write_json write_json
} }
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# Vergleicht die Traefik dynamic File-Provider-Dateien aus dem Repo-Spiegel
# gegen die produktive Host-Dateiablage. Der Check ist read-only:
# rsync laeuft mit --dry-run und schreibt nichts.
#
# Aufruf-Defaults siehe Variablen unten. Aufruf typischerweise:
# bash services/traefik-dynamic-diff.sh
#
# Exit-Codes:
# 0 Repo und Host sind identisch
# 1 Drift festgestellt (rsync itemized output auf stdout)
# 2 Repo- oder Host-Verzeichnis fehlt
# 4 internes Werkzeug fehlt (rsync)
set -uo pipefail
TRAEFIK_DYNAMIC_REPO_DIR="${TRAEFIK_DYNAMIC_REPO_DIR:-/mnt/user/services/homelab-infra/traefik/dynamic}"
TRAEFIK_DYNAMIC_HOST_DIR="${TRAEFIK_DYNAMIC_HOST_DIR:-/mnt/user/appdata/traefik/dynamic}"
if ! command -v rsync >/dev/null 2>&1; then
echo "traefik-dynamic-diff: missing required command 'rsync'" >&2
exit 4
fi
if [ ! -d "$TRAEFIK_DYNAMIC_REPO_DIR" ]; then
echo "traefik-dynamic-diff: repo directory not found: $TRAEFIK_DYNAMIC_REPO_DIR" >&2
exit 2
fi
if [ ! -d "$TRAEFIK_DYNAMIC_HOST_DIR" ]; then
echo "traefik-dynamic-diff: host directory not found: $TRAEFIK_DYNAMIC_HOST_DIR" >&2
exit 2
fi
diff_output="$(rsync -rni --checksum --delete \
"$TRAEFIK_DYNAMIC_REPO_DIR"/ \
"$TRAEFIK_DYNAMIC_HOST_DIR"/)"
if [ -n "$diff_output" ]; then
printf '%s\n' "$diff_output"
exit 1
fi
exit 0