Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f77a69a0b2 | |||
| f73cf48e41 | |||
| eea2697ca1 | |||
| a3d77d7529 | |||
| 02a50e1a58 | |||
| 267e76059a | |||
| 9d4fee02ca | |||
| 24ebcaa3c7 | |||
| 45bae13aa0 | |||
| ff5991cec8 | |||
| 5b6e7b8b66 | |||
| 5cb401797d | |||
| 1d0cba92bd | |||
| 9353a9fc44 | |||
| d50b11784d | |||
| 09eeac51e1 | |||
| 565940b9ef | |||
| b6bbca43ad | |||
| 388e57e385 | |||
| 0c2bb8484a | |||
| a7797fd02e | |||
| bac927bbcc | |||
| add8b71ea9 | |||
| e21e89e51b | |||
| 4e4684b616 | |||
| 84030956ac | |||
| 17fe8073bb | |||
| 9f32ba72c1 | |||
| e9a7f79025 | |||
| 43727151df | |||
| 66ee10cb55 | |||
| ab68900216 | |||
| 8f56c6edcd | |||
| 8e400fb3c3 | |||
| cd650b19ac | |||
| af231dd4e8 | |||
| 428223d2e4 |
@@ -27,3 +27,4 @@
|
|||||||
Thumbs.db
|
Thumbs.db
|
||||||
*.tmp
|
*.tmp
|
||||||
*.log
|
*.log
|
||||||
|
.serena/
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ Standard-Workflow:
|
|||||||
7. Komodo-Deploy/Runtime pruefen
|
7. Komodo-Deploy/Runtime pruefen
|
||||||
8. Dokumentation nachziehen
|
8. Dokumentation nachziehen
|
||||||
|
|
||||||
|
Neue produktive Komodo-Stacks aus `Micha/homelab-infra` brauchen verpflichtend einen aktiven Gitea->Komodo-Webhook auf die aktuelle Stack-ID. Ausnahmen muessen im selben Aenderungsblock dokumentiert werden.
|
||||||
|
|
||||||
Wenn Drift vermutet wird, nicht raten. Erst die Pflichtmatrix in `docs/GITOPS_DRIFT_RUNBOOK.md` abarbeiten.
|
Wenn Drift vermutet wird, nicht raten. Erst die Pflichtmatrix in `docs/GITOPS_DRIFT_RUNBOOK.md` abarbeiten.
|
||||||
|
|
||||||
## Sicherheitsregeln
|
## Sicherheitsregeln
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
> **Single Source of Truth** für Docker-Netzwerkarchitektur, Sicherheitsregeln, Zielbild und Migration des Kallilabcore-Homelabs.
|
> **Single Source of Truth** für Docker-Netzwerkarchitektur, Sicherheitsregeln, Zielbild und Migration des Kallilabcore-Homelabs.
|
||||||
> **Arbeitsregel für KI-Assistenten:** Dieses Dokument immer zuerst lesen, bevor Fragen zu Containern, Netzwerken, Traefik, Tailscale, Migration oder Security beantwortet werden.
|
> **Arbeitsregel für KI-Assistenten:** Dieses Dokument immer zuerst lesen, bevor Fragen zu Containern, Netzwerken, Traefik, Tailscale, Migration oder Security beantwortet werden.
|
||||||
|
|
||||||
**Stand:** 2026-05-16 | **Aktueller Schwerpunkt:** GitOps / Doku-Synchronisierung / Reproduzierbare Deployments
|
**Stand:** 2026-05-23 | **Aktueller Schwerpunkt:** GitOps / Doku-Synchronisierung / Reproduzierbare Deployments
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
## 2. Architektur-Prinzipien
|
## 2. Architektur-Prinzipien
|
||||||
|
|
||||||
### P1 — Traefik ist der einzige öffentliche HTTP(S)-Einstiegspunkt
|
### 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), `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
|
### P2 — Das Setup bleibt bewusst einfach: `frontend_net` + `backend_net` + app-interne Netze
|
||||||
- `frontend_net` = Proxy-/Web-Netz
|
- `frontend_net` = Proxy-/Web-Netz
|
||||||
@@ -60,7 +60,7 @@ Es gibt **keine künstlichen globalen Zusatznetze** wie `admin_net` oder `media_
|
|||||||
Postgres, Redis und ähnliche Dienste laufen ausschließlich in `backend_net` oder einem eigenen internen Compose-Netz.
|
Postgres, Redis und ähnliche Dienste laufen ausschließlich in `backend_net` oder einem eigenen internen Compose-Netz.
|
||||||
|
|
||||||
### P4 — Admin-UIs sind nicht öffentlich
|
### P4 — Admin-UIs sind nicht öffentlich
|
||||||
filebrowser, scrutiny, UptimeKuma, code-server, Traefik-Dashboard und borg-ui sind standardmaessig **Tailscale-only** oder hinter Traefik **mit zentraler Middleware** abgesichert. `Komodo` ist die dokumentierte Ausnahme und bleibt bewusst bei nativer Authentifizierung ohne pauschal vorgeschaltete ForwardAuth-Middleware.
|
filebrowser, scrutiny, code-server, Traefik-Dashboard und borg-ui sind standardmaessig **Tailscale-only** oder hinter Traefik **mit zentraler Middleware** abgesichert. `Komodo` ist die dokumentierte Ausnahme und bleibt bewusst bei nativer Authentifizierung ohne pauschal vorgeschaltete ForwardAuth-Middleware.
|
||||||
|
|
||||||
### P5 — Compose-first
|
### P5 — Compose-first
|
||||||
Alle produktiven Container werden als Compose verwaltet. Bestehende Dockerman-/Ad-hoc-Container werden schrittweise migriert.
|
Alle produktiven Container werden als Compose verwaltet. Bestehende Dockerman-/Ad-hoc-Container werden schrittweise migriert.
|
||||||
@@ -92,9 +92,7 @@ Jeder produktive Container nutzt `restart: unless-stopped`, außer eine Ausnahme
|
|||||||
| `nextcloud_internal` | bridge, `internal: true` | internes Netz nur fuer `nextcloud` + `nextcloud-postgres` + `nextcloud-redis` | ✅ vorbereitet |
|
| `nextcloud_internal` | bridge, `internal: true` | internes Netz nur fuer `nextcloud` + `nextcloud-postgres` + `nextcloud-redis` | ✅ vorbereitet |
|
||||||
| `monitoring_net` | Compose-intern, bridge | zentraler Observability-Stack fuer Prometheus, Loki, Grafana, Promtail, Exporter und InfluxDB | Zielzustand |
|
| `monitoring_net` | Compose-intern, bridge | zentraler Observability-Stack fuer Prometheus, Loki, Grafana, Promtail, Exporter und InfluxDB | Zielzustand |
|
||||||
| `monitoring_influx_lan` | Compose-intern, bridge | nicht-oeffentliches Zusatznetz nur fuer Docker Host-Port-Publishing von InfluxDB 8181 | Zielzustand |
|
| `monitoring_influx_lan` | Compose-intern, bridge | nicht-oeffentliches Zusatznetz nur fuer Docker Host-Port-Publishing von InfluxDB 8181 | Zielzustand |
|
||||||
| `glance_socket_net` | Compose-intern, `internal: true` | interner Zugriff von Glance auf den Docker-Socket-Proxy | vorbereitet |
|
| `glance_socket_net` | Compose-intern, `internal: true` | interner Zugriff von Glance auf den Docker-Socket-Proxy | umgesetzt |
|
||||||
| `grafana_influx_internal` | Compose-intern, `internal: true` | alte Grafana-zu-InfluxDB-Kommunikation | abgeloester Altstand |
|
|
||||||
| `grafana_influx_lan` | Compose-intern, bridge | altes Docker Host-Port-Publishing von InfluxDB 8181 | abgeloester Altstand |
|
|
||||||
| `host` | host | nur für echte Sonderfälle | begründet |
|
| `host` | host | nur für echte Sonderfälle | begründet |
|
||||||
|
|
||||||
### 3.2 Finales Diagramm (vereinfacht)
|
### 3.2 Finales Diagramm (vereinfacht)
|
||||||
@@ -106,7 +104,7 @@ traefik (80/443)
|
|||||||
│
|
│
|
||||||
└── frontend_net
|
└── frontend_net
|
||||||
├── öffentliche Apps (vaultwarden, mealie, paperless, immich, gitea, ntfy, mail-archiver, nextcloud)
|
├── öffentliche Apps (vaultwarden, mealie, paperless, immich, gitea, ntfy, mail-archiver, nextcloud)
|
||||||
├── geschützte UIs mit Middleware (homepage, glance, paperless-gpt, uptime-kuma, filebrowser, scrutiny, code-server, borg-ui, glances, speedtest, bentopdf, grafana)
|
├── geschützte UIs mit Middleware (glance, paperless-gpt, filebrowser, scrutiny, code-server, borg-ui, glances, speedtest, bentopdf, monitoring-grafana)
|
||||||
├── Admin-UI mit nativer Auth (komodo)
|
├── Admin-UI mit nativer Auth (komodo)
|
||||||
└── Dienste mit Internetbedarf ohne öffentliche UI (ddns-updater)
|
└── Dienste mit Internetbedarf ohne öffentliche UI (ddns-updater)
|
||||||
|
|
||||||
@@ -125,9 +123,7 @@ App-interne Netze
|
|||||||
├── immich_default (internal: true) ✅
|
├── immich_default (internal: true) ✅
|
||||||
├── nextcloud_internal (internal: true) ✅
|
├── nextcloud_internal (internal: true) ✅
|
||||||
├── monitoring_net (zentraler Observability-Stack)
|
├── monitoring_net (zentraler Observability-Stack)
|
||||||
├── monitoring_influx_lan (Bridge fuer LAN-Port-Publishing, keine Traefik-Route)
|
└── monitoring_influx_lan (Bridge fuer LAN-Port-Publishing, keine Traefik-Route)
|
||||||
├── grafana_influx_internal (Altstand)
|
|
||||||
└── grafana_influx_lan (Altstand)
|
|
||||||
|
|
||||||
Host-Sonderfälle
|
Host-Sonderfälle
|
||||||
├── tailscale
|
├── tailscale
|
||||||
@@ -154,12 +150,10 @@ Diese Dienste sind über echte `*.kaleschke.info`-Domains erreichbar:
|
|||||||
Diese Dienste sind **keine Public Apps**:
|
Diese Dienste sind **keine Public Apps**:
|
||||||
|
|
||||||
- `Komodo` — komodo.kaleschke.info (Traefik, aber bewusst ohne zentrale Middleware; native Auth bleibt aktiv)
|
- `Komodo` — komodo.kaleschke.info (Traefik, aber bewusst ohne zentrale Middleware; native Auth bleibt aktiv)
|
||||||
- `UptimeKuma` — uptime.kaleschke.info (Middleware)
|
|
||||||
- `filebrowser` — files.kaleschke.info (Middleware)
|
- `filebrowser` — files.kaleschke.info (Middleware)
|
||||||
- `scrutiny` — scrutiny.kaleschke.info (Middleware)
|
- `scrutiny` — scrutiny.kaleschke.info (Middleware)
|
||||||
- `code-server` — Traefik + Middleware
|
- `code-server` — Traefik + Middleware
|
||||||
- `borg-ui` — borg.kaleschke.info (Middleware)
|
- `borg-ui` — borg.kaleschke.info (Middleware)
|
||||||
- `homepage` — home.kaleschke.info (Middleware)
|
|
||||||
- `glance` — glance.kaleschke.info (Middleware)
|
- `glance` — glance.kaleschke.info (Middleware)
|
||||||
- `paperless-gpt` — paperless-gpt.kaleschke.info (Middleware)
|
- `paperless-gpt` — paperless-gpt.kaleschke.info (Middleware)
|
||||||
- `mail-archiver` — mail.kaleschke.info (Middleware + App-Auth)
|
- `mail-archiver` — mail.kaleschke.info (Middleware + App-Auth)
|
||||||
@@ -169,7 +163,7 @@ Diese Dienste sind **keine Public Apps**:
|
|||||||
- `monitoring-grafana` — monitoring.kaleschke.info (Middleware)
|
- `monitoring-grafana` — monitoring.kaleschke.info (Middleware)
|
||||||
- `hermes-dashboard` — hermes.kaleschke.info (Middleware)
|
- `hermes-dashboard` — hermes.kaleschke.info (Middleware)
|
||||||
- `Traefik-Dashboard`
|
- `Traefik-Dashboard`
|
||||||
- `AdGuard Home` — Port 8082 direkt auf die Admin-UI (`80` im Container), kein Traefik, nur LAN-Zugang
|
- `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
|
### 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:
|
Wenn ein Dienst im `frontend_net` hängt, heißt das **nicht automatisch öffentlich**. Admin-Dienste dürfen im `frontend_net` liegen, wenn:
|
||||||
@@ -241,11 +235,10 @@ Legende Status:
|
|||||||
| Container | Status | Soll-Netz(e) | Finaler Zugang | Finaler Sollzustand | Offene Punkte |
|
| 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 | — |
|
| `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 (DNS-Sonderfall) | Admin-Port per Traefik + Middleware absichern (Block F) |
|
| `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 | — |
|
| `unbound` | ✅ | `dns_net` | intern | Upstream-Resolver für AdGuard, isoliert | — |
|
||||||
| `ddns-updater` | ✅ | `frontend_net` | intern | Cloudflare DNS API; bleibt in `frontend_net` | Dokumentierte Ausnahme |
|
| `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 |
|
| `tailscale` | ✅ | `host` | VPN-Zugang | Git-Stack (`host-services/tailscale/`) | nutzt `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` als dokumentierte VPN-Ausnahme |
|
||||||
| `homepage` | ✅ | `frontend_net` | Traefik + Middleware | geschuetztes Start-Dashboard via `home.kaleschke.info` | — |
|
|
||||||
|
|
||||||
### 7.2 Sicherheit / Identity
|
### 7.2 Sicherheit / Identity
|
||||||
|
|
||||||
@@ -278,6 +271,7 @@ Legende Status:
|
|||||||
| `immich_server` | ✅ | `immich_default`, `frontend_net` | Traefik | aktiv via `immich.kaleschke.info` | — |
|
| `immich_server` | ✅ | `immich_default`, `frontend_net` | Traefik | aktiv via `immich.kaleschke.info` | — |
|
||||||
| `immich_machine_learning` | ✅ | `immich_default` | intern | bleibt intern | — |
|
| `immich_machine_learning` | ✅ | `immich_default` | intern | bleibt intern | — |
|
||||||
| `nextcloud` | ✅ | `frontend_net`, `nextcloud_internal` | Traefik | aktiv via `cloud.kaleschke.info`, nativer Nextcloud-Login, WebDAV/CardDAV faehig | CalDAV/CardDAV-Redirect via Traefik-Labels |
|
| `nextcloud` | ✅ | `frontend_net`, `nextcloud_internal` | Traefik | aktiv via `cloud.kaleschke.info`, nativer Nextcloud-Login, WebDAV/CardDAV faehig | CalDAV/CardDAV-Redirect via Traefik-Labels |
|
||||||
|
| `plex` | ✅ | `host` | Plex native / Host-Netz | Compose-Stack unter `host-services/plex/`; Host-Netz bleibt fuer Discovery / Plex GDM dokumentierte Ausnahme | — |
|
||||||
|
|
||||||
### 7.5 Admin / Operations
|
### 7.5 Admin / Operations
|
||||||
|
|
||||||
@@ -296,22 +290,21 @@ Legende Status:
|
|||||||
|
|
||||||
| Container | Status | Soll-Netz(e) | Finaler Zugang | Finaler Sollzustand | Offene Punkte |
|
| Container | Status | Soll-Netz(e) | Finaler Zugang | Finaler Sollzustand | Offene Punkte |
|
||||||
|---|---|---|---|---|---|
|
|---|---|---|---|---|---|
|
||||||
| `UptimeKuma` | ✅ | `frontend_net` | Traefik + Middleware | aktiv via `uptime.kaleschke.info` | — |
|
| `glance` | ✅ | `frontend_net`, `glance_socket_net` | Traefik + Middleware | einziges Homelab-Dashboard via `glance.kaleschke.info`; Docker-Status nur ueber internen Socket-Proxy | — |
|
||||||
| `glance` | vorbereitet | `frontend_net`, `glance_socket_net` | Traefik + Middleware | Homelab-Uebersicht via `glance.kaleschke.info`; Docker-Status nur ueber internen Socket-Proxy | Deploy und fachliche Abnahme offen |
|
|
||||||
| `glances` | ✅ | `frontend_net` | Traefik + Middleware | aktiv via `glances.kaleschke.info` | — |
|
| `glances` | ✅ | `frontend_net` | Traefik + Middleware | aktiv via `glances.kaleschke.info` | — |
|
||||||
| `scrutiny` | ✅ | `frontend_net` | Traefik + Middleware | aktiv via `scrutiny.kaleschke.info`, Git-Stack | `privileged` später prüfen |
|
| `scrutiny` | ✅ | `frontend_net` | Traefik + Middleware | aktiv via `scrutiny.kaleschke.info`, Git-Stack | `privileged` später prüfen |
|
||||||
| `speedtest-tracker` | ✅ | `frontend_net` | Traefik + Middleware | aktiv via `speedtest.kaleschke.info` | — |
|
| `speedtest-tracker` | ✅ | `frontend_net` | Traefik + Middleware | aktiv via `speedtest.kaleschke.info` | — |
|
||||||
| `monitoring-grafana` | Ziel | `frontend_net`, `monitoring_net` | Traefik + Middleware | zentrale UI via `monitoring.kaleschke.info`; Datasources fuer Prometheus, Loki und InfluxDB | nach Deploy testen |
|
| `monitoring-grafana` | ✅ | `frontend_net`, `monitoring_net` | Traefik + Middleware | zentrale UI via `monitoring.kaleschke.info`; Datasources fuer Prometheus, Loki und InfluxDB | — |
|
||||||
| `monitoring-influxdb3-core` | Ziel | `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 |
|
||||||
| `loki` | ✅ | `backend_net` | intern | interner Container-Logspeicher ohne Public Route; Grafana greift ueber Loki-Datasource zu | Retention/Storage nach erstem Produktivlauf beobachten |
|
| `monitoring-loki` | ✅ | `monitoring_net` | intern | interner Container-Logspeicher ohne Public Route; Monitoring-Grafana greift ueber Loki-Datasource zu | Retention/Storage beobachten |
|
||||||
| `monitoring-promtail` | Ziel | `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 |
|
||||||
| `grafana` / `influxdb3-core` / `loki` / `alloy` | Altstand | diverse | abgeloest | nicht parallel zum `monitoring/`-Zielstack betreiben | nach erfolgreicher Migration stoppen |
|
| `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 |
|
||||||
|
|
||||||
### 7.7 Noch offene Sonderfälle
|
### 7.7 Noch offene Sonderfälle
|
||||||
|
|
||||||
| Container | Status | Ziel |
|
| Container | Status | Ziel |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `Plex-Media-Server` | ⏳ Dockerman | Compose-Migration, `host`-Netz bleibt (Discovery) |
|
| — | — | Plex ist nicht mehr offen: der Dienst ist als Repo-Compose-Stack unter `host-services/plex/` dokumentiert; `host`-Netz bleibt als Discovery-Ausnahme. |
|
||||||
|
|
||||||
### 7.8 Entfernte Container
|
### 7.8 Entfernte Container
|
||||||
|
|
||||||
@@ -333,6 +326,9 @@ Legende Status:
|
|||||||
| `PortainerCE` | 2026-03-29 | abgeschaltet; Komodo ist alleiniger Stack-Manager |
|
| `PortainerCE` | 2026-03-29 | abgeschaltet; Komodo ist alleiniger Stack-Manager |
|
||||||
| `beszel` | nicht dokumentiert | bereits entfernt; nicht mehr Teil des Zielbilds |
|
| `beszel` | nicht dokumentiert | bereits entfernt; nicht mehr Teil des Zielbilds |
|
||||||
| `beszel-agent` | nicht dokumentiert | bereits entfernt; nicht mehr Teil des Zielbilds |
|
| `beszel-agent` | nicht dokumentiert | bereits entfernt; nicht mehr Teil des Zielbilds |
|
||||||
|
| `jellyfin` | 2026-05-25 | doppelter Medienserver neben Plex; Plex bleibt einziger Medienserver |
|
||||||
|
| `homepage` | 2026-05-25 | doppeltes Dashboard neben Glance; Glance bleibt einziges Homelab-Dashboard |
|
||||||
|
| `uptime-kuma` | 2026-05-25 | durch `monitoring-blackbox-exporter`, Prometheus-Alerts und `monitoring-grafana` ersetzt |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -393,7 +389,7 @@ Für den laufenden Betrieb gilt stattdessen:
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `traefik` | Host-Ports 80/443 | zentraler Reverse Proxy |
|
| `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 |
|
| `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 benötigt direkten Port 53; kein HTTP-Proxy für DNS möglich |
|
| `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 |
|
| `Plex-Media-Server` | `host` | Discovery / mDNS / Plex GDM |
|
||||||
| `scrutiny` | `privileged: true` | SMART-Datenzugriff auf Laufwerke |
|
| `scrutiny` | `privileged: true` | SMART-Datenzugriff auf Laufwerke |
|
||||||
| `Komodo` | Docker-Socket Zugriff | Stack-Deployments benötigen Socket |
|
| `Komodo` | Docker-Socket Zugriff | Stack-Deployments benötigen Socket |
|
||||||
@@ -495,7 +491,7 @@ Komodo ist nun der primäre GitOps-Stack-Manager:
|
|||||||
- AdGuard läuft als Git-Stack (`host-services/Adguard/docker-compose.yml`)
|
- AdGuard läuft als Git-Stack (`host-services/Adguard/docker-compose.yml`)
|
||||||
- Netzwerke: `dns_net` (feste IP 172.23.0.3) + `frontend_net`
|
- Netzwerke: `dns_net` (feste IP 172.23.0.3) + `frontend_net`
|
||||||
- Port 53 (DNS) direkt gebunden — dokumentierte Ausnahme
|
- Port 53 (DNS) direkt gebunden — dokumentierte Ausnahme
|
||||||
- Admin-UI direkt gebunden via Host-Port 8082 auf Container-Port 80 — Traefik-Absicherung ausstehend (Block F)
|
- 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`
|
- `unbound` läuft weiterhin als Upstream-Resolver in `dns_net`
|
||||||
|
|
||||||
### diun — Entfernung (2026-03-28)
|
### diun — Entfernung (2026-03-28)
|
||||||
@@ -563,7 +559,8 @@ Mutable Tags wie `latest`, `stable`, `release` oder reine Major-Tags wurden auf
|
|||||||
- `monitoring-influxdb3-core` bleibt ohne Traefik-/Public-Route; fuer interne Writer wie Home Assistant kann Port `8181` per `INFLUXDB_BIND_IP` auf eine LAN-Adresse gebunden werden.
|
- `monitoring-influxdb3-core` bleibt ohne Traefik-/Public-Route; fuer interne Writer wie Home Assistant kann Port `8181` per `INFLUXDB_BIND_IP` auf eine LAN-Adresse gebunden werden.
|
||||||
- Fuer dieses Port-Publishing nutzt `monitoring-influxdb3-core` zusaetzlich `monitoring_influx_lan`. Das ist keine Public-App-Freigabe und ersetzt nicht die Token-Authentifizierung.
|
- Fuer dieses Port-Publishing nutzt `monitoring-influxdb3-core` zusaetzlich `monitoring_influx_lan`. Das ist keine Public-App-Freigabe und ersetzt nicht die Token-Authentifizierung.
|
||||||
- InfluxDB 3 Core nutzt einen festen Versionstag statt `latest`, weil der InfluxDB-`latest`-Tag versionsstrategisch im Umbruch ist.
|
- InfluxDB 3 Core nutzt einen festen Versionstag statt `latest`, weil der InfluxDB-`latest`-Tag versionsstrategisch im Umbruch ist.
|
||||||
- Die alten Pfade `ops/grafana-influxdb` und `ops/loki` sind abgeloeste Altstaende und sollen nach erfolgreichem Monitoring-Deploy nicht parallel betrieben werden.
|
- Die alten Pfade `ops/grafana-influxdb` und `ops/loki` wurden am 2026-05-26 aus dem aktiven Repo entfernt; `monitoring/` ist der einzige Observability-Zielstack.
|
||||||
|
- Uptime Kuma wurde nach erfolgreichem Blackbox-/Grafana-Smoke-Test entfernt; `monitoring/` ist die Quelle fuer HTTP-Erreichbarkeit und Alerts.
|
||||||
|
|
||||||
### Monitoring-Logging-Baseline (2026-05-17)
|
### Monitoring-Logging-Baseline (2026-05-17)
|
||||||
- `monitoring-loki` laeuft intern auf `monitoring_net`, ohne Traefik-Route und ohne Host-Port.
|
- `monitoring-loki` laeuft intern auf `monitoring_net`, ohne Traefik-Route und ohne Host-Port.
|
||||||
|
|||||||
@@ -13,6 +13,14 @@ Bei Restore-, Host-Ausfall- oder Wiederanlauf-Fragen zusaetzlich:
|
|||||||
|
|
||||||
3. `docs/DISASTER_RECOVERY.md`
|
3. `docs/DISASTER_RECOVERY.md`
|
||||||
4. `docs/RESTORE_MATRIX.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
|
## Architektur
|
||||||
|
|
||||||
@@ -38,7 +46,8 @@ Bei Restore-, Host-Ausfall- oder Wiederanlauf-Fragen zusaetzlich:
|
|||||||
- `security/` -> sicherheitskritische Dienste
|
- `security/` -> sicherheitskritische Dienste
|
||||||
- `infra/` -> Datenbanken und technische Services
|
- `infra/` -> Datenbanken und technische Services
|
||||||
- `apps/` -> Anwendungen
|
- `apps/` -> Anwendungen
|
||||||
- `ops/` -> Monitoring und Tools
|
- `ops/` -> operative Tools
|
||||||
|
- `monitoring/` -> zentraler Observability-Stack
|
||||||
- `host-services/` -> Dienste mit Host-Netz
|
- `host-services/` -> Dienste mit Host-Netz
|
||||||
- `traefik/` -> Reverse Proxy Konfiguration
|
- `traefik/` -> Reverse Proxy Konfiguration
|
||||||
- `docs/` -> Dokumentation und Prozesse
|
- `docs/` -> Dokumentation und Prozesse
|
||||||
@@ -59,9 +68,11 @@ Bei Restore-, Host-Ausfall- oder Wiederanlauf-Fragen zusaetzlich:
|
|||||||
- Komodo ist der primaere und einzige produktive Stack-Manager.
|
- Komodo ist der primaere und einzige produktive Stack-Manager.
|
||||||
- Komodo bleibt bewusst bei nativer Authentifizierung; zentrale Traefik-Auth wird dort nicht pauschal vorgeschaltet.
|
- Komodo bleibt bewusst bei nativer Authentifizierung; zentrale Traefik-Auth wird dort nicht pauschal vorgeschaltet.
|
||||||
- Portainer CE ist abgeschaltet und kein Teil des aktiven Betriebs mehr.
|
- Portainer CE ist abgeschaltet und kein Teil des aktiven Betriebs mehr.
|
||||||
- Homepage ist das aktive produktive Frontend / Start-Dashboard.
|
- Glance ist das aktive produktive Homelab-Dashboard.
|
||||||
- Traefik `dynamic/` bleibt eine dokumentierte manuelle Host-Sync-Ausnahme ausserhalb des normalen Komodo-Deployments.
|
- 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.
|
- 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.
|
- 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`.
|
- 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.
|
- `nextcloud`, `bentopdf` und `monitoring` folgen dem dokumentierten Netz-/Secret-/Traefik-Modell; der zentrale Monitoring-Stack buendelt Prometheus, Loki, Promtail, Grafana und InfluxDB 3 Core.
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
services:
|
|
||||||
homepage:
|
|
||||||
image: ghcr.io/gethomepage/homepage:v1.12.3@sha256:cc84f2f5eb3c7734353701ccbaa24ed02dacb0d119114e50e4251e2005f3990a
|
|
||||||
container_name: homepage
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
HOMEPAGE_ALLOWED_HOSTS: home.kaleschke.info
|
|
||||||
HOMEPAGE_VAR_GITEA_TOKEN: ${HOMEPAGE_VAR_GITEA_TOKEN}
|
|
||||||
HOMEPAGE_VAR_ADGUARD_USERNAME: ${HOMEPAGE_VAR_ADGUARD_USERNAME}
|
|
||||||
HOMEPAGE_VAR_ADGUARD_PASSWORD: ${HOMEPAGE_VAR_ADGUARD_PASSWORD}
|
|
||||||
HOMEPAGE_VAR_KOMODO_API_KEY: ${HOMEPAGE_VAR_KOMODO_API_KEY}
|
|
||||||
HOMEPAGE_VAR_KOMODO_API_SECRET: ${HOMEPAGE_VAR_KOMODO_API_SECRET}
|
|
||||||
HOMEPAGE_VAR_SPEEDTEST_API_KEY: ${HOMEPAGE_VAR_SPEEDTEST_API_KEY}
|
|
||||||
HOMEPAGE_VAR_PAPERLESS_TOKEN: ${HOMEPAGE_VAR_PAPERLESS_TOKEN}
|
|
||||||
HOMEPAGE_VAR_FILEBROWSER_USERNAME: ${HOMEPAGE_VAR_FILEBROWSER_USERNAME}
|
|
||||||
HOMEPAGE_VAR_FILEBROWSER_PASSWORD: ${HOMEPAGE_VAR_FILEBROWSER_PASSWORD}
|
|
||||||
HOMEPAGE_VAR_IMMICH_API_KEY: ${HOMEPAGE_VAR_IMMICH_API_KEY}
|
|
||||||
HOMEPAGE_VAR_MEALIE_TOKEN: ${HOMEPAGE_VAR_MEALIE_TOKEN}
|
|
||||||
HOMEPAGE_VAR_UPTIME_SLUG: ${HOMEPAGE_VAR_UPTIME_SLUG}
|
|
||||||
volumes:
|
|
||||||
- /mnt/user/appdata/homepage:/app/config
|
|
||||||
- /mnt/user/appdata/homepage/images:/app/public/images
|
|
||||||
networks:
|
|
||||||
- frontend_net
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.docker.network=frontend_net
|
|
||||||
- traefik.http.routers.homepage.rule=Host(`home.kaleschke.info`)
|
|
||||||
- traefik.http.routers.homepage.entrypoints=websecure
|
|
||||||
- traefik.http.routers.homepage.tls=true
|
|
||||||
- traefik.http.routers.homepage.tls.certresolver=le
|
|
||||||
- traefik.http.routers.homepage.middlewares=authelia@file,secure-headers@file
|
|
||||||
- traefik.http.services.homepage.loadbalancer.server.port=3000
|
|
||||||
security_opt:
|
|
||||||
- no-new-privileges:true
|
|
||||||
|
|
||||||
networks:
|
|
||||||
frontend_net:
|
|
||||||
external: true
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
services:
|
|
||||||
jellyfin:
|
|
||||||
image: jellyfin/jellyfin:10.11.8@sha256:1694ff069f0c9dafb283c36765175606866769f5d72f2ed56b6a0f1be922fc37
|
|
||||||
container_name: jellyfin
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
TZ: Europe/Berlin
|
|
||||||
JELLYFIN_PublishedServerUrl: https://jellyfin.kaleschke.info
|
|
||||||
dns:
|
|
||||||
- 1.1.1.1
|
|
||||||
volumes:
|
|
||||||
- /mnt/user/appdata/jellyfin/config:/config
|
|
||||||
- /mnt/user/appdata/jellyfin/cache:/cache
|
|
||||||
- /mnt/user/media:/media:ro
|
|
||||||
- /mnt/user/photos:/photos:ro
|
|
||||||
networks:
|
|
||||||
- frontend_net
|
|
||||||
security_opt:
|
|
||||||
- no-new-privileges:true
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.docker.network=frontend_net
|
|
||||||
- traefik.http.routers.jellyfin.rule=Host(`jellyfin.kaleschke.info`)
|
|
||||||
- traefik.http.routers.jellyfin.entrypoints=websecure
|
|
||||||
- traefik.http.routers.jellyfin.tls=true
|
|
||||||
- traefik.http.routers.jellyfin.tls.certresolver=le
|
|
||||||
- traefik.http.routers.jellyfin.middlewares=secure-headers@file
|
|
||||||
- traefik.http.services.jellyfin.loadbalancer.server.port=8096
|
|
||||||
|
|
||||||
networks:
|
|
||||||
frontend_net:
|
|
||||||
external: true
|
|
||||||
@@ -11,9 +11,17 @@ services:
|
|||||||
- GITEA__server__DOMAIN=git.kaleschke.info
|
- GITEA__server__DOMAIN=git.kaleschke.info
|
||||||
- GITEA__server__ROOT_URL=https://git.kaleschke.info/
|
- GITEA__server__ROOT_URL=https://git.kaleschke.info/
|
||||||
- GITEA__database__DB_TYPE=sqlite3
|
- GITEA__database__DB_TYPE=sqlite3
|
||||||
|
- GITEA__service__DISABLE_REGISTRATION=true
|
||||||
|
- GITEA__service__REGISTER_EMAIL_CONFIRM=true
|
||||||
|
- GITEA__openid__ENABLE_OPENID_SIGNIN=false
|
||||||
|
- GITEA__openid__ENABLE_OPENID_SIGNUP=false
|
||||||
|
- GITEA__migrations__ALLOWED_DOMAINS=github.com
|
||||||
- GITEA__webhook__ALLOWED_HOST_LIST=komodo-core,localhost,127.0.0.1,192.168.178.0/24
|
- GITEA__webhook__ALLOWED_HOST_LIST=komodo-core,localhost,127.0.0.1,192.168.178.0/24
|
||||||
volumes:
|
volumes:
|
||||||
- /mnt/user/services/gitea/data:/data
|
- /mnt/user/services/gitea/data:/data
|
||||||
|
dns:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 8.8.8.8
|
||||||
ports:
|
ports:
|
||||||
- "222:22"
|
- "222:22"
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
+12
-7
@@ -39,6 +39,8 @@ Traefik ist der zentrale Web-Einstieg fuer HTTP(S). Admin-/Ops-UIs liegen entwed
|
|||||||
- Gitea hostet das Repo unter `git.kaleschke.info`.
|
- Gitea hostet das Repo unter `git.kaleschke.info`.
|
||||||
- Komodo ist Stack-Manager und Deploy-Consumer.
|
- Komodo ist Stack-Manager und Deploy-Consumer.
|
||||||
- Komodo Periphery braucht Docker-Socket und `/mnt/user/services` Mount, um Stacks reproduzierbar zu deployen.
|
- Komodo Periphery braucht Docker-Socket und `/mnt/user/services` Mount, um Stacks reproduzierbar zu deployen.
|
||||||
|
- Neue produktive Komodo-Stacks aus `Micha/homelab-infra` muessen einen aktiven Gitea->Komodo-Webhook auf die aktuelle Stack-ID haben; Ausnahmen wie deaktivierte/pausierte Stacks muessen dokumentiert werden.
|
||||||
|
- Der `komodo`-Self-Stack ist eine dokumentierte Ausnahme ohne aktiven Gitea-Webhook; Bootstrap/Recovery laeuft ueber `docs/SERVICES_RECOVERY.md`.
|
||||||
|
|
||||||
### Identity / Security
|
### Identity / Security
|
||||||
|
|
||||||
@@ -50,7 +52,7 @@ Traefik ist der zentrale Web-Einstieg fuer HTTP(S). Admin-/Ops-UIs liegen entwed
|
|||||||
|
|
||||||
### Apps
|
### Apps
|
||||||
|
|
||||||
Wichtige Apps sind Paperless, Immich, Mealie, Mail Archiver, Nextcloud, ntfy, Vaultwarden und Gitea. Admin-/Ops-Tools sind u. a. Homepage, Komodo, Borg UI, Uptime Kuma, Filebrowser, code-server, Glances, Scrutiny, Speedtest, Grafana und Hermes Agent.
|
Wichtige Apps sind Paperless, Immich, Mealie, Mail Archiver, Nextcloud, ntfy, Vaultwarden und Gitea. Admin-/Ops-Tools sind u. a. Glance, Komodo, Borg UI, Filebrowser, code-server, Glances, Scrutiny, Speedtest, Monitoring Grafana und Hermes Agent.
|
||||||
|
|
||||||
### Hermes Agent — Architektur und Ops-Monitor
|
### Hermes Agent — Architektur und Ops-Monitor
|
||||||
|
|
||||||
@@ -84,7 +86,8 @@ Nach Aenderungen an `services.json` oder `check_health.py`: `git pull` auf der V
|
|||||||
- `monitoring-influxdb3-core` ist nicht public und nicht im `frontend_net`.
|
- `monitoring-influxdb3-core` ist nicht public und nicht im `frontend_net`.
|
||||||
- Home Assistant schreibt ueber LAN-only Port 8181 nach InfluxDB, gebunden ueber `INFLUXDB_BIND_IP`.
|
- Home Assistant schreibt ueber LAN-only Port 8181 nach InfluxDB, gebunden ueber `INFLUXDB_BIND_IP`.
|
||||||
- Ein `401 Unauthorized` von InfluxDB ohne Token ist beim Reachability-Test ein Erfolgssignal.
|
- Ein `401 Unauthorized` von InfluxDB ohne Token ist beim Reachability-Test ein Erfolgssignal.
|
||||||
- `ops/loki` und `ops/grafana-influxdb` sind abgeloeste Altstaende und sollen nach erfolgreichem Monitoring-Deploy nicht parallel betrieben werden.
|
- Die frueheren Altstaende `ops/loki` und `ops/grafana-influxdb` wurden aus dem aktiven Repo entfernt; fuer Monitoring immer `monitoring/` verwenden, Rollback nur ueber Git-Historie.
|
||||||
|
- Uptime Kuma ist entfernt; HTTP-Verfuegbarkeit laeuft ueber Blackbox Exporter, Prometheus-Alerts und `Homelab / Availability` in Monitoring Grafana.
|
||||||
|
|
||||||
## Deployment-Logik
|
## Deployment-Logik
|
||||||
|
|
||||||
@@ -101,6 +104,8 @@ Normalfall:
|
|||||||
|
|
||||||
Wichtig: Komodo-Web-Editor ist nicht der Bearbeitungsort. Wenn Komodo und Git voneinander abweichen, zuerst Git und Komodo Workspace pruefen, nicht live herumprobieren.
|
Wichtig: Komodo-Web-Editor ist nicht der Bearbeitungsort. Wenn Komodo und Git voneinander abweichen, zuerst Git und Komodo Workspace pruefen, nicht live herumprobieren.
|
||||||
|
|
||||||
|
Beim Anlegen neuer produktiver Stacks ist der Gitea->Komodo-Webhook Pflicht. Nach dem Anlegen muss ein Test-Push oder Test-Delivery zeigen, dass Gitea die aktuelle Komodo-Stack-ID erreicht.
|
||||||
|
|
||||||
## Netzwerkmodell
|
## Netzwerkmodell
|
||||||
|
|
||||||
| Netzwerk | Bedeutung |
|
| Netzwerk | Bedeutung |
|
||||||
@@ -108,7 +113,7 @@ Wichtig: Komodo-Web-Editor ist nicht der Bearbeitungsort. Wenn Komodo und Git vo
|
|||||||
| `frontend_net` | Web-/Proxy-Netz fuer Traefik-geroutete Dienste und Dienste mit Internetbedarf |
|
| `frontend_net` | Web-/Proxy-Netz fuer Traefik-geroutete Dienste und Dienste mit Internetbedarf |
|
||||||
| `backend_net` | internes Netz fuer shared PostgreSQL, Redis und Backends |
|
| `backend_net` | internes Netz fuer shared PostgreSQL, Redis und Backends |
|
||||||
| `dns_net` | AdGuard + Unbound |
|
| `dns_net` | AdGuard + Unbound |
|
||||||
| app-interne Netze | Isolation von App + DB/Cache, z. B. Immich, Mealie, Nextcloud, Grafana/Influx |
|
| app-interne Netze | Isolation von App + DB/Cache, z. B. Immich, Mealie, Nextcloud, Monitoring |
|
||||||
| `host` | nur dokumentierte Sonderfaelle wie Tailscale/Plex |
|
| `host` | nur dokumentierte Sonderfaelle wie Tailscale/Plex |
|
||||||
|
|
||||||
Regeln:
|
Regeln:
|
||||||
@@ -116,7 +121,7 @@ Regeln:
|
|||||||
- Datenbanken nie ins `frontend_net`.
|
- Datenbanken nie ins `frontend_net`.
|
||||||
- Admin-UIs nur mit Traefik + Middleware oder dokumentierter Ausnahme.
|
- Admin-UIs nur mit Traefik + Middleware oder dokumentierter Ausnahme.
|
||||||
- Direkte Host-Ports sind Ausnahme, nicht Default.
|
- Direkte Host-Ports sind Ausnahme, nicht Default.
|
||||||
- Runtime-Netznamen koennen Compose-Projektpraefixe bekommen, z. B. `grafana_grafana_influx_lan`.
|
- Runtime-Netznamen koennen Compose-Projektpraefixe bekommen, z. B. `monitoring_monitoring_influx_lan`.
|
||||||
|
|
||||||
## Security-Modell
|
## Security-Modell
|
||||||
|
|
||||||
@@ -131,12 +136,12 @@ Bekannte Ausnahmen:
|
|||||||
|
|
||||||
- Traefik: 80/443
|
- Traefik: 80/443
|
||||||
- Gitea: SSH 222
|
- Gitea: SSH 222
|
||||||
- AdGuard: DNS 53 und Admin 8082
|
- 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`
|
- Tailscale: Host-Netz, `NET_ADMIN`, `NET_RAW`, `/dev/net/tun`
|
||||||
- Scrutiny: privileged
|
- Scrutiny: privileged
|
||||||
- Komodo: Docker-Socket, native Auth
|
- Komodo: Docker-Socket, native Auth
|
||||||
- InfluxDB: LAN-only 8181 fuer Home Assistant Writer
|
- InfluxDB: LAN-only 8181 fuer Home Assistant Writer
|
||||||
- Grafana/InfluxDB: `user: "0"` als dokumentierte Host-Appdata-Permissions-Ausnahme
|
- `monitoring-influxdb3-core`: `user: "0"` als dokumentierte Host-Appdata-Permissions-Ausnahme
|
||||||
- Traefik dynamic config: manueller Host-Sync
|
- Traefik dynamic config: manueller Host-Sync
|
||||||
|
|
||||||
## Backup- und Restore-Modell
|
## Backup- und Restore-Modell
|
||||||
@@ -185,7 +190,7 @@ KI-Agenten sollen konservativ arbeiten: keine indirekten Live-Aenderungen, keine
|
|||||||
- Authelia nutzt PostgreSQL, aber bewusst kein Redis-Session-Backend; Redis ist kein Authelia-Bootstrap-Blocker.
|
- Authelia nutzt PostgreSQL, aber bewusst kein Redis-Session-Backend; Redis ist kein Authelia-Bootstrap-Blocker.
|
||||||
- Authelia-Notifier ist SMTP; bei Auth-Aenderungen Host-Config backupen, `authelia validate-config` ausfuehren und erst danach neu starten.
|
- Authelia-Notifier ist SMTP; bei Auth-Aenderungen Host-Config backupen, `authelia validate-config` ausfuehren und erst danach neu starten.
|
||||||
- `paperless-ngx` nutzt fuer DB/Redis bewusst Stack ENV statt `_FILE`.
|
- `paperless-ngx` nutzt fuer DB/Redis bewusst Stack ENV statt `_FILE`.
|
||||||
- `homepage`, `glances` und `komodo-periphery` nutzen Docker-Socket-Mounts; Zugriff bewusst behandeln.
|
- `glance-docker-socket-proxy`, `glances` und `komodo-periphery` nutzen Docker-/Socket-Zugriff; Zugriff bewusst behandeln.
|
||||||
- `borg-ui` und `filebrowser` haben breite Mounts; bei Hardening nicht ad hoc, sondern gezielt vorgehen.
|
- `borg-ui` und `filebrowser` haben breite Mounts; bei Hardening nicht ad hoc, sondern gezielt vorgehen.
|
||||||
- `scrutiny` ist privilegiert und hat Device-Mounts.
|
- `scrutiny` ist privilegiert und hat Device-Mounts.
|
||||||
- `Plex-Media-Server` ist im Architekturziel als Host-Sonderfall dokumentiert, aber nicht als Repo-Compose-Stack enthalten.
|
- `Plex-Media-Server` ist im Architekturziel als Host-Sonderfall dokumentiert, aber nicht als Repo-Compose-Stack enthalten.
|
||||||
|
|||||||
@@ -0,0 +1,369 @@
|
|||||||
|
# Homelab Audit - 2026-05-23
|
||||||
|
|
||||||
|
Stand: 2026-05-23, repo-basiert. Erstellt nach `docs/WORKFLOW.md` und `docs/GITOPS_DRIFT_RUNBOOK.md`. Quellebasis: `origin/master` plus lokaler Clone, ohne Schreibbefehle, ohne Deploy.
|
||||||
|
|
||||||
|
Dieser Audit ist eine punktuelle Sollzustands-Bewertung, kein Live-Status. Die Live-Verifikations-Schritte stehen am Ende in Abschnitt 9; alle dortigen Outputs ersetzen Vermutungen durch Messwerte.
|
||||||
|
|
||||||
|
## 0. Executive Summary
|
||||||
|
|
||||||
|
Ampel-Bewertung pro Bereich:
|
||||||
|
|
||||||
|
| Bereich | Ampel | Kernaussage |
|
||||||
|
|---|---|---|
|
||||||
|
| GitOps-Konsistenz (lokal/Gitea) | 🟡 | Lokaler Clone ist **1 Commit voraus** auf `master` (`cd650b1`, Haertungs-Commit). Bis zum Push existiert dieser Stand nur lokal, nicht in Gitea — bei einem Clone-Verlust ist er weg. |
|
||||||
|
| GitOps-Konsistenz (Working Tree) | 🟢* | Keine echten Inhaltsaenderungen offen. Die 47 "modified files" aus `git status` im Linux-Mount sind voraussichtlich CRLF/LF-Mount-Artefakte (durch `git diff -w --stat` auf Stichprobe bestaetigt leer). Bitte am Windows-Host gegenpruefen. |
|
||||||
|
| Hardening-Sprint (Mai 2026) | 🟢 | Alle vier Post-Restore-Sprint-Items sind im Repo umgesetzt (Filebrowser-Mounts, Authelia Argon2id, Gitea Webhook-Allowlist, Backup-Dump-Konsistenz). |
|
||||||
|
| Backup/Restore-Readiness | 🟢 | `pre-backup-dumps.sh` deckt alle relevanten SQLite/PostgreSQL/Mongo-Quellen ab. Borg-UI-Scope umfasst `/mnt/user/services`, `homelab-infra`, `stacks`, `posture-check`. Live-Frische ist offen (Abschnitt 9). |
|
||||||
|
| Monitoring-Migration | 🟡 | `monitoring/` Stack im Repo komplett, aber Live-Deploy laut `docs/NEXT_SPRINT_TODO_2026-05-16.md` noch ausstehend. Alte Stacks `ops/grafana-influxdb` und `ops/loki` sollen erst nach Live-Smoke-Test gestoppt werden. |
|
||||||
|
| Doku-Drift Repo vs. Master-Doku | 🟠 | `apps/jellyfin/`, `host-services/plex/` und einige andere existieren produktiv als Compose-Stacks, sind aber in `HOMELAB_ARCHITECTURE_MASTER_V2.md`, `docs/SERVICE_CATALOG.md` und `docs/REPO_MAP.md` **nicht aufgefuehrt**. Authelia-ACL kennt `jellyfin.kaleschke.info` als bypass, im Masterdoku-Hostkatalog steht es nicht. |
|
||||||
|
| Repo-Hygiene | 🟡 | 8 leere Verzeichnisse im Working Tree (siehe 4.3). `.serena/` ist untracked und nicht in `.gitignore`. Drei `ops/windows-reinstall/*.ps1` sind untracked. |
|
||||||
|
| Bekannte Ausnahmen | 🟢 | Alle Ausnahmen aus `HOMELAB_ARCHITECTURE_MASTER_V2.md` Abschnitt 10 sind weiterhin dokumentiert und durch den Policy-Check abgedeckt (0 Critical, 4 Warnings, 9 Info – alles dokumentierte Ausnahmen). |
|
||||||
|
|
||||||
|
**Kernfazit:** Das Homelab ist sehr nah an der "Endstufe". Es gibt keine kritischen Befunde. Die einzige Pflichtaktion vor dem naechsten geplanten Schritt ist der **Push des lokalen Commits `cd650b1` nach Gitea**, damit `origin/master` wieder die Quelle der Wahrheit ist. Danach sind nur noch zwei priorisierte Pakete offen: Monitoring-Stack live finalisieren und Doku auf den Stand der neuen Stacks (Jellyfin/Plex/...) nachziehen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Methodik und Quellen
|
||||||
|
|
||||||
|
Diese Audit-Quellen wurden gelesen (repo-seitig):
|
||||||
|
|
||||||
|
- `HOMELAB_ARCHITECTURE_MASTER_V2.md`
|
||||||
|
- `docs/WORKFLOW.md`
|
||||||
|
- `docs/REPO_MAP.md`
|
||||||
|
- `docs/SERVICE_CATALOG.md`
|
||||||
|
- `docs/RESTORE_MATRIX.md`
|
||||||
|
- `docs/GITOPS_DRIFT_RUNBOOK.md`
|
||||||
|
- `docs/NEXT_SPRINT_TODO_2026-05-16.md`
|
||||||
|
- `ops/borg-ui/scripts/pre-backup-dumps.sh`
|
||||||
|
- `ops/borg-ui/docker-compose.yml`
|
||||||
|
- `ops/borg-ui/all-important-sources.txt`
|
||||||
|
- `ops/filebrowser/docker-compose.yml`
|
||||||
|
- `security/authelia/configuration.yml`
|
||||||
|
- `core/gitea/docker-compose.yml`
|
||||||
|
- `apps/jellyfin/docker-compose.yml`
|
||||||
|
- `host-services/plex/docker-compose.yml`
|
||||||
|
- `ops/policy-checks/last-report.md`
|
||||||
|
|
||||||
|
Schreibbefehle: keine. Deploys: keine. Containerlaufzeit, Komodo-Webhook-Status, Borg-Lauf-Frische und Host-Listener wurden bewusst nicht angetastet — dafuer steht der Live-Daten-Block in Abschnitt 9.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Schicht A — GitOps und Konsistenz
|
||||||
|
|
||||||
|
### 2.1 Lokaler Clone vs. `origin/master`
|
||||||
|
|
||||||
|
```
|
||||||
|
## master...origin/master [ahead 1]
|
||||||
|
HEAD = cd650b19ac057a1b74ac63503e5dba50eaf5b8ea
|
||||||
|
origin/master = af231dd4e835b19005cc0842509199d480af00d9
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Befund:** Lokaler Clone ist 1 Commit voraus.
|
||||||
|
- **Commit:** `cd650b1 Close Gitea signup, dedup posture-check alerts, extend Borg scope` (Sat May 23 11:01:24 2026 +0200).
|
||||||
|
- **Inhalt** (laut Commit-Message und betroffenen Dateien):
|
||||||
|
- Gitea: `DISABLE_REGISTRATION=true`, `ENABLE_OPENID_SIGNIN=false`, `ENABLE_OPENID_SIGNUP=false`
|
||||||
|
- Repo-Pflicht-Doku ergaenzt: Komodo-Stack-Webhook-Pflicht in `CLAUDE.md`, `AI_CONTEXT.md`, `WORKFLOW.md`
|
||||||
|
- `posture-check.sh`: Disk1-NTFS-Funktion ausgelagert, Inode-Check auf NTFS uebersprungen, ntfy-Dedup via Fingerprint-State + `ALERT_REPEAT_SECONDS`
|
||||||
|
- `docker-critical-events.sh`: JSON-Parsing, `die exit=0` gefiltert, strukturierte ntfy-Message
|
||||||
|
- `borg-ui`: `/mnt/user/services` als `/local/services:ro` gemountet, `all-important-sources.txt` ergaenzt
|
||||||
|
- Unraid User Scripts dokumentiert (daily report)
|
||||||
|
- `MIGRATION_LOG.md`, `RESTORE_MATRIX.md`, `DISASTER_RECOVERY.md` aktualisiert
|
||||||
|
- **Risiko:** Bei Verlust des Windows-Clones (Reinstall, Diskcrash) ist dieser Stand verloren, weil er nicht in Gitea liegt. Komodo deployt ausserdem aus Gitea und kennt diese Aenderungen noch nicht.
|
||||||
|
- **Empfohlene Aktion (Pflicht vor weiterer Arbeit):** In GitHub Desktop `Push origin` ausfuehren. Danach Komodo-Reaktion fuer die betroffenen Stacks (`gitea`, `borg-ui`) pruefen und Smoke-Tests laufen lassen.
|
||||||
|
|
||||||
|
### 2.2 Working-Tree-Status
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git status --short
|
||||||
|
M CLAUDE.md
|
||||||
|
M HOMELAB_ARCHITECTURE_MASTER_V2.md
|
||||||
|
M apps/homepage/docker-compose.yml
|
||||||
|
...
|
||||||
|
(47 Dateien als modified gemeldet, plus 4 Untracked)
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Bewertung:** Die 47 "modified files" sind mit hoher Wahrscheinlichkeit **Mount-Artefakte** durch CRLF/LF zwischen Windows-Clone und Linux-Mount. Stichprobe `git diff -w --stat CLAUDE.md HOMELAB_ARCHITECTURE_MASTER_V2.md` lieferte leer — d. h. keine inhaltlichen Diffs.
|
||||||
|
- **Aktion:** Bitte am Windows-Host in GitHub Desktop `git status --short` ausfuehren. Wenn dort der Tree leer ist (nur die 4 Untracked), gibt es keinen echten Working-Tree-Drift. Wenn dort echte Diffs erscheinen, hier bitte zurueckmelden — dann ist das ein eigener Befund.
|
||||||
|
- **Optional (nicht Pflicht):** `.gitattributes` mit `* text=auto eol=lf` haerten, damit dieser Mount-Effekt fuer KI-Audits aus dem Weg geht. Das ist ein eigener kleiner Commit, kein Audit-Output.
|
||||||
|
|
||||||
|
### 2.3 Untracked Files
|
||||||
|
|
||||||
|
```
|
||||||
|
?? .serena/
|
||||||
|
?? ops/windows-reinstall/backup-delta-after-2026-05-07.ps1
|
||||||
|
?? ops/windows-reinstall/cleanup-dualboot-bcd.ps1
|
||||||
|
?? ops/windows-reinstall/repair-disk0-boot-to-new-windows.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
- `.serena/` ist das Working-Directory des Serena Code-Search-Tools. Hat eigene `.gitignore` intern, aber das `.serena/`-Verzeichnis selbst ist nicht in der Repo-`.gitignore`.
|
||||||
|
- **Aktion (klein):** `.serena/` in `.gitignore` aufnehmen, damit es nicht versehentlich committet wird.
|
||||||
|
- Die drei PowerShell-Scripts unter `ops/windows-reinstall/` sind Windows-Reinstall-Helfer. Entscheidung offen: ins Repo aufnehmen (mit Kontextkommentar warum sie dort liegen) oder lokal halten und in `.gitignore` aufnehmen. Vorschlag: aufnehmen, weil `ops/` der dokumentierte Ort fuer Ops-Skripte ist.
|
||||||
|
|
||||||
|
### 2.4 Letzte Commit-Historie (Top 10)
|
||||||
|
|
||||||
|
```
|
||||||
|
cd650b1 Close Gitea signup, dedup posture-check alerts, extend Borg scope [LOKAL, NICHT GEPUSHT]
|
||||||
|
af231dd Fix zero-count noise pattern handling
|
||||||
|
428223d Mark posture report scripts executable
|
||||||
|
b6d3ed4 Tune homelab availability alerts
|
||||||
|
9e7bebb Add daily operations report with hardened log-noise filtering
|
||||||
|
b7cbbe5 Fix Jellyfin external DNS
|
||||||
|
71ac18b Fix Jellyfin native auth routing
|
||||||
|
90f270b Fix Jellyfin config permissions
|
||||||
|
e28f8da Add Jellyfin media server stack
|
||||||
|
edfec5b Add Plex media server stack
|
||||||
|
```
|
||||||
|
|
||||||
|
- Die letzten Tage waren sichtbar: Jellyfin/Plex hinzugefuegt, Availability-Alerts feinjustiert, Posture-Check-Skripte produktiv gemacht, dann der grosse Haertungs-Commit gestern (2026-05-23 11:01).
|
||||||
|
|
||||||
|
### 2.5 Compose-Inventar vs. Doku
|
||||||
|
|
||||||
|
Repo hat folgende Compose-Stacks, die in den Doku-Quellen (`HOMELAB_ARCHITECTURE_MASTER_V2.md`, `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md`) **nicht oder nur teilweise** aufgefuehrt sind:
|
||||||
|
|
||||||
|
| Stack | Status im Repo | Status in Master-Doku |
|
||||||
|
|---|---|---|
|
||||||
|
| `apps/jellyfin/docker-compose.yml` | produktiv vorhanden, gepinnt `jellyfin:10.11.8@sha256:...`, Traefik `jellyfin.kaleschke.info`, `secure-headers@file`, native Auth, `/mnt/user/media:ro` + `/mnt/user/photos:ro` | **fehlt** in 7.4 Apps; Authelia-ACL kennt aber bereits `jellyfin.kaleschke.info` als bypass — Doku hinkt hinterher |
|
||||||
|
| `host-services/plex/docker-compose.yml` | produktiv vorhanden, gepinnt `plexinc/pms-docker:1.43.1.10611-1e34174b1@sha256:...`, `network_mode: host`, `/mnt/user/media:ro` + `/mnt/user/photos:ro` | Master-Doku sagt explizit "Plex-Media-Server ist historischer Host-Sonderfall, nicht als Repo-Compose-Stack enthalten" — **das stimmt nicht mehr**, Plex ist jetzt ein Repo-Compose-Stack |
|
||||||
|
| `host-services/docker/` | leeres Verzeichnis | nicht erwaehnt |
|
||||||
|
| `infra/dns/` | leeres Verzeichnis | nicht erwaehnt |
|
||||||
|
| `ops/Semaphore/` | Skripten/Playbooks aber kein Compose | nicht erwaehnt |
|
||||||
|
| `ops/backrest/` | leeres Verzeichnis (Stack laut Master-Doku am 2026-05-15 entfernt) | korrekt als entfernt dokumentiert; Verzeichnis sollte leer bleiben oder weg |
|
||||||
|
| `apps/firefly/`, `apps/firefly-fints/` | leere Verzeichnisse | nicht erwaehnt |
|
||||||
|
| `apps/stirling-pdf/` | leeres Verzeichnis (durch `bentopdf` abgeloest) | korrekt als abgeloest dokumentiert |
|
||||||
|
|
||||||
|
- **Aktion (Doku-Synchronisierung):** `HOMELAB_ARCHITECTURE_MASTER_V2.md` Abschnitt 7 (Container-Zielbild), `docs/SERVICE_CATALOG.md` und `docs/REPO_MAP.md` um Jellyfin und Plex erweitern. Plex-Doku im Master umschreiben: nicht mehr "historisch ausserhalb Repo", sondern "Compose-Stack mit `network_mode: host` als VPN-Discovery-Ausnahme".
|
||||||
|
- **Aktion (Repo-Hygiene):** Die leeren Verzeichnisse `apps/firefly`, `apps/firefly-fints`, `apps/stirling-pdf`, `host-services/docker`, `infra/dns`, `ops/backrest`, `ops/grafana-influxdb/scripts`, `ops/Semaphore/playbooks`, `ops/Semaphore/Scripts` aufraeumen — Master-Doku sagt: "Leere `.keep`-Platzhalter wurden entfernt; neue Verzeichnisse sollen erst mit konkretem Inhalt ins Repo." Diese Verzeichnisse verletzen diese Regel passiv.
|
||||||
|
|
||||||
|
### 2.6 Image-Pinning
|
||||||
|
|
||||||
|
Lt. `docs/NEXT_SPRINT_TODO_2026-05-16.md` sind diese Stacks noch nicht voll versioniert gepinnt:
|
||||||
|
- `ddns-updater` — `latest...@sha256`
|
||||||
|
- `glances` — `latest-full@sha256`
|
||||||
|
- `scrutiny` — `latest-omnibus@sha256`
|
||||||
|
|
||||||
|
Das ist bewusst dokumentiert und kein Audit-Befund.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Schicht B — Hardening-Sprint 2026-05 (Sitrep)
|
||||||
|
|
||||||
|
Dies war der Sprint, der nach dem 2026-05 Restore explizit gesetzt wurde. Stand im Repo:
|
||||||
|
|
||||||
|
| Sprint-Item | Stand 2026-05-16 (Plan) | Stand 2026-05-23 (Repo) | Beleg |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **(1) Backup-Konsistenz** — `dump_sqlite_container` fuer Gitea/Vaultwarden/Uptime-Kuma/Speedtest/Filebrowser + `pg_dump` Nextcloud | offen | ✅ erledigt | `ops/borg-ui/scripts/pre-backup-dumps.sh` Z. 97–139 (`dump_sqlite_container`), Z. 253–258 (Nextcloud `pg_dump`), Z. 261–264 (alle SQLite-Container mit Host-Fallback), Z. 267 (Filebrowser BoltDB). Borg-Scope erweitert um `/mnt/user/services` (Borg-UI Compose Z. 26 + `all-important-sources.txt` Z. 23–25). |
|
||||||
|
| **(2) Filebrowser entschaerfen** — `/mnt/user/appdata:/srv/appdata` weg, gezielte RW-Subpfade | offen | ✅ erledigt | `ops/filebrowser/docker-compose.yml` Z. 11–16. Keine Appdata-Mounts mehr. Nur noch `/mnt/user/documents`, `/mnt/user/photos`, `/mnt/user/projekte` als Datenmounts plus eigener `/database` und `/config`. |
|
||||||
|
| **(3) Authelia Argon2id haerten** — iterations 3, memory 65536, parallelism 4 | offen | ✅ erledigt | `security/authelia/configuration.yml` Z. 17–25. Exakt die geplanten Parameter sind aktiv. |
|
||||||
|
| **(4) Gitea Webhook-Allowlist** — `ALLOWED_HOST_LIST=*` einschraenken | offen | ✅ erledigt | `core/gitea/docker-compose.yml` Z. 18: `GITEA__webhook__ALLOWED_HOST_LIST=komodo-core,localhost,127.0.0.1,192.168.178.0/24`. Zusatz aus heutigem Commit: Public Registration und OpenID-Signup/Signin sind deaktiviert. |
|
||||||
|
|
||||||
|
Alle vier Items sind **im Repo abgeschlossen**. Live-Wirksamkeit haengt am Komodo-Deploy aus Gitea — und genau da haengt aktuell der ungepushte Commit `cd650b1` davor (siehe 2.1). Solange er nicht in Gitea ist, ist insbesondere die Gitea-Signup-Schliessung im Live-Stand nicht garantiert.
|
||||||
|
|
||||||
|
**Bewusst nicht angefasste Liste (Operator-Entscheidung 2026-05-16) ist weiterhin gueltig:**
|
||||||
|
- Hermes — bleibt VM-seitig offen, NAS-Stack bewusst nicht starten
|
||||||
|
- Disk1 NTFS — Phase-2-Migration nach Plan
|
||||||
|
- Komodo native Auth ohne ForwardAuth
|
||||||
|
- Grafana/Influxdb3-core `user: "0"`
|
||||||
|
- Image-Pinning-Vereinheitlichung (`:latest@sha256:`) fuer ddns-updater/glances/scrutiny
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Schicht C — "Endstufe?"-Bewertung
|
||||||
|
|
||||||
|
### 4.1 Backup/Restore-Readiness
|
||||||
|
|
||||||
|
- **Dump-Coverage:** `pre-backup-dumps.sh` deckt 14 Quellen ab: PostgreSQL-Globals + 3 Shared-DBs + 3 dedizierte Postgres (mealie, immich, nextcloud) + 4 SQLite (gitea, vaultwarden, uptime-kuma, speedtest-tracker) + Filebrowser-BoltDB + Borg-UI + Grafana + Komodo-Mongo. Deckt 1:1 die Restore-Matrix-Eintraege ab.
|
||||||
|
- **Borg-Scope:** `all-important-sources.txt` enthaelt 27 Eintraege inkl. neuer `services/homelab-infra`, `services/stacks`, `services/posture-check` und `secrets`.
|
||||||
|
- **Restore-Validierungen:** Laut `docs/RESTORE_MATRIX.md` sind am 2026-05-07 Mini-Restores fuer `gitea`, `vaultwarden` und `paperless` validiert worden — dokumentierter Stand.
|
||||||
|
- **Live offen:** Wann lief der letzte Borg-Lauf? Sind alle Dumps unter `/mnt/user/backups/borg/dumps/latest` frischer als 24h? Siehe Live-Checkliste 9.4.
|
||||||
|
|
||||||
|
### 4.2 Monitoring-Migration
|
||||||
|
|
||||||
|
- Repo-Zielzustand `monitoring/docker-compose.yml` (337 Zeilen Compose) existiert mit Prometheus, Alertmanager, ntfy-Bridge, Blackbox-Exporter, Loki, Promtail, Grafana, node-exporter, cAdvisor, InfluxDB3 Core.
|
||||||
|
- Provisioning unter `monitoring/grafana/provisioning/` und `monitoring/prometheus/`, `monitoring/loki/`, `monitoring/promtail/`, `monitoring/alertmanager/`, `monitoring/blackbox/` vollstaendig vorhanden.
|
||||||
|
- Alte Stacks `ops/grafana-influxdb/` und `ops/loki/` bewusst noch im Repo (dokumentierter Altstand, Rollback-Referenz).
|
||||||
|
- **Live offen:** Ist `monitoring` schon als Komodo-Stack deployed? Laufen die Container? Sind die Secret-Dateien `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt`, `influxdb3_admin_token.json` auf dem Host? Siehe Live-Checkliste 9.5.
|
||||||
|
|
||||||
|
### 4.3 Repo-Hygiene
|
||||||
|
|
||||||
|
| Befund | Schwere | Aktion |
|
||||||
|
|---|---|---|
|
||||||
|
| 8 leere Verzeichnisse (`apps/firefly`, `apps/firefly-fints`, `apps/stirling-pdf`, `host-services/docker`, `infra/dns`, `ops/backrest`, `ops/grafana-influxdb/scripts`, `ops/Semaphore/playbooks`, `ops/Semaphore/Scripts`) | klein | Aufraeumen, danach committen |
|
||||||
|
| `.serena/` untracked, nicht in `.gitignore` | klein | `.serena/` zu `.gitignore` hinzufuegen |
|
||||||
|
| 3 `ops/windows-reinstall/*.ps1` untracked | klein | Entscheidung treffen: ins Repo oder ignorieren |
|
||||||
|
|
||||||
|
### 4.4 Bekannte dokumentierte Ausnahmen
|
||||||
|
|
||||||
|
Aus `HOMELAB_ARCHITECTURE_MASTER_V2.md` Abschnitt 10 — alle weiterhin gueltig und durch den Policy-Check abgedeckt (`ops/policy-checks/last-report.md` 0 Critical):
|
||||||
|
|
||||||
|
- Traefik 80/443
|
||||||
|
- Tailscale Host-Netz + Capabilities
|
||||||
|
- AdGuard Port 53 + 8082 (Admin-Port LAN-only, dokumentiert; **offener Punkt im Master:** "Traefik-Absicherung ausstehend (Block F)" — bewusst spaeter)
|
||||||
|
- Plex Host-Netz (aber Master-Doku-Eintrag jetzt falsch, siehe 2.5)
|
||||||
|
- Scrutiny `privileged: true`
|
||||||
|
- Komodo Docker-Socket + keine pauschale Middleware
|
||||||
|
- glance-docker-socket-proxy Read-only Socket
|
||||||
|
- Gitea SSH 222
|
||||||
|
- ddns-updater `frontend_net`
|
||||||
|
- mail-archiver Hybrid-Netze
|
||||||
|
- `traefik/dynamic/*` manueller Host-Sync
|
||||||
|
- nextcloud native Auth
|
||||||
|
- monitoring-influxdb3-core LAN 8181 + `user: "0"`
|
||||||
|
- monitoring-promtail Docker-Socket read-only
|
||||||
|
|
||||||
|
Keine ungeplanten neuen Ausnahmen.
|
||||||
|
|
||||||
|
### 4.5 Endstufen-Definition
|
||||||
|
|
||||||
|
"Endstufe" ist erreicht, wenn alle folgenden Punkte gruen sind:
|
||||||
|
|
||||||
|
1. **Gitea = Quelle der Wahrheit** — kein lokaler Commit ohne Push 🟡 (heute: `cd650b1` ungepusht)
|
||||||
|
2. **Hardening-Sprint im Repo abgeschlossen** 🟢
|
||||||
|
3. **Backup-Konsistenz live verifiziert (Borg laeuft, Dumps frisch)** ❓ Live
|
||||||
|
4. **Monitoring-Stack live, alte Altstaende gestoppt** 🟡
|
||||||
|
5. **Doku synchron mit Repo (Jellyfin/Plex, leere Verzeichnisse, ...)** 🟠
|
||||||
|
6. **Policy-Check 0 Critical** 🟢 (4 Warnings sind dokumentierte Ausnahmen)
|
||||||
|
7. **Restore-Lab gepflegt (`mail-archiver` als naechste Uebung empfohlen)** 🟡 dokumentiert offen
|
||||||
|
|
||||||
|
Sechs der sieben Punkte sind in Reichweite ohne neue Architekturentscheidungen. Punkt 3 und 4 brauchen Live-Daten (Abschnitt 9). Punkt 1 ist 1 Push entfernt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Priorisierte Restliste
|
||||||
|
|
||||||
|
| Prio | Aktion | Begruendung | Aufwand |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **P0** | `cd650b1` nach Gitea pushen | GitOps-Quelle-der-Wahrheit, Voraussetzung fuer alles weitere | 30 Sekunden |
|
||||||
|
| **P0** | Live-Daten aus Abschnitt 9 einholen | Ohne Live-Frische ist Endstufen-Bewertung unvollstaendig | 5 Minuten |
|
||||||
|
| **P1** | Monitoring-Stack live finalisieren (Secrets pruefen, deployen, Smoke-Test, alte Altstaende stoppen) | Aus `docs/NEXT_SPRINT_TODO_2026-05-16.md` der naechste produktive Schritt | 1–2 Stunden mit Tests |
|
||||||
|
| **P2** | Doku-Drift schliessen: Jellyfin und Plex in `HOMELAB_ARCHITECTURE_MASTER_V2.md` 7.4 + 7.1, `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md` ergaenzen; Plex-Eintrag in Abschnitt 7.7 "noch offene Sonderfaelle" entfernen (ist umgesetzt) | Doku ist Source of Truth fuer KI-Audits und Nachfolge | 30 Minuten |
|
||||||
|
| **P2** | Home Assistant -> InfluxDB final testen, HA-Dashboard in `monitoring-grafana` anlegen | aus NEXT_SPRINT_TODO | 1–2 Stunden |
|
||||||
|
| **P3** | Repo-Hygiene: 8 leere Verzeichnisse loeschen, `.serena/` in `.gitignore`, Entscheidung zu `ops/windows-reinstall/*.ps1` | minor, aber dokumentiert | 15 Minuten |
|
||||||
|
| **P3** | Naechster Restore-Lab-Lauf: `mail-archiver` (empfohlen in `RESTORE_MATRIX.md`) | Restore-Routine ueben, bevor sie gebraucht wird | 1 Stunde |
|
||||||
|
| **P4** | `.gitattributes` mit `* text=auto eol=lf` hinzufuegen, um CRLF/LF-Mount-Effekte bei KI-Audits zu vermeiden | klein, kosmetisch fuer kuenftige Audits | 5 Minuten |
|
||||||
|
| **bleibt** | Hermes VM-Seite, Disk1-NTFS Phase 2, AdGuard Admin-Port hinter Traefik (Block F), Image-Pinning ddns/glances/scrutiny | bewusste Operator-Entscheidung, kein Audit-Beduerfnis | nicht jetzt |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Was bewusst NICHT angetastet wurde (Audit-Verzicht)
|
||||||
|
|
||||||
|
Konsistent mit der bekannten Nicht-Anfassen-Liste:
|
||||||
|
|
||||||
|
- Hermes (VM-seitig offen)
|
||||||
|
- Disk1 NTFS (Phase-2-Migration nach Plan)
|
||||||
|
- Komodo native Auth ohne ForwardAuth
|
||||||
|
- Grafana / influxdb3-core `user: "0"` Uebergangsausnahme
|
||||||
|
- Image-Pinning-Vereinheitlichung fuer ddns-updater/glances/scrutiny
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Risiken und Drift-Indikatoren
|
||||||
|
|
||||||
|
| Risiko | Wahrscheinlichkeit | Wirkung | Migitation |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Lokaler Clone-Verlust (Disk, Reinstall) bevor `cd650b1` gepusht wurde | gering, aber real (Du bist im Reinstall-Kontext, siehe `ops/windows-reinstall/`!) | Verlust von Gitea-Signup-Closure und Posture-Check-Verbesserungen | **Sofort pushen** |
|
||||||
|
| Komodo deployt aus Gitea, `gitea` und `borg-ui` laufen aktuell ohne die heutigen Verbesserungen | mittel | Gitea Signup steht noch offen, Borg-Scope umfasst `/mnt/user/services` noch nicht | Push + Komodo-Reaktion pruefen |
|
||||||
|
| 47 vermeintlich modified files koennten doch echte Diffs sein, wenn der Windows-Host etwas anderes zeigt | gering | falsche Audit-Aussage | Punkt 9.1 auf dem Windows-Host pruefen |
|
||||||
|
| Doku-Drift wird groesser, wenn weitere Stacks ohne Doku-Update hinzukommen | mittel | KI-Audits und Onboarding leiden | P2-Doku-Sync nicht aufschieben |
|
||||||
|
| Monitoring-Stack-Migration unfertig, alter und neuer Stack koennten parallel werden | mittel | Doppelte Metric-/Log-Pipeline, Verwirrung bei Diagnose | Live-Status klaeren bevor Deploy |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Sources of Truth — Schnellzugriff
|
||||||
|
|
||||||
|
- Operative Quelle der Wahrheit: Gitea `origin/master` (https://git.kaleschke.info/Micha/homelab-infra)
|
||||||
|
- Architektur-Master: `HOMELAB_ARCHITECTURE_MASTER_V2.md`
|
||||||
|
- Workflow / GitOps-Regeln: `docs/WORKFLOW.md`
|
||||||
|
- Drift-Runbook: `docs/GITOPS_DRIFT_RUNBOOK.md`
|
||||||
|
- Restore-Quellen: `docs/RESTORE_MATRIX.md`, `docs/DISASTER_RECOVERY.md`
|
||||||
|
- Letzter Policy-Check: `ops/policy-checks/last-report.md` (0 Critical)
|
||||||
|
- Letzte Sprint-Restliste: `docs/NEXT_SPRINT_TODO_2026-05-16.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Live-Daten-Checkliste — bitte ausfuehren und zurueckspielen
|
||||||
|
|
||||||
|
Fuehre die folgenden Bloecke am Unraid-Host (per SSH oder Web-Terminal) und am Windows-Host (Git Bash / PowerShell in `G:\Gitea_Clone\homelab-infra`) aus und pastiere die Outputs zurueck. Ich integriere sie dann in diesen Report.
|
||||||
|
|
||||||
|
### 9.1 Windows-Host: Echter Working-Tree-Status
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd G:\Gitea_Clone\homelab-infra
|
||||||
|
git status --short
|
||||||
|
git log origin/master..HEAD --oneline
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartet: Working tree leer (oder nur die 4 Untracked). `cd650b1` als einziger lokaler Commit.
|
||||||
|
|
||||||
|
### 9.2 Unraid-Host: Gitea online
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sI --max-time 5 https://git.kaleschke.info/ | head -5
|
||||||
|
docker exec gitea sh -lc 'gitea --version'
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartet: `HTTP/2 200` (oder ein Auth-Code, der den Erreichbarkeitstest erfuellt). Gitea-Version stimmt mit Image-Tag `1.25.4` ueberein.
|
||||||
|
|
||||||
|
### 9.3 Unraid-Host: Komodo-Webhook-Status
|
||||||
|
|
||||||
|
In Komodo UI fuer jeden produktiven Stack aus `Micha/homelab-infra` pruefen:
|
||||||
|
- `webhook_enabled: true`
|
||||||
|
- Gitea-Hook auf `http://komodo-core:9120/listener/github/stack/<stack-id>/deploy` aktiv
|
||||||
|
- `last_status` der letzten Webhook-Delivery in Gitea (Repository -> Settings -> Webhooks)
|
||||||
|
|
||||||
|
Pflicht-Stacks zum Pruefen: `traefik`, `gitea`, `authelia`, `vaultwarden`, `postgresql17`, `redis`, `paperless-ngx`, `immich`, `nextcloud`, `mealie`, `mail-archiver`, `ntfy`, `homepage`, `paperless-gpt`, `borg-ui`, `filebrowser`, `code-server`, `uptime-kuma`, `glance`, `glances`, `scrutiny`, `speedtest-tracker`, `bentopdf`, `ddns-updater`, `komodo`, `jellyfin`, `plex`, `adguard`, `tailscale`, `monitoring`, `hermes-agent` (sofern produktiv).
|
||||||
|
|
||||||
|
Bei Stacks **ohne** aktiven Webhook bitte den Grund vermerken (dokumentierte Ausnahme oder Nachholbedarf).
|
||||||
|
|
||||||
|
### 9.4 Unraid-Host: Borg-Lauf-Frische und Dump-Coverage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -lah /mnt/user/backups/borg/dumps/latest/
|
||||||
|
stat -c '%y %n' /mnt/user/backups/borg/dumps/latest/*.dump /mnt/user/backups/borg/dumps/latest/*.sql /mnt/user/backups/borg/dumps/latest/*.archive.gz /mnt/user/backups/borg/dumps/latest/*.sqlite 2>/dev/null | sort
|
||||||
|
docker exec borg-ui borg list --short 2>&1 | tail -10
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartet: Alle 14 Artefakte aus 4.1 sind vorhanden, mtime juenger als 24h. Borg-Archive-Liste zeigt regelmaessige Laeufe.
|
||||||
|
|
||||||
|
### 9.5 Unraid-Host: Monitoring-Stack live?
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | grep monitoring
|
||||||
|
ls -la /mnt/user/appdata/secrets/ | grep -E 'monitoring|influxdb3'
|
||||||
|
curl -sI --max-time 5 https://monitoring.kaleschke.info/ | head -5
|
||||||
|
ss -ltnp 2>/dev/null | grep -E ':8181|:9090|:3100' | head -10
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartet: Entweder alle `monitoring-*` Container laufen (dann ist 4.2 🟢) oder gar nicht (dann ist 4.2 🟡 wie aktuell bewertet). Halb-Zustand ist ein Audit-Befund.
|
||||||
|
|
||||||
|
### 9.6 Unraid-Host: GitOps-Pflichtmatrix Spot-Check fuer einen Stack
|
||||||
|
|
||||||
|
Beispiel `gitea` (weil der heutige Commit ihn betrifft):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /mnt/user/services/stacks/gitea
|
||||||
|
git rev-parse --short HEAD
|
||||||
|
git status -sb
|
||||||
|
docker inspect gitea --format '{{.Image}}'
|
||||||
|
docker exec gitea sh -lc 'env | grep -E "ALLOWED_HOST_LIST|DISABLE_REGISTRATION|ENABLE_OPENID"'
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartet: Komodo-Workspace `HEAD` zeigt entweder auf `cd650b1` (wenn Push schon erfolgt + Komodo deployed) oder auf `af231dd` (vor dem Push). ENV-Vars in der Live-Runtime spiegeln den Commit, der Komodo zuletzt deployed hat.
|
||||||
|
|
||||||
|
### 9.7 Unraid-Host: Host-Listener-Spot-Check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ss -ltnp 2>/dev/null | grep -E ':80|:443|:53|:222|:8082|:8181' | sort
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartet exakt die dokumentierten Ausnahmen aus `HOMELAB_ARCHITECTURE_MASTER_V2.md` Abschnitt 10. Andere Listener = Befund.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Nachhalte-Vorschlag
|
||||||
|
|
||||||
|
Wenn Du moechtest, halte ich diesen Audit in zwei Schritten zu Ende:
|
||||||
|
|
||||||
|
1. Du fuehrst Abschnitt 9 aus und pastierst die Outputs zurueck.
|
||||||
|
2. Ich aktualisiere diesen Report mit den Live-Ergebnissen, ergaenze die Ampel und schliesse die offenen 🟡/🟠 Punkte oder benenne sie als echte Restliste.
|
||||||
|
|
||||||
|
Bis dahin gilt der Stand dieses Reports als Repo-Audit, nicht als Endstufen-Zertifikat.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Homelab Audit Final - 2026-05-23
|
||||||
|
|
||||||
|
Stand: 2026-05-25 07:33 CEST. Ergebnis nach Push, Live-Messung, Doku-Sync, Repo-Hygiene und erneuter Live-Nachmessung.
|
||||||
|
|
||||||
|
| Punkt | Ampel | Beleg |
|
||||||
|
|---|---|---|
|
||||||
|
| P0 `cd650b1` nach Gitea pushen | gruen | Push `af231dd..cd650b1 master -> master`; produktiver Runtime-Stand `66ee10c` enthaelt `cd650b1`. Die abschliessenden Audit-Doku-Commits liegen in Gitea; der runtime-relevante Stack-Inhalt fuer `gitea`, `borg-ui` und `monitoring` ist seit `66ee10c` unveraendert. |
|
||||||
|
| P0 Live-Daten ablegen | gruen | `docs/AUDIT_2026-05-23_LIVE.md` angelegt, keine Secret-Werte dokumentiert. |
|
||||||
|
| P1 Monitoring live / Altstaende down | gruen | 10 `monitoring-*` Container laufen, `0` unhealthy, `0` starting, `0` stopped; alte `grafana`/`influxdb3-core`/`loki`/`alloy` Container sind nicht vorhanden. `monitoring.kaleschke.info` liefert 302 zu Authelia, Prometheus ist bereit, Loki `/ready` liefert `ready`. |
|
||||||
|
| P1 Jellyfin/Plex Doku | gruen | `HOMELAB_ARCHITECTURE_MASTER_V2.md`, `docs/SERVICE_CATALOG.md` und `docs/REPO_MAP.md` ergaenzt; Plex ist als Repo-Compose-Stack dokumentiert, nicht mehr als nicht migrierter Dockerman-Sonderfall. |
|
||||||
|
| P2 Borg-Frische | gruen | Borg-UI DB: letzter Backup-Job `completed`, Archiv `Taegliche-Sicherung-2026-05-25T05:52:44.157`, `nfiles=100221`; 15 kanonische Dump-/Archive-Artefakte von 2026-05-25 06:09/06:10 CEST und damit juenger als 24 h. |
|
||||||
|
| P3 Repo-Hygiene | gruen | `.serena/` in `.gitignore`; leere Verzeichnisse entfernt; `ops/windows-reinstall/*.ps1` bewusst ins Repo aufgenommen. |
|
||||||
|
| Policy-Check | gruen | `ops/policy-checks/check_repo.ps1`: 0 Critical, dokumentierte Warnings zu Plex Host-Netz, InfluxDB/Grafana `user: "0"` und bekannten mutable-tag-Ausnahmen. |
|
||||||
|
|
||||||
|
## Bewusst offen
|
||||||
|
|
||||||
|
- Keine offenen Punkte aus der Audit-Restliste.
|
||||||
|
- Nicht Teil des Audits: Nach Disk1 Phase 2 laeuft seit 2026-05-25 eine nicht korrigierende Parity-Pruefung (`NOCORRECT`) im Hintergrund; Zwischenstand der Nachmessung: `mdResyncAction=check P`, `mdResyncCorr=0`.
|
||||||
|
|
||||||
|
## Schlussbewertung
|
||||||
|
|
||||||
|
Das Homelab ist fuer die Audit-Restliste in der Endstufe. Es gibt keine kritischen Repo-, GitOps- oder Live-Befunde aus diesem Audit. Monitoring ist produktiv und ready, alte Altstaende sind down, Backups und Dumps sind frisch.
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# Homelab Audit Live-Daten - 2026-05-23
|
||||||
|
|
||||||
|
Stand: 2026-05-23 11:27 CEST. Quelle: lokaler Windows-Clone und SSH auf `Kallilabcore`. Secret-Werte wurden nicht ausgelesen oder redaktiert; dokumentiert sind nur Status, Dateinamen, Modi, Env-Key-Namen und nicht geheime Bool-/Host-Werte.
|
||||||
|
|
||||||
|
## 9.1 Windows-Host / Git
|
||||||
|
|
||||||
|
- `git status --short` nach dem initialen Push: keine tracked Modifikationen, untracked waren `.serena/`, `docs/AUDIT_2026-05-23.md`, `docs/CODEX_ENDSTUFE_PROMPT_2026-05-23.md` und drei `ops/windows-reinstall/*.ps1`.
|
||||||
|
- `cd650b1` wurde nach `origin/master` gepusht: `af231dd..cd650b1 master -> master`.
|
||||||
|
|
||||||
|
## 9.2 Gitea online
|
||||||
|
|
||||||
|
- `curl -sI https://git.kaleschke.info/`: `HTTP/2 200`.
|
||||||
|
- `docker exec gitea gitea --version`: `1.25.4`.
|
||||||
|
- Signup-Smoke: `/user/sign_up` meldet Registration disabled.
|
||||||
|
|
||||||
|
## 9.3 Komodo / Workspace-Reaktion
|
||||||
|
|
||||||
|
| Stack | Workspace HEAD | Status | Live-Beleg |
|
||||||
|
|---|---:|---|---|
|
||||||
|
| `gitea` | `cd650b1` | `## master...origin/master` | Container neu gestartet, Env-Keys aktiv |
|
||||||
|
| `borg-ui` | `cd650b1` | `## master...origin/master` | Container healthy, `/mnt/user/services -> /local/services` gemountet |
|
||||||
|
| `monitoring` | `cd650b1` | `## master...origin/master`, untracked Host-Backup `monitoring/prometheus/alerts.yml.bak-20260520-incident` | Stack laeuft seit 4 Tagen |
|
||||||
|
|
||||||
|
Gitea Live-Env ohne Secrets:
|
||||||
|
|
||||||
|
```text
|
||||||
|
GITEA__service__DISABLE_REGISTRATION=true
|
||||||
|
GITEA__openid__ENABLE_OPENID_SIGNIN=false
|
||||||
|
GITEA__openid__ENABLE_OPENID_SIGNUP=false
|
||||||
|
GITEA__webhook__ALLOWED_HOST_LIST=komodo-core,localhost,127.0.0.1,192.168.178.0/24
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9.4 Borg-Lauf-Frische und Dump-Coverage
|
||||||
|
|
||||||
|
- Borg UI DB: Repository `appdata-critical`, `last_backup=2026-05-23 02:30:12 UTC`, `archive_count=30`, letzter Job `completed`, `nfiles=100056`.
|
||||||
|
- Schedule: `Taegliche Sicherung` enabled, letzter Lauf `2026-05-23 02:30:11 UTC`, naechster Lauf `2026-05-24 02:30:00 UTC`.
|
||||||
|
- Hinweis: `docker exec borg-ui borg list --short` funktioniert ohne Repository-Parameter/Env nicht (`Invalid location format: ""`). Der Borg-UI-Status wurde deshalb ueber die lokale Borg-UI-Datenbank ohne Secret-Spalten ausgewertet.
|
||||||
|
|
||||||
|
Frische Artefakte in `/mnt/user/backups/borg/dumps/latest`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
2026-05-23 04:00 postgresql17-globals.sql
|
||||||
|
2026-05-23 04:00 postgresql17-mailarchiver.dump
|
||||||
|
2026-05-23 04:00 postgresql17-paperless.dump
|
||||||
|
2026-05-23 04:01 postgresql17-authelia.dump
|
||||||
|
2026-05-23 04:01 mealie.dump
|
||||||
|
2026-05-23 04:01 gitea.sqlite.dump
|
||||||
|
2026-05-23 04:01 immich.dump
|
||||||
|
2026-05-23 04:01 nextcloud.dump
|
||||||
|
2026-05-23 04:01 uptime-kuma.sqlite.dump
|
||||||
|
2026-05-23 04:01 vaultwarden.sqlite.dump
|
||||||
|
2026-05-23 04:01 speedtest-tracker.sqlite.dump
|
||||||
|
2026-05-23 04:01 filebrowser.bolt.dump
|
||||||
|
2026-05-23 04:01 borg-ui.sqlite
|
||||||
|
2026-05-23 04:01 grafana.sqlite
|
||||||
|
2026-05-23 04:01 komodo-mongo.archive.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
Bewertung: 15 aktuelle Dump-/Archive-Artefakte sind juenger als 24 h. Aeltere nackte `.sqlite`-Dateien vom 2026-05-16 liegen noch im Verzeichnis, sind aber Legacy-Kopien ohne `.dump`-Suffix und nicht Teil der aktuellen Dump-Serie.
|
||||||
|
|
||||||
|
## 9.5 Monitoring-Stack
|
||||||
|
|
||||||
|
- Aktive Container: `monitoring-grafana`, `monitoring-promtail`, `monitoring-prometheus`, `monitoring-cadvisor`, `monitoring-influxdb3-core`, `monitoring-loki`, `monitoring-blackbox-exporter`, `monitoring-alertmanager-ntfy-bridge`, `monitoring-alertmanager`, `monitoring-node-exporter`.
|
||||||
|
- Alte Altcontainer `grafana`, `influxdb3-core`, `loki`, `alloy`: nicht vorhanden in `docker ps -a`.
|
||||||
|
- Secret-Dateien vorhanden und mode `600`: `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt`, `influxdb3_admin_token.json`.
|
||||||
|
- `https://monitoring.kaleschke.info/`: `HTTP/2 302` zu Authelia, wie erwartet.
|
||||||
|
- Host-Listener fuer Monitoring: nur `127.0.0.1:8181`; keine Host-Listener fuer `:9090` oder `:3100`.
|
||||||
|
- Prometheus readiness: `Prometheus Server is Ready.`
|
||||||
|
- Grafana health: `database=ok`, Version `12.4.3`.
|
||||||
|
- Loki: Container laeuft und API/metrics liefert `loki_build_info`; `/ready` liefert aktuell `503 Service Unavailable`, waehrend Query-/Metrics-Endpunkte 200-Zaehler zeigen. Kein Reparaturversuch gestartet, weil der produktive Logpfad nachweislich Daten annimmt und die Stop-Regel keine blinden Eingriffe erlaubt.
|
||||||
|
|
||||||
|
## 9.6 GitOps Spot-Check Gitea
|
||||||
|
|
||||||
|
- `/mnt/user/services/stacks/gitea`: `cd650b1`, `## master...origin/master`.
|
||||||
|
- Docker-Image: `docker.gitea.com/gitea:1.25.4`.
|
||||||
|
- Live-ENV spiegelt `cd650b1` fuer Registrierung, OpenID und Webhook-Allowlist.
|
||||||
|
|
||||||
|
## 9.7 Host-Listener
|
||||||
|
|
||||||
|
Dokumentierte Listener gefunden:
|
||||||
|
|
||||||
|
- `:80`, `:443` Traefik
|
||||||
|
- `:53` AdGuard DNS
|
||||||
|
- `:222` Gitea SSH
|
||||||
|
- `:8082` AdGuard Admin
|
||||||
|
- `127.0.0.1:8181` Monitoring InfluxDB
|
||||||
|
|
||||||
|
Zusaetzlich sichtbar: mehrere `wsdd2` Listener auf `:5355` je Interface. Das ist Host-/Unraid-Service-Discovery, kein Compose-Webdienst und kein GitOps-Stack-Port.
|
||||||
@@ -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 `<minor>@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-<datum>.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 2>/dev/null \
|
||||||
|
| openssl x509 -noout -dates 2>/dev/null
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### GitOps-Konsistenz
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Komodo Stack-Workspace vs. Repo
|
||||||
|
cd /mnt/user/services/stacks/<stackname>
|
||||||
|
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/<dienst>/` 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.
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
## Naechster Startpunkt 2026-05-26
|
||||||
|
|
||||||
|
Kontext bewusst gesichert, bevor weitere Live-Aenderungen passieren:
|
||||||
|
|
||||||
|
1. USV-Entscheidung treffen: aktuell ist keine funktionierende USV-Abschaltung nachgewiesen.
|
||||||
|
2. Gitea-Bundle-/Mirror-Mechanik und Borg-Passphrase-Offsite-Sicherung entscheiden.
|
||||||
|
3. Authelia 2FA/OIDC weiterhin nicht anfassen; das bleibt bewusst der letzte Block.
|
||||||
|
|
||||||
|
## Sprint 0 - Inventar und Baseline
|
||||||
|
|
||||||
|
| Status | Aufgabe | Ergebnis |
|
||||||
|
|---|---|---|
|
||||||
|
| erledigt | Hardware-Inventar ausfuellen | CPU, RAM, Mainboard, BIOS, NIC, Controller, Disks, SMART und Capacity-Baseline erfasst; USV ist als nicht validiert dokumentiert |
|
||||||
|
| in Arbeit | Netzwerk-Inventar ausfuellen | Host-IP, Gateway, Tailscale-IP und AdGuard-Bind erfasst; Router-/VLAN-Details offen |
|
||||||
|
| erledigt (Baseline) | Externe Abhaengigkeiten dokumentieren | `docs/EXTERNAL_DEPENDENCIES.md` enthaelt bekannte Provider, Kritikalitaet, Ausfallplaene; Account-Recovery-Codes/Zahlungswege bleiben Off-Repo-Operatorcheck |
|
||||||
|
| erledigt (Baseline) | Services-Recovery-Pfade beschreiben | `docs/SERVICES_RECOVERY.md` enthaelt Gitea-/Komodo-/Secrets-Sonderpfade; Gitea-Bundle-/Mirror-Mechanik bleibt als Umsetzungsentscheidung offen |
|
||||||
|
| erledigt | 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 |
|
||||||
|
| erledigt | AdGuard Admin-Port auf Tailscale-IP binden | Live validiert: `ss -ltnp` zeigt `100.80.98.33:8082`, DNS auf Port 53 funktioniert, LAN-Zugriff auf `192.168.178.58:8082` schlaegt fehl |
|
||||||
|
| erledigt | Alte Monitoring-Verzeichnisse entfernen | `ops/grafana-influxdb/` und `ops/loki/` sind aus dem aktiven Repo entfernt; Rollback erfolgt ueber Git-Historie |
|
||||||
|
| erledigt | Komodo/Gitea-Restdrift bereinigen | alter Komodo-Stack `grafana` ist inert und ohne Repo-Pfad/Webhook; Gitea-Hook `35` und `komodo`-Self-Hook `11` sind inaktiv; aktive Gitea-Hooks haben keine Fehlstatus |
|
||||||
|
| erledigt | Policy-Warnings triagieren | Plex Host-Netz und digest-gepinnte mutable Tags sind dokumentierte Info-Ausnahmen; `monitoring-influxdb3-core` als Root-Ausnahme bleibt bewusst als Warning sichtbar |
|
||||||
|
|
||||||
|
## 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 |
|
||||||
|
| erledigt (Baseline) | Capacity-/Lifecycle-Review erstellen | Cache 6 %, Array/User-Shares 33 %, lokale Backups 2.2G; externe Backup-/Cold-Storage-Groessen bleiben offen |
|
||||||
|
| 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
|
||||||
|
```
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# Capacity and Lifecycle - KalliLab CORE
|
||||||
|
|
||||||
|
Status: Initiale Capacity-Baseline 2026-05-26; externe Backup-/Cold-Storage-Groessen offen.
|
||||||
|
|
||||||
|
## 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 | 1.9T | 97G | 1.8T | 70 % Planung / 85 % Aktion | gruen, 6 % belegt |
|
||||||
|
| Disk1 / Array | 5.5T | 1.8T | 3.7T | 80 % Planung / 90 % Aktion | gruen, 33 % belegt |
|
||||||
|
| User Shares gesamt | 5.5T | 1.8T | 3.7T | 80 % Planung / 90 % Aktion | gruen, entspricht aktuell Disk1 |
|
||||||
|
| Backups lokal | 5.5T geteilter Array-Space | 2.2G unter `/mnt/user/backups` | 3.7T Share-frei | Review bei Borg-/Dump-Wachstum | lokal nicht unabhaengig vom Array |
|
||||||
|
| 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 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Medien | aktuell ca. 1.7T | groesster Speicherblock | Array-Erweiterung vor 80 % planen |
|
||||||
|
| Immich Fotos/Videos | aktuell ca. 23G | hoechster privater Datentopf | Restore-Test priorisieren |
|
||||||
|
| Paperless/Dokumente | aktuell ca. 199M im Documents-Share | wichtig, moderates Wachstum | Restore-Test existiert, Share-Wachstum beobachten |
|
||||||
|
| Nextcloud | TBD | Familiennutzung kann stark wachsen | Quota/Backup pruefen |
|
||||||
|
| Monitoring/Loki | begrenzt durch Retention | Retention kann Disk fuellen | Retention und Volume-Groesse bei Reviews pruefen |
|
||||||
|
| Borg Dumps | aktuell ca. 2.2G lokale Backups | 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 |
|
||||||
|
| Keine USV-Abschaltung | USV anschaffen/anschliessen oder Power-Loss-Risiko bewusst akzeptieren |
|
||||||
|
|
||||||
|
## 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 |
|
||||||
|
|---|---|---|
|
||||||
|
| 2026-05-26 | Cache 6 %, Array/User-Shares 33 %, lokale Backups 2.2G; keine validierte USV-Abschaltung | Capacity gruen; naechste operative Risiken sind USV-Entscheidung und externe Backup-/Cold-Storage-Groessen |
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Codex-Prompt: KalliLab Endstufe
|
||||||
|
|
||||||
|
Du hast Vollzugriff auf `G:\Gitea_Clone\homelab-infra`, Gitea-Push, Komodo, und SSH auf Unraid `Kallilabcore`.
|
||||||
|
|
||||||
|
## Lies zuerst
|
||||||
|
1. `CLAUDE.md`
|
||||||
|
2. `docs/AUDIT_2026-05-23.md` — dort steht die komplette Restliste
|
||||||
|
|
||||||
|
## Auftrag
|
||||||
|
Den Audit von oben verifizieren und die offenen Punkte abarbeiten, bis das Homelab in der Endstufe ist. Reihenfolge:
|
||||||
|
|
||||||
|
1. **P0** — Lokalen Commit `cd650b1` nach Gitea pushen, danach Komodo-Reaktion fuer `gitea` und `borg-ui` pruefen.
|
||||||
|
2. **P0** — Live-Daten aus Audit-Abschnitt 9 messen und in `docs/AUDIT_2026-05-23_LIVE.md` ablegen (Secrets redacten).
|
||||||
|
3. **P1** — Monitoring-Stack (`monitoring/`) live deployen, alte `ops/grafana-influxdb` und `ops/loki` `down` (nicht loeschen).
|
||||||
|
4. **P1** — Jellyfin und Plex in `HOMELAB_ARCHITECTURE_MASTER_V2.md`, `docs/SERVICE_CATALOG.md`, `docs/REPO_MAP.md` nachtragen. Plex-Eintrag "nicht als Repo-Stack enthalten" korrigieren.
|
||||||
|
5. **P2** — Borg-Lauf-Frische pruefen, ggf. neuen Lauf ausloesen, alle 14 Dump-Artefakte juenger als 24 h.
|
||||||
|
6. **P3** — Repo-Hygiene: 8 leere Verzeichnisse weg, `.serena/` in `.gitignore`, Entscheidung zu `ops/windows-reinstall/*.ps1`.
|
||||||
|
|
||||||
|
## Regeln (aus CLAUDE.md, nicht verhandelbar)
|
||||||
|
- Secrets nie im Klartext ausgeben.
|
||||||
|
- Keine Aenderungen direkt in Komodo, nur ueber Git → Push → Komodo.
|
||||||
|
- Kein `push --force`, kein blindes Loeschen von `/mnt/user/{appdata,documents,photos,services,backups}`.
|
||||||
|
- Working-Tree-Status nur aus `git status --short` ableiten, nie aus `git diff` ueber Linux-Mount.
|
||||||
|
- Traefik dynamic config wird nicht von Komodo deployed — Aenderungen dort manuell auf `/mnt/user/appdata/traefik/dynamic/` syncen.
|
||||||
|
- Nicht anfassen: Hermes, Disk1 NTFS Phase 2, Komodo-Auth, Grafana/InfluxDB `user: "0"`, Image-Pinning ddns/glances/scrutiny.
|
||||||
|
- Wenn zwei Reparaturversuche scheitern: stoppen, Drift-Runbook Pflichtmatrix, Operator fragen.
|
||||||
|
|
||||||
|
## Arbeitsmodus pro Block
|
||||||
|
Lesen → minimal aendern → `ops/policy-checks/check_repo.ps1` lokal → Commit → Push → Komodo-Reaktion + Smoke-Test → eine Zeile in `docs/MIGRATION_LOG.md`.
|
||||||
|
|
||||||
|
## Fertig
|
||||||
|
Wenn alles abgearbeitet ist (oder ein Punkt bewusst offen bleibt): `docs/AUDIT_2026-05-23_FINAL.md` schreiben mit Ampel + konkretem Beleg pro Punkt, committen, pushen, kurz an mich melden.
|
||||||
+34
-25
@@ -9,6 +9,8 @@ Verwandte Dokumente:
|
|||||||
- `docs/ROLLBACK.md` - Rueckweg bei Fehlern im laufenden GitOps-Betrieb
|
- `docs/ROLLBACK.md` - Rueckweg bei Fehlern im laufenden GitOps-Betrieb
|
||||||
- `docs/RESTORE_MATRIX.md` - Restore-Quellen und Verifikationsregeln pro Dienst
|
- `docs/RESTORE_MATRIX.md` - Restore-Quellen und Verifikationsregeln pro Dienst
|
||||||
- `docs/RESTORE_HANDBOOK.md` - praktische Restore-Betriebsanleitung
|
- `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
|
- `ops/borg-ui/BACKUP_SCOPE.md` - Zielbild des Borg-Scopes
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -60,12 +62,14 @@ Diese Punkte sollten **vor** einem echten Ausfall geklaert sein:
|
|||||||
|
|
||||||
| Thema | Sollzustand |
|
| Thema | Sollzustand |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Repo-Zugang ausserhalb von Gitea | externer Mirror oder lokaler aktueller Clone vorhanden |
|
| Repo-Zugang ausserhalb von Gitea | privater GitHub-Push-Mirror `michaelkaleschke-spec/homelab-infra` und lokaler aktueller Clone vorhanden |
|
||||||
| Unraid USB-/Flash-Backup | eingerichtet und wiederherstellbar |
|
| Unraid USB-/Flash-Backup | `unraid-flash-config.tar.gz` wird vor Borg unter `/mnt/user/backups/borg/dumps/latest` erzeugt und nach Hetzner/Borg gesichert; Unraid-Connect-Cloud-Backup optional zusaetzlich |
|
||||||
| Borg-Ziel | nicht nur lokal auf demselben Ausfallpfad |
|
| Borg-Ziel | nicht nur lokal auf demselben Ausfallpfad |
|
||||||
| Borg-Passphrase | extern sicher hinterlegt |
|
| 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 |
|
| Secrets-Dateien | ueber Borg bzw. Restore-Quellen abgedeckt |
|
||||||
| Komodo Stack ENV-Werte | extern dokumentiert, z. B. Vaultwarden |
|
| 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 |
|
| Restore-Smoke-Tests | fuer mindestens 1-2 kritische Dienste nachgewiesen |
|
||||||
|
|
||||||
**Wichtig:** Dieses Dokument ist nur so gut wie die Vorbereitung ausserhalb des Repos.
|
**Wichtig:** Dieses Dokument ist nur so gut wie die Vorbereitung ausserhalb des Repos.
|
||||||
@@ -81,13 +85,13 @@ Deshalb gilt:
|
|||||||
1. Wenn moeglich, Repo ueber einen externen Mirror oder einen lokalen aktuellen Clone holen.
|
1. Wenn moeglich, Repo ueber einen externen Mirror oder einen lokalen aktuellen Clone holen.
|
||||||
2. Nur wenn `gitea` bereits wieder verfuegbar ist, direkt aus `git.kaleschke.info` klonen.
|
2. Nur wenn `gitea` bereits wieder verfuegbar ist, direkt aus `git.kaleschke.info` klonen.
|
||||||
|
|
||||||
Empfohlene Wege:
|
Verfuegbare Wege:
|
||||||
|
|
||||||
- externer Push-Mirror
|
- externer Push-Mirror: `https://github.com/michaelkaleschke-spec/homelab-infra`
|
||||||
- lokaler Bare-Clone auf dem PC
|
- lokaler Bare-Clone auf dem PC
|
||||||
- normaler lokaler Arbeits-Clone auf dem PC
|
- normaler lokaler Arbeits-Clone auf dem PC
|
||||||
|
|
||||||
Wenn **kein externer Repo-Zugang** besteht, ist `services/gitea/data` selbst ein kritischer Restore-Pfad.
|
Wenn **weder GitHub-Mirror noch lokaler Repo-Clone** verfuegbar sind, ist `services/gitea/data` selbst ein kritischer Restore-Pfad.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -100,6 +104,8 @@ Wenn **kein externer Repo-Zugang** besteht, ist `services/gitea/data` selbst ein
|
|||||||
3. Array wieder zuweisen und starten
|
3. Array wieder zuweisen und starten
|
||||||
4. Grundlegende Shares pruefen
|
4. Grundlegende Shares pruefen
|
||||||
|
|
||||||
|
Primaere lokale/off-site Restore-Quelle fuer die bestehende Flash-Konfiguration ist das Borg-Artefakt `unraid-flash-config.tar.gz` aus `/mnt/user/backups/borg/dumps/latest`. Dieses Archiv enthaelt `/boot/config` und muss wie Secret-Material behandelt werden.
|
||||||
|
|
||||||
### 5.2 Erwartete Shares / Pfade
|
### 5.2 Erwartete Shares / Pfade
|
||||||
|
|
||||||
Mindestens diese Pfade muessen wieder verfuegbar sein:
|
Mindestens diese Pfade muessen wieder verfuegbar sein:
|
||||||
@@ -165,7 +171,6 @@ Diese Werte sind vor dem Start der betroffenen Dienste zu pruefen bzw. wieder in
|
|||||||
- `KOMODO_JWT_SECRET`
|
- `KOMODO_JWT_SECRET`
|
||||||
- `KOMODO_MONGO_PASSWORD`
|
- `KOMODO_MONGO_PASSWORD`
|
||||||
- `KOMODO_PERIPHERY_PASSKEY`
|
- `KOMODO_PERIPHERY_PASSKEY`
|
||||||
- relevante `HOMEPAGE_VAR_*`
|
|
||||||
- `APP_KEY` und `ADMIN_PASSWORD` fuer `speedtest-tracker`
|
- `APP_KEY` und `ADMIN_PASSWORD` fuer `speedtest-tracker`
|
||||||
|
|
||||||
### 6.3 Rechte
|
### 6.3 Rechte
|
||||||
@@ -200,11 +205,16 @@ Besonders kritisch:
|
|||||||
|
|
||||||
- `/mnt/user/appdata/secrets`
|
- `/mnt/user/appdata/secrets`
|
||||||
- `/mnt/user/appdata/traefik`
|
- `/mnt/user/appdata/traefik`
|
||||||
|
- `/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/services/gitea/data`
|
||||||
- `/mnt/user/appdata/authelia/config`
|
- `/mnt/user/appdata/authelia/config`
|
||||||
- `/mnt/user/appdata/komodo/core`
|
- `/mnt/user/appdata/komodo/core`
|
||||||
- `/mnt/user/appdata/komodo/periphery`
|
- `/mnt/user/appdata/komodo/periphery`
|
||||||
- `/mnt/user/backups/borg/dumps/latest`
|
- `/mnt/user/backups/borg/dumps/latest`
|
||||||
|
- `/mnt/user/backups/borg/dumps/latest/unraid-flash-config.tar.gz`
|
||||||
- dienstspezifische App- und Nutzdatenpfade
|
- dienstspezifische App- und Nutzdatenpfade
|
||||||
|
|
||||||
**Nicht blind alles extrahieren**, wenn nur einzelne Pfade oder Dienste betroffen sind.
|
**Nicht blind alles extrahieren**, wenn nur einzelne Pfade oder Dienste betroffen sind.
|
||||||
@@ -263,19 +273,18 @@ Ziel:
|
|||||||
|
|
||||||
### Stufe 5 - Restliche Apps und Ops
|
### Stufe 5 - Restliche Apps und Ops
|
||||||
|
|
||||||
15. `apps/homepage/`
|
15. `apps/ntfy/`
|
||||||
16. `apps/ntfy/`
|
16. `apps/paperless-gpt/`
|
||||||
17. `apps/paperless-gpt/`
|
17. `apps/bentopdf/`
|
||||||
18. `apps/bentopdf/`
|
18. `ops/glance/`
|
||||||
19. `ops/uptime-kuma/`
|
19. `ops/borg-ui/`
|
||||||
20. `ops/borg-ui/`
|
20. `ops/filebrowser/`
|
||||||
21. `ops/filebrowser/`
|
21. `ops/glances/`
|
||||||
22. `ops/glances/`
|
22. `ops/scrutiny/`
|
||||||
23. `ops/scrutiny/`
|
23. `ops/speedtest/`
|
||||||
24. `ops/speedtest/`
|
24. `monitoring/`
|
||||||
25. `monitoring/`
|
25. `ops/hermes-agent/`
|
||||||
26. `ops/hermes-agent/`
|
26. `infra/ddns-updater/`
|
||||||
27. `infra/ddns-updater/`
|
|
||||||
|
|
||||||
**Regel:** Nach jeder Stufe kurz pruefen, bevor die naechste beginnt.
|
**Regel:** Nach jeder Stufe kurz pruefen, bevor die naechste beginnt.
|
||||||
|
|
||||||
@@ -310,7 +319,7 @@ Ziel:
|
|||||||
|
|
||||||
- Borg UI startet und kennt sein Repo noch
|
- Borg UI startet und kennt sein Repo noch
|
||||||
- aktuelle Dump-Artefakte sind vorhanden
|
- aktuelle Dump-Artefakte sind vorhanden
|
||||||
- Uptime Kuma / Homepage / ntfy sind wieder da
|
- Glance / Monitoring / ntfy sind wieder da
|
||||||
- Hermes Gateway und Dashboard starten; `hermes.kaleschke.info` leitet anonym zu Authelia weiter
|
- Hermes Gateway und Dashboard starten; `hermes.kaleschke.info` leitet anonym zu Authelia weiter
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -367,6 +376,7 @@ Relevant:
|
|||||||
|
|
||||||
- Dump-Ziel: `/mnt/user/backups/borg/dumps/latest`
|
- Dump-Ziel: `/mnt/user/backups/borg/dumps/latest`
|
||||||
- Skript: `ops/borg-ui/scripts/pre-backup-dumps.sh`
|
- Skript: `ops/borg-ui/scripts/pre-backup-dumps.sh`
|
||||||
|
- Unraid-Flash-Artefakt: `unraid-flash-config.tar.gz` plus `.sha256` und Manifest im selben Zielpfad
|
||||||
|
|
||||||
### Hermes Agent
|
### Hermes Agent
|
||||||
|
|
||||||
@@ -383,15 +393,14 @@ Smoke-Test: `hermes-gateway` healthcheck ist gruen, `hermes.kaleschke.info` leit
|
|||||||
|
|
||||||
### Gitea
|
### Gitea
|
||||||
|
|
||||||
Wenn weder externer Mirror noch lokaler Clone verfuegbar sind, ist `services/gitea/data` selbst Teil des kritischen Wiederanlaufs.
|
`Micha/homelab-infra` wird als privater GitHub-Push-Mirror gespiegelt. Dieser Mirror ist der bevorzugte Repo-Bootstrap, falls Gitea selbst nach einem Ausfall noch nicht laeuft. Wenn weder GitHub-Mirror noch lokaler Clone verfuegbar sind, ist `services/gitea/data` selbst Teil des kritischen Wiederanlaufs.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 11. Offene Vorbereitungs-To-dos
|
## 11. Offene Vorbereitungs-To-dos
|
||||||
|
|
||||||
- externer Repo-Zugang fuer den Ernstfall absichern
|
- Unraid-USB-/Flash-Backup regelmaessig ueber `unraid-flash-config.tar.gz` und optional Unraid Connect pruefen
|
||||||
- Unraid-USB-/Flash-Backup pruefen
|
- Borg-Passphrase aus `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` extern analog sicher hinterlegen
|
||||||
- Borg-Passphrase extern sicher hinterlegen
|
|
||||||
- Komodo Stack-ENV-Werte zentral ausserhalb von Komodo dokumentieren
|
- Komodo Stack-ENV-Werte zentral ausserhalb von Komodo dokumentieren
|
||||||
- regelmaessige automatisierte Restore-Smoke-Tests fuer Vaultwarden, Gitea und Paperless etablieren
|
- regelmaessige automatisierte Restore-Smoke-Tests fuer Vaultwarden, Gitea und Paperless etablieren
|
||||||
- `komodo-mongo`-Dump nach Major-Upgrades gezielt kontrollieren
|
- `komodo-mongo`-Dump nach Major-Upgrades gezielt kontrollieren
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
# Disk1 Phase 2 - NTFS to XFS Migration
|
||||||
|
|
||||||
|
Stand: 2026-05-25 06:15 CEST. Ziel erreicht: `/mnt/disk1` wurde von `ntfs3` auf XFS migriert, ohne produktive Compose-Pfade zu aendern. Container nutzen weiter `/mnt/user/...`.
|
||||||
|
|
||||||
|
## Preflight
|
||||||
|
|
||||||
|
| Check | Ergebnis |
|
||||||
|
|---|---|
|
||||||
|
| Disk1 Mount | `/dev/md1p1` auf `/mnt/disk1`, `ntfs3`, 5.5T, 1.7T genutzt, 3.8T frei |
|
||||||
|
| Cache Mount | `/dev/nvme0n1p1` auf `/mnt/cache`, `xfs`, 1.9T, 100G genutzt |
|
||||||
|
| H: Backup-Ziel | `H:\`, Label `Externe HDD`, NTFS, 8T, 5.96T frei, healthy |
|
||||||
|
| Compose-Binds | Keine Treffer fuer direkte `/mnt/disk1`, `/mnt/cache`, `/mnt/disks`, `/mnt/remotes` Binds |
|
||||||
|
| SMB-Zugriff | `\\Kallilabcore\services`, `documents`, `photos`, `media` erreichbar |
|
||||||
|
|
||||||
|
## Zu sichernde Shares
|
||||||
|
|
||||||
|
| Share | Groesse laut Preflight |
|
||||||
|
|---|---:|
|
||||||
|
| `services` | 451M |
|
||||||
|
| `documents` | 196M |
|
||||||
|
| `photos` | 23G |
|
||||||
|
| `backups` | 2.2G |
|
||||||
|
| `media` | 1.7T |
|
||||||
|
| `finance` | 0 |
|
||||||
|
| `projekte` | 92K |
|
||||||
|
|
||||||
|
Zusaetzliche Disk1-Top-Level-Pfade: `scripts` 3.3M, `isos` 0, `System Volume Information` 0.
|
||||||
|
|
||||||
|
## Backup-Strategie
|
||||||
|
|
||||||
|
- Zielroot: `H:\kallilab-recovery\disk1-phase2-2026-05-23`.
|
||||||
|
- Kritischer Linux-/GitOps-Pfad `services` wird zusaetzlich als Tar-Archiv ueber SSH gesichert, damit Linux-Metadaten erhalten bleiben.
|
||||||
|
- User-Shares werden per SMB/Robocopy kopiert, mit Logs und anschliessender Zaehler-/Groessenverifikation.
|
||||||
|
- Keine produktiven Datenpfade werden geloescht.
|
||||||
|
|
||||||
|
## Gates
|
||||||
|
|
||||||
|
1. Backup komplett und verifiziert.
|
||||||
|
2. Dienste-Freeze vorbereitet und letzte Dumps frisch.
|
||||||
|
3. Direkt vor Format/Array-Prozedur: Operator-Bestaetigung im konkreten Moment.
|
||||||
|
|
||||||
|
## Backup-Ergebnis
|
||||||
|
|
||||||
|
Stand: 2026-05-24 13:07 CEST.
|
||||||
|
|
||||||
|
| Bereich | Sicherungsart | Ergebnis |
|
||||||
|
|---|---|---|
|
||||||
|
| `media` | Robocopy/SMB nach `H:\kallilab-recovery\disk1-phase2-2026-05-23\shares\media` | 2722 Dateien, 1677.11 GiB, Manifestvergleich: 0 missing, 0 size mismatch, 0 extra |
|
||||||
|
| `services` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `services.tar`, 0.441 GiB, `tar -tf` gueltig |
|
||||||
|
| `documents` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `documents.tar`, 0.192 GiB, `tar -tf` gueltig |
|
||||||
|
| `photos` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `photos.tar`, 22.876 GiB, `tar -tf` gueltig |
|
||||||
|
| `backups` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `backups.tar`, 2.099 GiB, `tar -tf` gueltig |
|
||||||
|
| `finance` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `finance.tar`, leerer Share, `tar -tf` gueltig |
|
||||||
|
| `projekte` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `projekte.tar`, klein, `tar -tf` gueltig |
|
||||||
|
| Disk1-Extras `scripts`, `isos` | Host-Tar auf Unraid Cache, danach binaer per `scp` nach H: | `disk1-extra.tar`, 0.003 GiB, `tar -tf` gueltig |
|
||||||
|
|
||||||
|
Hinweis: Erste Tar-Versuche per PowerShell-Redirect wurden verworfen, weil PowerShell den binaeren SSH-Stream als UTF-16 geschrieben hatte. Die ungueltige `media.tar` und unvollstaendige SMB-Teilkopien fuer `services`/`documents` wurden vom Backup-Ziel entfernt, damit nur verwertbare Sicherungen uebrig bleiben.
|
||||||
|
|
||||||
|
## Abschluss
|
||||||
|
|
||||||
|
Stand: 2026-05-25 06:15 CEST.
|
||||||
|
|
||||||
|
| Check | Ergebnis |
|
||||||
|
|---|---|
|
||||||
|
| Freeze-Dumps | `pre-backup-dumps.sh` vor Format ausgefuehrt; nach Wiederanlauf erneut erfolgreich, 15 kanonische Dump-Artefakte juenger als 24 h |
|
||||||
|
| Disk1 Filesystem | `/dev/md1p1` auf `/mnt/disk1`, `xfs`, 5.5T, 1.8T genutzt, 3.7T frei |
|
||||||
|
| Restore `media` | 2722 Dateien, 1,800,782,188,226 Bytes; finaler Manifestvergleich: 0 missing, 0 size mismatch, 0 extra |
|
||||||
|
| Restore Tar-Shares | `services`, `documents`, `photos`, `backups`, `finance`, `projekte` und Disk1-Extras aus H:-Freeze-Archiven nach `/mnt/disk1` extrahiert |
|
||||||
|
| Docker/Services | 49 Container laufend, 0 stopped, 0 unhealthy, 0 starting |
|
||||||
|
| Smoke-Tests | `git.kaleschke.info` 200, `komodo.kaleschke.info` 200, `borg.kaleschke.info` 302, `jellyfin.kaleschke.info` 302, `monitoring.kaleschke.info` 302 |
|
||||||
|
| Service-Mounts | Gitea SSH `:222` offen; Jellyfin und Plex sehen `/media`; Prometheus readiness ok |
|
||||||
|
| Backup-Lauf | Borg-UI Repository `appdata-critical`, letzter Job `completed`, Archiv `Taegliche-Sicherung-2026-05-25T05:52:44.157`, `nfiles=100221` |
|
||||||
|
| Temp-Cleanup | `/mnt/cache/disk1-phase2-tmp/*.tar` nach H:-Verifikation geloescht; Cache weiter XFS mit ca. 1.7T frei |
|
||||||
|
|
||||||
|
Hinweis zum Docker-Wiederanlauf: Nach dem manuellen Docker-Start liefen die Container, aber Healthcheck-Execs scheiterten wegen `dockerd` mit `XDG_RUNTIME_DIR=/run/user/0`. Ein gezielter Docker-Neustart mit unsetztem `XDG_RUNTIME_DIR` behob den Runtime-Fehler; danach wurden alle Healthchecks gruen. `monitoring-prometheus` war durch den geplanten Docker-Stop sauber beendet und wurde als bestehender Container wieder gestartet.
|
||||||
|
|
||||||
|
Offener Nachlauf: Die Array-Parity-Anzeige zeigte nach Abschluss weiter `mdNumDisabled=1`, `mdNumInvalid=1` und `mdResyncAction=check P`, waehrend beide beteiligten Devices `rdevStatus=DISK_OK` und `rdevNumErrors=0` melden. Parity-Zustand separat in Unraid pruefen und keinen Parity-/Disk-Slot ohne Operator-Entscheid aendern.
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# External Dependencies - KalliLab CORE
|
||||||
|
|
||||||
|
Status: Initiale Betreiber-Baseline 2026-05-26; konkrete Account-Recovery-Codes und Besitznachweise muessen ausserhalb des Repos bestaetigt werden.
|
||||||
|
|
||||||
|
## 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 | Operator-Konto ausserhalb Repo, konkreten Registrar im Account pruefen | Registrar-Zugang, 2FA-Recovery und Zahlungsweg analog/off-system sichern |
|
||||||
|
| Cloudflare DNS | Authoritative DNS, ACME DNS-Challenge, DDNS | hoch | Neue Zertifikate/DNS-Aenderungen blockiert | Cloudflare-Konto; API-Token liegt als Host-Secret | API-Token rotierbar halten, Account-Recovery und Zone-Besitz pruefen |
|
||||||
|
| Hetzner Storage Box | Off-site Borg Backup | kritisch | Restore aus Off-site ggf. nicht moeglich | Hetzner-Konto / Storage-Box-Zugang ausserhalb Repo | Zweites Off-site-Ziel oder Cold-Platte etablieren; Borg-Passphrase extern sichern |
|
||||||
|
| GitHub Mirror | Externer Repo-Mirror `michaelkaleschke-spec/homelab-infra` | mittel/hoch | Gitea-Verlust abfederbar, Repo-Bootstrap bleibt moeglich | GitHub-Konto; PAT liegt in Gitea-Mirror-Settings, nicht im Repo | Mirror-Status regelmaessig pruefen; lokalen Clone als zweite Kopie behalten |
|
||||||
|
| 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 | mittel | Mail-Notifier faellt aus, Login selbst nicht zwingend | GMX-Konto; SMTP-Secret liegt hostseitig | ntfy/zweiter SMTP als Fallback 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 |
|
||||||
|
| Plex Konto/Remote Access | Plex native Auth, ggf. Remote Access und Claim | mittel | Plex-Clients/Remote-Funktionen koennen ausfallen | Plex-Konto ausserhalb Repo; `PLEX_CLAIM` nur fuer Setup | LAN-Medienpfade bleiben lokal; 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 |
|
||||||
|
|
||||||
|
## 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 |
|
||||||
|
| Domain-Registrar Recovery | Domain-Besitz und Zahlung | Account, 2FA und Zahlungsweg ausserhalb des Homelabs sichern |
|
||||||
|
| Hetzner Storage Box Zugang | Off-site Backup-Ziel | SSH-/Web-Zugang und Zahlungsweg extern sichern |
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
- Seit 2026-05-26 ist AdGuard Admin nur ueber `100.80.98.33:8082` gebunden; bei Tailnet-Ausfall ist lokaler Host-/Compose-Zugriff der Break-glass-Weg.
|
||||||
|
|
||||||
|
### 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 |
|
||||||
|
|---|---|---|
|
||||||
|
| 2026-05-26 | Bekannte externe Abhaengigkeiten aus Repo-/Betriebsdoku dokumentiert; keine Secret-Werte aufgenommen | Account-Besitz, 2FA-Recovery-Codes, Zahlungswege und Borg-Passphrase extern bestaetigen |
|
||||||
@@ -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 |
|
||||||
|
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
# Hardware Inventory - KalliLab CORE
|
||||||
|
|
||||||
|
Status: Hardware-Baseline erfasst; USV/Power-Loss bleibt offene Betreiberentscheidung.
|
||||||
|
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.9 GiB genutzt, ca. 23 GiB verfuegbar |
|
||||||
|
| Slots / Ausbau | 4x 8 GB DDR4 belegt, gemischte Module |
|
||||||
|
| Module | Crucial CT8G4DFS8266.C8FE, Crucial CT8G4DFS8213.C8FDD1, 2x G.Skill F4-3600C17-8GVK |
|
||||||
|
| Konfigurierter Takt | 2133 MT/s |
|
||||||
|
| 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 | American Megatrends International F21, Release 2025-06-19 |
|
||||||
|
| SATA/HBA Controller | Intel Raptor Lake SATA AHCI Controller, onboard |
|
||||||
|
| NVMe Controller | Samsung SM981/PM981/PM983 NVMe Controller |
|
||||||
|
| 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 | Realtek RTL8125 2.5GbE Controller, Link aktuell 1G; 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 <interface>
|
||||||
|
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 | Nicht validiert / keine erkannte USV |
|
||||||
|
| Modell | Kein APC/Eaton/CyberPower-Geraet per `lsusb` erkannt |
|
||||||
|
| Verbindung | `apcupsd` ist auf USB vorkonfiguriert, aber kein passendes USB-USV-Geraet sichtbar |
|
||||||
|
| Software | `apcaccess` vorhanden; `apcupsd` laeuft nicht, `localhost:3551` liefert Connection refused |
|
||||||
|
| Konfigurierte Schwellen | `BATTERYLEVEL 5`, `MINUTES 3`, `TIMEOUT 0`, aber inaktiv solange `apcupsd` nicht laeuft |
|
||||||
|
| Laufzeit im Idle | Nicht messbar |
|
||||||
|
| Letzter Shutdown-Test | Nicht durchgefuehrt |
|
||||||
|
|
||||||
|
Bewertung:
|
||||||
|
|
||||||
|
- Aktueller Befund 2026-05-26: keine funktionierende USV-Absicherung nachgewiesen.
|
||||||
|
- `apcupsd` ist zwar auf dem System vorhanden, aber nicht aktiv.
|
||||||
|
- Power-Loss bleibt damit ein bewusst offenes Risiko fuer Docker-/DB-State und laufende Writes.
|
||||||
|
- Naechste Entscheidung: echte USV anschliessen und Shutdown testen oder Risiko bewusst akzeptieren und dokumentieren.
|
||||||
|
|
||||||
|
## Stromverbrauch
|
||||||
|
|
||||||
|
| Zustand | Verbrauch | Messmethode | Datum |
|
||||||
|
|---|---:|---|---|
|
||||||
|
| Idle | TBD | externes Messgeraet erforderlich | TBD |
|
||||||
|
| Normalbetrieb | TBD | externes Messgeraet erforderlich | TBD |
|
||||||
|
| Backup-Lauf | TBD | externes Messgeraet erforderlich | TBD |
|
||||||
|
| Last | TBD | externes Messgeraet erforderlich | 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 |
|
||||||
|
| USV | keine funktionierende USV-Abschaltung | USV anschaffen/anschliessen oder Risiko schriftlich akzeptieren |
|
||||||
|
|
||||||
|
## Audit-Kommandos
|
||||||
|
|
||||||
|
```bash
|
||||||
|
hostname
|
||||||
|
uname -a
|
||||||
|
cat /etc/unraid-version 2>/dev/null || true
|
||||||
|
lscpu
|
||||||
|
free -h
|
||||||
|
dmidecode -t baseboard | head -30
|
||||||
|
dmidecode -t bios -t system -t baseboard
|
||||||
|
dmidecode -t memory | grep -E "Size|Speed|Locator|Type" | head -40
|
||||||
|
lspci | egrep -i 'sata|ahci|raid|nvme|ethernet|network'
|
||||||
|
ip -br link
|
||||||
|
ethtool eth0
|
||||||
|
tailscale ip -4
|
||||||
|
lsblk -o NAME,SIZE,MODEL,SERIAL,FSTYPE,MOUNTPOINT,VENDOR
|
||||||
|
df -Th /mnt/cache /mnt/disk1 /mnt/user /boot
|
||||||
|
smartctl -a /dev/nvme0n1 | head -100
|
||||||
|
smartctl -a /dev/sdb | head -100
|
||||||
|
smartctl -a /dev/sdc | head -100
|
||||||
|
apcaccess status
|
||||||
|
/etc/rc.d/rc.apcupsd status
|
||||||
|
lsusb
|
||||||
|
```
|
||||||
+143
-2
@@ -8,6 +8,7 @@ Dieses Dokument ist nur noch ein historischer Verlauf. Der aktuelle operative Ab
|
|||||||
- Komodo ist der einzige produktive Stack-Manager.
|
- Komodo ist der einzige produktive Stack-Manager.
|
||||||
- Portainer CE ist entfernt.
|
- Portainer CE ist entfernt.
|
||||||
- Firefly, Firefly-Fints und Semaphore sind entfernt.
|
- Firefly, Firefly-Fints und Semaphore sind entfernt.
|
||||||
|
- `monitoring/` ist der einzige aktive Observability-Stack; alte Repo-Pfade `ops/grafana-influxdb` und `ops/loki` sind entfernt.
|
||||||
- Borg UI ist produktiv, Dump-Automatisierung laeuft host-seitig und ein Restore-Smoke-Test wurde erfolgreich durchgefuehrt.
|
- Borg UI ist produktiv, Dump-Automatisierung laeuft host-seitig und ein Restore-Smoke-Test wurde erfolgreich durchgefuehrt.
|
||||||
- GitHub Desktop ist der bevorzugte lokale Workflow fuer `Fetch`, `Pull`, `Commit` und `Push`.
|
- GitHub Desktop ist der bevorzugte lokale Workflow fuer `Fetch`, `Pull`, `Commit` und `Push`.
|
||||||
- Mutable Image-Tags sind auf die aktuell laufenden Digests eingefroren.
|
- Mutable Image-Tags sind auf die aktuell laufenden Digests eingefroren.
|
||||||
@@ -16,6 +17,146 @@ Dieses Dokument ist nur noch ein historischer Verlauf. Der aktuelle operative Ab
|
|||||||
|
|
||||||
## Historische Meilensteine
|
## Historische Meilensteine
|
||||||
|
|
||||||
|
### 2026-05-26 - Audit-Baseline-Tag gesetzt
|
||||||
|
|
||||||
|
- Der Stand nach Hardware-/Capacity-Baseline, Policy-Triage und Recovery-Doku wurde als `audit-2026-05-25-baseline` markiert und nach Gitea gepusht.
|
||||||
|
|
||||||
|
### 2026-05-26 - Externe Abhaengigkeiten und Services-Recovery baseline dokumentiert
|
||||||
|
|
||||||
|
- `docs/EXTERNAL_DEPENDENCIES.md` von Template auf Betreiber-Baseline angehoben: Domain, Cloudflare, Hetzner, GitHub-Mirror, Tailscale, GMX, Let's Encrypt, Registries, Plex und mobile Push-Pfade sind mit Ausfallwirkung und Notfallplan dokumentiert.
|
||||||
|
- `docs/SERVICES_RECOVERY.md` finalisiert den Komodo-Bootstrap-Anker: `ops/komodo/docker-compose.yml` bleibt verbindlich; der `komodo`-Self-Stack hat keinen aktiven Gitea-Webhook und ist nicht der Recovery-Anker.
|
||||||
|
- Offene Off-Repo-Betreiberchecks bleiben Account-Besitz, 2FA-Recovery-Codes, Zahlungswege, Borg-Passphrase-Hinterlegung und Gitea-Bundle-/Mirror-Mechanik.
|
||||||
|
|
||||||
|
### 2026-05-26 - Policy-Warnings triagiert
|
||||||
|
|
||||||
|
- Plex `network_mode: host` wurde in den Policy-Ausnahmen als dokumentierte Discovery-Ausnahme erfasst.
|
||||||
|
- Mutable Tags bei `ddns-updater`, `glances` und `scrutiny` bleiben wegen vorhandener SHA256-Digests reproduzierbar gepinnt und werden im Policy-Report als Info-Ausnahmen sichtbar gehalten.
|
||||||
|
- `monitoring-influxdb3-core` bleibt als dokumentierte `user: "0"`-Ausnahme bewusst eine Warning, damit der Hardening-Punkt nicht aus dem Blick faellt.
|
||||||
|
|
||||||
|
### 2026-05-26 - Hardware-/Capacity-Baseline abgeschlossen
|
||||||
|
|
||||||
|
- Hardware-Inventar auf Host-Befund aktualisiert: BIOS AMI F21 vom 2025-06-19, Intel Raptor Lake SATA AHCI, Samsung NVMe Controller und Realtek RTL8125 2.5GbE mit aktuellem 1G-Link.
|
||||||
|
- RAM-Baseline dokumentiert: 4x 8 GB DDR4 ohne ECC, gemischte Module, aktuell 2133 MT/s konfiguriert.
|
||||||
|
- Capacity-Baseline dokumentiert: Cache 1.9T mit 97G genutzt (6 %), Disk1/User-Shares 5.5T mit 1.8T genutzt (33 %), lokale Backups 2.2G unter `/mnt/user/backups`.
|
||||||
|
- USV-Befund dokumentiert: `apcupsd` ist vorhanden und auf USB vorkonfiguriert, laeuft aber nicht; `apcaccess status` liefert Connection refused und `lsusb` zeigt keine erkannte USV. Power-Loss bleibt damit eine offene Betreiberentscheidung.
|
||||||
|
|
||||||
|
### 2026-05-26 - Komodo/Gitea-Restdrift bereinigt
|
||||||
|
|
||||||
|
- Der alte Komodo-Stack `grafana` wurde als historischer Altstand inert gemacht: keine Repo-Dateipfade, kein Webhook, keine alte Stack-ENV, keine `missing_files`/`remote_errors`. Rollback bleibt Git-Historie, nicht der alte Komodo-Stack.
|
||||||
|
- Der Gitea-Hook `35` fuer den alten `grafana`-Stack bleibt inaktiv. Der nicht sinnvolle `komodo`-Self-Hook `11` wurde deaktiviert, weil Komodo selbst nicht per Gitea-Webhook auf `master` deployed wird.
|
||||||
|
- Ein kurz sichtbarer Komodo-DB-Typfehler durch `updated_at` als Float wurde im selben Kontrollfenster auf nativen Mongo `Long` korrigiert; danach traten keine neuen `invalid type`-Fehler mehr auf.
|
||||||
|
- Nach der Bereinigung: aktive Gitea-Komodo-Hooks haben `0` Fehlstatus; `komodo-core`, `komodo-periphery`, `komodo-mongo`, `nextcloud` und die aktuellen `monitoring-*` Container laufen weiter.
|
||||||
|
|
||||||
|
### 2026-05-26 - Monitoring-Altstaende aus aktivem Repo entfernt
|
||||||
|
|
||||||
|
- Die abgeloesten Pfade `ops/grafana-influxdb` und `ops/loki` wurden per `git rm` aus dem aktiven Repo entfernt. `monitoring/` bleibt der einzige Observability-Zielstack.
|
||||||
|
- Live-Check vor dem Cleanup: nur `monitoring-grafana`, `monitoring-promtail`, `monitoring-influxdb3-core` und `monitoring-loki` laufen; alte Container `grafana`, `influxdb3-core`, `loki` und `alloy` sind nicht vorhanden.
|
||||||
|
- Rollback erfolgt bei Bedarf ueber Git-Historie, nicht ueber parallel gepflegte Compose-Verzeichnisse.
|
||||||
|
- Im selben GitOps-Kontrollfenster wurde Gitea-Webhook `35` fuer den alten `grafana`-Rollback-Stack als inaktiv bestaetigt. Der aktive Nextcloud-Hook `36` hatte einen Signaturfehler; sein Secret wurde ohne Ausgabe des Werts aus der Komodo-Stack-Konfiguration zurueck nach Gitea synchronisiert.
|
||||||
|
|
||||||
|
### 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 wurde nach Fast-Forward des AdGuard-Workspaces auf `5cb4017` mit `docker compose -p adguard ... up -d` ausgefuehrt. Validierung erfolgreich: `ss -ltnp` zeigt `100.80.98.33:8082`, DNS via `@127.0.0.1` und `@192.168.178.58` funktioniert, `http://100.80.98.33:8082/` liefert HTTP 302, `http://192.168.178.58:8082/` ist nicht mehr erreichbar.
|
||||||
|
- Nachpruefung des GitOps-Pfads: Gitea-Hook `1` zeigt auf Komodo-Stack `69c7b9e26b77cd827811b9d0` und lieferte HTTP 200. Komodo-Deploys fuer AdGuard scheiterten zunaechst im `Git pull` einmal an `.git/index.lock` und danach an `fatal: Cannot rebase onto multiple branches`; der Workspace wurde aufgeraeumt und steht sauber auf `origin/master`.
|
||||||
|
|
||||||
|
### 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`.
|
||||||
|
- Da `/local/borg-dumps` bereits Teil des Borg-Scopes ist, wird das Flash-Konfigurationsartefakt mit dem bestehenden Hetzner/Borg-Backup historisiert. Downloadbare Plugin-Paketarchive unter `/boot/config/plugins/*/` werden aus dem Artefakt ausgeschlossen; Restore-relevante Konfiguration bleibt enthalten.
|
||||||
|
- Live-Erstlauf erfolgreich: `pre-borg.sh` lieferte `critical_count=0`, Freshness `Critical: 0`, `unraid-flash-config.tar.gz` ist 297 KiB gross, `0600 root:root`, SHA256-Pruefung `OK`, 356 Archiv-Eintraege, Manifest fuer Unraid `7.2.4`. Der Borg-UI-Job `Taegliche Sicherung` ist aktiv und umfasst `/local/borg-dumps`; der naechste planmaessige Hetzner-Lauf nimmt das neue Flash-Artefakt mit. Der Host-Repo-Clone unter `/mnt/user/services/homelab-infra` wurde wegen eines fehlgeschlagenen Fast-Forward-Checkouts frisch geklont; der vorherige Stand liegt archiviert unter `/mnt/user/services/_archive/homelab-infra-pre-refresh-20260525-194209`.
|
||||||
|
|
||||||
|
### 2026-05-25 - Monitoring-Zielstack finalisiert und Uptime Kuma entfernt
|
||||||
|
|
||||||
|
- `monitoring` und `glance` wurden auf Commit `b6bbca4` deployed; Komodo zeigt fuer beide `latest_hash` = `deployed_hash` = `b6bbca4` ohne `remote_errors`. Die zehn `monitoring-*` Container laufen, `monitoring.kaleschke.info` und `glance.kaleschke.info` leiten anonym zu Authelia, Prometheus ist ready und Loki `/ready` liefert `ready`.
|
||||||
|
- Alte Monitoring-Altcontainer `grafana`, `influxdb3-core`, `loki` und `alloy` sind in Docker nicht vorhanden; `ops/grafana-influxdb` und `ops/loki` bleiben nur als Rollback-/Migrationsreferenz im Repo. Der noch aktive Gitea-Hook `35` des alten `grafana`-Rollback-Stacks wurde deaktiviert, damit zukuenftige Pushes den Altstand nicht reaktivieren.
|
||||||
|
- Uptime Kuma wurde durch Blackbox/Prometheus/Grafana ersetzt: aktive Blackbox-Zielliste enthaelt 19 HTTPS-Ziele, `uptime.kaleschke.info` ist dort nicht mehr enthalten und liefert nach Stack-Removal 404. Der Komodo-Stack `uptime-kuma` wurde gestoppt/destroyed/geloescht, Gitea-Webhook `23` deaktiviert, Appdata nach `/mnt/user/appdata/_archive/uptime-kuma-removed-2026-05-25` und der alte Stack-Workspace nach `/mnt/user/services/stacks/_archive/uptime-kuma-removed-2026-05-25` verschoben.
|
||||||
|
- Authelia-Hostconfig wurde mit Backup `configuration.yml.pre-uptime-removal-20260525-164343.bak` um den toten `uptime.kaleschke.info`-Eintrag bereinigt, validiert und neu gestartet. Prometheus wurde wegen eines `Stale NFS file handle` auf der gebundenen Konfigurationsdatei per Komodo-Restart neu gemountet.
|
||||||
|
|
||||||
|
### 2026-05-25 - AdGuard Admin-Port bewusst LAN-direkt belassen
|
||||||
|
|
||||||
|
- Strategische Option `adguard.kaleschke.info` hinter Traefik/Authelia-2FA wurde bewertet, aber vom Operator bewusst verworfen, weil der Betriebsweg einfach bleiben soll. AdGuard bleibt als dokumentierte Ausnahme mit DNS `53/tcp+udp` und Admin `8082:80` LAN-direkt; keine Live-Aenderung an AdGuard, Authelia oder Traefik wurde vorgenommen.
|
||||||
|
|
||||||
|
### 2026-05-25 - Borg-Passphrase Host-Secret verifiziert
|
||||||
|
|
||||||
|
- Erwartete Host-Secret-Datei `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` aus der bestehenden Borg-UI-Repo-Konfiguration erzeugt, mit `root:root` und Modus `600` gesichert und per `borg info` gegen das Hetzner-Borg-Repo verifiziert. Analoge Offline-Hinterlegung bleibt bewusste Operator-Aufgabe; Secret-Wert wurde nicht ausgegeben oder dokumentiert.
|
||||||
|
|
||||||
|
### 2026-05-25 - Dashboard auf Glance konsolidiert
|
||||||
|
|
||||||
|
- Glance bleibt das einzige Homelab-Dashboard; Homepage wurde aus dem Zielbild entfernt. Authelia-Default-Redirect, Monitoring-Blackbox-Ziele, Cert-Check-Domains und Glance-Konfiguration zeigen nicht mehr auf `home.kaleschke.info`; Homepage wurde via Komodo-API gestoppt/destroyed, der Komodo-Stack geloescht, der alte Gitea-Webhook deaktiviert und Appdata nach `/mnt/user/appdata/_archive/homepage-removed-2026-05-25` verschoben.
|
||||||
|
|
||||||
|
### 2026-05-25 - Jellyfin aus Zielbild entfernt
|
||||||
|
|
||||||
|
- Plex-Smoke-Test erfolgreich (`/identity` HTTP 200, Container healthy, `/data/movies` und `/photos` sichtbar). Jellyfin wurde repo-seitig entfernt, aus Authelia-Baseline und Zielbild-Doku ausgetragen, via Komodo-API gestoppt/destroyed, der Komodo-Stack geloescht und Appdata nach `/mnt/user/appdata/_archive/jellyfin-removed-2026-05-25` verschoben; Plex bleibt einziger Medienserver.
|
||||||
|
|
||||||
|
### 2026-05-25 - Externer Repo-Mirror eingerichtet
|
||||||
|
|
||||||
|
- Gitea erlaubt fuer Repo-Migrationen und Mirror-Targets gezielt `github.com` und nutzt explizite externe DNS-Resolver. `Micha/homelab-infra` spiegelt nun als privater GitHub-Push-Mirror nach `michaelkaleschke-spec/homelab-infra`; erster manueller Sync erfolgreich, Gitea `push_mirror.last_error` leer. Token-Werte bleiben ausschliesslich in Gitea/GitHub und werden nicht dokumentiert.
|
||||||
|
|
||||||
|
### 2026-05-25 - Audit-Final nachgemessen
|
||||||
|
|
||||||
|
- Audit-Restliste erneut live geprueft: runtime-relevanter Stack-Inhalt fuer `gitea`, `borg-ui` und `monitoring` seit `66ee10c` unveraendert; abschliessende Audit-Doku-Commits liegen in Gitea; Monitoring inklusive Loki `/ready` gruen; Borg-Job und 15 kanonische Dump-Artefakte frisch; `docs/AUDIT_2026-05-23_FINAL.md` auf den Live-Stand aktualisiert.
|
||||||
|
|
||||||
|
### 2026-05-25 - Disk1 Phase 2 abgeschlossen
|
||||||
|
|
||||||
|
- Disk1 wurde nach H:-Freeze-Backup und finalem Service-Freeze von NTFS/`ntfs3` auf XFS migriert.
|
||||||
|
- Restore verifiziert: `media` final 2722 Dateien und 1,800,782,188,226 Bytes mit 0 missing/extra/size mismatch; Tar-Shares und Disk1-Extras aus den H:-Freeze-Archiven wiederhergestellt.
|
||||||
|
- Docker/Services nach XDG-Runtime-Fix wieder stabil: 49 Container laufend, 0 stopped, 0 unhealthy, 0 starting; Gitea, Komodo, Borg, Jellyfin und Monitoring per Smoke-Test erreichbar.
|
||||||
|
- Borg-UI meldet den letzten Backup-Job `completed`; `pre-backup-dumps.sh` wurde nach Wiederanlauf erneut ausgefuehrt und 15 kanonische Dump-Artefakte sind juenger als 24 h.
|
||||||
|
- `posture-check` erwartet Disk1 nun standardmaessig als XFS (`ALLOW_DISK1_NTFS=0`).
|
||||||
|
|
||||||
|
### 2026-05-23 - Audit-Endstufe verifiziert
|
||||||
|
|
||||||
|
- Lokalen Hardening-Commit `cd650b1` nach Gitea gepusht; Komodo-Workspaces fuer `gitea`, `borg-ui` und `monitoring` stehen auf `cd650b1`.
|
||||||
|
- Live-Audit in `docs/AUDIT_2026-05-23_LIVE.md` dokumentiert: Gitea-Registration geschlossen, Borg-Dumps frisch, Monitoring-Stack aktiv, alte Grafana/Loki-Altcontainer nicht mehr vorhanden.
|
||||||
|
- Jellyfin und Plex in Architektur, Service-Katalog und Repo-Map nachgetragen; Plex ist jetzt als Repo-Compose-Stack mit dokumentierter Host-Netz-Ausnahme gefuehrt.
|
||||||
|
- Repo-Hygiene abgeschlossen: `.serena/` ignoriert, leere Verzeichnisse entfernt, Windows-Reinstall-Helfer unter `ops/windows-reinstall/` bewusst versioniert.
|
||||||
|
|
||||||
|
### 2026-05-20 - Gitea 5xx-Bursts untersucht und Signup geschlossen
|
||||||
|
|
||||||
|
- Live-Befund zu `HomelabTraefik5xx`: kurze externe `POST /`-Bursts auf `gitea@docker` von `103.153.183.69` und `103.153.183.73`, jeweils HTTP 500 in unter 10 ms; normale Gitea-Checks und Git-Reads liefen parallel mit HTTP 200.
|
||||||
|
- Keine Hinweise auf erfolgreichen Zugriff: Gitea-Container ohne Restart/OOM, nur User `micha`, keine neuen User der letzten 30 Tage, keine neuen Repos, SSH-Keys oder Access-Tokens im Untersuchungsfenster.
|
||||||
|
- Live-Prometheus lief noch mit der alten Regel `rate(...[5m]) > 0`; die bereits im Repo vorbereitete Regel `increase(...[5m]) >= 5` wurde auf den Live-Mount kopiert und per Prometheus-Reload aktiviert.
|
||||||
|
- Gitea-Registrierung und OpenID-Signup wurden geschlossen: `DISABLE_REGISTRATION=true`, `REGISTER_EMAIL_CONFIRM=true`, `ENABLE_OPENID_SIGNIN=false`, `ENABLE_OPENID_SIGNUP=false`; Signup-Seite zeigt danach "Registration is disabled", OpenID-Login liefert 403.
|
||||||
|
|
||||||
|
### 2026-05-18 - Komodo Webhooks vollstaendig abgeglichen
|
||||||
|
|
||||||
|
- Live-Befund auf `Kallilabcore`: Komodo hatte fuer mehrere aktuelle Stacks `webhook_enabled: true`, aber Gitea enthielt noch nicht fuer alle aktuellen Stack-IDs aktive Webhooks.
|
||||||
|
- In der Gitea-Datenbank wurden aktive Webhooks fuer `monitoring` (`6a08d5297707b0930ab95c72`), `glance` (`6a09d7347707b0930ab96eae`), `grafana` (`69f31ecdf65eb72b757c497d`) und `nextcloud` (`69e519085fd5e8bc51f121f0`) nach dem bestehenden Komodo-Hook-Muster angelegt.
|
||||||
|
- Stale aktive Gitea-Hooks auf nicht mehr vorhandene bzw. alte Komodo-Stack-IDs wurden deaktiviert.
|
||||||
|
- Abgleich danach: 30 aktive Gitea-Komodo-Hooks fuer 30 Komodo-Stacks mit aktiviertem Webhook; `hermes` bleibt in Komodo bewusst `webhook_enabled: false`.
|
||||||
|
- Netzwerkpfad aus dem `gitea`-Container zu `komodo-core:9120` wurde erfolgreich verifiziert; `last_status=0` fuer neue Hooks bleibt bis zum ersten Push erwartbar.
|
||||||
|
|
||||||
|
### 2026-05-19 - Posture-Check Host-Version verifiziert
|
||||||
|
|
||||||
|
- Ursache fuer wiederholte ntfy-Warnings war nicht mehr die Repo-Logik allein, sondern dass auf dem Unraid-Host noch die alte Skriptversion unter `/mnt/user/services/homelab-infra/services/posture-check/posture-check.sh` ausgefuehrt wurde.
|
||||||
|
- Host-Skript wurde mit Backup ersetzt und mit `SEND_NTFY=0` direkt auf dem Host verifiziert.
|
||||||
|
- Ergebnis des echten Host-Laufs: `status: ok`, `critical_count: 0`, `warning_count: 0`.
|
||||||
|
- Betriebsregel daraus: Bei Host-User-Scripts nach Repo-Aenderungen immer den tatsaechlich ausgefuehrten Host-Pfad und den Live-Output pruefen.
|
||||||
|
|
||||||
|
### 2026-05-19 - Borg-Scope fuer GitOps Host Automation erweitert
|
||||||
|
|
||||||
|
- Nach den Gitea-/Komodo-Webhook- und Posture-Check-Aenderungen wurde der Backup-Scope um Host-GitOps-Pfade erweitert.
|
||||||
|
- Borg UI mountet kuenftig `/mnt/user/services` read-only als `/local/services`.
|
||||||
|
- In `all-important-sources.txt` wurden `/local/services/homelab-infra`, `/local/services/stacks` und `/local/services/posture-check` aufgenommen.
|
||||||
|
- `pre-backup-dumps.sh` wurde auf dem Host ausgefuehrt; frische Dumps fuer `gitea.sqlite.dump` und `komodo-mongo.archive.gz` liegen unter `/mnt/user/backups/borg/dumps/latest`.
|
||||||
|
- Wirksam wird der neue `/local/services`-Mount nach Redeploy/Recreate des `borg-ui`-Stacks.
|
||||||
|
|
||||||
|
### 2026-05-19 - Traefik-5xx Alert entstoert
|
||||||
|
|
||||||
|
- `HomelabTraefik5xx` hatte auf einzelne 5xx-Antworten reagiert, weil die Regel `rate(...[5m]) > 0` nutzte.
|
||||||
|
- Live-Befund fuer `gitea@docker`: zwei kurze `POST /` mit HTTP 500 von einer externen IP, danach durchgehend erfolgreiche Gitea-Checks; kein Container-Restart.
|
||||||
|
- Prometheus-Regel auf `increase(...[5m]) >= 5` geaendert, damit einzelne externe Fehlrequests keinen ntfy-Alarm ausloesen.
|
||||||
|
|
||||||
### 2026-05-17 - Glance Homelab-Dashboard vorbereitet
|
### 2026-05-17 - Glance Homelab-Dashboard vorbereitet
|
||||||
|
|
||||||
- `ops/glance` als geschuetztes Homelab-Dashboard unter `glance.kaleschke.info` vorbereitet.
|
- `ops/glance` als geschuetztes Homelab-Dashboard unter `glance.kaleschke.info` vorbereitet.
|
||||||
@@ -90,7 +231,7 @@ Dieses Dokument ist nur noch ein historischer Verlauf. Der aktuelle operative Ab
|
|||||||
|
|
||||||
### 2026-05-16 - Backup-Konsistenz und erster Hardening-Schnitt
|
### 2026-05-16 - Backup-Konsistenz und erster Hardening-Schnitt
|
||||||
|
|
||||||
- SQLite-Dumps fuer Gitea, Vaultwarden, Uptime Kuma, Speedtest Tracker und Filebrowser werden containerseitig als `*.sqlite.dump` erzeugt und per Freshness-Check geprueft.
|
- SQLite-Dumps fuer Gitea, Vaultwarden, Speedtest Tracker und Filebrowser werden containerseitig als `*.sqlite.dump` erzeugt und per Freshness-Check geprueft; Uptime Kuma wurde am 2026-05-25 aus dem aktiven Dump-Scope entfernt.
|
||||||
- `nextcloud.dump` und die Nextcloud-Userdaten sind als Option A im Borg-Scope dokumentiert.
|
- `nextcloud.dump` und die Nextcloud-Userdaten sind als Option A im Borg-Scope dokumentiert.
|
||||||
- Filebrowser mountet keine breite `/mnt/user/appdata`-Flaeche mehr, sondern nur noch Documents, Photos, Projekte sowie eigenen App-State.
|
- Filebrowser mountet keine breite `/mnt/user/appdata`-Flaeche mehr, sondern nur noch Documents, Photos, Projekte sowie eigenen App-State.
|
||||||
- Authelia Argon2id-Parameter in der Repo-Baseline auf `iterations: 3`, `memory: 65536`, `parallelism: 4` gesetzt; produktive Host-Config muss kontrolliert gemerged und mit Test-User validiert werden.
|
- Authelia Argon2id-Parameter in der Repo-Baseline auf `iterations: 3`, `memory: 65536`, `parallelism: 4` gesetzt; produktive Host-Config muss kontrolliert gemerged und mit Test-User validiert werden.
|
||||||
@@ -162,7 +303,7 @@ Dieses Dokument ist nur noch ein historischer Verlauf. Der aktuelle operative Ab
|
|||||||
- Pre-Backup-Dumps host-seitig ueber Unraid User Scripts etabliert.
|
- Pre-Backup-Dumps host-seitig ueber Unraid User Scripts etabliert.
|
||||||
- Dump-Zielpfad auf `/mnt/user/backups/borg/dumps` umgestellt.
|
- Dump-Zielpfad auf `/mnt/user/backups/borg/dumps` umgestellt.
|
||||||
- Restore-Smoke-Test fuer `postgresql17-globals.sql` und `gitea.db` erfolgreich nachgewiesen.
|
- Restore-Smoke-Test fuer `postgresql17-globals.sql` und `gitea.db` erfolgreich nachgewiesen.
|
||||||
- Monitoring fuer Borg ueber `ntfy` und Uptime Kuma eingerichtet.
|
- Monitoring fuer Borg war historisch ueber `ntfy` und Uptime Kuma eingerichtet; seit 2026-05-25 ersetzt durch `ntfy`, Blackbox/Prometheus und Monitoring Grafana.
|
||||||
|
|
||||||
### 2026-04-15 - Repo- und Betriebsbereinigung
|
### 2026-04-15 - Repo- und Betriebsbereinigung
|
||||||
|
|
||||||
|
|||||||
@@ -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 | live validiert 2026-05-26 | Compose bindet Admin-Port auf `100.80.98.33:8082`; DNS auf Port 53 funktioniert, LAN-Zugriff auf `192.168.178.58:8082` schlaegt fehl |
|
||||||
|
| 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 |
|
||||||
@@ -13,6 +13,7 @@ Diese Datei ersetzt die alte Sprint-Liste vom 2026-05-16. Die damaligen Backup-,
|
|||||||
- Posture-/Cert-/Drift-Checks: Skripte und Unraid User Scripts sind vorhanden und geplant.
|
- Posture-/Cert-/Drift-Checks: Skripte und Unraid User Scripts sind vorhanden und geplant.
|
||||||
- Monitoring-Zielstack: `monitoring/` buendelt Prometheus, Loki, Promtail, Grafana, node-exporter, cAdvisor und InfluxDB 3 Core im Repo-Zielzustand.
|
- Monitoring-Zielstack: `monitoring/` buendelt Prometheus, Loki, Promtail, Grafana, node-exporter, cAdvisor und InfluxDB 3 Core im Repo-Zielzustand.
|
||||||
- Docker-Log-Rotation: Unraid-native Rotation ist dokumentiert; keine separate `/etc/docker/daemon.json` setzen.
|
- Docker-Log-Rotation: Unraid-native Rotation ist dokumentiert; keine separate `/etc/docker/daemon.json` setzen.
|
||||||
|
- Disk1-NTFS-Migration Phase 2: am 2026-05-25 abgeschlossen; Disk1 ist XFS, `posture-check` akzeptiert NTFS nicht mehr als Standard.
|
||||||
|
|
||||||
## Morgen / bewusst spaeter
|
## Morgen / bewusst spaeter
|
||||||
|
|
||||||
@@ -25,9 +26,6 @@ Diese Datei ersetzt die alte Sprint-Liste vom 2026-05-16. Die damaligen Backup-,
|
|||||||
- HA-Token/Writer final pruefen
|
- HA-Token/Writer final pruefen
|
||||||
- erste Messwerte in InfluxDB verifizieren
|
- erste Messwerte in InfluxDB verifizieren
|
||||||
- Grafana-HA-/Wetter-Dashboard in `monitoring-grafana` aufbauen
|
- Grafana-HA-/Wetter-Dashboard in `monitoring-grafana` aufbauen
|
||||||
- Disk1-NTFS-Migration Phase 2:
|
|
||||||
- bleibt bewusst separates Migrationsfenster
|
|
||||||
- `posture-check` darf bis dahin die dokumentierte NTFS-Warnung melden
|
|
||||||
- Hermes VM-Seite:
|
- Hermes VM-Seite:
|
||||||
- Runner-VM, echte `.env`, SSH-Key und Dashboard/Gateway final zusammenfuehren
|
- Runner-VM, echte `.env`, SSH-Key und Dashboard/Gateway final zusammenfuehren
|
||||||
- NAS-Stack erst starten, wenn VM-Seite bereit ist
|
- NAS-Stack erst starten, wenn VM-Seite bereit ist
|
||||||
|
|||||||
+21
-31
@@ -1,6 +1,6 @@
|
|||||||
# Repository Map
|
# Repository Map
|
||||||
|
|
||||||
Stand: 2026-05-04
|
Stand: 2026-05-23
|
||||||
|
|
||||||
Diese Datei ist eine technische Landkarte des Repositories. Sie wurde aus Markdown-Dokumenten, `docker-compose.yml`-Dateien, Env-Beispielen, Traefik-Dynamic-Configs, Komodo/Periphery-Dateien und Skripten abgeleitet. Sie beschreibt den Repo-Sollzustand, nicht zwingend den Live-Zustand auf dem Host.
|
Diese Datei ist eine technische Landkarte des Repositories. Sie wurde aus Markdown-Dokumenten, `docker-compose.yml`-Dateien, Env-Beispielen, Traefik-Dynamic-Configs, Komodo/Periphery-Dateien und Skripten abgeleitet. Sie beschreibt den Repo-Sollzustand, nicht zwingend den Live-Zustand auf dem Host.
|
||||||
|
|
||||||
@@ -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/GITOPS_DRIFT_RUNBOOK.md` | Pflichtmatrix fuer Git/Gitea/Komodo/Docker/Host-Drift |
|
||||||
| `docs/DISASTER_RECOVERY.md` | Wiederanlauf nach Host-/Systemausfall |
|
| `docs/DISASTER_RECOVERY.md` | Wiederanlauf nach Host-/Systemausfall |
|
||||||
| `docs/RESTORE_MATRIX.md` | Restore-Quellen, Dump-Artefakte und Smoke-Tests je Dienst |
|
| `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/ALERTING_MAP.md` | ntfy Topic-Konvention und Sender-Mapping fuer Homelab-Alerts |
|
||||||
| `docs/ROLLBACK.md` | Rueckweg bei Fehlern im GitOps-Betrieb |
|
| `docs/ROLLBACK.md` | Rueckweg bei Fehlern im GitOps-Betrieb |
|
||||||
| `docs/SECRETS_MAP.md` | Secret-Namen, Pfade und Einbindungsarten ohne Werte |
|
| `docs/SECRETS_MAP.md` | Secret-Namen, Pfade und Einbindungsarten ohne Werte |
|
||||||
@@ -46,10 +53,6 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
|
|||||||
| `traefik/dynamic/dashboards.yml` | leer; File-Provider-Platzhalter |
|
| `traefik/dynamic/dashboards.yml` | leer; File-Provider-Platzhalter |
|
||||||
| `traefik/dynamic/tls.yml` | leer; File-Provider-Platzhalter |
|
| `traefik/dynamic/tls.yml` | leer; File-Provider-Platzhalter |
|
||||||
| `security/authelia/configuration.yml` | versionierte Authelia-Baseline fuer nicht geheime ACL-/Session-/Storage-Einstellungen; manuelle Host-Merge-Pflicht, User-Daten, OIDC-Client-Konfiguration und Secret-Werte bleiben ausserhalb von Git |
|
| `security/authelia/configuration.yml` | versionierte Authelia-Baseline fuer nicht geheime ACL-/Session-/Storage-Einstellungen; manuelle Host-Merge-Pflicht, User-Daten, OIDC-Client-Konfiguration und Secret-Werte bleiben ausserhalb von Git |
|
||||||
| `ops/grafana-influxdb/provisioning/datasources/influxdb.yml` | Grafana Datasource Provisioning fuer InfluxDB 3 Core und Loki |
|
|
||||||
| `ops/grafana-influxdb/provisioning/dashboards/*.json` | Grafana Dashboards fuer Container-Logs, Restart-Events und Error-Rate |
|
|
||||||
| `ops/loki/config/loki-config.yml` | Loki Filesystem/Retention-Konfiguration fuer internen Logspeicher |
|
|
||||||
| `ops/loki/config/config.alloy` | Alloy Docker-Log-Collector-Konfiguration |
|
|
||||||
| `monitoring/prometheus/prometheus.yml` | Prometheus Scrape-Konfiguration fuer dedizierten Monitoring-Stack |
|
| `monitoring/prometheus/prometheus.yml` | Prometheus Scrape-Konfiguration fuer dedizierten Monitoring-Stack |
|
||||||
| `monitoring/loki/loki-config.yml` | Loki Filesystem/Retention-Konfiguration fuer dedizierten Monitoring-Stack |
|
| `monitoring/loki/loki-config.yml` | Loki Filesystem/Retention-Konfiguration fuer dedizierten Monitoring-Stack |
|
||||||
| `monitoring/promtail/promtail-config.yml` | Promtail Docker-Socket-Discovery fuer dedizierten Monitoring-Stack |
|
| `monitoring/promtail/promtail-config.yml` | Promtail Docker-Socket-Discovery fuer dedizierten Monitoring-Stack |
|
||||||
@@ -62,7 +65,6 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
|
|||||||
| `ops/hermes-agent/config/hermes/config.yaml` | Hermes Agent Konfiguration mit Env-Platzhaltern |
|
| `ops/hermes-agent/config/hermes/config.yaml` | Hermes Agent Konfiguration mit Env-Platzhaltern |
|
||||||
| `ops/hermes-agent/hermes.env.example` | Beispiel fuer Hermes `.env`; echte Datei liegt auf Host-Appdata |
|
| `ops/hermes-agent/hermes.env.example` | Beispiel fuer Hermes `.env`; echte Datei liegt auf Host-Appdata |
|
||||||
| `ops/hermes-agent/stack.env.example` | Beispiel fuer Hermes Stack-ENV; echte `stack.env` bleibt host-/komodoseitig und ist per `.gitignore` ausgeschlossen |
|
| `ops/hermes-agent/stack.env.example` | Beispiel fuer Hermes Stack-ENV; echte `stack.env` bleibt host-/komodoseitig und ist per `.gitignore` ausgeschlossen |
|
||||||
| `ops/grafana-influxdb/stack.env.example` | `INFLUXDB_BIND_IP` Default `127.0.0.1`; auf Unraid fuer Home Assistant auf LAN-IP setzen |
|
|
||||||
| `monitoring/stack.env.example` | `INFLUXDB_BIND_IP` Default `127.0.0.1`; im Zielzustand fuer Home Assistant auf LAN-IP setzen |
|
| `monitoring/stack.env.example` | `INFLUXDB_BIND_IP` Default `127.0.0.1`; im Zielzustand fuer Home Assistant auf LAN-IP setzen |
|
||||||
| `ops/komodo/stack.env.example` | Komodo Stack-ENV-Beispiel, Secret-Werte nicht enthalten |
|
| `ops/komodo/stack.env.example` | Komodo Stack-ENV-Beispiel, Secret-Werte nicht enthalten |
|
||||||
|
|
||||||
@@ -73,7 +75,6 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
|
|||||||
| Stack | Compose | Services / Images | Traefik Hosts | Networks | Ports | Abhaengigkeiten |
|
| Stack | Compose | Services / Images | Traefik Hosts | Networks | Ports | Abhaengigkeiten |
|
||||||
|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|
|
||||||
| BentoPDF | `apps/bentopdf/docker-compose.yml` | `bentopdf` -> `bentopdfteam/bentopdf:2.8.4` | `pdf.kaleschke.info` | `frontend_net` | keine | Traefik + Authelia; COOP/COEP Middleware |
|
| BentoPDF | `apps/bentopdf/docker-compose.yml` | `bentopdf` -> `bentopdfteam/bentopdf:2.8.4` | `pdf.kaleschke.info` | `frontend_net` | keine | Traefik + Authelia; COOP/COEP Middleware |
|
||||||
| Homepage | `apps/homepage/docker-compose.yml` | `homepage` -> `ghcr.io/gethomepage/homepage:v1.12.3@sha256:...` | `home.kaleschke.info` | `frontend_net` | keine | Docker-Socket read-only fuer Widgets; viele `HOMEPAGE_VAR_*` Env-Keys |
|
|
||||||
| Immich | `apps/immich/docker-compose.yml` | `immich-server`, `immich-machine-learning`, `database`, `redis` | `immich.kaleschke.info` | `frontend_net`, `immich_default` | keine | `immich-server` depends on `database`, `redis` |
|
| Immich | `apps/immich/docker-compose.yml` | `immich-server`, `immich-machine-learning`, `database`, `redis` | `immich.kaleschke.info` | `frontend_net`, `immich_default` | keine | `immich-server` depends on `database`, `redis` |
|
||||||
| Mail Archiver | `apps/mail-archiver/docker-compose.yml` | `mail-archiver` -> `s1t5/mailarchiver@sha256:...` | `mail.kaleschke.info` | `frontend_net`, `backend_net` | keine | shared PostgreSQL via env connection string; Internet fuer IMAP |
|
| Mail Archiver | `apps/mail-archiver/docker-compose.yml` | `mail-archiver` -> `s1t5/mailarchiver@sha256:...` | `mail.kaleschke.info` | `frontend_net`, `backend_net` | keine | shared PostgreSQL via env connection string; Internet fuer IMAP |
|
||||||
| Mealie | `apps/mealie/docker-compose.yml` | `mealie`, `mealie-postgres` | `mealie.kaleschke.info` | `frontend_net`, `mealie_internal` | keine | eigene PostgreSQL im internen Netz |
|
| Mealie | `apps/mealie/docker-compose.yml` | `mealie`, `mealie-postgres` | `mealie.kaleschke.info` | `frontend_net`, `mealie_internal` | keine | eigene PostgreSQL im internen Netz |
|
||||||
@@ -87,7 +88,7 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
|
|||||||
|
|
||||||
| Stack | Compose | Services / Images | Traefik Hosts | Networks | Ports | Abhaengigkeiten |
|
| Stack | Compose | Services / Images | Traefik Hosts | Networks | Ports | Abhaengigkeiten |
|
||||||
|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|
|
||||||
| Gitea | `core/gitea/docker-compose.yml` | `gitea` -> `docker.gitea.com/gitea:1.25.4@sha256:...` | `git.kaleschke.info` | `frontend_net` | `222:22/tcp` | SQLite in `/data`; SSH-Port ist dokumentierte Ausnahme |
|
| Gitea | `core/gitea/docker-compose.yml` | `gitea` -> `docker.gitea.com/gitea:1.25.4@sha256:...` | `git.kaleschke.info` | `frontend_net` | `222:22/tcp` | SQLite in `/data`; SSH-Port ist dokumentierte Ausnahme; `github.com` ist als Mirror-Ziel erlaubt und externe DNS-Resolver sind gesetzt |
|
||||||
| Authelia | `security/authelia/docker-compose.yml` | `authelia` -> `authelia/authelia:latest@sha256:...` | `auth.kaleschke.info` | `frontend_net`, `backend_net` | keine | PostgreSQL 17 Storage, Traefik ForwardAuth; bewusst ohne Redis-Session-Backend |
|
| Authelia | `security/authelia/docker-compose.yml` | `authelia` -> `authelia/authelia:latest@sha256:...` | `auth.kaleschke.info` | `frontend_net`, `backend_net` | keine | PostgreSQL 17 Storage, Traefik ForwardAuth; bewusst ohne Redis-Session-Backend |
|
||||||
| Vaultwarden | `security/vaultwarden/docker-compose.yml` | `vaultwarden` -> `vaultwarden/server:latest@sha256:...` | `vault.kaleschke.info` | `frontend_net` | keine | Datei-Persistenz, `ADMIN_TOKEN_FILE` |
|
| Vaultwarden | `security/vaultwarden/docker-compose.yml` | `vaultwarden` -> `vaultwarden/server:latest@sha256:...` | `vault.kaleschke.info` | `frontend_net` | keine | Datei-Persistenz, `ADMIN_TOKEN_FILE` |
|
||||||
| ddns-updater | `infra/ddns-updater/docker-compose.yml` | `ddns-updater` -> `ghcr.io/qdm12/ddns-updater:latest@sha256:...` | keine | `frontend_net` | keine | Cloudflare/API-Internetbedarf |
|
| ddns-updater | `infra/ddns-updater/docker-compose.yml` | `ddns-updater` -> `ghcr.io/qdm12/ddns-updater:latest@sha256:...` | keine | `frontend_net` | keine | Cloudflare/API-Internetbedarf |
|
||||||
@@ -98,7 +99,8 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
|
|||||||
|
|
||||||
| Stack | Compose | Services / Images | Hosts | Networks | Ports / Mode | Abhaengigkeiten |
|
| 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 |
|
| 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 |
|
| Tailscale | `host-services/tailscale/docker-compose.yml` | `Tailscale-Docker` -> `tailscale/tailscale:stable@sha256:...` | keine | `network_mode: host` | host network | VPN/Remote-Zugang |
|
||||||
|
|
||||||
### Operations
|
### Operations
|
||||||
@@ -110,14 +112,11 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
|
|||||||
| Filebrowser | `ops/filebrowser/docker-compose.yml` | `filebrowser` -> `filebrowser/filebrowser:v2.63.2@sha256:...` | `files.kaleschke.info` | `frontend_net` | keine | Documents/Photos/Projekte-Mounts, Admin-UI hinter Authelia |
|
| Filebrowser | `ops/filebrowser/docker-compose.yml` | `filebrowser` -> `filebrowser/filebrowser:v2.63.2@sha256:...` | `files.kaleschke.info` | `frontend_net` | keine | Documents/Photos/Projekte-Mounts, Admin-UI hinter Authelia |
|
||||||
| Glance | `ops/glance/docker-compose.yml` | `glance` -> `glanceapp/glance:v0.8.4`, `glance-docker-socket-proxy` -> `tecnativa/docker-socket-proxy:v0.4.2` | `glance.kaleschke.info` | `frontend_net`, `glance_socket_net` | keine | Homelab-Dashboard mit Home- und Infrastructure-Seite, Monitor-, Community-, Docker-, Internet-/DNS-/VPN- und Server-Stats-Widgets; aktives Community-Widget: Immich; Docker-API nur ueber internen Socket-Proxy |
|
| Glance | `ops/glance/docker-compose.yml` | `glance` -> `glanceapp/glance:v0.8.4`, `glance-docker-socket-proxy` -> `tecnativa/docker-socket-proxy:v0.4.2` | `glance.kaleschke.info` | `frontend_net`, `glance_socket_net` | keine | Homelab-Dashboard mit Home- und Infrastructure-Seite, Monitor-, Community-, Docker-, Internet-/DNS-/VPN- und Server-Stats-Widgets; aktives Community-Widget: Immich; Docker-API nur ueber internen Socket-Proxy |
|
||||||
| Glances | `ops/glances/docker-compose.yml` | `glances` -> `nicolargo/glances:latest-full@sha256:...` | `glances.kaleschke.info` | `frontend_net` | keine | Rootfs/Docker-Socket fuer Monitoring |
|
| Glances | `ops/glances/docker-compose.yml` | `glances` -> `nicolargo/glances:latest-full@sha256:...` | `glances.kaleschke.info` | `frontend_net` | keine | Rootfs/Docker-Socket fuer Monitoring |
|
||||||
| Grafana/InfluxDB | `ops/grafana-influxdb/docker-compose.yml` | `grafana`, `influxdb3-core` | `grafana.kaleschke.info` | `frontend_net`, `grafana_influx_internal`, `grafana_influx_lan` | `influxdb3-core`: `${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181` | abgeloester Altstand; nach erfolgreicher Migration durch `monitoring/` ersetzen |
|
|
||||||
| Loki/Alloy | `ops/loki/docker-compose.yml` | `loki`, `alloy` | keine | `backend_net` | keine | abgeloester Altstand; nach erfolgreicher Migration durch `monitoring-loki`/`monitoring-promtail` ersetzen |
|
|
||||||
| Monitoring | `monitoring/docker-compose.yml` | `monitoring-prometheus`, `monitoring-alertmanager`, `monitoring-alertmanager-ntfy-bridge`, `monitoring-blackbox-exporter`, `monitoring-loki`, `monitoring-promtail`, `monitoring-grafana`, `monitoring-node-exporter`, `monitoring-cadvisor`, `monitoring-influxdb3-core`, optional `monitoring-grafana-dashboard-importer` | `monitoring.kaleschke.info` | `frontend_net`, `monitoring_net`, `monitoring_influx_lan` | `monitoring-influxdb3-core`: `${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181` | zentraler Zielstack fuer Prometheus/Loki/Grafana/InfluxDB; Alertmanager sendet via ntfy-Bridge nach `homelab-alerts`; Blackbox ersetzt Uptime-Kuma-Checks nach Parallelphase; Promtail nutzt Docker socket read-only; Dashboard-Importer nur via `bootstrap`-Profil |
|
| Monitoring | `monitoring/docker-compose.yml` | `monitoring-prometheus`, `monitoring-alertmanager`, `monitoring-alertmanager-ntfy-bridge`, `monitoring-blackbox-exporter`, `monitoring-loki`, `monitoring-promtail`, `monitoring-grafana`, `monitoring-node-exporter`, `monitoring-cadvisor`, `monitoring-influxdb3-core`, optional `monitoring-grafana-dashboard-importer` | `monitoring.kaleschke.info` | `frontend_net`, `monitoring_net`, `monitoring_influx_lan` | `monitoring-influxdb3-core`: `${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181` | zentraler Zielstack fuer Prometheus/Loki/Grafana/InfluxDB; Alertmanager sendet via ntfy-Bridge nach `homelab-alerts`; Blackbox ersetzt Uptime-Kuma-Checks nach Parallelphase; Promtail nutzt Docker socket read-only; Dashboard-Importer nur via `bootstrap`-Profil |
|
||||||
| Hermes Agent | `ops/hermes-agent/docker-compose.yml` | `hermes-gateway`, `hermes-dashboard` -> local build from Dockerfile | `hermes.kaleschke.info` via `${HERMES_DASHBOARD_HOST}` | `hermes_net`, dashboard zusaetzlich `frontend_net` | `8642` nur expose intern | SSH runner, Home Assistant optional, LLM provider env; Dashboard hinter Authelia |
|
| Hermes Agent | `ops/hermes-agent/docker-compose.yml` | `hermes-gateway`, `hermes-dashboard` -> local build from Dockerfile | `hermes.kaleschke.info` via `${HERMES_DASHBOARD_HOST}` | `hermes_net`, dashboard zusaetzlich `frontend_net` | `8642` nur expose intern | SSH runner, Home Assistant optional, LLM provider env; Dashboard hinter Authelia |
|
||||||
| Komodo | `ops/komodo/docker-compose.yml` | `komodo-core`, `komodo-mongo`, `komodo-periphery` | `komodo.kaleschke.info` | `frontend_net`, `komodo_net` | keine | Mongo, Docker socket, `/mnt/user/services` workspace mount, Gitea DNS override |
|
| Komodo | `ops/komodo/docker-compose.yml` | `komodo-core`, `komodo-mongo`, `komodo-periphery` | `komodo.kaleschke.info` | `frontend_net`, `komodo_net` | keine | Mongo, Docker socket, `/mnt/user/services` workspace mount, Gitea DNS override |
|
||||||
| Scrutiny | `ops/scrutiny/docker-compose.yml` | `scrutiny` -> `ghcr.io/starosdev/scrutiny:latest-omnibus@sha256:...` | `scrutiny.kaleschke.info` | `frontend_net` | keine | `privileged: true`, device mounts fuer SMART |
|
| Scrutiny | `ops/scrutiny/docker-compose.yml` | `scrutiny` -> `ghcr.io/starosdev/scrutiny:latest-omnibus@sha256:...` | `scrutiny.kaleschke.info` | `frontend_net` | keine | `privileged: true`, device mounts fuer SMART |
|
||||||
| Speedtest Tracker | `ops/speedtest/docker-compose.yml` | `speedtest-tracker` -> `lscr.io/linuxserver/speedtest-tracker:1.13.12@sha256:...` | `speedtest.kaleschke.info` | `frontend_net` | keine | App key/admin env, SQLite/config path |
|
| Speedtest Tracker | `ops/speedtest/docker-compose.yml` | `speedtest-tracker` -> `lscr.io/linuxserver/speedtest-tracker:1.13.12@sha256:...` | `speedtest.kaleschke.info` | `frontend_net` | keine | App key/admin env, SQLite/config path |
|
||||||
| Uptime Kuma | `ops/uptime-kuma/docker-compose.yml` | `UptimeKuma` -> `louislam/uptime-kuma:1@sha256:...` | `uptime.kaleschke.info` | `frontend_net` | keine | Monitor-State in Appdata |
|
|
||||||
|
|
||||||
### Traefik
|
### Traefik
|
||||||
|
|
||||||
@@ -137,9 +136,7 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
|
|||||||
| `git.kaleschke.info` | Gitea Web | Traefik |
|
| `git.kaleschke.info` | Gitea Web | Traefik |
|
||||||
| `glance.kaleschke.info` | Glance | Traefik + Authelia |
|
| `glance.kaleschke.info` | Glance | Traefik + Authelia |
|
||||||
| `glances.kaleschke.info` | Glances | Traefik + Authelia |
|
| `glances.kaleschke.info` | Glances | Traefik + Authelia |
|
||||||
| `grafana.kaleschke.info` | Grafana | Traefik + Authelia |
|
|
||||||
| `hermes.kaleschke.info` | Hermes Dashboard | Traefik + Authelia |
|
| `hermes.kaleschke.info` | Hermes Dashboard | Traefik + Authelia |
|
||||||
| `home.kaleschke.info` | Homepage | Traefik + Authelia; faellt in Authelia unter die 1FA-Wildcard-Regel |
|
|
||||||
| `immich.kaleschke.info` | Immich | Traefik, native App-Auth |
|
| `immich.kaleschke.info` | Immich | Traefik, native App-Auth |
|
||||||
| `komodo.kaleschke.info` | Komodo | Traefik, native Komodo-Auth; keine pauschale ForwardAuth |
|
| `komodo.kaleschke.info` | Komodo | Traefik, native Komodo-Auth; keine pauschale ForwardAuth |
|
||||||
| `mail.kaleschke.info` | Mail Archiver | Traefik + Authelia + App-Auth |
|
| `mail.kaleschke.info` | Mail Archiver | Traefik + Authelia + App-Auth |
|
||||||
@@ -152,7 +149,6 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
|
|||||||
| `scrutiny.kaleschke.info` | Scrutiny | Traefik + Authelia |
|
| `scrutiny.kaleschke.info` | Scrutiny | Traefik + Authelia |
|
||||||
| `speedtest.kaleschke.info` | Speedtest Tracker | Traefik + Authelia |
|
| `speedtest.kaleschke.info` | Speedtest Tracker | Traefik + Authelia |
|
||||||
| `traefik.kaleschke.info` | Traefik Dashboard | Traefik + Authelia |
|
| `traefik.kaleschke.info` | Traefik Dashboard | Traefik + Authelia |
|
||||||
| `uptime.kaleschke.info` | Uptime Kuma | Traefik + Authelia |
|
|
||||||
| `vault.kaleschke.info` | Vaultwarden | Traefik |
|
| `vault.kaleschke.info` | Vaultwarden | Traefik |
|
||||||
|
|
||||||
## Networks
|
## Networks
|
||||||
@@ -160,19 +156,17 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
|
|||||||
| Network | Typ / Status | Nutzer |
|
| Network | Typ / Status | Nutzer |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `frontend_net` | external bridge | Web-/Proxy-Netz fuer Traefik und alle gerouteten UIs |
|
| `frontend_net` | external bridge | Web-/Proxy-Netz fuer Traefik und alle gerouteten UIs |
|
||||||
| `backend_net` | external/internal laut Architektur | PostgreSQL 17, Redis, Authelia, Paperless, Mail Archiver, Traefik, Loki, Alloy, Grafana-Loki-Datasource |
|
| `backend_net` | external/internal laut Architektur | PostgreSQL 17, Redis, Authelia, Paperless, Mail Archiver, Traefik |
|
||||||
| `dns_net` | App-/Host-Netz | AdGuard Home und Unbound |
|
| `dns_net` | App-/Host-Netz | AdGuard Home und Unbound |
|
||||||
| `immich_default` | Compose-intern, `internal: true` | Immich Server, ML, Postgres, Redis |
|
| `immich_default` | Compose-intern, `internal: true` | Immich Server, ML, Postgres, Redis |
|
||||||
| `mealie_internal` | Compose-intern; Laufzeitname mit Compose-Projektpraefix typischerweise `mealie_mealie_internal` | Mealie und Mealie Postgres |
|
| `mealie_internal` | Compose-intern; Laufzeitname mit Compose-Projektpraefix typischerweise `mealie_mealie_internal` | Mealie und Mealie Postgres |
|
||||||
| `nextcloud_internal` | Compose-intern | Nextcloud, Nextcloud Postgres, Nextcloud Redis |
|
| `nextcloud_internal` | Compose-intern | Nextcloud, Nextcloud Postgres, Nextcloud Redis |
|
||||||
| `grafana_influx_internal` | Compose-intern, `internal: true` | Grafana und InfluxDB |
|
|
||||||
| `grafana_influx_lan` | Compose-intern bridge | InfluxDB Host-Port-Publishing fuer LAN Writer |
|
|
||||||
| `monitoring_net` | Compose-/Stack-Netz bridge | Prometheus, Loki, Promtail, Monitoring-Grafana, node-exporter, cAdvisor; Traefik fuer Metrics-Scrape |
|
| `monitoring_net` | Compose-/Stack-Netz bridge | Prometheus, Loki, Promtail, Monitoring-Grafana, node-exporter, cAdvisor; Traefik fuer Metrics-Scrape |
|
||||||
| `monitoring_influx_lan` | Compose-intern bridge | InfluxDB Host-Port-Publishing fuer LAN Writer im zentralen Monitoring-Stack |
|
| `monitoring_influx_lan` | Compose-intern bridge | InfluxDB Host-Port-Publishing fuer LAN Writer im zentralen Monitoring-Stack |
|
||||||
| `glance_socket_net` | Compose-intern, `internal: true` | Glance und `glance-docker-socket-proxy`; keine Traefik-Anbindung |
|
| `glance_socket_net` | Compose-intern, `internal: true` | Glance und `glance-docker-socket-proxy`; keine Traefik-Anbindung |
|
||||||
| `komodo_net` | Compose-intern, `internal: true` | Komodo Core, Mongo, Periphery |
|
| `komodo_net` | Compose-intern, `internal: true` | Komodo Core, Mongo, Periphery |
|
||||||
| `hermes_net` | Compose-intern bridge | Hermes Gateway/Dashboard |
|
| `hermes_net` | Compose-intern bridge | Hermes Gateway/Dashboard |
|
||||||
| `host` | Host-Netz | Tailscale; Plex historisch ausserhalb Repo |
|
| `host` | Host-Netz | Tailscale; Plex als Repo-Compose-Stack unter `host-services/plex/` |
|
||||||
|
|
||||||
## Volumes und Datenpfade
|
## Volumes und Datenpfade
|
||||||
|
|
||||||
@@ -189,22 +183,19 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
|
|||||||
| Mealie | `/mnt/user/appdata/mealie/data`, `/mnt/user/appdata/mealie/postgres` |
|
| Mealie | `/mnt/user/appdata/mealie/data`, `/mnt/user/appdata/mealie/postgres` |
|
||||||
| Mail Archiver | `/mnt/user/appdata/mailarchiver/data-protection-keys` |
|
| Mail Archiver | `/mnt/user/appdata/mailarchiver/data-protection-keys` |
|
||||||
| Nextcloud | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data`, `/mnt/user/appdata/nextcloud/postgres`, `/mnt/user/appdata/nextcloud/redis` |
|
| Nextcloud | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data`, `/mnt/user/appdata/nextcloud/postgres`, `/mnt/user/appdata/nextcloud/redis` |
|
||||||
| Homepage | `/mnt/user/appdata/homepage`, `/mnt/user/appdata/homepage/images`, Docker socket read-only |
|
| Plex | `/mnt/user/appdata/plex/config`, `/mnt/user/appdata/plex/transcode`, `/mnt/user/media`, `/mnt/user/photos` |
|
||||||
| ntfy | `/mnt/user/appdata/ntfy` |
|
| ntfy | `/mnt/user/appdata/ntfy` |
|
||||||
| Paperless-GPT | `/mnt/user/appdata/paperless-gpt/data`, `/mnt/user/appdata/paperless-gpt/prompts` |
|
| Paperless-GPT | `/mnt/user/appdata/paperless-gpt/data`, `/mnt/user/appdata/paperless-gpt/prompts` |
|
||||||
| AdGuard | `/mnt/user/appdata/adguard/work`, `/mnt/user/appdata/adguard/conf` |
|
| AdGuard | `/mnt/user/appdata/adguard/work`, `/mnt/user/appdata/adguard/conf` |
|
||||||
| Tailscale | `/mnt/user/appdata/tailscale` |
|
| Tailscale | `/mnt/user/appdata/tailscale` |
|
||||||
| Borg UI | `/mnt/user/appdata/borg-ui/data`, `/mnt/user/appdata/borg-ui/cache`, `/mnt/user/backups/borg/dumps`, selected restore/source mounts |
|
| Borg UI | `/mnt/user/appdata/borg-ui/data`, `/mnt/user/appdata/borg-ui/cache`, `/mnt/user/backups/borg/dumps`, selected restore/source mounts |
|
||||||
| code-server | `/mnt/user/appdata/code-server`, `/mnt/user/services/dev`, Homepage production mount |
|
| code-server | `/mnt/user/appdata/code-server`, `/mnt/user/services/dev` |
|
||||||
| Filebrowser | `/mnt/user/documents`, `/mnt/user/photos`, `/mnt/user/projekte`, Filebrowser database/config paths |
|
| Filebrowser | `/mnt/user/documents`, `/mnt/user/photos`, `/mnt/user/projekte`, Filebrowser database/config paths |
|
||||||
| Glance | Repo-Konfiguration unter `ops/glance/config/glance.yml`; keine produktive Datenpersistenz; Docker-Socket nur am internen Proxy |
|
| Glance | Repo-Konfiguration unter `ops/glance/config/glance.yml`; keine produktive Datenpersistenz; Docker-Socket nur am internen Proxy |
|
||||||
| Glances | `/`, Docker socket, `/etc/os-release` |
|
| Glances | `/`, Docker socket, `/etc/os-release` |
|
||||||
| Scrutiny | `/mnt/user/appdata/scrutiny/*`, `/run/udev`, selected `/dev/...` disks |
|
| Scrutiny | `/mnt/user/appdata/scrutiny/*`, `/run/udev`, selected `/dev/...` disks |
|
||||||
| Speedtest | `/mnt/user/appdata/speedtest-tracker/config` |
|
| Speedtest | `/mnt/user/appdata/speedtest-tracker/config` |
|
||||||
| Uptime Kuma | `/mnt/user/appdata/uptime-kuma` |
|
| Monitoring | named volumes `prometheus_data`, `loki_data`, `promtail_positions`, `grafana_data`; InfluxDB-Persistenz unter `/mnt/user/appdata/influxdb3/data` und `/mnt/user/appdata/influxdb3/plugins`; Provisioning im Repo unter `monitoring/grafana/provisioning`; Dashboards unter `monitoring/grafana/dashboards`; historische Altstandsdaten unter `/mnt/user/appdata/grafana`, `/mnt/user/appdata/loki` und `/mnt/user/appdata/alloy` nicht blind loeschen |
|
||||||
| Grafana/InfluxDB | `/mnt/user/appdata/grafana`, Grafana provisioning, `/mnt/user/appdata/influxdb3/data`, `/mnt/user/appdata/influxdb3/plugins` |
|
|
||||||
| Loki/Alloy | `/mnt/user/appdata/loki/config`, `/mnt/user/appdata/loki/data`, `/mnt/user/appdata/alloy/config`, `/mnt/user/appdata/alloy/data` |
|
|
||||||
| Monitoring | named volumes `prometheus_data`, `loki_data`, `promtail_positions`, `grafana_data`; InfluxDB-Persistenz unter `/mnt/user/appdata/influxdb3/data` und `/mnt/user/appdata/influxdb3/plugins`; Provisioning im Repo unter `monitoring/grafana/provisioning`; Dashboards unter `monitoring/grafana/dashboards` |
|
|
||||||
| Hermes Agent | `/mnt/user/appdata/hermes-agent/data`, `/mnt/user/appdata/hermes-agent/ssh`, SSH private key path |
|
| Hermes Agent | `/mnt/user/appdata/hermes-agent/data`, `/mnt/user/appdata/hermes-agent/ssh`, SSH private key path |
|
||||||
| Komodo | `komodo_keys`, `/mnt/user/appdata/komodo/core`, `/mnt/user/appdata/komodo/mongo`, `/mnt/user/appdata/komodo/periphery`, `/mnt/user/services` |
|
| Komodo | `komodo_keys`, `/mnt/user/appdata/komodo/core`, `/mnt/user/appdata/komodo/mongo`, `/mnt/user/appdata/komodo/periphery`, `/mnt/user/services` |
|
||||||
|
|
||||||
@@ -220,16 +211,13 @@ Secret-Werte werden hier nicht dokumentiert. Aufgefuehrt werden nur Variablennam
|
|||||||
| Paperless | `PAPERLESS_DBPASS`, `PAPERLESS_REDIS` als Komodo Stack ENV |
|
| Paperless | `PAPERLESS_DBPASS`, `PAPERLESS_REDIS` als Komodo Stack ENV |
|
||||||
| Immich | `IMMICH_DB_PASSWORD` Stack ENV; `immich_postgres_password.txt` fuer Postgres |
|
| Immich | `IMMICH_DB_PASSWORD` Stack ENV; `immich_postgres_password.txt` fuer Postgres |
|
||||||
| Mail Archiver | `MAILARCHIVER_DB_CONNECTION`, `MAILARCHIVER_AUTH_PASSWORD` als Stack ENV |
|
| Mail Archiver | `MAILARCHIVER_DB_CONNECTION`, `MAILARCHIVER_AUTH_PASSWORD` als Stack ENV |
|
||||||
| Homepage | viele `HOMEPAGE_VAR_*` Stack ENV Keys fuer Tokens/Logins |
|
|
||||||
| Glance | `GLANCE_IMMICH_API_KEY`, `GLANCE_ADGUARD_USERNAME`, `GLANCE_ADGUARD_PASSWORD`, `GLANCE_SPEEDTEST_API_KEY` als Stack ENV fuer Community-/Live-Widgets |
|
| Glance | `GLANCE_IMMICH_API_KEY`, `GLANCE_ADGUARD_USERNAME`, `GLANCE_ADGUARD_PASSWORD`, `GLANCE_SPEEDTEST_API_KEY` als Stack ENV fuer Community-/Live-Widgets |
|
||||||
| Speedtest | `APP_KEY`, `ADMIN_PASSWORD` als Stack ENV |
|
| Speedtest | `APP_KEY`, `ADMIN_PASSWORD` als Stack ENV |
|
||||||
| Nextcloud | Admin User, Admin Password, Postgres Password via Secret-Dateien |
|
| Nextcloud | Admin User, Admin Password, Postgres Password via Secret-Dateien |
|
||||||
| Komodo | `KOMODO_SECRET_KEY`, `KOMODO_WEBHOOK_SECRET`, `KOMODO_JWT_SECRET`, `KOMODO_MONGO_PASSWORD`, `KOMODO_PERIPHERY_PASSKEY`; Mongo Passwort-Datei |
|
| Komodo | `KOMODO_SECRET_KEY`, `KOMODO_WEBHOOK_SECRET`, `KOMODO_JWT_SECRET`, `KOMODO_MONGO_PASSWORD`, `KOMODO_PERIPHERY_PASSKEY`; Mongo Passwort-Datei |
|
||||||
| Borg UI | Borg Credentials, Admin Login, SSH Keys in persistentem Appdata, nicht im Git |
|
| Borg UI | Borg Credentials, Admin Login, SSH Keys in persistentem Appdata, nicht im Git |
|
||||||
| Hermes Agent | provider keys, API server key, messaging tokens, Home Assistant token in Host `.env`; SSH private key als Host-Secret |
|
| Hermes Agent | provider keys, API server key, messaging tokens, Home Assistant token in Host `.env`; SSH private key als Host-Secret |
|
||||||
| Grafana/InfluxDB | Grafana Admin Password, InfluxDB Admin Token JSON, Grafana Datasource Token |
|
| Monitoring | `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt`, `influxdb3_admin_token.json` fuer zentrale Grafana-/InfluxDB-Funktionen; ersetzt Uptime Kuma fuer HTTP-Verfuegbarkeit |
|
||||||
| Loki/Alloy | keine zusaetzlichen Secrets; Zugriff nur intern ueber `backend_net` |
|
|
||||||
| Monitoring | `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt`, `influxdb3_admin_token.json` fuer zentrale Grafana-/InfluxDB-Funktionen |
|
|
||||||
|
|
||||||
## Skripte
|
## Skripte
|
||||||
|
|
||||||
@@ -244,13 +232,15 @@ Das Skript liest Secret-Dateien auf dem Host und schreibt Dump-Artefakte. Bei An
|
|||||||
## Unsicherheiten / TODOs aus Repo-Sicht
|
## Unsicherheiten / TODOs aus Repo-Sicht
|
||||||
|
|
||||||
- Echte `stack.env`- und `.env`-Dateien sind per `.gitignore` ausgeschlossen; nur `*.example`-Dateien gehoeren ins Repo.
|
- 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.
|
- 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.
|
- `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.
|
- Einige Images bleiben trotz Digest-Pin semantisch auf mutable Tags (`latest@sha256`, `release@sha256`). Das ist bewusst dokumentiert, aber bei Updates gesondert pruefen.
|
||||||
- Stateful Datenhalter sind seit 2026-05-05 bevorzugt mit Minor-/Patch-Tag plus Digest gepinnt; Redis-Caches wurden im Hardening-Sprint 2026-05-16 auf `redis:7.4-alpine@sha256:...` vereinheitlicht.
|
- Stateful Datenhalter sind seit 2026-05-05 bevorzugt mit Minor-/Patch-Tag plus Digest gepinnt; Redis-Caches wurden im Hardening-Sprint 2026-05-16 auf `redis:7.4-alpine@sha256:...` vereinheitlicht.
|
||||||
- `scrutiny` bleibt `privileged: true`; dokumentierte Ausnahme, aber weiterhin pruefenswert.
|
- `scrutiny` bleibt `privileged: true`; dokumentierte Ausnahme, aber weiterhin pruefenswert.
|
||||||
- `tailscale` nutzt Host-Netz, `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` als dokumentierte VPN-Ausnahme.
|
- `tailscale` nutzt Host-Netz, `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` als dokumentierte VPN-Ausnahme.
|
||||||
- `grafana`, `influxdb3-core` und `monitoring-influxdb3-core` laufen aktuell als `user: "0"`; UID/GID-Hardening nur als eigener Sprint.
|
- `monitoring-influxdb3-core` laeuft aktuell als `user: "0"`; UID/GID-Hardening nur als eigener Sprint.
|
||||||
- Leere `.keep`-Platzhalter wurden entfernt; neue Verzeichnisse sollen erst mit konkretem Inhalt ins Repo.
|
- Leere `.keep`-Platzhalter wurden entfernt; neue Verzeichnisse sollen erst mit konkretem Inhalt ins Repo.
|
||||||
- `Plex-Media-Server` ist als historischer Host-Sonderfall dokumentiert, aber nicht als Repo-Compose-Stack enthalten.
|
- `plex` ist als Repo-Compose-Stack unter `host-services/plex/` enthalten; `network_mode: host` bleibt die dokumentierte Discovery-Ausnahme.
|
||||||
- BentoPDF kann je nach Live-Stand vorbereitet statt produktiv sein; Hermes Dashboard ist produktiv unter `hermes.kaleschke.info`.
|
- BentoPDF kann je nach Live-Stand vorbereitet statt produktiv sein; Hermes Dashboard ist produktiv unter `hermes.kaleschke.info`.
|
||||||
|
|||||||
@@ -26,14 +26,16 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`.
|
|||||||
|
|
||||||
| Dienst | Fuehrende Quelle | Datei-Restore | Dump / DB | Secrets / ENV | Abhaengigkeiten | Smoke-Test |
|
| Dienst | Fuehrende Quelle | Datei-Restore | Dump / DB | Secrets / ENV | Abhaengigkeiten | Smoke-Test |
|
||||||
|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|
|
||||||
|
| Unraid OS Flash | Borg-Artefakt + optional Unraid Connect | `/boot/config` aus `unraid-flash-config.tar.gz` | `unraid-flash-config.tar.gz`, `.sha256`, Manifest | enthaelt sensible Host-Konfiguration, wie Secret-Material behandeln | Unraid USB Flash Creator / neuer Boot-Stick | Unraid bootet, Array-Zuordnung und Shares sind sichtbar |
|
||||||
| Traefik | Share / Borg | `/mnt/user/appdata/traefik`, besonders `dynamic/`, `letsencrypt`, `secrets` | keine eigene DB | `cloudflare_dns_api_token` | `frontend_net`, `backend_net` | `https://traefik.kaleschke.info` erreichbar, Dashboard ueber Authelia |
|
| Traefik | Share / Borg | `/mnt/user/appdata/traefik`, besonders `dynamic/`, `letsencrypt`, `secrets` | keine eigene DB | `cloudflare_dns_api_token` | `frontend_net`, `backend_net` | `https://traefik.kaleschke.info` erreichbar, Dashboard ueber Authelia |
|
||||||
| AdGuard Home | Share / Borg | `/mnt/user/appdata/adguard/conf` | keine | keine zusaetzlichen Repo-Secrets dokumentiert | `dns_net`, `frontend_net` | DNS-Aufloesung funktioniert |
|
| AdGuard Home | Share / Borg | `/mnt/user/appdata/adguard/conf` | keine | keine zusaetzlichen Repo-Secrets dokumentiert | `dns_net`, `frontend_net` | DNS-Aufloesung funktioniert |
|
||||||
| Tailscale | Share / Borg | `/mnt/user/appdata/tailscale` | keine | Tailscale-State im Pfad | Host-Netz | Tailscale verbunden |
|
| Tailscale | Share / Borg | `/mnt/user/appdata/tailscale` | keine | Tailscale-State im Pfad | Host-Netz | Tailscale verbunden |
|
||||||
| PostgreSQL 17 | Share + Dumps | `/mnt/user/appdata/postgresql17` | `postgresql17-globals.sql`, `postgresql17-mailarchiver.dump`, `postgresql17-paperless.dump`, optional `postgresql17-authelia.dump` | `postgres_password.txt` | `backend_net` | DB startet, Ziel-Datenbanken vorhanden |
|
| PostgreSQL 17 | Share + Dumps | `/mnt/user/appdata/postgresql17` | `postgresql17-globals.sql`, `postgresql17-mailarchiver.dump`, `postgresql17-paperless.dump`, optional `postgresql17-authelia.dump` | `postgres_password.txt` | `backend_net` | DB startet, Ziel-Datenbanken vorhanden |
|
||||||
| Redis | Share / Host | `/mnt/user/appdata/redis` | keine | `redis_password.txt` | `backend_net` | Redis startet, Apps verbinden sich |
|
| Redis | Share / Host | `/mnt/user/appdata/redis` | keine | `redis_password.txt` | `backend_net` | Redis startet, Apps verbinden sich |
|
||||||
| Authelia | Borg | `/mnt/user/appdata/authelia/config`, `/mnt/user/appdata/secrets/*authelia*` | Shared PostgreSQL, optional Dump `postgresql17-authelia.dump` | JWT/Session/Storage/Postgres-/SMTP-Secret-Dateien | PostgreSQL 17, Traefik, GMX SMTP | Login-Seite und ForwardAuth funktionieren; SMTP-Notifier startet; aktive Sessions werden nach Restart neu aufgebaut |
|
| Authelia | Borg | `/mnt/user/appdata/authelia/config`, `/mnt/user/appdata/secrets/*authelia*` | Shared PostgreSQL, optional Dump `postgresql17-authelia.dump` | JWT/Session/Storage/Postgres-/SMTP-Secret-Dateien | PostgreSQL 17, Traefik, GMX SMTP | Login-Seite und ForwardAuth funktionieren; SMTP-Notifier startet; aktive Sessions werden nach Restart neu aufgebaut |
|
||||||
| Gitea | Borg + Dump | `/mnt/user/services/gitea/data` | `gitea.sqlite.dump` | `borg_repo_passphrase.txt` fuer Restore-Tests | Traefik | Web-UI erreichbar, Repo sichtbar, SSH-Port reagiert; Mini-Restore nach `/mnt/user/backups/restore-lab/gitea` am 2026-05-07 erfolgreich validiert |
|
| Gitea | GitHub-Mirror fuer Repo-Bootstrap, Borg + Dump fuer Gitea-Appstate | `/mnt/user/services/gitea/data` | `gitea.sqlite.dump` | `borg_repo_passphrase.txt` fuer Restore-Tests; GitHub-Push-Mirror-PAT liegt nur in Gitea-Mirror-Settings | Traefik | Web-UI erreichbar, Repo sichtbar, SSH-Port reagiert; GitHub-Push-Mirror synchronisiert ohne `last_error`; Mini-Restore nach `/mnt/user/backups/restore-lab/gitea` am 2026-05-07 erfolgreich validiert |
|
||||||
| Komodo | Borg / Share | `/mnt/user/appdata/komodo/core`, `/mnt/user/appdata/komodo/periphery` | `komodo-mongo.archive.gz` falls verifiziert | `komodo_mongo_password.txt`, `KOMODO_*` Stack ENV | Traefik, Mongo, Gitea | UI erreichbar, Periphery verbunden |
|
| Komodo | Borg / Share | `/mnt/user/appdata/komodo/core`, `/mnt/user/appdata/komodo/periphery`, `/mnt/user/services/stacks` | `komodo-mongo.archive.gz` falls verifiziert | `komodo_mongo_password.txt`, `KOMODO_*` Stack ENV | Traefik, Mongo, Gitea | UI erreichbar, Periphery verbunden |
|
||||||
|
| GitOps Host Automation | Borg / Git | `/mnt/user/services/homelab-infra`, `/mnt/user/services/posture-check` | keine eigene DB | keine | Gitea, Komodo, Unraid User Scripts | `posture-check` laeuft vom Host-Pfad und liefert `warning_count: 0` im bekannten Uebergangszustand |
|
||||||
| Vaultwarden | Borg + Dump | `/mnt/user/appdata/vaultwarden` | `vaultwarden.sqlite.dump` | `vaultwarden_admin_token.txt`, `borg_repo_passphrase.txt` fuer Restore-Tests | Traefik | Login-Seite erreichbar, Tresor-Daten sichtbar; Mini-Restore nach `/mnt/user/backups/restore-lab/vaultwarden` am 2026-05-07 erfolgreich validiert |
|
| Vaultwarden | Borg + Dump | `/mnt/user/appdata/vaultwarden` | `vaultwarden.sqlite.dump` | `vaultwarden_admin_token.txt`, `borg_repo_passphrase.txt` fuer Restore-Tests | Traefik | Login-Seite erreichbar, Tresor-Daten sichtbar; Mini-Restore nach `/mnt/user/backups/restore-lab/vaultwarden` am 2026-05-07 erfolgreich validiert |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -47,7 +49,7 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`.
|
|||||||
| Immich | Borg + Dump | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive` | `immich.dump` | `IMMICH_DB_PASSWORD`, `immich_postgres_password.txt` | `immich_postgres`, `immich_redis`, Traefik | UI startet, Medienbibliothek sichtbar |
|
| Immich | Borg + Dump | `/mnt/user/photos/immich`, `/mnt/user/photos/family_archive` | `immich.dump` | `IMMICH_DB_PASSWORD`, `immich_postgres_password.txt` | `immich_postgres`, `immich_redis`, Traefik | UI startet, Medienbibliothek sichtbar |
|
||||||
| Mail-Archiver | Borg + Shared Dump | `/mnt/user/appdata/mailarchiver/data-protection-keys` | `postgresql17-mailarchiver.dump` | `MAILARCHIVER_DB_CONNECTION`, `MAILARCHIVER_AUTH_PASSWORD` | PostgreSQL 17, Traefik, Authelia | Authelia-Weiterleitung greift; nach Login startet die Web-UI und das Archiv laesst sich oeffnen |
|
| Mail-Archiver | Borg + Shared Dump | `/mnt/user/appdata/mailarchiver/data-protection-keys` | `postgresql17-mailarchiver.dump` | `MAILARCHIVER_DB_CONNECTION`, `MAILARCHIVER_AUTH_PASSWORD` | PostgreSQL 17, Traefik, Authelia | Authelia-Weiterleitung greift; nach Login startet die Web-UI und das Archiv laesst sich oeffnen |
|
||||||
| Nextcloud | Borg + Dump | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data` | `nextcloud.dump` | `nextcloud_admin_user.txt`, `nextcloud_admin_password.txt`, `nextcloud_postgres_password.txt` | `nextcloud-postgres`, `nextcloud-redis`, Traefik | Web-UI startet, Login funktioniert, Dateien sichtbar |
|
| Nextcloud | Borg + Dump | `/mnt/user/appdata/nextcloud/html`, `/mnt/user/documents/nextcloud-data` | `nextcloud.dump` | `nextcloud_admin_user.txt`, `nextcloud_admin_password.txt`, `nextcloud_postgres_password.txt` | `nextcloud-postgres`, `nextcloud-redis`, Traefik | Web-UI startet, Login funktioniert, Dateien sichtbar |
|
||||||
| Homepage | Borg / Share | `/mnt/user/appdata/homepage` | keine | `HOMEPAGE_VAR_*` | Traefik, Authelia | Dashboard startet, Widgets laden |
|
| Glance | Git / Borg-Repo | Repo-Konfiguration unter `ops/glance/config/glance.yml`; keine kritische Datenpersistenz | keine | `GLANCE_IMMICH_API_KEY`, `GLANCE_ADGUARD_USERNAME`, `GLANCE_ADGUARD_PASSWORD`, `GLANCE_SPEEDTEST_API_KEY` | Traefik, Authelia, optional interne API-Ziele | Dashboard startet, Widgets laden, Docker-Status laeuft nur ueber `glance-docker-socket-proxy` |
|
||||||
| ntfy | Borg / Share | `/mnt/user/appdata/ntfy` | keine | keine besonderen Secret-Dateien dokumentiert | Traefik | UI und Push-Endpunkt erreichbar |
|
| ntfy | Borg / Share | `/mnt/user/appdata/ntfy` | keine | keine besonderen Secret-Dateien dokumentiert | Traefik | UI und Push-Endpunkt erreichbar |
|
||||||
| Paperless-GPT | Borg / Share | `/mnt/user/appdata/paperless-gpt` | keine eigene DB | `PAPERLESS_API_TOKEN` | Traefik, Paperless | UI startet, Konfiguration vorhanden |
|
| Paperless-GPT | Borg / Share | `/mnt/user/appdata/paperless-gpt` | keine eigene DB | `PAPERLESS_API_TOKEN` | Traefik, Paperless | UI startet, Konfiguration vorhanden |
|
||||||
|
|
||||||
@@ -58,15 +60,14 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`.
|
|||||||
| Dienst | Fuehrende Quelle | Datei-Restore | Dump / DB | Secrets / ENV | Abhaengigkeiten | Smoke-Test |
|
| Dienst | Fuehrende Quelle | Datei-Restore | Dump / DB | Secrets / ENV | Abhaengigkeiten | Smoke-Test |
|
||||||
|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|
|
||||||
| Borg UI | Borg + Dump | `/mnt/user/appdata/borg-ui/data` | `borg-ui.sqlite` | Borg-Repo-Creds in `/data` | Traefik | UI startet, Repo-Verbindung bekannt |
|
| Borg UI | Borg + Dump | `/mnt/user/appdata/borg-ui/data` | `borg-ui.sqlite` | Borg-Repo-Creds in `/data` | Traefik | UI startet, Repo-Verbindung bekannt |
|
||||||
| Uptime Kuma | Share / Fresh + Dump | `/mnt/user/appdata/uptime-kuma` | `uptime-kuma.sqlite.dump` | `uptime_kuma_admin_password.txt` bei Fresh-Rebuild | Traefik, Authelia | UI startet, Admin-User vorhanden, Monitore ggf. neu anlegen |
|
|
||||||
| Filebrowser | Share / Fresh + Dump | `/mnt/user/appdata/filebrowser` | `filebrowser.bolt.dump` | `filebrowser_admin_password.txt` bei Fresh-Rebuild | Traefik, Authelia | UI startet, Admin-User vorhanden |
|
| Filebrowser | Share / Fresh + Dump | `/mnt/user/appdata/filebrowser` | `filebrowser.bolt.dump` | `filebrowser_admin_password.txt` bei Fresh-Rebuild | Traefik, Authelia | UI startet, Admin-User vorhanden |
|
||||||
| Glances | Rebuildbar | kein kritischer Zustand | keine | keine | Traefik, Authelia | UI startet |
|
| Glances | Rebuildbar | kein kritischer Zustand | keine | keine | Traefik, Authelia | UI startet |
|
||||||
| Scrutiny | Teilweise rebuildbar | `/mnt/user/appdata/scrutiny` falls gewuenscht | InfluxDB bewusst nicht Teil des Critical-Scope | keine | Traefik, Authelia | UI startet, Laufwerke sichtbar |
|
| Scrutiny | Teilweise rebuildbar | `/mnt/user/appdata/scrutiny` falls gewuenscht | InfluxDB bewusst nicht Teil des Critical-Scope | keine | Traefik, Authelia | UI startet, Laufwerke sichtbar |
|
||||||
| Speedtest Tracker | Share + Dump | `/mnt/user/appdata/speedtest-tracker/config` | `speedtest-tracker.sqlite.dump` | `APP_KEY`, `ADMIN_PASSWORD` | Traefik, Authelia | UI startet |
|
| Speedtest Tracker | Share + Dump | `/mnt/user/appdata/speedtest-tracker/config` | `speedtest-tracker.sqlite.dump` | `APP_KEY`, `ADMIN_PASSWORD` | Traefik, Authelia | UI startet |
|
||||||
| BentoPDF | Rebuildbar | keine kritische Persistenz; alte Stirling-PDF-Daten unter `/mnt/user/appdata/stirling-pdf` bis zur Abnahme behalten | keine | keine separaten Secret-Dateien dokumentiert | Traefik, Authelia | UI startet, PDF-Tools verfuegbar, Office-Konvertierung ueber HTTPS funktioniert |
|
| BentoPDF | Rebuildbar | keine kritische Persistenz; alte Stirling-PDF-Daten unter `/mnt/user/appdata/stirling-pdf` bis zur Abnahme behalten | keine | keine separaten Secret-Dateien dokumentiert | Traefik, Authelia | UI startet, PDF-Tools verfuegbar, Office-Konvertierung ueber HTTPS funktioniert |
|
||||||
| Grafana | abgeloester Altstand | `/mnt/user/appdata/grafana`, inklusive `provisioning/datasources/influxdb.yml` | `grafana.sqlite` | `grafana_admin_password.txt`, `grafana_influxdb_token.txt` | Traefik, Authelia, InfluxDB 3 Core | Nur als Rollback-/Migrationsreferenz behalten, wenn `monitoring/` produktiv ist |
|
| Grafana | historischer Altstand | `/mnt/user/appdata/grafana` | `grafana.sqlite` | `grafana_admin_password.txt`, `grafana_influxdb_token.txt` | nicht aktiv | Compose-Pfad aus aktivem Repo entfernt; nur ueber Git-Historie wiederherstellen, falls ein Rollback wirklich noetig ist |
|
||||||
| InfluxDB 3 Core | abgeloester Altstand | `/mnt/user/appdata/influxdb3/data`, `/mnt/user/appdata/influxdb3/plugins` | dateibasierter Object Store | `influxdb3_admin_token.json` | internes `grafana_influx_internal` Netz | Nach Migration nicht blind loeschen; Datenuebernahme zu `monitoring-influxdb3-core` separat pruefen |
|
| InfluxDB 3 Core | historischer Altstand / Datenuebernahme | `/mnt/user/appdata/influxdb3/data`, `/mnt/user/appdata/influxdb3/plugins` | dateibasierter Object Store | `influxdb3_admin_token.json` | `monitoring-influxdb3-core` | Datenpfad wird vom Monitoring-Zielstack weitergenutzt und darf nicht blind geloescht werden |
|
||||||
| Loki / Alloy | abgeloester Altstand | `/mnt/user/appdata/loki/config`, `/mnt/user/appdata/loki/data`, `/mnt/user/appdata/alloy/config` | keine primaere DB; Loki-Dateispeicher mit 30 Tagen Retention | keine zusaetzlichen Secrets | `backend_net`, Docker socket read-only, Grafana | Durch `monitoring-loki`/`monitoring-promtail` ersetzen |
|
| Loki / Alloy | historischer Altstand | `/mnt/user/appdata/loki/config`, `/mnt/user/appdata/loki/data`, `/mnt/user/appdata/alloy/config` | keine primaere DB; Loki-Dateispeicher war transient | keine zusaetzlichen Secrets | nicht aktiv | Compose-Pfad aus aktivem Repo entfernt; aktuelle Logsammlung laeuft ueber `monitoring-loki`/`monitoring-promtail` |
|
||||||
| Monitoring Stack | Rebuild + named volumes + InfluxDB-Appdata | `prometheus_data`, `loki_data`, `promtail_positions`, `grafana_data`; InfluxDB unter `/mnt/user/appdata/influxdb3/data` und `/mnt/user/appdata/influxdb3/plugins`; Provisioning aus `monitoring/grafana/provisioning` | Prometheus-TSDB, Loki-Dateispeicher und InfluxDB-Dateistore; Diagnose-/Langzeitdaten, keine Tier-1-Restore-Quelle | `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt`, `influxdb3_admin_token.json` | `monitoring_net`, `monitoring_influx_lan`, `frontend_net`, Traefik, Authelia, Docker socket read-only fuer Promtail, Host-Mounts fuer node-exporter/cAdvisor | `https://monitoring.kaleschke.info` leitet zu Authelia; Prometheus Targets sind up; Grafana-Datasources `Prometheus`, `Loki` und `InfluxDB 3 Core` funktionieren |
|
| Monitoring Stack | Rebuild + named volumes + InfluxDB-Appdata | `prometheus_data`, `loki_data`, `promtail_positions`, `grafana_data`; InfluxDB unter `/mnt/user/appdata/influxdb3/data` und `/mnt/user/appdata/influxdb3/plugins`; Provisioning aus `monitoring/grafana/provisioning` | Prometheus-TSDB, Loki-Dateispeicher und InfluxDB-Dateistore; Diagnose-/Langzeitdaten, keine Tier-1-Restore-Quelle | `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt`, `influxdb3_admin_token.json` | `monitoring_net`, `monitoring_influx_lan`, `frontend_net`, Traefik, Authelia, Docker socket read-only fuer Promtail, Host-Mounts fuer node-exporter/cAdvisor | `https://monitoring.kaleschke.info` leitet zu Authelia; Prometheus Targets sind up; Grafana-Datasources `Prometheus`, `Loki` und `InfluxDB 3 Core` funktionieren |
|
||||||
| Hermes Agent | VM-seitig offen | `/mnt/user/appdata/hermes-agent/data`, `/mnt/user/appdata/hermes-agent/ssh` | keine eigene DB | Host-`.env` fuer Provider-/API-/Home-Assistant-Tokens, `hermes_runner_id_ed25519`, `HERMES_DASHBOARD_HOST` | separate Hermes-VM/Runner, Traefik, Authelia, `hermes_net` | NAS-Stack nicht starten, solange Runner-VM und echte `.env` fehlen |
|
| Hermes Agent | VM-seitig offen | `/mnt/user/appdata/hermes-agent/data`, `/mnt/user/appdata/hermes-agent/ssh` | keine eigene DB | Host-`.env` fuer Provider-/API-/Home-Assistant-Tokens, `hermes_runner_id_ed25519`, `HERMES_DASHBOARD_HOST` | separate Hermes-VM/Runner, Traefik, Authelia, `hermes_net` | NAS-Stack nicht starten, solange Runner-VM und echte `.env` fehlen |
|
||||||
| ddns-updater | Rebuildbar | geringe Persistenzrelevanz | keine | Provider-Zugang ueber Stack ENV | Internetzugang | Update-Job laeuft |
|
| ddns-updater | Rebuildbar | geringe Persistenzrelevanz | keine | Provider-Zugang ueber Stack ENV | Internetzugang | Update-Job laeuft |
|
||||||
@@ -86,11 +87,11 @@ Aktuell relevante Dump-Artefakte unter `/mnt/user/backups/borg/dumps/latest`:
|
|||||||
- `nextcloud.dump`
|
- `nextcloud.dump`
|
||||||
- `gitea.sqlite.dump`
|
- `gitea.sqlite.dump`
|
||||||
- `vaultwarden.sqlite.dump`
|
- `vaultwarden.sqlite.dump`
|
||||||
- `uptime-kuma.sqlite.dump`
|
|
||||||
- `speedtest-tracker.sqlite.dump`
|
- `speedtest-tracker.sqlite.dump`
|
||||||
- `filebrowser.bolt.dump`
|
- `filebrowser.bolt.dump`
|
||||||
- `borg-ui.sqlite`
|
- `borg-ui.sqlite`
|
||||||
- `grafana.sqlite`
|
- `grafana.sqlite`
|
||||||
|
- `unraid-flash-config.tar.gz` plus `unraid-flash-config.tar.gz.sha256` und Manifest
|
||||||
- Monitoring-Stack: keine verpflichtenden Dump-Artefakte; Prometheus/Loki/Grafana named volumes sind Diagnose-/Dashboard-Zustand, keine primaere Restore-Quelle.
|
- Monitoring-Stack: keine verpflichtenden Dump-Artefakte; Prometheus/Loki/Grafana named volumes sind Diagnose-/Dashboard-Zustand, keine primaere Restore-Quelle.
|
||||||
- `komodo-mongo.archive.gz` (noch gesondert verifizieren)
|
- `komodo-mongo.archive.gz` (noch gesondert verifizieren)
|
||||||
|
|
||||||
|
|||||||
+12
-2
@@ -89,7 +89,7 @@ Vor dem ersten produktiven Einsatz reicht es, den vorbereiteten Stack nicht zu d
|
|||||||
|
|
||||||
Nach einem Deploy:
|
Nach einem Deploy:
|
||||||
|
|
||||||
1. `ops/grafana-influxdb` in Komodo stoppen oder den letzten Git-Stand ohne diesen Stack deployen
|
1. alten Grafana/InfluxDB-Stack in Komodo gestoppt lassen; der fruehere Compose-Pfad `ops/grafana-influxdb` ist seit 2026-05-26 nicht mehr im aktiven Repo
|
||||||
2. Persistenz unter `/mnt/user/appdata/grafana` und `/mnt/user/appdata/influxdb3` unangetastet lassen
|
2. Persistenz unter `/mnt/user/appdata/grafana` und `/mnt/user/appdata/influxdb3` unangetastet lassen
|
||||||
3. Secrets unter `/mnt/user/appdata/secrets/grafana_admin_password.txt`, `/mnt/user/appdata/secrets/grafana_influxdb_token.txt` und `/mnt/user/appdata/secrets/influxdb3_admin_token.json` nur nach bewusstem Entscheid entfernen
|
3. Secrets unter `/mnt/user/appdata/secrets/grafana_admin_password.txt`, `/mnt/user/appdata/secrets/grafana_influxdb_token.txt` und `/mnt/user/appdata/secrets/influxdb3_admin_token.json` nur nach bewusstem Entscheid entfernen
|
||||||
4. Grafana-Domain und InfluxDB-Zugriff testen, bis klar ist, dass keine produktiven Dashboards oder Writer mehr davon abhaengen
|
4. Grafana-Domain und InfluxDB-Zugriff testen, bis klar ist, dass keine produktiven Dashboards oder Writer mehr davon abhaengen
|
||||||
@@ -99,12 +99,22 @@ Nach einem Deploy:
|
|||||||
Der Zielzustand ist `monitoring/` als einziger Observability-Stack. Bei Problemen nach der Migration:
|
Der Zielzustand ist `monitoring/` als einziger Observability-Stack. Bei Problemen nach der Migration:
|
||||||
|
|
||||||
1. `monitoring` in Komodo stoppen oder auf den letzten funktionierenden Commit zurueckgehen
|
1. `monitoring` in Komodo stoppen oder auf den letzten funktionierenden Commit zurueckgehen
|
||||||
2. bei Bedarf die abgeloesten Altstaende `ops/loki` und/oder `ops/grafana-influxdb` wieder starten
|
2. nur im echten Notfall die abgeloesten Altstaende aus der Git-Historie vor dem Repo-Cleanup wiederherstellen, z. B. aus Commit `ff5991c`; nicht dauerhaft parallel zum Zielstack betreiben
|
||||||
3. named volumes `prometheus_data`, `loki_data`, `promtail_positions`, `grafana_data` sowie `/mnt/user/appdata/influxdb3` nicht blind loeschen
|
3. named volumes `prometheus_data`, `loki_data`, `promtail_positions`, `grafana_data` sowie `/mnt/user/appdata/influxdb3` nicht blind loeschen
|
||||||
4. Secrets `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt` und `influxdb3_admin_token.json` nur nach bewusstem Entscheid entfernen
|
4. Secrets `monitoring_grafana_admin_password.txt`, `monitoring_grafana_influxdb_token.txt` und `influxdb3_admin_token.json` nur nach bewusstem Entscheid entfernen
|
||||||
5. Home Assistant Writer erst wieder umstellen, wenn `curl -i http://192.168.178.58:8181/` erwartbar `401 Unauthorized` liefert
|
5. Home Assistant Writer erst wieder umstellen, wenn `curl -i http://192.168.178.58:8181/` erwartbar `401 Unauthorized` liefert
|
||||||
6. Grafana-Datasources `Prometheus`, `Loki` und `InfluxDB 3 Core` testen
|
6. Grafana-Datasources `Prometheus`, `Loki` und `InfluxDB 3 Core` testen
|
||||||
|
|
||||||
|
## Uptime Kuma Removal Rollback
|
||||||
|
|
||||||
|
Falls die Blackbox-/Grafana-Ablösung unerwartet nicht ausreicht:
|
||||||
|
|
||||||
|
1. per Ruecknahme-Commit `ops/uptime-kuma/docker-compose.yml`, die Blackbox-/Glance-/Authelia-Referenzen und die Restore-Freshness-Pruefung auf den letzten Uptime-Kuma-Stand zurueckbringen
|
||||||
|
2. nach Gitea pushen und den Uptime-Kuma-Stack in Komodo neu anlegen oder aus dem letzten Stack-Backup wiederherstellen
|
||||||
|
3. `/mnt/user/appdata/_archive/uptime-kuma-removed-2026-05-25` nach `/mnt/user/appdata/uptime-kuma` zurueckverschieben, falls die Archivierung bereits erfolgt ist
|
||||||
|
4. `https://uptime.kaleschke.info` und die Monitore pruefen
|
||||||
|
5. erst danach den Blackbox-/Grafana-Zielzustand erneut bewerten
|
||||||
|
|
||||||
## Glance Dashboard Rollback
|
## Glance Dashboard Rollback
|
||||||
|
|
||||||
Vor dem ersten produktiven Einsatz reicht es, den vorbereiteten Stack `ops/glance` nicht zu deployen oder per Ruecknahme-Commit aus dem Repo zu entfernen.
|
Vor dem ersten produktiven Einsatz reicht es, den vorbereiteten Stack `ops/glance` nicht zu deployen oder per Ruecknahme-Commit aus dem Repo zu entfernen.
|
||||||
|
|||||||
+8
-7
@@ -26,7 +26,6 @@ Dieses Dokument listet sensible Daten, deren Ablageorte und die vorgesehene Einb
|
|||||||
| Paperless-ngx | Redis URL | Stack ENV `${PAPERLESS_REDIS}` | aktiv |
|
| Paperless-ngx | Redis URL | Stack ENV `${PAPERLESS_REDIS}` | aktiv |
|
||||||
| code-server | Passwort | `/mnt/user/appdata/code-server/secrets/password` -> `FILE__PASSWORD` | aktiv |
|
| code-server | Passwort | `/mnt/user/appdata/code-server/secrets/password` -> `FILE__PASSWORD` | aktiv |
|
||||||
| Filebrowser | Admin Password | `/mnt/user/appdata/secrets/filebrowser_admin_password.txt` -> initialisierte SQLite-DB | aktiv |
|
| Filebrowser | Admin Password | `/mnt/user/appdata/secrets/filebrowser_admin_password.txt` -> initialisierte SQLite-DB | aktiv |
|
||||||
| Uptime Kuma | Admin Password | `/mnt/user/appdata/secrets/uptime_kuma_admin_password.txt` -> initialisierte SQLite-DB | aktiv |
|
|
||||||
| Immich (server) | DB Password | Stack ENV `${IMMICH_DB_PASSWORD}` | aktiv |
|
| Immich (server) | DB Password | Stack ENV `${IMMICH_DB_PASSWORD}` | aktiv |
|
||||||
| immich-postgres | DB Password | `/mnt/user/appdata/secrets/immich_postgres_password.txt` -> `POSTGRES_PASSWORD_FILE` | aktiv |
|
| immich-postgres | DB Password | `/mnt/user/appdata/secrets/immich_postgres_password.txt` -> `POSTGRES_PASSWORD_FILE` | aktiv |
|
||||||
| mail-archiver | DB Connection | Stack ENV `${MAILARCHIVER_DB_CONNECTION}` | aktiv |
|
| mail-archiver | DB Connection | Stack ENV `${MAILARCHIVER_DB_CONNECTION}` | aktiv |
|
||||||
@@ -38,18 +37,18 @@ Dieses Dokument listet sensible Daten, deren Ablageorte und die vorgesehene Einb
|
|||||||
| Authelia | Postgres Password | `/mnt/user/appdata/secrets/authelia_postgres_password.txt` -> `AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE` | aktiv |
|
| Authelia | Postgres Password | `/mnt/user/appdata/secrets/authelia_postgres_password.txt` -> `AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE` | aktiv |
|
||||||
| Komodo Mongo | Root Password | `/mnt/user/appdata/secrets/komodo_mongo_password.txt` -> `MONGO_INITDB_ROOT_PASSWORD_FILE` | aktiv |
|
| Komodo Mongo | Root Password | `/mnt/user/appdata/secrets/komodo_mongo_password.txt` -> `MONGO_INITDB_ROOT_PASSWORD_FILE` | aktiv |
|
||||||
| Komodo Core | App Secrets | Stack ENV `${KOMODO_SECRET_KEY}`, `${KOMODO_WEBHOOK_SECRET}`, `${KOMODO_JWT_SECRET}`, `${KOMODO_MONGO_PASSWORD}`, `${KOMODO_PERIPHERY_PASSKEY}` | aktiv |
|
| Komodo Core | App Secrets | Stack ENV `${KOMODO_SECRET_KEY}`, `${KOMODO_WEBHOOK_SECRET}`, `${KOMODO_JWT_SECRET}`, `${KOMODO_MONGO_PASSWORD}`, `${KOMODO_PERIPHERY_PASSKEY}` | aktiv |
|
||||||
| Homepage | API Tokens / Zugangsdaten | Stack ENV `HOMEPAGE_VAR_*` | aktiv |
|
| Gitea Push Mirror | GitHub fine-grained PAT fuer `michaelkaleschke-spec/homelab-infra` | Gitea Repository Mirror Settings, persistent in `/mnt/user/services/gitea/data`; kein Datei-Secret im Repo | aktiv |
|
||||||
| Glance | Community Widget API Tokens | Stack ENV `${GLANCE_IMMICH_API_KEY}`, `${GLANCE_ADGUARD_USERNAME}`, `${GLANCE_ADGUARD_PASSWORD}`, `${GLANCE_SPEEDTEST_API_KEY}` | aktiv |
|
| Glance | Community Widget API Tokens | Stack ENV `${GLANCE_IMMICH_API_KEY}`, `${GLANCE_ADGUARD_USERNAME}`, `${GLANCE_ADGUARD_PASSWORD}`, `${GLANCE_SPEEDTEST_API_KEY}` | aktiv |
|
||||||
| speedtest-tracker | App Key / Admin-Zugang | Stack ENV `${APP_KEY}`, `${ADMIN_PASSWORD}` | aktiv |
|
| speedtest-tracker | App Key / Admin-Zugang | Stack ENV `${APP_KEY}`, `${ADMIN_PASSWORD}` | aktiv |
|
||||||
| Nextcloud | Admin User | `/mnt/user/appdata/secrets/nextcloud_admin_user.txt` -> `NEXTCLOUD_ADMIN_USER_FILE` | neu |
|
| Nextcloud | Admin User | `/mnt/user/appdata/secrets/nextcloud_admin_user.txt` -> `NEXTCLOUD_ADMIN_USER_FILE` | neu |
|
||||||
| Nextcloud | Admin Password | `/mnt/user/appdata/secrets/nextcloud_admin_password.txt` -> `NEXTCLOUD_ADMIN_PASSWORD_FILE` | neu |
|
| Nextcloud | Admin Password | `/mnt/user/appdata/secrets/nextcloud_admin_password.txt` -> `NEXTCLOUD_ADMIN_PASSWORD_FILE` | neu |
|
||||||
| nextcloud-postgres | DB Password | `/mnt/user/appdata/secrets/nextcloud_postgres_password.txt` -> `POSTGRES_PASSWORD_FILE` | neu |
|
| nextcloud-postgres | DB Password | `/mnt/user/appdata/secrets/nextcloud_postgres_password.txt` -> `POSTGRES_PASSWORD_FILE` | neu |
|
||||||
| Borg UI / Borg | Admin-Login, `SECRET_KEY`, SSH-Keys, Repo-Credentials | persistent unter `/mnt/user/appdata/borg-ui/data/` | aktiv |
|
| Borg UI / Borg | Admin-Login, `SECRET_KEY`, SSH-Keys, Repo-Credentials | persistent unter `/mnt/user/appdata/borg-ui/data/` | aktiv |
|
||||||
|
| Borg Repo | Borg-Passphrase fuer Restore-Tests und Notfallzugriff | `/mnt/user/appdata/secrets/borg_repo_passphrase.txt` -> Host-Secret-Datei, nicht im Repo | aktiv |
|
||||||
|
| Unraid Flash Backup | Boot-/Array-/Share-/Plugin-Konfiguration, ggf. Hashes/Keys/Templates | `/mnt/user/backups/borg/dumps/latest/unraid-flash-config.tar.gz`, via Borg/Hetzner gesichert | aktiv; wie Secret-Material behandeln |
|
||||||
| Hermes Agent | Provider-Keys, Bot-Tokens, API-Server-Key | `/mnt/user/appdata/hermes-agent/data/.env` | VM-seitig offen |
|
| Hermes Agent | Provider-Keys, Bot-Tokens, API-Server-Key | `/mnt/user/appdata/hermes-agent/data/.env` | VM-seitig offen |
|
||||||
| Hermes Agent | SSH-Runner Private Key | `/mnt/user/appdata/secrets/hermes_runner_id_ed25519` -> `/root/.ssh/id_ed25519` | VM-seitig offen |
|
| Hermes Agent | SSH-Runner Private Key | `/mnt/user/appdata/secrets/hermes_runner_id_ed25519` -> `/root/.ssh/id_ed25519` | VM-seitig offen |
|
||||||
| Grafana | Admin Password | `/mnt/user/appdata/secrets/grafana_admin_password.txt` -> `GF_SECURITY_ADMIN_PASSWORD__FILE` | aktiv |
|
|
||||||
| InfluxDB 3 Core | Admin Token JSON | `/mnt/user/appdata/secrets/influxdb3_admin_token.json` -> Docker Secret `/run/secrets/influxdb3_admin_token` | aktiv |
|
| InfluxDB 3 Core | Admin Token JSON | `/mnt/user/appdata/secrets/influxdb3_admin_token.json` -> Docker Secret `/run/secrets/influxdb3_admin_token` | aktiv |
|
||||||
| Grafana -> InfluxDB | Datasource Token | `/mnt/user/appdata/secrets/grafana_influxdb_token.txt` -> Docker Secret `/run/secrets/grafana_influxdb_token` | aktiv |
|
|
||||||
| Monitoring Grafana | Admin Password | `/mnt/user/appdata/secrets/monitoring_grafana_admin_password.txt` -> Docker Secret `/run/secrets/monitoring_grafana_admin_password` -> `GF_SECURITY_ADMIN_PASSWORD__FILE` | aktiv |
|
| Monitoring Grafana | Admin Password | `/mnt/user/appdata/secrets/monitoring_grafana_admin_password.txt` -> Docker Secret `/run/secrets/monitoring_grafana_admin_password` -> `GF_SECURITY_ADMIN_PASSWORD__FILE` | aktiv |
|
||||||
| Monitoring Grafana -> InfluxDB | Datasource Token | `/mnt/user/appdata/secrets/monitoring_grafana_influxdb_token.txt` -> Docker Secret `/run/secrets/monitoring_grafana_influxdb_token` | aktiv |
|
| Monitoring Grafana -> InfluxDB | Datasource Token | `/mnt/user/appdata/secrets/monitoring_grafana_influxdb_token.txt` -> Docker Secret `/run/secrets/monitoring_grafana_influxdb_token` | aktiv |
|
||||||
| Home Assistant -> InfluxDB | HA InfluxDB Token | `/homeassistant/secrets.yaml` -> `influxdb3_homeassistant_token` | geplant |
|
| Home Assistant -> InfluxDB | HA InfluxDB Token | `/homeassistant/secrets.yaml` -> `influxdb3_homeassistant_token` | geplant |
|
||||||
@@ -62,6 +61,8 @@ Dieses Dokument listet sensible Daten, deren Ablageorte und die vorgesehene Einb
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Gotify | `gotify_password.txt` / `GOTIFY_DEFAULTUSER_PASS_FILE` | Dienst nicht mehr aktiv |
|
| Gotify | `gotify_password.txt` / `GOTIFY_DEFAULTUSER_PASS_FILE` | Dienst nicht mehr aktiv |
|
||||||
| diun | Stack ENV | Container entfernt |
|
| diun | Stack ENV | Container entfernt |
|
||||||
|
| Uptime Kuma | `uptime_kuma_admin_password.txt` | Dienst am 2026-05-25 entfernt; nur fuer Alt-Appdata/Archiv behalten |
|
||||||
|
| Grafana Altstand | `grafana_admin_password.txt`, `grafana_influxdb_token.txt` | Compose-Pfad aus aktivem Repo entfernt; nur fuer Git-Historie-/Rollback-Fall behalten |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -84,11 +85,9 @@ Dieses Dokument listet sensible Daten, deren Ablageorte und die vorgesehene Einb
|
|||||||
|-- nextcloud_postgres_password.txt
|
|-- nextcloud_postgres_password.txt
|
||||||
|-- postgres_password.txt
|
|-- postgres_password.txt
|
||||||
|-- redis_password.txt
|
|-- redis_password.txt
|
||||||
|-- grafana_admin_password.txt
|
|-- borg_repo_passphrase.txt
|
||||||
|-- grafana_influxdb_token.txt
|
|
||||||
|-- influxdb3_admin_token.json
|
|-- influxdb3_admin_token.json
|
||||||
|-- filebrowser_admin_password.txt
|
|-- filebrowser_admin_password.txt
|
||||||
|-- uptime_kuma_admin_password.txt
|
|
||||||
`-- vaultwarden_admin_token.txt
|
`-- vaultwarden_admin_token.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -98,6 +97,8 @@ Weitere dokumentierte Secret-Pfade:
|
|||||||
- `/mnt/user/appdata/secrets/hermes_runner_id_ed25519`
|
- `/mnt/user/appdata/secrets/hermes_runner_id_ed25519`
|
||||||
- `/mnt/user/appdata/traefik/secrets/cloudflare_dns_api_token`
|
- `/mnt/user/appdata/traefik/secrets/cloudflare_dns_api_token`
|
||||||
- Borg UI verwaltet Session-Secret, Admin-Login, SSH-Keys und Repo-Credentials in seiner persistenten `/data`-Struktur. Diese Daten liegen nicht im Git, muessen aber gesichert werden.
|
- Borg UI verwaltet Session-Secret, Admin-Login, SSH-Keys und Repo-Credentials in seiner persistenten `/data`-Struktur. Diese Daten liegen nicht im Git, muessen aber gesichert werden.
|
||||||
|
- Die Borg-Repo-Passphrase liegt zusaetzlich als Host-Secret-Datei fuer Restore-Tests und Notfallzugriff vor; der Wert muss ausserhalb des Homelabs analog gesichert werden.
|
||||||
|
- Gitea verwaltet den GitHub-Push-Mirror-PAT in den Repository-Mirror-Settings. Der Wert wird nicht dokumentiert und nicht in Dateien unter `docs/` oder `core/gitea/` geschrieben.
|
||||||
- `paperless-ngx` ist eine bewusste Ausnahme: DB-Passwort und Redis-URL bleiben aktuell als Komodo Stack Environment Variables hinterlegt, um den stabil laufenden Produktionsstand nicht fuer eine reine Secret-Mechanik-Migration zu riskieren.
|
- `paperless-ngx` ist eine bewusste Ausnahme: DB-Passwort und Redis-URL bleiben aktuell als Komodo Stack Environment Variables hinterlegt, um den stabil laufenden Produktionsstand nicht fuer eine reine Secret-Mechanik-Migration zu riskieren.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# Services Recovery - KalliLab CORE
|
||||||
|
|
||||||
|
Status: Initiale Recovery-Baseline 2026-05-26, 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.
|
||||||
|
|
||||||
|
Komodo ist deshalb bewusst kein normaler Auto-Deploy-Stack: der `komodo`-Self-Stack hat keinen aktiven Gitea-Webhook. Recovery und Aenderungen laufen ueber den dokumentierten Bootstrap-Pfad und muessen nach dem Start in Komodo validiert werden.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
- Entscheidung 2026-05-26: `ops/komodo/docker-compose.yml` bleibt die verbindliche Bootstrap-Quelle. Der `komodo`-Self-Stack hat keinen aktiven Gitea-Webhook und ist nicht der Recovery-Anker.
|
||||||
|
- Offen bleibt nur ein spaeterer Trockenlauf, bei dem die Komodo-Startkommandos gegen echte Restore-Pfade getestet werden.
|
||||||
|
|
||||||
|
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 |
|
||||||
|
| erledigt | Komodo-Bootstrap-Quelle finalisieren |
|
||||||
|
| offen | Restore-Kommandos nach erstem Trockenlauf mit echten Pfaden ergaenzen |
|
||||||
|
| erledigt | Services-Recovery in `docs/DISASTER_RECOVERY.md` verlinken |
|
||||||
+8
-13
@@ -1,6 +1,6 @@
|
|||||||
# Service Catalog
|
# Service Catalog
|
||||||
|
|
||||||
Stand: 2026-05-04
|
Stand: 2026-05-23
|
||||||
|
|
||||||
Dieser Katalog beschreibt produktive und repo-vorbereitete Dienste aus Sicht von Betrieb, Restore und KI-Kontext. Er basiert auf dem Repo-Sollzustand. Vor produktiven Eingriffen immer den Live-Zustand in Komodo/Docker pruefen.
|
Dieser Katalog beschreibt produktive und repo-vorbereitete Dienste aus Sicht von Betrieb, Restore und KI-Kontext. Er basiert auf dem Repo-Sollzustand. Vor produktiven Eingriffen immer den Live-Zustand in Komodo/Docker pruefen.
|
||||||
|
|
||||||
@@ -11,10 +11,10 @@ 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 |
|
||||||
|---|---|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|---|---|
|
||||||
| `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 |
|
| `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 spaeter ggf. absichern |
|
| `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 |
|
| `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 |
|
| `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` | `/mnt/user/services/gitea/data` | Tier 1, `gitea.sqlite.dump` + Share | ja | SSH-Port 222 direkte Host-Port-Ausnahme; ohne externen Mirror im DR kritisch |
|
| `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 |
|
||||||
|
|
||||||
## Security / Identity
|
## Security / Identity
|
||||||
|
|
||||||
@@ -47,6 +47,7 @@ Secret-Werte sind nicht enthalten. Es werden nur Secret-Namen, Env-Key-Namen und
|
|||||||
| `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 |
|
||||||
| `nextcloud-postgres` | Nextcloud-Datenbank | `apps/nextcloud/docker-compose.yml` | intern | `nextcloud_internal` | `/mnt/user/appdata/nextcloud/postgres`, `nextcloud_postgres_password.txt` | `nextcloud.dump`, raw DB nicht primaerer Restore-Weg | nein | interne DB |
|
| `nextcloud-postgres` | Nextcloud-Datenbank | `apps/nextcloud/docker-compose.yml` | intern | `nextcloud_internal` | `/mnt/user/appdata/nextcloud/postgres`, `nextcloud_postgres_password.txt` | `nextcloud.dump`, raw DB nicht primaerer Restore-Weg | nein | interne DB |
|
||||||
| `nextcloud-redis` | Nextcloud Cache/Locking | `apps/nextcloud/docker-compose.yml` | intern | `nextcloud_internal` | `/mnt/user/appdata/nextcloud/redis` | Teil von Nextcloud-Restore | nein | interne Redis |
|
| `nextcloud-redis` | Nextcloud Cache/Locking | `apps/nextcloud/docker-compose.yml` | intern | `nextcloud_internal` | `/mnt/user/appdata/nextcloud/redis` | Teil von Nextcloud-Restore | nein | interne Redis |
|
||||||
|
| `plex` | Medienserver mit LAN-/Client-Discovery | `host-services/plex/docker-compose.yml` | Plex native / LAN / Remote je Plex-Konfiguration | Host-Netz | `/mnt/user/appdata/plex/config`, `/mnt/user/appdata/plex/transcode`, `/mnt/user/media`, `/mnt/user/photos` | Tier 2, Appdata + Medienpfade im Borg-/Share-Scope | nein | Repo-Compose-Stack; `network_mode: host` bleibt dokumentierte Discovery-Ausnahme, kein Traefik-Stack |
|
||||||
| `ntfy` | Push-Benachrichtigungen | `apps/ntfy/docker-compose.yml` | `https://ntfy.kaleschke.info` | Traefik, upstream mobile push | `/mnt/user/appdata/ntfy` | Tier 2 | ja | `NTFY_BEHIND_PROXY=true`; Problem-Alerts gehen gebuendelt an `homelab-alerts`, optionale Erfolgsmeldungen an `homelab-info` |
|
| `ntfy` | Push-Benachrichtigungen | `apps/ntfy/docker-compose.yml` | `https://ntfy.kaleschke.info` | Traefik, upstream mobile push | `/mnt/user/appdata/ntfy` | Tier 2 | ja | `NTFY_BEHIND_PROXY=true`; Problem-Alerts gehen gebuendelt an `homelab-alerts`, optionale Erfolgsmeldungen an `homelab-info` |
|
||||||
| `bentopdf` | PDF-Tooling / Ersatz fuer Stirling-PDF | `apps/bentopdf/docker-compose.yml` | `https://pdf.kaleschke.info` | Traefik + Authelia | keine kritische Persistenz im Compose | Tier 3, rebuildbar | ja + Authelia | COOP/COEP per Middleware; fachliche Abnahme/Live-Status pruefen |
|
| `bentopdf` | PDF-Tooling / Ersatz fuer Stirling-PDF | `apps/bentopdf/docker-compose.yml` | `https://pdf.kaleschke.info` | Traefik + Authelia | keine kritische Persistenz im Compose | Tier 3, rebuildbar | ja + Authelia | COOP/COEP per Middleware; fachliche Abnahme/Live-Status pruefen |
|
||||||
|
|
||||||
@@ -54,32 +55,26 @@ 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 |
|
||||||
|---|---|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|---|---|
|
||||||
| `homepage` | Start-Dashboard | `apps/homepage/docker-compose.yml` | `https://home.kaleschke.info` | Traefik, viele API Tokens | `/mnt/user/appdata/homepage`, `/mnt/user/appdata/homepage/images` | Tier 2 | ja + Authelia laut Compose | Authelia schuetzt die Domain ueber die 1FA-Wildcard-Regel; Docker socket im Recovery entfernt, Docker-Widgets erst wieder via dokumentierter Ausnahme oder Socket-Proxy |
|
| `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` | Homelab-Uebersicht / 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 |
|
||||||
| `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 |
|
||||||
| `uptime-kuma` | Monitoring / Uptime Checks | `ops/uptime-kuma/docker-compose.yml` | `https://uptime.kaleschke.info` | Traefik + Authelia | `/mnt/user/appdata/uptime-kuma` | Tier 3, `uptime-kuma.sqlite.dump` | ja + Authelia | Monitore nach Restore pruefen |
|
|
||||||
| `glances` | System-/Container-Monitoring | `ops/glances/docker-compose.yml` | `https://glances.kaleschke.info` | Docker socket, rootfs, Traefik + Authelia | kein kritischer Zustand | Tier 3, rebuildbar | ja + Authelia | Dokumentierte Host-Observability-Ausnahme: `pid: host`, `/:/rootfs:ro`, `/var/run/docker.sock:/var/run/docker.sock:ro`, `/etc/os-release:/etc/os-release:ro`; keine Appdaten ausserhalb `/mnt/user/...` |
|
| `glances` | System-/Container-Monitoring | `ops/glances/docker-compose.yml` | `https://glances.kaleschke.info` | Docker socket, rootfs, Traefik + Authelia | kein kritischer Zustand | Tier 3, rebuildbar | ja + Authelia | Dokumentierte Host-Observability-Ausnahme: `pid: host`, `/:/rootfs:ro`, `/var/run/docker.sock:/var/run/docker.sock:ro`, `/etc/os-release:/etc/os-release:ro`; keine Appdaten ausserhalb `/mnt/user/...` |
|
||||||
| `scrutiny` | Laufwerks-/SMART-Monitoring | `ops/scrutiny/docker-compose.yml` | `https://scrutiny.kaleschke.info` | Device mounts, Traefik + Authelia | `/mnt/user/appdata/scrutiny/config`, `/mnt/user/appdata/scrutiny/influxdb` | Tier 3, Metrics nicht kritisch | ja + Authelia | Dokumentierte Host-Observability-Ausnahme: `privileged: true`, `/run/udev:/run/udev:ro`, `/dev/sdb:/dev/sdb`, `/dev/sdc:/dev/sdc`, `/dev/nvme0n1:/dev/nvme0n1`; keine Appdaten ausserhalb `/mnt/user/...` |
|
| `scrutiny` | Laufwerks-/SMART-Monitoring | `ops/scrutiny/docker-compose.yml` | `https://scrutiny.kaleschke.info` | Device mounts, Traefik + Authelia | `/mnt/user/appdata/scrutiny/config`, `/mnt/user/appdata/scrutiny/influxdb` | Tier 3, Metrics nicht kritisch | ja + Authelia | Dokumentierte Host-Observability-Ausnahme: `privileged: true`, `/run/udev:/run/udev:ro`, `/dev/sdb:/dev/sdb`, `/dev/sdc:/dev/sdc`, `/dev/nvme0n1:/dev/nvme0n1`; keine Appdaten ausserhalb `/mnt/user/...` |
|
||||||
| `speedtest-tracker` | Speedtest-Monitoring | `ops/speedtest/docker-compose.yml` | `https://speedtest.kaleschke.info` | Traefik + Authelia | `/mnt/user/appdata/speedtest-tracker/config` | Tier 3, `speedtest-tracker.sqlite.dump` | ja + Authelia | `APP_KEY`, `ADMIN_PASSWORD` Stack ENV |
|
| `speedtest-tracker` | Speedtest-Monitoring | `ops/speedtest/docker-compose.yml` | `https://speedtest.kaleschke.info` | Traefik + Authelia | `/mnt/user/appdata/speedtest-tracker/config` | Tier 3, `speedtest-tracker.sqlite.dump` | ja + Authelia | `APP_KEY`, `ADMIN_PASSWORD` Stack ENV |
|
||||||
| `filebrowser` | Datei-Browser fuer Documents/Photos/Projekte | `ops/filebrowser/docker-compose.yml` | `https://files.kaleschke.info` | Traefik + Authelia | `/mnt/user/appdata/filebrowser/*`, `/mnt/user/documents`, `/mnt/user/photos`, `/mnt/user/projekte` | Tier 3, `filebrowser.bolt.dump` + Share | ja + Authelia | Breiter Appdata-Mount entfernt; Secrets und Traefik-Dynamic-Config sind nicht mehr ueber Filebrowser gemountet |
|
| `filebrowser` | Datei-Browser fuer Documents/Photos/Projekte | `ops/filebrowser/docker-compose.yml` | `https://files.kaleschke.info` | Traefik + Authelia | `/mnt/user/appdata/filebrowser/*`, `/mnt/user/documents`, `/mnt/user/photos`, `/mnt/user/projekte` | Tier 3, `filebrowser.bolt.dump` + Share | ja + Authelia | Breiter Appdata-Mount entfernt; Secrets und Traefik-Dynamic-Config sind nicht mehr ueber Filebrowser gemountet |
|
||||||
| `code-server` | Web-Editor / Operations Workspace | `ops/code-server/docker-compose.yml` | `https://code.kaleschke.info` | Traefik + Authelia | `/mnt/user/appdata/code-server`, `/mnt/user/services/dev` | Tier 3 | ja + Authelia | Passwort ueber LSIO `FILE__PASSWORD`; Workspaces beachten |
|
| `code-server` | Web-Editor / Operations Workspace | `ops/code-server/docker-compose.yml` | `https://code.kaleschke.info` | Traefik + Authelia | `/mnt/user/appdata/code-server`, `/mnt/user/services/dev` | Tier 3 | ja + Authelia | Passwort ueber LSIO `FILE__PASSWORD`; Workspaces beachten |
|
||||||
| `grafana` | abgeloester Altstand fuer Grafana/InfluxDB | `ops/grafana-influxdb/docker-compose.yml` | `https://grafana.kaleschke.info` | Traefik + Authelia, InfluxDB 3 Core | `/mnt/user/appdata/grafana`, Grafana provisioning | Tier 3, `grafana.sqlite` | ja + Authelia | Nicht parallel zum neuen `monitoring/`-Zielstack betreiben; bleibt vorerst als Rollback-/Migrationsreferenz |
|
|
||||||
| `influxdb3-core` | abgeloester Altstand fuer Home-Assistant-Langzeitdaten | `ops/grafana-influxdb/docker-compose.yml` | LAN `8181` je `INFLUXDB_BIND_IP`, keine Public URL | Grafana, Home Assistant Writer | `/mnt/user/appdata/influxdb3/data`, `/mnt/user/appdata/influxdb3/plugins` | Tier 3 | nein | Nach erfolgreicher Migration durch `monitoring-influxdb3-core` ersetzen; alten Datenpfad nicht blind loeschen |
|
|
||||||
| `monitoring-grafana` | zentrale Observability-UI fuer Metriken, Logs und InfluxDB | `monitoring/docker-compose.yml` | `https://monitoring.kaleschke.info` | Traefik + Authelia, Prometheus, Loki, InfluxDB 3 Core | named volume `grafana_data`, Provisioning unter `monitoring/grafana/provisioning`, Dashboards unter `monitoring/grafana/dashboards` | Tier 3, named volume | ja + Authelia | Admin-Passwort ueber `monitoring_grafana_admin_password.txt`; Zielbestand: `Homelab / Availability`, `Homelab / Host Overview`, `Homelab / Containers + Logs`, `Traefik Official Standalone Dashboard`; Dashboard-Importer ist optionales `bootstrap`-Profil fuer Traefik |
|
| `monitoring-grafana` | zentrale Observability-UI fuer Metriken, Logs und InfluxDB | `monitoring/docker-compose.yml` | `https://monitoring.kaleschke.info` | Traefik + Authelia, Prometheus, Loki, InfluxDB 3 Core | named volume `grafana_data`, Provisioning unter `monitoring/grafana/provisioning`, Dashboards unter `monitoring/grafana/dashboards` | Tier 3, named volume | ja + Authelia | Admin-Passwort ueber `monitoring_grafana_admin_password.txt`; Zielbestand: `Homelab / Availability`, `Homelab / Host Overview`, `Homelab / Containers + Logs`, `Traefik Official Standalone Dashboard`; Dashboard-Importer ist optionales `bootstrap`-Profil fuer Traefik |
|
||||||
| `monitoring-prometheus` | Metrik-Speicher fuer Homelab-Monitoring | `monitoring/docker-compose.yml`, `monitoring/prometheus/prometheus.yml`, `monitoring/prometheus/alerts.yml` | intern `http://prometheus:9090` | `monitoring_net`, node-exporter, cAdvisor, Traefik-Metrics, Blackbox Exporter, Alertmanager | named volume `prometheus_data` | Tier 3, transiente Metriken mit 30 Tagen Retention | nein | Scrapes: Prometheus, node-exporter, cAdvisor, Traefik `:8082`, `blackbox-http`; Prometheus-Regeln senden an Alertmanager und von dort nach ntfy |
|
| `monitoring-prometheus` | Metrik-Speicher fuer Homelab-Monitoring | `monitoring/docker-compose.yml`, `monitoring/prometheus/prometheus.yml`, `monitoring/prometheus/alerts.yml` | intern `http://prometheus:9090` | `monitoring_net`, node-exporter, cAdvisor, Traefik-Metrics, Blackbox Exporter, Alertmanager | named volume `prometheus_data` | Tier 3, transiente Metriken mit 30 Tagen Retention | nein | Scrapes: Prometheus, node-exporter, cAdvisor, Traefik `:8082`, `blackbox-http`; Prometheus-Regeln senden an Alertmanager und von dort nach ntfy |
|
||||||
| `monitoring-alertmanager` | Alert-Routing fuer Prometheus-Regeln | `monitoring/docker-compose.yml`, `monitoring/alertmanager/alertmanager.yml` | intern `:9093` | Prometheus, ntfy Bridge | named volume `alertmanager_data` | Tier 3 | nein | sendet firing und resolved Alerts an `monitoring-alertmanager-ntfy-bridge` |
|
| `monitoring-alertmanager` | Alert-Routing fuer Prometheus-Regeln | `monitoring/docker-compose.yml`, `monitoring/alertmanager/alertmanager.yml` | intern `:9093` | Prometheus, ntfy Bridge | named volume `alertmanager_data` | Tier 3 | nein | sendet firing und resolved Alerts an `monitoring-alertmanager-ntfy-bridge` |
|
||||||
| `monitoring-alertmanager-ntfy-bridge` | Alertmanager-Webhook nach ntfy Push | `monitoring/docker-compose.yml`, `monitoring/alertmanager-ntfy-bridge/bridge.py` | intern `:8080` | Alertmanager, `https://ntfy.kaleschke.info/homelab-alerts` | kein kritischer Zustand | rebuildbar | nein | formatiert Alertmanager JSON als ntfy Titel, Nachricht, Priority und Tags; keine Secrets |
|
| `monitoring-alertmanager-ntfy-bridge` | Alertmanager-Webhook nach ntfy Push | `monitoring/docker-compose.yml`, `monitoring/alertmanager-ntfy-bridge/bridge.py` | intern `:8080` | Alertmanager, `https://ntfy.kaleschke.info/homelab-alerts` | kein kritischer Zustand | rebuildbar | nein | formatiert Alertmanager JSON als ntfy Titel, Nachricht, Priority und Tags; keine Secrets |
|
||||||
| `monitoring-blackbox-exporter` | HTTP-Erreichbarkeitspruefungen fuer Uptime-Kuma-Abloesung | `monitoring/docker-compose.yml`, `monitoring/blackbox/blackbox.yml` | intern `:9115` | Prometheus, externe HTTPS-Ziele | kein kritischer Zustand | rebuildbar | nein | Uptime Kuma erst nach sieben Tagen Parallelbetrieb und Grafana-Alerting-Paritaet stoppen |
|
| `monitoring-blackbox-exporter` | HTTP-Erreichbarkeitspruefungen als Uptime-Kuma-Ersatz | `monitoring/docker-compose.yml`, `monitoring/blackbox/blackbox.yml` | intern `:9115` | Prometheus, externe HTTPS-Ziele | kein kritischer Zustand | rebuildbar | nein | Uptime Kuma wurde 2026-05-25 nach erfolgreichem Blackbox-/Grafana-Smoke-Test entfernt |
|
||||||
| `monitoring-loki` | Logspeicher fuer Monitoring-Stack | `monitoring/docker-compose.yml`, `monitoring/loki/loki-config.yml` | intern `http://loki:3100` | `monitoring_net`, Promtail, Grafana | named volume `loki_data` | Tier 3, transiente Logs mit 30 Tagen Retention | nein | Von bestehendem `ops/loki` getrennt; Doppelbetrieb bewusst pruefen |
|
| `monitoring-loki` | Logspeicher fuer Monitoring-Stack | `monitoring/docker-compose.yml`, `monitoring/loki/loki-config.yml` | intern `http://loki:3100` | `monitoring_net`, Promtail, Grafana | named volume `loki_data` | Tier 3, transiente Logs mit 30 Tagen Retention | nein | Ersetzt den alten `ops/loki`-Stack; kein paralleler Altcontainer aktiv |
|
||||||
| `monitoring-promtail` | Docker-Log-Collector fuer Monitoring-Loki | `monitoring/docker-compose.yml`, `monitoring/promtail/promtail-config.yml` | intern | Docker socket read-only, Docker json-file Logs, Loki | named volume `promtail_positions` | rebuildbar | nein | Dokumentierte Host-Observability-Ausnahme: `/var/run/docker.sock:/var/run/docker.sock:ro` und `/var/lib/docker/containers:ro`; keine Appdaten, nur Log-Discovery |
|
| `monitoring-promtail` | Docker-Log-Collector fuer Monitoring-Loki | `monitoring/docker-compose.yml`, `monitoring/promtail/promtail-config.yml` | intern | Docker socket read-only, Docker json-file Logs, Loki | named volume `promtail_positions` | rebuildbar | nein | Dokumentierte Host-Observability-Ausnahme: `/var/run/docker.sock:/var/run/docker.sock:ro` und `/var/lib/docker/containers:ro`; keine Appdaten, nur Log-Discovery |
|
||||||
| `monitoring-node-exporter` | Host-Metriken fuer Prometheus | `monitoring/docker-compose.yml` | intern `:9100` | Host `/proc`, `/sys`, `/` read-only, Prometheus | kein kritischer Zustand | rebuildbar | nein | Host-Observability-Ausnahme mit read-only Rootfs/Proc/Sys-Mounts |
|
| `monitoring-node-exporter` | Host-Metriken fuer Prometheus | `monitoring/docker-compose.yml` | intern `:9100` | Host `/proc`, `/sys`, `/` read-only, Prometheus | kein kritischer Zustand | rebuildbar | nein | Host-Observability-Ausnahme mit read-only Rootfs/Proc/Sys-Mounts |
|
||||||
| `monitoring-cadvisor` | Container-Metriken fuer Prometheus | `monitoring/docker-compose.yml` | intern `:8080` | Docker/Host read-only Mounts, Prometheus | kein kritischer Zustand | rebuildbar | nein | Host-Observability-Ausnahme fuer Container-Metriken; keine direkten Ports |
|
| `monitoring-cadvisor` | Container-Metriken fuer Prometheus | `monitoring/docker-compose.yml` | intern `:8080` | Docker/Host read-only Mounts, Prometheus | kein kritischer Zustand | rebuildbar | nein | Host-Observability-Ausnahme fuer Container-Metriken; keine direkten Ports |
|
||||||
| `monitoring-influxdb3-core` | InfluxDB 3 Core fuer Home-Assistant-/Ecowitt-Langzeitdaten | `monitoring/docker-compose.yml` | LAN `8181` je `INFLUXDB_BIND_IP`, keine Public URL | Monitoring-Grafana, Home Assistant Writer | `/mnt/user/appdata/influxdb3/data`, `/mnt/user/appdata/influxdb3/plugins` | Tier 3 | nein | LAN-only Host-Port-Ausnahme; `user: "0"` ist fuer den lokalen Object-Store-Pfad dokumentiert; uebernimmt den bisherigen InfluxDB-Daten-/Token-Katalog; `401 Unauthorized` beim Curl ohne Token ist erwarteter Reachability-Test |
|
| `monitoring-influxdb3-core` | InfluxDB 3 Core fuer Home-Assistant-/Ecowitt-Langzeitdaten | `monitoring/docker-compose.yml` | LAN `8181` je `INFLUXDB_BIND_IP`, keine Public URL | Monitoring-Grafana, Home Assistant Writer | `/mnt/user/appdata/influxdb3/data`, `/mnt/user/appdata/influxdb3/plugins` | Tier 3 | nein | LAN-only Host-Port-Ausnahme; `user: "0"` ist fuer den lokalen Object-Store-Pfad dokumentiert; uebernimmt den bisherigen InfluxDB-Daten-/Token-Katalog; `401 Unauthorized` beim Curl ohne Token ist erwarteter Reachability-Test |
|
||||||
| `loki` | abgeloester Altstand fuer Container-Logs | `ops/loki/docker-compose.yml`, `ops/loki/config/loki-config.yml` | intern `http://loki:3100`, keine Public URL | `backend_net`, Grafana | `/mnt/user/appdata/loki/config`, `/mnt/user/appdata/loki/data` | Tier 3, transiente Logs mit 30 Tagen Retention | nein | Durch `monitoring-loki` ersetzen; nicht parallel betreiben, ausser bewusst zur Migration |
|
|
||||||
| `alloy` | abgeloester Altstand fuer Docker-Log-Collection | `ops/loki/docker-compose.yml`, `ops/loki/config/config.alloy` | intern | Docker socket read-only, Loki, `backend_net` | `/mnt/user/appdata/alloy/config`, `/mnt/user/appdata/alloy/data` | rebuildbar | nein | Durch `monitoring-promtail` ersetzen; Socket-Ausnahme bleibt nur fuer aktive Collector relevant |
|
|
||||||
| `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 |
|
||||||
|
|
||||||
@@ -87,7 +82,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 und Fuellstand | `services/posture-check/posture-check.sh` | Unraid User-Script / Cron / Borg Pre-Hook | `findmnt`, `df`, `nvme`, optional `curl` fuer ntfy | `/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; `ALLOW_DISK1_NTFS=1` ist die dokumentierte Uebergangsausnahme bis Disk1-Migration Phase 2; Warning/Critical alarmieren via ntfy |
|
| `posture-check` | Host-Posture-Audit fuer Filesystem, Mover-Drift, NVMe-SMART und Fuellstand | `services/posture-check/posture-check.sh` | Unraid User-Script / Cron / Borg Pre-Hook | `findmnt`, `df`, `nvme`, optional `curl` fuer ntfy | `/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` |
|
||||||
| `docker-critical-events` | Live-Alarmierung fuer Docker `die`/`oom`/`kill` Events | `services/posture-check/docker-critical-events.sh` | Unraid User-Script / Hintergrundprozess | Docker CLI, ntfy | `/mnt/user/services/posture-check/docker-critical-events-last.log` | Repo-Skript + letzter Event-Log | nein | Optional als Unraid User-Script `at array start` starten; sendet nach `homelab-alerts` |
|
| `docker-critical-events` | Live-Alarmierung fuer Docker `die`/`oom`/`kill` Events | `services/posture-check/docker-critical-events.sh` | Unraid User-Script / Hintergrundprozess | Docker CLI, ntfy | `/mnt/user/services/posture-check/docker-critical-events-last.log` | Repo-Skript + letzter Event-Log | nein | Optional als Unraid User-Script `at array start` starten; sendet nach `homelab-alerts` |
|
||||||
|
|
||||||
## Backup- und Restore-Hinweise
|
## Backup- und Restore-Hinweise
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ Es ist **vor** jeder Storage- oder Compose-Änderung zu lesen. Wenn ein neuer St
|
|||||||
| Slot | Device | Filesystem | Größe | Zweck | Status nach Recovery |
|
| Slot | Device | Filesystem | Größe | Zweck | Status nach Recovery |
|
||||||
|------|--------|------------|-------|-------|----------------------|
|
|------|--------|------------|-------|-------|----------------------|
|
||||||
| Cache (Pool) | Samsung 970 EVO Plus, NVMe | **XFS** | 2 TB | Appdata, system, domains | Reformat von NTFS auf XFS, Phase 1 |
|
| Cache (Pool) | Samsung 970 EVO Plus, NVMe | **XFS** | 2 TB | Appdata, system, domains | Reformat von NTFS auf XFS, Phase 1 |
|
||||||
| Disk1 (Array) | HDD (Modell TBD) | **XFS** | TBD | Nutzdaten, Backups, Services | Migration von NTFS auf XFS, Phase 2 (Folgeprojekt) |
|
| Disk1 (Array) | HDD (Modell TBD) | **XFS** | TBD | Nutzdaten, Backups, Services | NTFS-zu-XFS-Migration Phase 2 abgeschlossen am 2026-05-25 |
|
||||||
| Parity | HDD (Modell TBD) | — (keine FS) | TBD | Redundanz für Array | Unverändert |
|
| Parity | HDD (Modell TBD) | — (keine FS) | TBD | Redundanz für Array | Unverändert |
|
||||||
| Boot | USB-Stick | FAT32 | klein | Unraid-OS, Konfiguration | Unverändert, regelmäßig per Flash-Backup gesichert |
|
| Boot | USB-Stick | FAT32 | klein | Unraid-OS, Konfiguration | Unverändert, regelmäßig per Flash-Backup gesichert |
|
||||||
| Externe Backup-Platte | Wechselplatte (Modell TBD) | XFS oder ext4 | ~8 TB | Ausgelagertes Backup-Ziel, Recovery-Material | NEU, ersetzt WD MyBookLive |
|
| Externe Backup-Platte | Wechselplatte (Modell TBD) | XFS oder ext4 | ~8 TB | Ausgelagertes Backup-Ziel, Recovery-Material | NEU, ersetzt WD MyBookLive |
|
||||||
@@ -292,7 +292,7 @@ Diese Regeln sind nicht optional. Verstoß ist Incident, kein Feature-Request.
|
|||||||
12. **Kein Backup-Lauf ohne vorgeschalteten Posture-Check (siehe §11).** Backup auf kompromittiertem Filesystem überschreibt unter Umständen den letzten guten Stand und kontaminiert die Backup-Historie.
|
12. **Kein Backup-Lauf ohne vorgeschalteten Posture-Check (siehe §11).** Backup auf kompromittiertem Filesystem überschreibt unter Umständen den letzten guten Stand und kontaminiert die Backup-Historie.
|
||||||
|
|
||||||
**Dokumentierte Host-Observability-Ausnahmen (Operator-Entscheidung 2026-05-16):**
|
**Dokumentierte Host-Observability-Ausnahmen (Operator-Entscheidung 2026-05-16):**
|
||||||
`glances`, `scrutiny`, `monitoring-promtail`, `monitoring-node-exporter` und `monitoring-cadvisor` duerfen gezielt Host-/Device-Bind-Mounts ausserhalb `/mnt/user/...` nutzen, weil ihre Kernfunktion sonst nicht erfuellbar ist. Erlaubt sind nur die in `docs/SERVICE_CATALOG.md` pro Dienst genannten Binds. Diese Ausnahmen sind keine Datenpersistenz-Pfade und duerfen nicht fuer Appdaten, Backups oder normale Service-Konfiguration erweitert werden. Neue oder geaenderte Host-Binds brauchen eine explizite Doku-Aenderung im selben Commit. `alloy` ist nur noch als abgeloester Altstand relevant.
|
`glances`, `scrutiny`, `monitoring-promtail`, `monitoring-node-exporter` und `monitoring-cadvisor` duerfen gezielt Host-/Device-Bind-Mounts ausserhalb `/mnt/user/...` nutzen, weil ihre Kernfunktion sonst nicht erfuellbar ist. Erlaubt sind nur die in `docs/SERVICE_CATALOG.md` pro Dienst genannten Binds. Diese Ausnahmen sind keine Datenpersistenz-Pfade und duerfen nicht fuer Appdaten, Backups oder normale Service-Konfiguration erweitert werden. Neue oder geaenderte Host-Binds brauchen eine explizite Doku-Aenderung im selben Commit.
|
||||||
|
|
||||||
## 13. Soft Rules — Konventionen
|
## 13. Soft Rules — Konventionen
|
||||||
|
|
||||||
@@ -400,7 +400,7 @@ Diese Sektion dokumentiert offene Operator-Entscheidungen und Lücken. **Vor Sta
|
|||||||
|
|
||||||
| Nr. | Item | Status | Verantwortung |
|
| Nr. | Item | Status | Verantwortung |
|
||||||
|-----|------|--------|---------------|
|
|-----|------|--------|---------------|
|
||||||
| 1 | Disk-Größen und Modelle in §3 (Disk1, Parity, externe Backup-Platte) | **DEFERRED 2026-05-15** — wird automatisch via Posture-Check-Detection-Lauf ergänzt; kein Blocker für Inkraftsetzung | Operator + Posture-Check |
|
| 1 | Disk-Größen und Modelle in §3 (Disk1, Parity, externe Backup-Platte) | **DEFERRED 2026-05-15** — wird automatisch via Posture-Check-Detection-Lauf ergänzt; Disk1-Filesystem ist seit 2026-05-25 XFS | Operator + Posture-Check |
|
||||||
| 2 | Retention-Werte in §8.1 (Borg-Repos lokal/remote) — abhängig von tatsächlicher Storage-Kapazität | Vorschlag steht, anzupassen | Operator |
|
| 2 | Retention-Werte in §8.1 (Borg-Repos lokal/remote) — abhängig von tatsächlicher Storage-Kapazität | Vorschlag steht, anzupassen | Operator |
|
||||||
| 3 | Backup-Tooling: Backrest abschalten, Borg alleinige Backup-Technologie | **ENTSCHIEDEN 2026-05-15** | erledigt (siehe §8.0) |
|
| 3 | Backup-Tooling: Backrest abschalten, Borg alleinige Backup-Technologie | **ENTSCHIEDEN 2026-05-15** | erledigt (siehe §8.0) |
|
||||||
| 4 | Backup-Scope für persönliche Daten: `documents`, `photos`, `finance`, `projekte` (und `media` falls behalten) **vollständig** im Pflicht-Scope | **ENTSCHIEDEN 2026-05-15** | erledigt (siehe §8.2) |
|
| 4 | Backup-Scope für persönliche Daten: `documents`, `photos`, `finance`, `projekte` (und `media` falls behalten) **vollständig** im Pflicht-Scope | **ENTSCHIEDEN 2026-05-15** | erledigt (siehe §8.2) |
|
||||||
|
|||||||
@@ -115,6 +115,23 @@ Komodo ist in diesem Setup:
|
|||||||
- Pushes koennen automatisch einen Komodo-Deploy ausloesen
|
- Pushes koennen automatisch einen Komodo-Deploy ausloesen
|
||||||
- wenn Komodo und Git voneinander abweichen, gewinnt Git
|
- wenn Komodo und Git voneinander abweichen, gewinnt Git
|
||||||
|
|
||||||
|
### Pflicht bei neuen Komodo-Stacks
|
||||||
|
|
||||||
|
Jeder neue produktive Komodo-Stack, der aus `Micha/homelab-infra` deployed wird, braucht einen aktiven Gitea-Webhook auf die aktuelle Komodo-Stack-ID.
|
||||||
|
|
||||||
|
Pflichtschritte beim Anlegen:
|
||||||
|
|
||||||
|
1. Stack in Komodo aus Gitea anlegen
|
||||||
|
2. `webhook_enabled` in Komodo aktivieren
|
||||||
|
3. passenden Gitea-Webhook fuer die aktuelle Stack-ID anlegen
|
||||||
|
4. Gitea-Hook gegen `http://komodo-core:9120/listener/github/stack/<stack-id>/deploy` pruefen
|
||||||
|
5. einen Push oder Test-Delivery ausloesen und `last_status`/Komodo-Deploy pruefen
|
||||||
|
6. Ausnahmen explizit dokumentieren
|
||||||
|
|
||||||
|
**Regel:** Kein neuer produktiver GitOps-Stack ohne funktionierenden Gitea->Komodo-Webhook. Bewusste Ausnahmen muessen im selben Aenderungsblock dokumentiert werden, inklusive Grund und Alternativ-Deploy-Weg.
|
||||||
|
|
||||||
|
Der Standardfall nutzt den globalen `KOMODO_WEBHOOK_SECRET` aus der Komodo-Host-`.env`, ausser Komodo zeigt fuer den Stack explizit ein eigenes per-Stack-Secret.
|
||||||
|
|
||||||
### Ausnahme: Komodo-Zugangsmodell
|
### Ausnahme: Komodo-Zugangsmodell
|
||||||
|
|
||||||
Komodo bleibt **bewusst** ohne zentrale Traefik-ForwardAuth-Middleware.
|
Komodo bleibt **bewusst** ohne zentrale Traefik-ForwardAuth-Middleware.
|
||||||
@@ -301,6 +318,9 @@ Nach jeder erfolgreichen Migration oder relevanten Aenderung muessen diese Datei
|
|||||||
- `docs/MIGRATION_LOG.md`
|
- `docs/MIGRATION_LOG.md`
|
||||||
- `docs/SECRETS_MAP.md`
|
- `docs/SECRETS_MAP.md`
|
||||||
- `docs/ROLLBACK.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
|
- `HOMELAB_ARCHITECTURE_MASTER_V2.md` falls Architektur betroffen ist
|
||||||
- `docs/GITOPS_DRIFT_RUNBOOK.md` falls GitOps-/Komodo-/Runtime-Drift betroffen ist
|
- `docs/GITOPS_DRIFT_RUNBOOK.md` falls GitOps-/Komodo-/Runtime-Drift betroffen ist
|
||||||
|
|
||||||
|
|||||||
Vendored
-1
@@ -1,5 +1,4 @@
|
|||||||
BASE_DOMAIN=kaleschke.info
|
BASE_DOMAIN=kaleschke.info
|
||||||
TRAEFIK_DOMAIN=traefik.kaleschke.info
|
TRAEFIK_DOMAIN=traefik.kaleschke.info
|
||||||
AUTH_DOMAIN=auth.kaleschke.info
|
AUTH_DOMAIN=auth.kaleschke.info
|
||||||
HOME_DOMAIN=home.kaleschke.info
|
|
||||||
GLANCE_DOMAIN=glance.kaleschke.info
|
GLANCE_DOMAIN=glance.kaleschke.info
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "53:53/tcp"
|
- "53:53/tcp"
|
||||||
- "53:53/udp"
|
- "53:53/udp"
|
||||||
- "8082:80"
|
- "100.80.98.33:8082:80"
|
||||||
security_opt:
|
security_opt:
|
||||||
- no-new-privileges:true
|
- no-new-privileges:true
|
||||||
|
|
||||||
|
|||||||
+13
-10
@@ -12,10 +12,12 @@ Zielzustand: ein zentraler Observability-Stack fuer KalliLab CORE.
|
|||||||
- `monitoring-promtail`: Docker-Log-Discovery ueber read-only Docker-Socket
|
- `monitoring-promtail`: Docker-Log-Discovery ueber read-only Docker-Socket
|
||||||
- `monitoring-node-exporter`: Host-Metriken
|
- `monitoring-node-exporter`: Host-Metriken
|
||||||
- `monitoring-cadvisor`: Container-Metriken
|
- `monitoring-cadvisor`: Container-Metriken
|
||||||
- `monitoring-blackbox-exporter`: externe HTTP-Erreichbarkeit als Uptime-Kuma-Ablösepfad
|
- `monitoring-blackbox-exporter`: externe HTTP-Erreichbarkeit als Uptime-Kuma-Ersatz
|
||||||
- `monitoring-influxdb3-core`: InfluxDB 3 Core fuer Home-Assistant-/Ecowitt-Langzeitdaten
|
- `monitoring-influxdb3-core`: InfluxDB 3 Core fuer Home-Assistant-/Ecowitt-Langzeitdaten
|
||||||
|
|
||||||
Die alten Pfade `ops/loki` und `ops/grafana-influxdb` sind damit abgeloeste Altstaende. Sie bleiben vorerst im Repo als Rollback- und Migrationsreferenz, sollen aber nach erfolgreichem Live-Deploy nicht parallel betrieben werden.
|
Die alten Pfade `ops/loki` und `ops/grafana-influxdb` wurden am 2026-05-26 aus dem aktiven Repo entfernt. Rollback erfolgt bei Bedarf ueber Git-Historie, nicht ueber parallel gepflegte Compose-Verzeichnisse.
|
||||||
|
|
||||||
|
Live-Stand 2026-05-25: die zehn `monitoring-*` Container laufen produktiv, die alten Container `grafana`, `influxdb3-core`, `loki` und `alloy` sind in Docker nicht mehr vorhanden. Uptime Kuma ist durch Blackbox Exporter, Prometheus-Alerts und das Dashboard `Homelab / Availability` abgeloest.
|
||||||
|
|
||||||
## Secrets
|
## Secrets
|
||||||
|
|
||||||
@@ -47,12 +49,13 @@ INFLUXDB_BIND_IP=192.168.178.58
|
|||||||
|
|
||||||
## Migration
|
## Migration
|
||||||
|
|
||||||
1. Secrets anlegen.
|
1. Secrets anlegen. Erledigt.
|
||||||
2. Alten `ops/loki`-Stack stoppen, wenn `monitoring-loki` und `monitoring-promtail` live gehen.
|
2. Alten `ops/loki`-Stack stoppen, wenn `monitoring-loki` und `monitoring-promtail` live gehen. Erledigt.
|
||||||
3. Alten `ops/grafana-influxdb`-Stack stoppen, bevor `monitoring-influxdb3-core` den LAN-Port `192.168.178.58:8181` uebernimmt.
|
3. Alten `ops/grafana-influxdb`-Stack stoppen, bevor `monitoring-influxdb3-core` den LAN-Port `192.168.178.58:8181` uebernimmt. Erledigt.
|
||||||
4. `monitoring` via Komodo deployen und `INFLUXDB_BIND_IP=192.168.178.58` erst setzen, wenn der Altcontainer den Port freigegeben hat.
|
4. `monitoring` via Komodo deployen und `INFLUXDB_BIND_IP=192.168.178.58` erst setzen, wenn der Altcontainer den Port freigegeben hat. Erledigt.
|
||||||
5. Optionales Dashboard-Bootstrap-Profil einmalig ausfuehren.
|
5. Alte Repo-Verzeichnisse `ops/loki` und `ops/grafana-influxdb` entfernen. Erledigt.
|
||||||
6. Home Assistant Writer gegen `http://192.168.178.58:8181/` pruefen; `401 Unauthorized` ohne Token ist erwartbar.
|
6. Optionales Dashboard-Bootstrap-Profil einmalig ausfuehren.
|
||||||
|
7. Home Assistant Writer gegen `http://192.168.178.58:8181/` pruefen; `401 Unauthorized` ohne Token ist erwartbar.
|
||||||
|
|
||||||
## Smoke-Tests
|
## Smoke-Tests
|
||||||
|
|
||||||
@@ -63,11 +66,11 @@ INFLUXDB_BIND_IP=192.168.178.58
|
|||||||
- Loki zeigt Container-Logs mit Labels `container`, `compose_project`, `compose_service`.
|
- Loki zeigt Container-Logs mit Labels `container`, `compose_project`, `compose_service`.
|
||||||
- InfluxDB 3 Core enthaelt die Datenbank `homelab`.
|
- InfluxDB 3 Core enthaelt die Datenbank `homelab`.
|
||||||
|
|
||||||
## Ablösepfad
|
## Abloesestand
|
||||||
|
|
||||||
- Dozzle bleibt abgeloest: `Homelab / Containers + Logs` ersetzt Live-Logs und Error-Rate.
|
- Dozzle bleibt abgeloest: `Homelab / Containers + Logs` ersetzt Live-Logs und Error-Rate.
|
||||||
- Glances erst stoppen, wenn `Homelab / Host Overview` und `Homelab / Containers + Logs` fuer CPU, RAM, Disk, Network, Container-CPU und Container-RAM passen.
|
- Glances erst stoppen, wenn `Homelab / Host Overview` und `Homelab / Containers + Logs` fuer CPU, RAM, Disk, Network, Container-CPU und Container-RAM passen.
|
||||||
- Uptime Kuma erst stoppen, wenn `Homelab / Availability` und Grafana-Alerting mindestens sieben Tage parallel sauber laufen.
|
- Uptime Kuma ist entfernt; `Homelab / Availability`, Blackbox Exporter und Prometheus-Alerts sind der Zielzustand fuer HTTP-Verfuegbarkeit.
|
||||||
- Dashboard-Zielbestand: `Homelab / Availability`, `Homelab / Containers + Logs`, `Homelab / Host Overview`, `Traefik Official Standalone Dashboard`.
|
- Dashboard-Zielbestand: `Homelab / Availability`, `Homelab / Containers + Logs`, `Homelab / Host Overview`, `Traefik Official Standalone Dashboard`.
|
||||||
|
|
||||||
## Alerting
|
## Alerting
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ scrape_configs:
|
|||||||
- https://auth.kaleschke.info
|
- https://auth.kaleschke.info
|
||||||
- https://git.kaleschke.info
|
- https://git.kaleschke.info
|
||||||
- https://komodo.kaleschke.info
|
- https://komodo.kaleschke.info
|
||||||
- https://uptime.kaleschke.info
|
- https://glance.kaleschke.info
|
||||||
- https://home.kaleschke.info
|
|
||||||
- https://paperless.kaleschke.info
|
- https://paperless.kaleschke.info
|
||||||
- https://paperless-gpt.kaleschke.info
|
- https://paperless-gpt.kaleschke.info
|
||||||
- https://immich.kaleschke.info
|
- https://immich.kaleschke.info
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ Der technische Scope für `critical_infra` ist in `all-important-sources.txt` fe
|
|||||||
- `/local/secrets`
|
- `/local/secrets`
|
||||||
- `/local/appdata/authelia/config`
|
- `/local/appdata/authelia/config`
|
||||||
- `/local/appdata/traefik`
|
- `/local/appdata/traefik`
|
||||||
- `/local/appdata/homepage`
|
|
||||||
- `/local/appdata/ntfy`
|
- `/local/appdata/ntfy`
|
||||||
- `/local/appdata/paperless-gpt`
|
- `/local/appdata/paperless-gpt`
|
||||||
- `/local/appdata/tailscale`
|
- `/local/appdata/tailscale`
|
||||||
@@ -78,7 +77,6 @@ Der technische Scope für `critical_infra` ist in `all-important-sources.txt` fe
|
|||||||
| Mail-archiver | DataProtection-Keys | Shared PostgreSQL Dump vorhanden | Ja | gut |
|
| Mail-archiver | DataProtection-Keys | Shared PostgreSQL Dump vorhanden | Ja | gut |
|
||||||
| Authelia | Config + Secrets | Shared PostgreSQL Dump vorgesehen / erzeugt | Ja | gut |
|
| Authelia | Config + Secrets | Shared PostgreSQL Dump vorgesehen / erzeugt | Ja | gut |
|
||||||
| Traefik | dynamische Config + Let's Encrypt | keine separate DB | Ja | gut |
|
| Traefik | dynamische Config + Let's Encrypt | keine separate DB | Ja | gut |
|
||||||
| Homepage | Config + Bilder | keine separate DB | Ja | gut |
|
|
||||||
| ntfy | Datei-Daten | keine separate DB | Ja | gut |
|
| ntfy | Datei-Daten | keine separate DB | Ja | gut |
|
||||||
| Paperless-GPT | lokale Daten / Prompts | keine separate DB | Ja | gut |
|
| Paperless-GPT | lokale Daten / Prompts | keine separate DB | Ja | gut |
|
||||||
| Tailscale | State-Verzeichnis | keine separate DB | Ja | gut |
|
| Tailscale | State-Verzeichnis | keine separate DB | Ja | gut |
|
||||||
@@ -143,7 +141,7 @@ Seit dem ersten erfolgreichen Lauf wurde der Zielzustand weiter konkret umgesetz
|
|||||||
- Ein Restore-Smoke-Test war erfolgreich:
|
- Ein Restore-Smoke-Test war erfolgreich:
|
||||||
- `postgresql17-globals.sql` wurde wiederhergestellt
|
- `postgresql17-globals.sql` wurde wiederhergestellt
|
||||||
- `gitea.db` wurde wiederhergestellt
|
- `gitea.db` wurde wiederhergestellt
|
||||||
- `ntfy` und Uptime Kuma sind für Borg-Monitoring eingerichtet.
|
- `ntfy` bleibt fuer Borg-Alerts eingerichtet; Uptime Kuma wurde 2026-05-25 durch Blackbox/Prometheus/Grafana abgeloest.
|
||||||
- `Firefly`, `Firefly-Fints` und `Semaphore` wurden aus Git und vom Homelab entfernt.
|
- `Firefly`, `Firefly-Fints` und `Semaphore` wurden aus Git und vom Homelab entfernt.
|
||||||
|
|
||||||
## Was aktuell bewusst nicht als Problem gewertet wird
|
## Was aktuell bewusst nicht als Problem gewertet wird
|
||||||
|
|||||||
@@ -11,16 +11,18 @@ Use Borg as the single backup system for:
|
|||||||
- critical file-backed application data
|
- critical file-backed application data
|
||||||
- secrets, keys, and reverse-proxy state
|
- secrets, keys, and reverse-proxy state
|
||||||
- database dumps generated before each Borg backup
|
- database dumps generated before each Borg backup
|
||||||
|
- Unraid flash configuration artifacts generated before each Borg backup
|
||||||
|
|
||||||
Do not back up raw live database storage directories as the primary recovery artifact.
|
Do not back up raw live database storage directories as the primary recovery artifact.
|
||||||
|
|
||||||
## Strategy
|
## Strategy
|
||||||
|
|
||||||
1. A pre-backup dump script runs on the host and writes fresh dumps to `/mnt/user/backups/borg/dumps/latest`.
|
1. A pre-backup dump script runs on the host and writes fresh dumps plus `unraid-flash-config.tar.gz` to `/mnt/user/backups/borg/dumps/latest`.
|
||||||
2. Borg backs up `/local/borg-dumps` plus the critical mounted paths below.
|
2. Borg backs up `/local/borg-dumps` plus the critical mounted paths below.
|
||||||
3. Borg retention handles history; the dump directory itself keeps only the latest artifacts.
|
3. Borg retention handles history; the dump directory itself keeps only the latest artifacts.
|
||||||
|
|
||||||
The inclusion of `/local/secrets` is intentional: Borg is expected to cover disaster recovery for selected secret material as part of the current homelab restore strategy.
|
The inclusion of `/local/secrets` is intentional: Borg is expected to cover disaster recovery for selected secret material as part of the current homelab restore strategy.
|
||||||
|
The Unraid flash configuration archive is intentional as well and must be treated as secret backup material.
|
||||||
|
|
||||||
## Service Inventory
|
## Service Inventory
|
||||||
|
|
||||||
@@ -34,13 +36,14 @@ The inclusion of `/local/secrets` is intentional: Borg is expected to cover disa
|
|||||||
| Mail-archiver | shared Postgres dump + data protection keys | `/local/borg-dumps`, `/local/appdata/mailarchiver/data-protection-keys` |
|
| Mail-archiver | shared Postgres dump + data protection keys | `/local/borg-dumps`, `/local/appdata/mailarchiver/data-protection-keys` |
|
||||||
| Authelia | shared Postgres dump + config + secrets | `/local/borg-dumps`, `/local/appdata/authelia/config`, `/local/secrets` |
|
| Authelia | shared Postgres dump + config + secrets | `/local/borg-dumps`, `/local/appdata/authelia/config`, `/local/secrets` |
|
||||||
| Traefik | file data | `/local/appdata/traefik` |
|
| Traefik | file data | `/local/appdata/traefik` |
|
||||||
| Homepage | file data | `/local/appdata/homepage` |
|
|
||||||
| ntfy | file data | `/local/appdata/ntfy` |
|
| ntfy | file data | `/local/appdata/ntfy` |
|
||||||
| Paperless-GPT | file data | `/local/appdata/paperless-gpt` |
|
| Paperless-GPT | file data | `/local/appdata/paperless-gpt` |
|
||||||
| Tailscale | file data | `/local/appdata/tailscale` |
|
| Tailscale | file data | `/local/appdata/tailscale` |
|
||||||
| AdGuard | config only | `/local/appdata/adguard/conf` |
|
| AdGuard | config only | `/local/appdata/adguard/conf` |
|
||||||
| Borg UI | SQLite dump + self-backup | `/local/borg-dumps`, `/local/appdata/borg-ui/data` |
|
| Borg UI | SQLite dump + self-backup | `/local/borg-dumps`, `/local/appdata/borg-ui/data` |
|
||||||
| Komodo | config + Mongo dump | `/local/borg-dumps`, `/local/appdata/komodo/periphery`, `/local/appdata/komodo/core` |
|
| Komodo | config + Mongo dump | `/local/borg-dumps`, `/local/appdata/komodo/periphery`, `/local/appdata/komodo/core` |
|
||||||
|
| GitOps host automation | repo clone + Komodo workspaces + host-check state | `/local/services/homelab-infra`, `/local/services/stacks`, `/local/services/posture-check` |
|
||||||
|
| Unraid OS flash | generated config archive | `/local/borg-dumps/unraid-flash-config.tar.gz` plus checksum and manifest |
|
||||||
| Nextcloud | DB dump + file data | `/local/borg-dumps`, `/local/appdata/nextcloud/html`, `/local/nextcloud/data` |
|
| Nextcloud | DB dump + file data | `/local/borg-dumps`, `/local/appdata/nextcloud/html`, `/local/nextcloud/data` |
|
||||||
| Grafana | SQLite dump + file data | `/local/borg-dumps`, `/local/appdata/grafana` |
|
| Grafana | SQLite dump + file data | `/local/borg-dumps`, `/local/appdata/grafana` |
|
||||||
| Filebrowser | file-backed state dump + file data | `/local/borg-dumps`, `/local/appdata/filebrowser` |
|
| Filebrowser | file-backed state dump + file data | `/local/borg-dumps`, `/local/appdata/filebrowser` |
|
||||||
@@ -60,6 +63,10 @@ Option A umgesetzt: `pre-backup-dumps.sh` writes `nextcloud.dump` from `nextclou
|
|||||||
|
|
||||||
`komodo-mongo.archive.gz` was produced and verified on 2026-05-04 (`gzip -t` ok). The dump function is in place in `pre-backup-dumps.sh`. Re-verify after any Komodo or Mongo major upgrade.
|
`komodo-mongo.archive.gz` was produced and verified on 2026-05-04 (`gzip -t` ok). The dump function is in place in `pre-backup-dumps.sh`. Re-verify after any Komodo or Mongo major upgrade.
|
||||||
|
|
||||||
|
### GitOps host automation
|
||||||
|
|
||||||
|
The live Unraid User Scripts execute repo scripts from `/mnt/user/services/homelab-infra`, while Komodo keeps stack workspaces below `/mnt/user/services/stacks`. These paths are now mounted into Borg UI as `/local/services/...` and included explicitly so host-side script hotfixes, stack workspace state, and posture-check state are recoverable.
|
||||||
|
|
||||||
## Database Dumps Required
|
## Database Dumps Required
|
||||||
|
|
||||||
### Shared PostgreSQL (`postgresql17`)
|
### Shared PostgreSQL (`postgresql17`)
|
||||||
@@ -77,8 +84,9 @@ Option A umgesetzt: `pre-backup-dumps.sh` writes `nextcloud.dump` from `nextclou
|
|||||||
### Other Databases
|
### Other Databases
|
||||||
|
|
||||||
- Komodo MongoDB
|
- Komodo MongoDB
|
||||||
- SQLite: `gitea`, `vaultwarden`, `uptime-kuma`, `speedtest-tracker`, `borg-ui`, `grafana`
|
- SQLite: `gitea`, `vaultwarden`, `speedtest-tracker`, `borg-ui`, `grafana`
|
||||||
- File-backed state: `filebrowser.bolt.dump`
|
- File-backed state: `filebrowser.bolt.dump`
|
||||||
|
- Unraid flash config: `unraid-flash-config.tar.gz` plus `unraid-flash-config.tar.gz.sha256`
|
||||||
|
|
||||||
## Explicitly Not Backed Up as Raw Live DB Files
|
## Explicitly Not Backed Up as Raw Live DB Files
|
||||||
|
|
||||||
@@ -97,7 +105,6 @@ These are not part of the first-class Borg scope:
|
|||||||
- Plex metadata and cache
|
- Plex metadata and cache
|
||||||
- AdGuard query log
|
- AdGuard query log
|
||||||
- code-server extensions cache
|
- code-server extensions cache
|
||||||
- uptime-kuma
|
|
||||||
- scrutiny metrics history
|
- scrutiny metrics history
|
||||||
- dozzle, glances, speedtest
|
- dozzle, glances, speedtest
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
/local/secrets
|
/local/secrets
|
||||||
/local/appdata/authelia/config
|
/local/appdata/authelia/config
|
||||||
/local/appdata/traefik
|
/local/appdata/traefik
|
||||||
/local/appdata/homepage
|
|
||||||
/local/appdata/ntfy
|
/local/appdata/ntfy
|
||||||
/local/appdata/paperless-gpt
|
/local/appdata/paperless-gpt
|
||||||
/local/appdata/tailscale
|
/local/appdata/tailscale
|
||||||
@@ -20,3 +19,6 @@
|
|||||||
/local/appdata/borg-ui/data
|
/local/appdata/borg-ui/data
|
||||||
/local/appdata/komodo/periphery
|
/local/appdata/komodo/periphery
|
||||||
/local/appdata/komodo/core
|
/local/appdata/komodo/core
|
||||||
|
/local/services/homelab-infra
|
||||||
|
/local/services/stacks
|
||||||
|
/local/services/posture-check
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ services:
|
|||||||
- /mnt/user/documents/nextcloud-data:/local/nextcloud/data:ro
|
- /mnt/user/documents/nextcloud-data:/local/nextcloud/data:ro
|
||||||
- /mnt/user/photos/immich:/local/immich/upload:ro
|
- /mnt/user/photos/immich:/local/immich/upload:ro
|
||||||
- /mnt/user/photos/family_archive:/local/immich/external:ro
|
- /mnt/user/photos/family_archive:/local/immich/external:ro
|
||||||
|
- /mnt/user/services:/local/services:ro
|
||||||
- /mnt/user/services/gitea/data:/local/gitea/data:ro
|
- /mnt/user/services/gitea/data:/local/gitea/data:ro
|
||||||
- /mnt/user/appdata/borg-ui/restore:/restore
|
- /mnt/user/appdata/borg-ui/restore:/restore
|
||||||
dns:
|
dns:
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ Fresh dump artifacts are written to:
|
|||||||
|
|
||||||
Borg UI should include `/local/borg-dumps` as a backup source.
|
Borg UI should include `/local/borg-dumps` as a backup source.
|
||||||
|
|
||||||
|
The dump set also includes `unraid-flash-config.tar.gz`, a host-generated
|
||||||
|
archive of `/boot/config` plus checksum and manifest. Treat this archive as
|
||||||
|
secret backup material.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- The script is written for host execution where `docker` is available.
|
- The script is written for host execution where `docker` is available.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ It should **not** be implemented as a Borg UI inline hook in the current design.
|
|||||||
`pre-borg.sh` currently chains the host-side checks:
|
`pre-borg.sh` currently chains the host-side checks:
|
||||||
|
|
||||||
- `services/posture-check/posture-check.sh`
|
- `services/posture-check/posture-check.sh`
|
||||||
- `ops/borg-ui/scripts/pre-backup-dumps.sh`
|
- `ops/borg-ui/scripts/pre-backup-dumps.sh` including the Unraid flash config archive
|
||||||
- `ops/restore-tests/check-restore-freshness.sh`
|
- `ops/restore-tests/check-restore-freshness.sh`
|
||||||
|
|
||||||
The dump step assumes:
|
The dump step assumes:
|
||||||
@@ -56,9 +56,10 @@ The intended sequence is:
|
|||||||
|
|
||||||
1. Host wrapper checks posture.
|
1. Host wrapper checks posture.
|
||||||
2. Host script refreshes `latest` dump artifacts.
|
2. Host script refreshes `latest` dump artifacts.
|
||||||
3. Freshness check verifies expected dumps.
|
3. Host script writes `unraid-flash-config.tar.gz` plus checksum and manifest into the same dump set.
|
||||||
4. Borg UI backs up `/local/borg-dumps` together with the rest of `critical_infra`.
|
4. Freshness check verifies expected dumps and the flash config archive.
|
||||||
5. Borg history preserves dump history, so the host only needs to keep the most recent dump set.
|
5. Borg UI backs up `/local/borg-dumps` together with the rest of `critical_infra`.
|
||||||
|
6. Borg history preserves dump history, so the host only needs to keep the most recent dump set.
|
||||||
|
|
||||||
## Current dump target
|
## Current dump target
|
||||||
|
|
||||||
|
|||||||
@@ -155,6 +155,56 @@ dump_file_copy() {
|
|||||||
atomic_write "$output" "$tmp"
|
atomic_write "$output" "$tmp"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backup_unraid_flash_config() {
|
||||||
|
output="$LATEST_DIR/unraid-flash-config.tar.gz"
|
||||||
|
checksum="$LATEST_DIR/unraid-flash-config.tar.gz.sha256"
|
||||||
|
manifest="$LATEST_DIR/unraid-flash-config.manifest.txt"
|
||||||
|
tmp="$TMP_DIR/unraid-flash-config.tar.gz.tmp"
|
||||||
|
tmp_checksum="$TMP_DIR/unraid-flash-config.tar.gz.sha256.tmp"
|
||||||
|
tmp_manifest="$TMP_DIR/unraid-flash-config.manifest.txt.tmp"
|
||||||
|
|
||||||
|
if [ ! -d /boot/config ]; then
|
||||||
|
warn "Skipping Unraid flash config backup because /boot/config is missing"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Backing up Unraid flash configuration from /boot/config"
|
||||||
|
rm -f "$tmp" "$tmp_checksum" "$tmp_manifest"
|
||||||
|
|
||||||
|
tar -C /boot \
|
||||||
|
--exclude='config/plugins/*/*.txz' \
|
||||||
|
--exclude='config/plugins/*/*.tgz' \
|
||||||
|
--exclude='config/plugins/*/*.tar' \
|
||||||
|
--exclude='config/plugins/*/*.tar.*' \
|
||||||
|
--exclude='config/plugins/*/*.zip' \
|
||||||
|
--exclude='config/plugins/*/*.md5' \
|
||||||
|
-czf "$tmp" config
|
||||||
|
chmod 600 "$tmp"
|
||||||
|
atomic_write "$output" "$tmp"
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "$LATEST_DIR"
|
||||||
|
sha256sum "$(basename "$output")"
|
||||||
|
) > "$tmp_checksum"
|
||||||
|
chmod 600 "$tmp_checksum"
|
||||||
|
atomic_write "$checksum" "$tmp_checksum"
|
||||||
|
|
||||||
|
{
|
||||||
|
printf 'created_utc=%s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
||||||
|
printf 'host=%s\n' "$(hostname)"
|
||||||
|
if [ -f /etc/unraid-version ]; then
|
||||||
|
sed 's/^/unraid_/' /etc/unraid-version
|
||||||
|
fi
|
||||||
|
printf 'source=/boot/config\n'
|
||||||
|
printf 'archive=%s\n' "$(basename "$output")"
|
||||||
|
printf 'checksum=%s\n' "$(basename "$checksum")"
|
||||||
|
printf 'note=%s\n' 'Contains Unraid configuration and must be treated as secret backup material.'
|
||||||
|
printf 'excluded=%s\n' 'downloadable plugin package archives under /boot/config/plugins/*/'
|
||||||
|
} > "$tmp_manifest"
|
||||||
|
chmod 600 "$tmp_manifest"
|
||||||
|
atomic_write "$manifest" "$tmp_manifest"
|
||||||
|
}
|
||||||
|
|
||||||
dump_optional_pg_db() {
|
dump_optional_pg_db() {
|
||||||
container="$1"
|
container="$1"
|
||||||
password="$2"
|
password="$2"
|
||||||
@@ -219,6 +269,8 @@ dump_mongo_container() {
|
|||||||
main() {
|
main() {
|
||||||
need_cmd docker
|
need_cmd docker
|
||||||
need_cmd sqlite3
|
need_cmd sqlite3
|
||||||
|
need_cmd tar
|
||||||
|
need_cmd sha256sum
|
||||||
ensure_dirs
|
ensure_dirs
|
||||||
|
|
||||||
# Shared PostgreSQL 17
|
# Shared PostgreSQL 17
|
||||||
@@ -260,7 +312,6 @@ main() {
|
|||||||
# SQLite databases
|
# SQLite databases
|
||||||
dump_sqlite_container "gitea" "/data/gitea/gitea.db" "$LATEST_DIR/gitea.sqlite.dump" "/mnt/user/services/gitea/data/gitea/gitea.db"
|
dump_sqlite_container "gitea" "/data/gitea/gitea.db" "$LATEST_DIR/gitea.sqlite.dump" "/mnt/user/services/gitea/data/gitea/gitea.db"
|
||||||
dump_sqlite_container "vaultwarden" "/data/db.sqlite3" "$LATEST_DIR/vaultwarden.sqlite.dump" "/mnt/user/appdata/vaultwarden/db.sqlite3"
|
dump_sqlite_container "vaultwarden" "/data/db.sqlite3" "$LATEST_DIR/vaultwarden.sqlite.dump" "/mnt/user/appdata/vaultwarden/db.sqlite3"
|
||||||
dump_sqlite_container "uptime-kuma" "/app/data/kuma.db" "$LATEST_DIR/uptime-kuma.sqlite.dump" "/mnt/user/appdata/uptime-kuma/kuma.db"
|
|
||||||
dump_sqlite_container "speedtest-tracker" "/config/database.sqlite" "$LATEST_DIR/speedtest-tracker.sqlite.dump" "/mnt/user/appdata/speedtest-tracker/config/database.sqlite"
|
dump_sqlite_container "speedtest-tracker" "/config/database.sqlite" "$LATEST_DIR/speedtest-tracker.sqlite.dump" "/mnt/user/appdata/speedtest-tracker/config/database.sqlite"
|
||||||
|
|
||||||
# Filebrowser uses file-backed app state, but this installation is not SQLite.
|
# Filebrowser uses file-backed app state, but this installation is not SQLite.
|
||||||
@@ -273,6 +324,10 @@ main() {
|
|||||||
# MongoDB
|
# MongoDB
|
||||||
dump_mongo_container "komodo-mongo" "$LATEST_DIR/komodo-mongo.archive.gz"
|
dump_mongo_container "komodo-mongo" "$LATEST_DIR/komodo-mongo.archive.gz"
|
||||||
|
|
||||||
|
# Unraid USB flash configuration. This is generated into the existing dump
|
||||||
|
# set so Borg carries it off-site together with the database artifacts.
|
||||||
|
backup_unraid_flash_config
|
||||||
|
|
||||||
log "Finished refreshing dump set in $LATEST_DIR"
|
log "Finished refreshing dump set in $LATEST_DIR"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,18 @@ NTFY_SCRIPT="${NTFY_SCRIPT:-$REPO_ROOT/ops/restore-tests/send-ntfy.sh}"
|
|||||||
NTFY_TOPIC="${NTFY_TOPIC:-homelab-alerts}"
|
NTFY_TOPIC="${NTFY_TOPIC:-homelab-alerts}"
|
||||||
ALLOW_POSTURE_WARNING="${ALLOW_POSTURE_WARNING:-1}"
|
ALLOW_POSTURE_WARNING="${ALLOW_POSTURE_WARNING:-1}"
|
||||||
|
|
||||||
|
case "${DUMP_ROOT:-}" in
|
||||||
|
*/latest)
|
||||||
|
FRESHNESS_DUMP_ROOT="${FRESHNESS_DUMP_ROOT:-$DUMP_ROOT}"
|
||||||
|
;;
|
||||||
|
"")
|
||||||
|
FRESHNESS_DUMP_ROOT="${FRESHNESS_DUMP_ROOT:-/mnt/user/backups/borg/dumps/latest}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
FRESHNESS_DUMP_ROOT="${FRESHNESS_DUMP_ROOT:-$DUMP_ROOT/latest}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
notify_failure() {
|
notify_failure() {
|
||||||
local step="$1"
|
local step="$1"
|
||||||
local message="$2"
|
local message="$2"
|
||||||
@@ -45,6 +57,6 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
run_step "pre-backup-dumps" "$PRE_BACKUP_DUMPS"
|
run_step "pre-backup-dumps" "$PRE_BACKUP_DUMPS"
|
||||||
run_step "restore-freshness" "$FRESHNESS_CHECK"
|
run_step "restore-freshness" env DUMP_ROOT="$FRESHNESS_DUMP_ROOT" "$FRESHNESS_CHECK"
|
||||||
|
|
||||||
echo "[pre-borg] All pre-flight checks passed"
|
echo "[pre-borg] All pre-flight checks passed"
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- /mnt/user/appdata/code-server:/config
|
- /mnt/user/appdata/code-server:/config
|
||||||
- /mnt/user/services/dev:/workspace
|
- /mnt/user/services/dev:/workspace
|
||||||
- /mnt/user/appdata/homepage:/prod/homepage
|
|
||||||
- /mnt/user/appdata/code-server/secrets/password:/run/secrets/password:ro
|
- /mnt/user/appdata/code-server/secrets/password:/run/secrets/password:ro
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -127,9 +127,6 @@ pages:
|
|||||||
- title: Core
|
- title: Core
|
||||||
color: 212 100 50
|
color: 212 100 50
|
||||||
links:
|
links:
|
||||||
- title: Home
|
|
||||||
url: https://home.kaleschke.info
|
|
||||||
icon: sh:homepage
|
|
||||||
- title: Komodo
|
- title: Komodo
|
||||||
url: https://komodo.kaleschke.info
|
url: https://komodo.kaleschke.info
|
||||||
icon: sh:komodo
|
icon: sh:komodo
|
||||||
@@ -145,9 +142,6 @@ pages:
|
|||||||
- title: Borg
|
- title: Borg
|
||||||
url: https://borg.kaleschke.info
|
url: https://borg.kaleschke.info
|
||||||
icon: mdi:archive
|
icon: mdi:archive
|
||||||
- title: Uptime
|
|
||||||
url: https://uptime.kaleschke.info
|
|
||||||
icon: sh:uptime-kuma
|
|
||||||
- title: Glances
|
- title: Glances
|
||||||
url: https://glances.kaleschke.info
|
url: https://glances.kaleschke.info
|
||||||
icon: sh:glances
|
icon: sh:glances
|
||||||
@@ -299,24 +293,12 @@ pages:
|
|||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glance.svg
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glance.svg
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
- title: Homepage
|
|
||||||
url: https://home.kaleschke.info
|
|
||||||
check-url: http://homepage:3000
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/homepage.png
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Monitoring Grafana
|
- title: Monitoring Grafana
|
||||||
url: https://monitoring.kaleschke.info
|
url: https://monitoring.kaleschke.info
|
||||||
check-url: http://monitoring-grafana:3000/api/health
|
check-url: http://monitoring-grafana:3000/api/health
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/grafana.svg
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/grafana.svg
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
alt-status-codes: [200, 302, 401, 403]
|
||||||
- title: Uptime Kuma
|
|
||||||
url: https://uptime.kaleschke.info
|
|
||||||
check-url: http://uptime-kuma:3001
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/uptime-kuma.svg
|
|
||||||
timeout: 5s
|
|
||||||
alt-status-codes: [200, 302, 401, 403]
|
|
||||||
- title: Glances
|
- title: Glances
|
||||||
url: https://glances.kaleschke.info
|
url: https://glances.kaleschke.info
|
||||||
check-url: http://glances:61208
|
check-url: http://glances:61208
|
||||||
@@ -616,13 +598,6 @@ pages:
|
|||||||
description: PDF Tools
|
description: PDF Tools
|
||||||
category: apps
|
category: apps
|
||||||
hide: false
|
hide: false
|
||||||
homepage:
|
|
||||||
name: Homepage
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/homepage.png
|
|
||||||
url: https://home.kaleschke.info
|
|
||||||
description: Bestehendes Start-Dashboard
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
glance:
|
glance:
|
||||||
name: Glance
|
name: Glance
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glance.svg
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glance.svg
|
||||||
@@ -689,13 +664,6 @@ pages:
|
|||||||
parent: monitoring
|
parent: monitoring
|
||||||
category: ops
|
category: ops
|
||||||
hide: false
|
hide: false
|
||||||
uptime-kuma:
|
|
||||||
name: Uptime Kuma
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/uptime-kuma.svg
|
|
||||||
url: https://uptime.kaleschke.info
|
|
||||||
description: Uptime Checks
|
|
||||||
category: ops
|
|
||||||
hide: false
|
|
||||||
glances:
|
glances:
|
||||||
name: Glances
|
name: Glances
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glances.svg
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glances.svg
|
||||||
@@ -900,9 +868,6 @@ pages:
|
|||||||
- title: Tools
|
- title: Tools
|
||||||
color: 4 78 57
|
color: 4 78 57
|
||||||
links:
|
links:
|
||||||
- title: Uptime Kuma
|
|
||||||
url: https://uptime.kaleschke.info
|
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/uptime-kuma.svg
|
|
||||||
- title: Glances
|
- title: Glances
|
||||||
url: https://glances.kaleschke.info
|
url: https://glances.kaleschke.info
|
||||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glances.svg
|
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/glances.svg
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
# Grafana + InfluxDB 3 Core
|
|
||||||
|
|
||||||
Status: abgeloester Altstand. Der zentrale Zielzustand ist `monitoring/` mit `monitoring-grafana`, `monitoring-influxdb3-core`, Prometheus, Loki und Promtail.
|
|
||||||
|
|
||||||
Monitoring-Stack fuer Grafana + InfluxDB 3 Core. InfluxDB bleibt ohne Public Route; interne Writer wie Home Assistant koennen ueber einen gezielt gebundenen LAN-Port schreiben.
|
|
||||||
|
|
||||||
Nach erfolgreichem `monitoring/`-Deploy diesen Stack nicht parallel weiterbetreiben. Er bleibt vorerst als Rollback- und Migrationsreferenz im Repo.
|
|
||||||
|
|
||||||
## Quellen / Entscheidungen
|
|
||||||
|
|
||||||
- Grafana nutzt das offizielle OSS-Image `grafana/grafana:12.4.3`.
|
|
||||||
- InfluxDB nutzt `influxdb:3.9.1-core`, nicht `latest`, weil `latest` bei InfluxDB aktiv in Richtung InfluxDB 3 umgestellt wird.
|
|
||||||
- Grafana wird ueber Traefik + `authelia@file,secure-headers@file` unter `grafana.kaleschke.info` veroeffentlicht.
|
|
||||||
- InfluxDB bleibt ohne Traefik-Route. Der HTTP-Port `8181` kann fuer interne Writer wie Home Assistant ueber `INFLUXDB_BIND_IP` auf eine LAN-Adresse gebunden werden; Default ist `127.0.0.1`.
|
|
||||||
- InfluxDB haengt an zwei Compose-Netzen: `grafana_influx_internal` fuer Grafana und `grafana_influx_lan` fuer das Docker Host-Port-Publishing. Im laufenden Komodo-Stack heissen sie durch den Compose-Projektpraefix `grafana_grafana_influx_internal` und `grafana_grafana_influx_lan`. InfluxDB haengt bewusst nicht im `frontend_net`.
|
|
||||||
- Grafana provisioning legt eine SQL-Datenquelle fuer InfluxDB 3 Core mit der Datenbank `homelab` und eine Loki-Datasource fuer Container-Logs an.
|
|
||||||
- Der Grafana-Datasource-Token liegt als Secret-Datei auf dem Host und wird beim Containerstart nur containerintern in die fuer Grafana-Provisioning noetige Environment-Variable geladen.
|
|
||||||
- Home Assistant schreibt mit der InfluxDB-v2-API-Kompatibilitaet nach InfluxDB 3; Details: `docs/HOME_ASSISTANT_INFLUXDB_ECOWITT.md`.
|
|
||||||
|
|
||||||
## Initiale Einrichtung
|
|
||||||
|
|
||||||
1. Secret fuer Grafana anlegen:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
install -m 600 /dev/null /mnt/user/appdata/secrets/grafana_admin_password.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Offline-Admin-Token fuer InfluxDB 3 als JSON anlegen:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"token": "apiv3_REPLACE_WITH_STRONG_RANDOM_TOKEN",
|
|
||||||
"name": "admin",
|
|
||||||
"description": "Admin token for KalliLab InfluxDB 3 Core"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Pfad: `/mnt/user/appdata/secrets/influxdb3_admin_token.json`, Rechte `600`.
|
|
||||||
|
|
||||||
3. Grafana-Datasource-Token anlegen. Fuer InfluxDB 3 Core aktuell einen eigenen Named-Admin-Token verwenden, damit der Grafana-Zugang getrennt vom initialen Operator-/Admin-Token rotiert werden kann:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
install -m 600 /dev/null /mnt/user/appdata/secrets/grafana_influxdb_token.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Provisioning-Dateien aus dem Git-Checkout auf den Host-Appdata-Pfad kopieren:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p /mnt/user/appdata/grafana/provisioning/datasources
|
|
||||||
mkdir -p /mnt/user/appdata/grafana/provisioning/dashboards
|
|
||||||
cp /mnt/user/appdata/komodo/core/repos/homelab-infra/ops/grafana-influxdb/provisioning/datasources/influxdb.yml /mnt/user/appdata/grafana/provisioning/datasources/influxdb.yml
|
|
||||||
cp /mnt/user/appdata/komodo/core/repos/homelab-infra/ops/grafana-influxdb/provisioning/dashboards/* /mnt/user/appdata/grafana/provisioning/dashboards/
|
|
||||||
chmod 644 /mnt/user/appdata/grafana/provisioning/datasources/influxdb.yml
|
|
||||||
chmod 644 /mnt/user/appdata/grafana/provisioning/dashboards/*
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Nach dem ersten Start die Datenbank anlegen:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker exec influxdb3-core influxdb3 create database homelab --token "$INFLUXDB3_AUTH_TOKEN"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Smoke-Test nach Deploy
|
|
||||||
|
|
||||||
- `https://grafana.kaleschke.info` oeffnet nach Authelia die Grafana-Loginseite.
|
|
||||||
- Grafana `Connections -> Data sources -> InfluxDB 3 Core -> Save & test` ist erfolgreich.
|
|
||||||
- Grafana `Connections -> Data sources -> Loki -> Save & test` ist erfolgreich, sobald der Loki/Alloy-Stack laeuft.
|
|
||||||
- Die provisionierten Dashboards `Logs - Last 60m`, `Container Restart Events` und `Container Error Rate` sind sichtbar.
|
|
||||||
- InfluxDB bleibt ohne Public Route. Falls `INFLUXDB_BIND_IP` auf die LAN-IP gesetzt ist, ist Port `8181` nur im internen Netz fuer Writer wie Home Assistant erreichbar.
|
|
||||||
- `docker ps` zeigt fuer `influxdb3-core` `192.168.178.58:8181->8181/tcp` oder den per `INFLUXDB_BIND_IP` gesetzten Host.
|
|
||||||
- `ss -ltnp | grep 8181` zeigt einen Listener auf der gebundenen Host-IP.
|
|
||||||
- `curl -i http://192.168.178.58:8181/` liefert ohne Token erwartbar `401 Unauthorized`.
|
|
||||||
|
|
||||||
## Drift-Check
|
|
||||||
|
|
||||||
Wenn Komodo, Gitea und Runtime nicht zusammenpassen, zuerst `docs/GITOPS_DRIFT_RUNBOOK.md` verwenden. Besonders wichtig:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /mnt/user/services/stacks/grafana
|
|
||||||
git rev-parse --short HEAD
|
|
||||||
grep -nE "ports:|grafana_influx_lan|grafana_influx_internal" -A4 -B2 ops/grafana-influxdb/docker-compose.yml
|
|
||||||
docker inspect influxdb3-core --format '{{json .NetworkSettings.Ports}}'
|
|
||||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep influx
|
|
||||||
ss -ltnp | grep 8181
|
|
||||||
```
|
|
||||||
|
|
||||||
## Rollback
|
|
||||||
|
|
||||||
- Stack in Komodo stoppen oder Git auf den letzten Stand ohne `ops/grafana-influxdb` zuruecknehmen.
|
|
||||||
- Persistente Daten liegen unter `/mnt/user/appdata/grafana` und `/mnt/user/appdata/influxdb3`; nicht automatisch loeschen.
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
services:
|
|
||||||
grafana:
|
|
||||||
image: grafana/grafana:12.4.3@sha256:2e986801428cd689c2358605289c90ab37d2b39e24808874971f54c99bcdc412
|
|
||||||
container_name: grafana
|
|
||||||
restart: unless-stopped
|
|
||||||
user: "0"
|
|
||||||
environment:
|
|
||||||
GF_SERVER_ROOT_URL: https://grafana.kaleschke.info/
|
|
||||||
GF_SECURITY_ADMIN_PASSWORD__FILE: /run/secrets/grafana_admin_password
|
|
||||||
GF_USERS_ALLOW_SIGN_UP: "false"
|
|
||||||
GF_AUTH_ANONYMOUS_ENABLED: "false"
|
|
||||||
dns:
|
|
||||||
- 1.1.1.1
|
|
||||||
- 8.8.8.8
|
|
||||||
entrypoint:
|
|
||||||
- /bin/sh
|
|
||||||
- -c
|
|
||||||
- |
|
|
||||||
export GRAFANA_INFLUXDB_TOKEN="$$(cat /run/secrets/grafana_influxdb_token)"
|
|
||||||
exec /run.sh
|
|
||||||
volumes:
|
|
||||||
- /mnt/user/appdata/grafana:/var/lib/grafana
|
|
||||||
- /mnt/user/appdata/grafana/provisioning:/etc/grafana/provisioning:ro
|
|
||||||
secrets:
|
|
||||||
- grafana_admin_password
|
|
||||||
- grafana_influxdb_token
|
|
||||||
networks:
|
|
||||||
- frontend_net
|
|
||||||
- backend_net
|
|
||||||
- grafana_influx_internal
|
|
||||||
security_opt:
|
|
||||||
- no-new-privileges:true
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/api/health"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 40s
|
|
||||||
labels:
|
|
||||||
- traefik.enable=true
|
|
||||||
- traefik.docker.network=frontend_net
|
|
||||||
- traefik.http.routers.grafana.rule=Host(`grafana.kaleschke.info`)
|
|
||||||
- traefik.http.routers.grafana.entrypoints=websecure
|
|
||||||
- traefik.http.routers.grafana.tls=true
|
|
||||||
- traefik.http.routers.grafana.tls.certresolver=le
|
|
||||||
- traefik.http.routers.grafana.middlewares=authelia@file,secure-headers@file
|
|
||||||
- traefik.http.services.grafana.loadbalancer.server.port=3000
|
|
||||||
|
|
||||||
influxdb3-core:
|
|
||||||
image: influxdb:3.9.1-core@sha256:1d58c8b9ac90153ae3a020ede2810c8284933dda50ac71e7573389ab6f012128
|
|
||||||
container_name: influxdb3-core
|
|
||||||
restart: unless-stopped
|
|
||||||
user: "0"
|
|
||||||
ports:
|
|
||||||
- "${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181"
|
|
||||||
command:
|
|
||||||
- influxdb3
|
|
||||||
- serve
|
|
||||||
- --node-id=kallilabcore
|
|
||||||
- --object-store=file
|
|
||||||
- --data-dir=/var/lib/influxdb3/data
|
|
||||||
- --plugin-dir=/var/lib/influxdb3/plugins
|
|
||||||
- --admin-token-file=/run/secrets/influxdb3_admin_token
|
|
||||||
volumes:
|
|
||||||
- /mnt/user/appdata/influxdb3/data:/var/lib/influxdb3/data
|
|
||||||
- /mnt/user/appdata/influxdb3/plugins:/var/lib/influxdb3/plugins
|
|
||||||
secrets:
|
|
||||||
- influxdb3_admin_token
|
|
||||||
networks:
|
|
||||||
- grafana_influx_lan
|
|
||||||
- grafana_influx_internal
|
|
||||||
security_opt:
|
|
||||||
- no-new-privileges:true
|
|
||||||
|
|
||||||
secrets:
|
|
||||||
grafana_admin_password:
|
|
||||||
file: /mnt/user/appdata/secrets/grafana_admin_password.txt
|
|
||||||
influxdb3_admin_token:
|
|
||||||
file: /mnt/user/appdata/secrets/influxdb3_admin_token.json
|
|
||||||
grafana_influxdb_token:
|
|
||||||
file: /mnt/user/appdata/secrets/grafana_influxdb_token.txt
|
|
||||||
|
|
||||||
networks:
|
|
||||||
frontend_net:
|
|
||||||
external: true
|
|
||||||
backend_net:
|
|
||||||
external: true
|
|
||||||
grafana_influx_lan:
|
|
||||||
driver: bridge
|
|
||||||
grafana_influx_internal:
|
|
||||||
internal: true
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"uid": "kallilab-container-error-rate",
|
|
||||||
"title": "Container Error Rate",
|
|
||||||
"schemaVersion": 39,
|
|
||||||
"version": 1,
|
|
||||||
"refresh": "5m",
|
|
||||||
"time": { "from": "now-24h", "to": "now" },
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"type": "table",
|
|
||||||
"title": "Container Errors Last 24h",
|
|
||||||
"datasource": { "type": "loki", "uid": "loki" },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"refId": "A",
|
|
||||||
"expr": "sum by (container_name) (count_over_time({platform=\"docker\"} |~ \"(?i)(level=error|error|fatal|panic)\" [24h]))"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"gridPos": { "h": 16, "w": 24, "x": 0, "y": 0 }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
{
|
|
||||||
"uid": "kallilab-logs-last-60m",
|
|
||||||
"title": "Last 60 min before now",
|
|
||||||
"schemaVersion": 39,
|
|
||||||
"version": 1,
|
|
||||||
"refresh": "30s",
|
|
||||||
"time": { "from": "now-60m", "to": "now" },
|
|
||||||
"templating": {
|
|
||||||
"list": [
|
|
||||||
{
|
|
||||||
"name": "container",
|
|
||||||
"type": "query",
|
|
||||||
"datasource": { "type": "loki", "uid": "loki" },
|
|
||||||
"query": "label_values(container_name)",
|
|
||||||
"includeAll": true,
|
|
||||||
"allValue": ".+",
|
|
||||||
"refresh": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"type": "logs",
|
|
||||||
"title": "Docker Log Stream",
|
|
||||||
"datasource": { "type": "loki", "uid": "loki" },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"refId": "A",
|
|
||||||
"expr": "{platform=\"docker\", container_name=~\"$container\"}"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"gridPos": { "h": 20, "w": 24, "x": 0, "y": 0 },
|
|
||||||
"options": {
|
|
||||||
"showTime": true,
|
|
||||||
"showLabels": true,
|
|
||||||
"wrapLogMessage": false,
|
|
||||||
"enableLogDetails": true,
|
|
||||||
"sortOrder": "Descending"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
apiVersion: 1
|
|
||||||
|
|
||||||
providers:
|
|
||||||
- name: KalliLab Observability
|
|
||||||
orgId: 1
|
|
||||||
folder: KalliLab Observability
|
|
||||||
type: file
|
|
||||||
disableDeletion: false
|
|
||||||
updateIntervalSeconds: 60
|
|
||||||
allowUiUpdates: false
|
|
||||||
options:
|
|
||||||
path: /etc/grafana/provisioning/dashboards
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"uid": "kallilab-restart-events",
|
|
||||||
"title": "Restart Events",
|
|
||||||
"schemaVersion": 39,
|
|
||||||
"version": 1,
|
|
||||||
"refresh": "5m",
|
|
||||||
"time": { "from": "now-24h", "to": "now" },
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"type": "heatmap",
|
|
||||||
"title": "Restart-like Log Events",
|
|
||||||
"datasource": { "type": "loki", "uid": "loki" },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"refId": "A",
|
|
||||||
"expr": "sum by (container_name) (count_over_time({platform=\"docker\"} |~ \"(?i)(restart|restarting|started|exited|oom)\" [5m]))"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"gridPos": { "h": 16, "w": 24, "x": 0, "y": 0 }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
apiVersion: 1
|
|
||||||
|
|
||||||
prune: true
|
|
||||||
|
|
||||||
datasources:
|
|
||||||
- name: InfluxDB 3 Core
|
|
||||||
uid: influxdb3-core
|
|
||||||
type: influxdb
|
|
||||||
access: proxy
|
|
||||||
url: http://influxdb3-core:8181
|
|
||||||
isDefault: true
|
|
||||||
jsonData:
|
|
||||||
version: SQL
|
|
||||||
dbName: homelab
|
|
||||||
httpMode: POST
|
|
||||||
insecureGrpc: true
|
|
||||||
secureJsonData:
|
|
||||||
token: $GRAFANA_INFLUXDB_TOKEN
|
|
||||||
- name: Loki
|
|
||||||
uid: loki
|
|
||||||
type: loki
|
|
||||||
access: proxy
|
|
||||||
url: http://loki:3100
|
|
||||||
isDefault: false
|
|
||||||
jsonData:
|
|
||||||
maxLines: 1000
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Safe default: local host only.
|
|
||||||
# Set this to the Unraid LAN IP, for example 192.168.178.58, when a VM such as Home Assistant must write to InfluxDB.
|
|
||||||
INFLUXDB_BIND_IP=127.0.0.1
|
|
||||||
@@ -315,48 +315,41 @@
|
|||||||
"first_check": "HTTPS erreichbar? NTFY_BEHIND_PROXY=true gesetzt? Traefik healthy?",
|
"first_check": "HTTPS erreichbar? NTFY_BEHIND_PROXY=true gesetzt? Traefik healthy?",
|
||||||
"notes": "KRITISCH: Ausfall bedeutet keine anderen Alerts ankommen"
|
"notes": "KRITISCH: Ausfall bedeutet keine anderen Alerts ankommen"
|
||||||
},
|
},
|
||||||
"homepage": {
|
"glance": {
|
||||||
"description": "Start-Dashboard",
|
"description": "Homelab-Dashboard",
|
||||||
"tier": 3,
|
"tier": 3,
|
||||||
"category": "ops",
|
"category": "ops",
|
||||||
"container_name": "homepage",
|
"container_name": "glance",
|
||||||
"dependencies": ["traefik"],
|
"dependencies": ["traefik"],
|
||||||
"url": "https://home.kaleschke.info",
|
"url": "https://glance.kaleschke.info",
|
||||||
"dump_file": null,
|
"dump_file": null,
|
||||||
"data_paths": ["/mnt/user/appdata/homepage"],
|
"data_paths": [],
|
||||||
"first_check": "Traefik erreichbar? Docker-Socket read-only lesbar? API-Tokens gueltig?",
|
"first_check": "Traefik erreichbar? Docker-Socket-Proxy intern erreichbar? API-Tokens fuer Widgets gueltig?",
|
||||||
"notes": "Docker socket read-only; viele API Tokens in Config"
|
"notes": "aktives Homelab-Dashboard; Homepage wurde entfernt"
|
||||||
},
|
},
|
||||||
"uptime-kuma": {
|
"monitoring-grafana": {
|
||||||
"description": "Monitoring / Uptime Checks",
|
"description": "Zentrale Observability-UI",
|
||||||
"tier": 3,
|
"tier": 3,
|
||||||
"category": "ops",
|
"category": "ops",
|
||||||
"container_name": "UptimeKuma",
|
"container_name": "monitoring-grafana",
|
||||||
"dependencies": ["traefik"],
|
"dependencies": [
|
||||||
"url": "https://uptime.kaleschke.info",
|
"monitoring-prometheus",
|
||||||
|
"monitoring-loki",
|
||||||
|
"monitoring-influxdb3-core",
|
||||||
|
"traefik"
|
||||||
|
],
|
||||||
|
"url": "https://monitoring.kaleschke.info",
|
||||||
"dump_file": null,
|
"dump_file": null,
|
||||||
"data_paths": ["/mnt/user/appdata/uptime-kuma"],
|
"data_paths": ["grafana_data"],
|
||||||
"first_check": "Datenbank-Volume intakt? Traefik erreichbar?",
|
"first_check": "Authelia-Redirect? Datasources Prometheus, Loki und InfluxDB 3 Core gruen?",
|
||||||
"notes": "Monitore nach Restore manuell pruefen"
|
"notes": "ersetzt alten Grafana-Altstand und Uptime-Kuma-Views"
|
||||||
},
|
},
|
||||||
"grafana": {
|
"monitoring-influxdb3-core": {
|
||||||
"description": "Metrik-Dashboard",
|
"description": "Zeitreihen- / Metrikdaten fuer Monitoring und Home Assistant",
|
||||||
"tier": 3,
|
"tier": 3,
|
||||||
"category": "ops",
|
"category": "ops",
|
||||||
"container_name": "grafana",
|
"container_name": "monitoring-influxdb3-core",
|
||||||
"dependencies": ["influxdb3-core", "traefik"],
|
"dependencies": ["monitoring-grafana"],
|
||||||
"url": "https://grafana.kaleschke.info",
|
|
||||||
"dump_file": null,
|
|
||||||
"data_paths": ["/mnt/user/appdata/grafana"],
|
|
||||||
"first_check": "influxdb3-core healthy? Datasource-Token gesetzt? Provisioning-Konfig vorhanden?",
|
|
||||||
"notes": "laeuft als user 0; Datasource wird provisioniert"
|
|
||||||
},
|
|
||||||
"influxdb3-core": {
|
|
||||||
"description": "Zeitreihen- / Metrikdaten fuer Grafana und Home Assistant",
|
|
||||||
"tier": 3,
|
|
||||||
"category": "ops",
|
|
||||||
"container_name": "influxdb3-core",
|
|
||||||
"dependencies": [],
|
|
||||||
"url": null,
|
"url": null,
|
||||||
"dump_file": null,
|
"dump_file": null,
|
||||||
"data_paths": [
|
"data_paths": [
|
||||||
|
|||||||
@@ -395,55 +395,43 @@ services:
|
|||||||
# TIER 3 — Ops / Tools (Ausfall schmerzt, blockiert nichts Kritisches)
|
# TIER 3 — Ops / Tools (Ausfall schmerzt, blockiert nichts Kritisches)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
homepage:
|
glance:
|
||||||
description: Start-Dashboard
|
description: Homelab-Dashboard
|
||||||
tier: 3
|
tier: 3
|
||||||
category: ops
|
category: ops
|
||||||
container_name: homepage
|
container_name: glance
|
||||||
dependencies:
|
dependencies:
|
||||||
- traefik
|
- traefik
|
||||||
url: https://home.kaleschke.info
|
url: https://glance.kaleschke.info
|
||||||
dump_file: null
|
dump_file: null
|
||||||
data_paths:
|
data_paths: []
|
||||||
- /mnt/user/appdata/homepage
|
first_check: "Traefik erreichbar? Docker-Socket-Proxy intern erreichbar? API-Tokens fuer Widgets gueltig?"
|
||||||
first_check: "Traefik erreichbar? Docker-Socket read-only lesbar? API-Tokens gueltig?"
|
notes: "aktives Homelab-Dashboard; Homepage wurde entfernt"
|
||||||
notes: "Docker socket read-only; viele API Tokens in Config"
|
|
||||||
|
|
||||||
uptime-kuma:
|
monitoring-grafana:
|
||||||
description: Monitoring / Uptime Checks
|
description: Zentrale Observability-UI
|
||||||
tier: 3
|
tier: 3
|
||||||
category: ops
|
category: ops
|
||||||
container_name: UptimeKuma
|
container_name: monitoring-grafana
|
||||||
dependencies:
|
dependencies:
|
||||||
|
- monitoring-prometheus
|
||||||
|
- monitoring-loki
|
||||||
|
- monitoring-influxdb3-core
|
||||||
- traefik
|
- traefik
|
||||||
url: https://uptime.kaleschke.info
|
url: https://monitoring.kaleschke.info
|
||||||
dump_file: null
|
dump_file: null
|
||||||
data_paths:
|
data_paths:
|
||||||
- /mnt/user/appdata/uptime-kuma
|
- grafana_data
|
||||||
first_check: "Datenbank-Volume intakt? Traefik erreichbar?"
|
first_check: "Authelia-Redirect? Datasources Prometheus, Loki und InfluxDB 3 Core gruen?"
|
||||||
notes: "Monitore nach Restore manuell pruefen"
|
notes: "ersetzt alten Grafana-Altstand und Uptime-Kuma-Views"
|
||||||
|
|
||||||
grafana:
|
monitoring-influxdb3-core:
|
||||||
description: Metrik-Dashboard
|
description: Zeitreihen- / Metrikdaten fuer Monitoring und Home Assistant
|
||||||
tier: 3
|
tier: 3
|
||||||
category: ops
|
category: ops
|
||||||
container_name: grafana
|
container_name: monitoring-influxdb3-core
|
||||||
dependencies:
|
dependencies:
|
||||||
- influxdb3-core
|
- monitoring-grafana
|
||||||
- traefik
|
|
||||||
url: https://grafana.kaleschke.info
|
|
||||||
dump_file: null
|
|
||||||
data_paths:
|
|
||||||
- /mnt/user/appdata/grafana
|
|
||||||
first_check: "influxdb3-core healthy? Datasource-Token in Secret gesetzt? Provisioning-Konfig vorhanden?"
|
|
||||||
notes: "laeuft als user 0 wegen Host-Appdata-Permissions (dokumentiert); Datasource wird provisioniert"
|
|
||||||
|
|
||||||
influxdb3-core:
|
|
||||||
description: Zeitreihen- / Metrikdaten fuer Grafana und Home Assistant
|
|
||||||
tier: 3
|
|
||||||
category: ops
|
|
||||||
container_name: influxdb3-core
|
|
||||||
dependencies: []
|
|
||||||
url: null
|
url: null
|
||||||
dump_file: null
|
dump_file: null
|
||||||
data_paths:
|
data_paths:
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
# Loki / Alloy
|
|
||||||
|
|
||||||
Status: abgeloester Altstand. Der zentrale Zielzustand ist `monitoring/` mit `monitoring-loki` und `monitoring-promtail`.
|
|
||||||
|
|
||||||
Internal logging stack for KalliLab CORE.
|
|
||||||
|
|
||||||
## Services
|
|
||||||
|
|
||||||
- `loki`: internal log store on `backend_net`, no Traefik route, `auth_enabled: false` because access is limited to internal Docker networking.
|
|
||||||
- `alloy`: Docker log collector. It mounts `/var/run/docker.sock:ro` as a documented observability exception and forwards Docker container logs to Loki.
|
|
||||||
|
|
||||||
## Migration note
|
|
||||||
|
|
||||||
Do not run this stack in parallel with `monitoring/` unless you are deliberately comparing collectors during migration. After `monitoring-loki` and `monitoring-promtail` are live and Grafana can query logs, stop this Komodo stack and keep the files only as rollback reference.
|
|
||||||
|
|
||||||
## Host sync
|
|
||||||
|
|
||||||
Before first deploy, sync the checked-in config files to appdata:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p /mnt/user/appdata/loki/config /mnt/user/appdata/loki/data
|
|
||||||
mkdir -p /mnt/user/appdata/alloy/config /mnt/user/appdata/alloy/data
|
|
||||||
cp /mnt/user/services/homelab-infra/ops/loki/config/loki-config.yml /mnt/user/appdata/loki/config/loki-config.yml
|
|
||||||
cp /mnt/user/services/homelab-infra/ops/loki/config/config.alloy /mnt/user/appdata/alloy/config/config.alloy
|
|
||||||
chown -R 10001:10001 /mnt/user/appdata/loki/data
|
|
||||||
chmod 644 /mnt/user/appdata/loki/config/loki-config.yml /mnt/user/appdata/alloy/config/config.alloy
|
|
||||||
```
|
|
||||||
|
|
||||||
## Restore posture
|
|
||||||
|
|
||||||
Loki data is transient operational telemetry. Docker raw logs remain the first fallback, Loki chunks on disk are a convenience cache, and ntfy critical events provide the external first-crash marker.
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
discovery.docker "containers" {
|
|
||||||
host = "unix:///var/run/docker.sock"
|
|
||||||
}
|
|
||||||
|
|
||||||
discovery.relabel "docker_logs" {
|
|
||||||
targets = []
|
|
||||||
|
|
||||||
rule {
|
|
||||||
source_labels = ["__meta_docker_container_name"]
|
|
||||||
regex = "/(.*)"
|
|
||||||
target_label = "container_name"
|
|
||||||
}
|
|
||||||
|
|
||||||
rule {
|
|
||||||
source_labels = ["__meta_docker_container_label_com_docker_compose_project"]
|
|
||||||
target_label = "compose_project"
|
|
||||||
}
|
|
||||||
|
|
||||||
rule {
|
|
||||||
source_labels = ["__meta_docker_container_label_com_docker_compose_service"]
|
|
||||||
target_label = "compose_service"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loki.source.docker "containers" {
|
|
||||||
host = "unix:///var/run/docker.sock"
|
|
||||||
targets = discovery.docker.containers.targets
|
|
||||||
labels = { platform = "docker", host = "kallilabcore" }
|
|
||||||
relabel_rules = discovery.relabel.docker_logs.rules
|
|
||||||
forward_to = [loki.process.docker.receiver]
|
|
||||||
}
|
|
||||||
|
|
||||||
loki.process "docker" {
|
|
||||||
forward_to = [loki.write.local.receiver]
|
|
||||||
|
|
||||||
stage.docker {}
|
|
||||||
}
|
|
||||||
|
|
||||||
loki.write "local" {
|
|
||||||
endpoint {
|
|
||||||
url = "http://loki:3100/loki/api/v1/push"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
auth_enabled: false
|
|
||||||
|
|
||||||
server:
|
|
||||||
http_listen_port: 3100
|
|
||||||
grpc_listen_port: 9096
|
|
||||||
|
|
||||||
common:
|
|
||||||
instance_addr: 127.0.0.1
|
|
||||||
path_prefix: /loki
|
|
||||||
storage:
|
|
||||||
filesystem:
|
|
||||||
chunks_directory: /loki/chunks
|
|
||||||
rules_directory: /loki/rules
|
|
||||||
replication_factor: 1
|
|
||||||
ring:
|
|
||||||
kvstore:
|
|
||||||
store: inmemory
|
|
||||||
|
|
||||||
query_range:
|
|
||||||
results_cache:
|
|
||||||
cache:
|
|
||||||
embedded_cache:
|
|
||||||
enabled: true
|
|
||||||
max_size_mb: 100
|
|
||||||
|
|
||||||
schema_config:
|
|
||||||
configs:
|
|
||||||
- from: 2026-05-16
|
|
||||||
store: tsdb
|
|
||||||
object_store: filesystem
|
|
||||||
schema: v13
|
|
||||||
index:
|
|
||||||
prefix: index_
|
|
||||||
period: 24h
|
|
||||||
|
|
||||||
limits_config:
|
|
||||||
retention_period: 720h
|
|
||||||
allow_structured_metadata: true
|
|
||||||
ingestion_rate_mb: 16
|
|
||||||
ingestion_burst_size_mb: 32
|
|
||||||
|
|
||||||
compactor:
|
|
||||||
working_directory: /loki/compactor
|
|
||||||
compaction_interval: 10m
|
|
||||||
retention_enabled: true
|
|
||||||
retention_delete_delay: 2h
|
|
||||||
delete_request_store: filesystem
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
services:
|
|
||||||
loki:
|
|
||||||
image: grafana/loki:3.7.2@sha256:191d4fdfb7264f16989f0a57f320872620a5a7c2ceeec6229212c4190ec49b86
|
|
||||||
container_name: loki
|
|
||||||
restart: unless-stopped
|
|
||||||
command:
|
|
||||||
- -config.file=/etc/loki/loki-config.yml
|
|
||||||
volumes:
|
|
||||||
- /mnt/user/appdata/loki/config:/etc/loki:ro
|
|
||||||
- /mnt/user/appdata/loki/data:/loki
|
|
||||||
networks:
|
|
||||||
- backend_net
|
|
||||||
security_opt:
|
|
||||||
- no-new-privileges:true
|
|
||||||
alloy:
|
|
||||||
image: grafana/alloy:v1.16.1@sha256:51aeb9d829239345070619dad3edd6873186f913c84f45b365b74574fcb38ec0
|
|
||||||
container_name: alloy
|
|
||||||
restart: unless-stopped
|
|
||||||
command:
|
|
||||||
- run
|
|
||||||
- /etc/alloy/config.alloy
|
|
||||||
- --storage.path=/var/lib/alloy/data
|
|
||||||
volumes:
|
|
||||||
- /mnt/user/appdata/alloy/config:/etc/alloy:ro
|
|
||||||
- /mnt/user/appdata/alloy/data:/var/lib/alloy/data
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
networks:
|
|
||||||
- backend_net
|
|
||||||
security_opt:
|
|
||||||
- no-new-privileges:true
|
|
||||||
depends_on:
|
|
||||||
- loki
|
|
||||||
|
|
||||||
networks:
|
|
||||||
backend_net:
|
|
||||||
external: true
|
|
||||||
@@ -32,6 +32,7 @@ powershell -ExecutionPolicy Bypass -File .\ops\policy-checks\check_repo.ps1 -Rep
|
|||||||
- Host-Port-Mappings
|
- Host-Port-Mappings
|
||||||
- Traefik-Router mit `Host(...)` und Middleware-Standard fuer geschuetzte Admin-/Ops-Dienste
|
- Traefik-Router mit `Host(...)` und Middleware-Standard fuer geschuetzte Admin-/Ops-Dienste
|
||||||
- sichtbare Report-Punkte fuer dokumentierte Ausnahmen wie `user: "0"`, `privileged: true` oder `network_mode: host`
|
- sichtbare Report-Punkte fuer dokumentierte Ausnahmen wie `user: "0"`, `privileged: true` oder `network_mode: host`
|
||||||
|
- digest-gepinnte mutable Tags bleiben sichtbar; nur explizit dokumentierte Ausnahmen werden als Info statt Warning gewertet
|
||||||
|
|
||||||
## Wichtige Betriebsregel
|
## Wichtige Betriebsregel
|
||||||
|
|
||||||
|
|||||||
@@ -253,8 +253,12 @@ function Test-ServicePolicies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($service.Image -match ':[Ll]atest(?:[-@]|$)') {
|
if ($service.Image -match ':[Ll]atest(?:[-@]|$)') {
|
||||||
|
if (($service.Image -match '@sha256:') -and (Test-IdentityMatch -Service $service -Candidates $Exceptions.allowed_mutable_tag_identities)) {
|
||||||
|
Add-Finding -Findings $Findings -Severity 'info' -Code 'IMAGE002' -Target $targetBase -Message 'Image uses a latest tag but is digest-pinned and documented as an exception.'
|
||||||
|
} else {
|
||||||
Add-Finding -Findings $Findings -Severity 'warning' -Code 'IMAGE001' -Target $targetBase -Message 'Image uses a latest tag. Prefer a concrete version tag, even when a digest is present.'
|
Add-Finding -Findings $Findings -Severity 'warning' -Code 'IMAGE001' -Target $targetBase -Message 'Image uses a latest tag. Prefer a concrete version tag, even when a digest is present.'
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$isDataService = $false
|
$isDataService = $false
|
||||||
$identityText = ($service.ServiceName + ' ' + $service.ContainerName + ' ' + $service.Image).ToLowerInvariant()
|
$identityText = ($service.ServiceName + ' ' + $service.ContainerName + ' ' + $service.Image).ToLowerInvariant()
|
||||||
@@ -362,6 +366,7 @@ $exceptionsRaw = Get-Content -LiteralPath $exceptionsPath -Raw | ConvertFrom-Jso
|
|||||||
$exceptions = @{
|
$exceptions = @{
|
||||||
middleware_exempt_identities = @($exceptionsRaw.middleware_exempt_identities)
|
middleware_exempt_identities = @($exceptionsRaw.middleware_exempt_identities)
|
||||||
allowed_root_identities = @($exceptionsRaw.allowed_root_identities)
|
allowed_root_identities = @($exceptionsRaw.allowed_root_identities)
|
||||||
|
allowed_mutable_tag_identities = @($exceptionsRaw.allowed_mutable_tag_identities)
|
||||||
allowed_privileged_identities = @($exceptionsRaw.allowed_privileged_identities)
|
allowed_privileged_identities = @($exceptionsRaw.allowed_privileged_identities)
|
||||||
allowed_host_network_identities = @($exceptionsRaw.allowed_host_network_identities)
|
allowed_host_network_identities = @($exceptionsRaw.allowed_host_network_identities)
|
||||||
allowed_host_port_identities = @{}
|
allowed_host_port_identities = @{}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
"gitea",
|
"gitea",
|
||||||
"immich-server",
|
"immich-server",
|
||||||
"immich_server",
|
"immich_server",
|
||||||
"jellyfin",
|
|
||||||
"komodo-core",
|
"komodo-core",
|
||||||
"mealie",
|
"mealie",
|
||||||
"nextcloud",
|
"nextcloud",
|
||||||
@@ -17,14 +16,11 @@
|
|||||||
"adguard": [
|
"adguard": [
|
||||||
"53:53/tcp",
|
"53:53/tcp",
|
||||||
"53:53/udp",
|
"53:53/udp",
|
||||||
"8082:80"
|
"100.80.98.33:8082:80"
|
||||||
],
|
],
|
||||||
"gitea": [
|
"gitea": [
|
||||||
"222:22"
|
"222:22"
|
||||||
],
|
],
|
||||||
"influxdb3-core": [
|
|
||||||
"${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181"
|
|
||||||
],
|
|
||||||
"monitoring-influxdb3-core": [
|
"monitoring-influxdb3-core": [
|
||||||
"${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181"
|
"${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181"
|
||||||
],
|
],
|
||||||
@@ -34,14 +30,18 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"allowed_root_identities": [
|
"allowed_root_identities": [
|
||||||
"grafana",
|
|
||||||
"influxdb3-core",
|
|
||||||
"monitoring-influxdb3-core"
|
"monitoring-influxdb3-core"
|
||||||
],
|
],
|
||||||
|
"allowed_mutable_tag_identities": [
|
||||||
|
"ddns-updater",
|
||||||
|
"glances",
|
||||||
|
"scrutiny"
|
||||||
|
],
|
||||||
"allowed_privileged_identities": [
|
"allowed_privileged_identities": [
|
||||||
"scrutiny"
|
"scrutiny"
|
||||||
],
|
],
|
||||||
"allowed_host_network_identities": [
|
"allowed_host_network_identities": [
|
||||||
|
"plex",
|
||||||
"tailscale",
|
"tailscale",
|
||||||
"Tailscale-Docker"
|
"Tailscale-Docker"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,25 +3,26 @@
|
|||||||
## Summary
|
## Summary
|
||||||
- Compose files checked: 29
|
- Compose files checked: 29
|
||||||
- Critical findings: 0
|
- Critical findings: 0
|
||||||
- Warnings: 4
|
- Warnings: 1
|
||||||
- Info findings: 9
|
- Info findings: 13
|
||||||
|
|
||||||
## Critical
|
## Critical
|
||||||
- none
|
- none
|
||||||
|
|
||||||
## Warnings
|
## Warnings
|
||||||
- [SEC001] infra\ddns-updater\docker-compose.yml :: ddns-updater: Missing security_opt no-new-privileges:true.
|
- [USER001] monitoring\docker-compose.yml :: influxdb3-core: Runs as user 0. Documented exception, keep visible for hardening.
|
||||||
- [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.
|
|
||||||
|
|
||||||
## Info
|
## Info
|
||||||
- [PORT001] core\gitea\docker-compose.yml :: gitea: Allowed host port mapping: 222:22
|
- [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/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: 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\plex\docker-compose.yml :: plex: network_mode: host is a documented exception.
|
||||||
- [HOSTNET001] host-services\tailscale\docker-compose.yml :: tailscale: network_mode: host is a documented exception.
|
- [HOSTNET001] host-services\tailscale\docker-compose.yml :: tailscale: network_mode: host is a documented exception.
|
||||||
- [PORT001] ops\grafana-influxdb\docker-compose.yml :: influxdb3-core: Allowed host port mapping: ${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181
|
- [IMAGE002] infra\ddns-updater\docker-compose.yml :: ddns-updater: Image uses a latest tag but is digest-pinned and documented as an exception.
|
||||||
|
- [PORT001] monitoring\docker-compose.yml :: influxdb3-core: Allowed host port mapping: ${INFLUXDB_BIND_IP:-127.0.0.1}:8181:8181
|
||||||
|
- [IMAGE002] ops\glances\docker-compose.yml :: glances: Image uses a latest tag but is digest-pinned and documented as an exception.
|
||||||
|
- [IMAGE002] ops\scrutiny\docker-compose.yml :: scrutiny: Image uses a latest tag but is digest-pinned and documented as an exception.
|
||||||
- [PRIV001] ops\scrutiny\docker-compose.yml :: scrutiny: Privileged mode is a documented exception.
|
- [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
|
- [PORT001] traefik\docker-compose.yml :: traefik: Allowed host port mapping: 80:80
|
||||||
- [PORT001] traefik\docker-compose.yml :: traefik: Allowed host port mapping: 443:443
|
- [PORT001] traefik\docker-compose.yml :: traefik: Allowed host port mapping: 443:443
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ $checks = @(
|
|||||||
@{ Name = "nextcloud.dump"; Path = Join-Path $DumpRoot "nextcloud.dump" },
|
@{ Name = "nextcloud.dump"; Path = Join-Path $DumpRoot "nextcloud.dump" },
|
||||||
@{ Name = "gitea.sqlite.dump"; Path = Join-Path $DumpRoot "gitea.sqlite.dump" },
|
@{ Name = "gitea.sqlite.dump"; Path = Join-Path $DumpRoot "gitea.sqlite.dump" },
|
||||||
@{ Name = "vaultwarden.sqlite.dump"; Path = Join-Path $DumpRoot "vaultwarden.sqlite.dump" },
|
@{ Name = "vaultwarden.sqlite.dump"; Path = Join-Path $DumpRoot "vaultwarden.sqlite.dump" },
|
||||||
@{ Name = "uptime-kuma.sqlite.dump"; Path = Join-Path $DumpRoot "uptime-kuma.sqlite.dump" },
|
|
||||||
@{ Name = "speedtest-tracker.sqlite.dump"; Path = Join-Path $DumpRoot "speedtest-tracker.sqlite.dump" },
|
@{ Name = "speedtest-tracker.sqlite.dump"; Path = Join-Path $DumpRoot "speedtest-tracker.sqlite.dump" },
|
||||||
@{ Name = "filebrowser.bolt.dump"; Path = Join-Path $DumpRoot "filebrowser.bolt.dump" }
|
@{ Name = "filebrowser.bolt.dump"; Path = Join-Path $DumpRoot "filebrowser.bolt.dump" },
|
||||||
|
@{ Name = "unraid-flash-config.tar.gz"; Path = Join-Path $DumpRoot "unraid-flash-config.tar.gz" }
|
||||||
)
|
)
|
||||||
|
|
||||||
$reportChecks = @(
|
$reportChecks = @(
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ for dump in \
|
|||||||
nextcloud.dump \
|
nextcloud.dump \
|
||||||
gitea.sqlite.dump \
|
gitea.sqlite.dump \
|
||||||
vaultwarden.sqlite.dump \
|
vaultwarden.sqlite.dump \
|
||||||
uptime-kuma.sqlite.dump \
|
|
||||||
speedtest-tracker.sqlite.dump \
|
speedtest-tracker.sqlite.dump \
|
||||||
filebrowser.bolt.dump; do
|
filebrowser.bolt.dump \
|
||||||
|
unraid-flash-config.tar.gz; do
|
||||||
path="$DUMP_ROOT/$dump"
|
path="$DUMP_ROOT/$dump"
|
||||||
if [ ! -f "$path" ]; then
|
if [ ! -f "$path" ]; then
|
||||||
critical+=("DUMP_MISSING $dump")
|
critical+=("DUMP_MISSING $dump")
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
services:
|
|
||||||
uptime-kuma:
|
|
||||||
image: louislam/uptime-kuma:1@sha256:3d632903e6af34139a37f18055c4f1bfd9b7205ae1138f1e5e8940ddc1d176f9
|
|
||||||
container_name: uptime-kuma
|
|
||||||
restart: unless-stopped
|
|
||||||
dns:
|
|
||||||
- 8.8.8.8
|
|
||||||
- 1.1.1.1
|
|
||||||
security_opt:
|
|
||||||
- no-new-privileges:true
|
|
||||||
volumes:
|
|
||||||
- /mnt/user/appdata/uptime-kuma:/app/data
|
|
||||||
networks:
|
|
||||||
- frontend_net
|
|
||||||
labels:
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.docker.network=frontend_net"
|
|
||||||
- "traefik.http.routers.uptime-kuma.rule=Host(`uptime.kaleschke.info`)"
|
|
||||||
- "traefik.http.routers.uptime-kuma.entrypoints=websecure"
|
|
||||||
- "traefik.http.routers.uptime-kuma.tls=true"
|
|
||||||
- "traefik.http.routers.uptime-kuma.tls.certresolver=le"
|
|
||||||
- "traefik.http.routers.uptime-kuma.middlewares=authelia@file,secure-headers@file"
|
|
||||||
- "traefik.http.services.uptime-kuma.loadbalancer.server.port=3001"
|
|
||||||
|
|
||||||
networks:
|
|
||||||
frontend_net:
|
|
||||||
external: true
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Windows Reinstall Helpers
|
||||||
|
|
||||||
|
Diese Skripte sind bewusst versionierte Operator-Hilfen fuer den Windows-Neuaufsetzen-/Dual-Boot-Kontext vom Mai 2026.
|
||||||
|
|
||||||
|
- `backup-delta-after-2026-05-07.ps1` kopiert nach einem definierten Cutoff lokale Nutzdaten in ein Backup-Ziel.
|
||||||
|
- `repair-disk0-boot-to-new-windows.ps1` repariert EFI/Bootdateien fuer das neue Windows auf der Intel-SSD und verlangt Adminrechte.
|
||||||
|
- `cleanup-dualboot-bcd.ps1` bereinigt BCD-Bootmenueeintraege und verlangt eine explizite Textbestaetigung.
|
||||||
|
|
||||||
|
Die Skripte enthalten keine Secrets, arbeiten aber mit lokalen Windows-Datentraegern und duerfen nur interaktiv und mit vorheriger Sichtpruefung ausgefuehrt werden.
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
param(
|
||||||
|
[datetime]$Cutoff = "2026-05-07T15:30:00",
|
||||||
|
[string]$BackupRoot = "H:\Windows-Neuaufsetzen-Backup",
|
||||||
|
[string]$UserProfilePath = "C:\Users\michi"
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
function Invoke-RobocopyDelta {
|
||||||
|
param(
|
||||||
|
[string]$Source,
|
||||||
|
[string]$Destination,
|
||||||
|
[string]$LogName
|
||||||
|
)
|
||||||
|
|
||||||
|
if (-not (Test-Path $Source)) {
|
||||||
|
Write-Host "SKIP missing source: $Source"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Force -Path $Destination | Out-Null
|
||||||
|
$LogPath = Join-Path $DeltaLogDir $LogName
|
||||||
|
|
||||||
|
Write-Host "DELTA COPY $Source"
|
||||||
|
Write-Host " -> $Destination"
|
||||||
|
|
||||||
|
robocopy $Source $Destination /E /COPY:DAT /DCOPY:DAT /R:2 /W:2 /XJ /MAXAGE:$CutoffString /TEE /LOG:$LogPath
|
||||||
|
$ExitCode = $LASTEXITCODE
|
||||||
|
if ($ExitCode -gt 7) {
|
||||||
|
throw "Robocopy failed with exit code $ExitCode for source: $Source"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $BackupRoot)) {
|
||||||
|
throw "BackupRoot does not exist: $BackupRoot"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $UserProfilePath)) {
|
||||||
|
throw "UserProfilePath does not exist: $UserProfilePath"
|
||||||
|
}
|
||||||
|
|
||||||
|
$DeltaRoot = Join-Path $BackupRoot "_Delta_2026-05-19"
|
||||||
|
$DeltaLogDir = Join-Path $DeltaRoot "00_Logs"
|
||||||
|
New-Item -ItemType Directory -Force -Path $DeltaLogDir | Out-Null
|
||||||
|
|
||||||
|
$CutoffString = $Cutoff.ToString("yyyyMMdd")
|
||||||
|
|
||||||
|
$Manifest = [ordered]@{
|
||||||
|
CreatedAt = (Get-Date).ToString("s")
|
||||||
|
Cutoff = $Cutoff.ToString("s")
|
||||||
|
CutoffNote = "Robocopy /MAXAGE uses date granularity, so files changed on or after the cutoff day are included."
|
||||||
|
RunningSystemDrive = $env:SystemDrive
|
||||||
|
RunningWinDir = $env:windir
|
||||||
|
BackupRoot = $BackupRoot
|
||||||
|
DeltaRoot = $DeltaRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
$Manifest.GetEnumerator() |
|
||||||
|
ForEach-Object { "$($_.Key)=$($_.Value)" } |
|
||||||
|
Out-File (Join-Path $DeltaRoot "delta_manifest.txt") -Encoding UTF8
|
||||||
|
|
||||||
|
$Jobs = @(
|
||||||
|
@{ Source = Join-Path $UserProfilePath "Desktop"; Destination = Join-Path $DeltaRoot "01_Desktop"; Log = "delta_current_desktop.log" },
|
||||||
|
@{ Source = Join-Path $UserProfilePath "Documents"; Destination = Join-Path $DeltaRoot "02_Dokumente"; Log = "delta_current_documents.log" },
|
||||||
|
@{ Source = Join-Path $UserProfilePath "Pictures"; Destination = Join-Path $DeltaRoot "03_Bilder"; Log = "delta_current_pictures.log" },
|
||||||
|
@{ Source = Join-Path $UserProfilePath "Videos"; Destination = Join-Path $DeltaRoot "04_Videos"; Log = "delta_current_videos.log" },
|
||||||
|
@{ Source = Join-Path $UserProfilePath "Downloads"; Destination = Join-Path $DeltaRoot "05_Downloads"; Log = "delta_current_downloads.log" },
|
||||||
|
@{ Source = Join-Path $UserProfilePath ".ssh"; Destination = Join-Path $DeltaRoot "09_Programme_Settings_Lizenzen\ssh"; Log = "delta_current_ssh.log" },
|
||||||
|
@{ Source = Join-Path $UserProfilePath "AppData\Local\Subsembly"; Destination = Join-Path $DeltaRoot "09_Programme_Settings_Lizenzen\Subsembly_Local"; Log = "delta_subsembly_local.log" },
|
||||||
|
@{ Source = Join-Path $UserProfilePath "AppData\Local\Buhl"; Destination = Join-Path $DeltaRoot "09_Programme_Settings_Lizenzen\Buhl_Local"; Log = "delta_buhl_local.log" },
|
||||||
|
@{ Source = Join-Path $UserProfilePath "AppData\Local\Buhl Data Service GmbH"; Destination = Join-Path $DeltaRoot "09_Programme_Settings_Lizenzen\Buhl_Data_Service_Local"; Log = "delta_buhl_data_service_local.log" },
|
||||||
|
@{ Source = "C:\ProgramData\Buhl Data Service GmbH"; Destination = Join-Path $DeltaRoot "09_Programme_Settings_Lizenzen\Buhl_Data_Service_ProgramData"; Log = "delta_buhl_data_service_programdata.log" },
|
||||||
|
@{ Source = "G:\Gitea_Clone"; Destination = Join-Path $DeltaRoot "06_Projekte\Gitea_Clone"; Log = "delta_g_gitea_clone.log" },
|
||||||
|
@{ Source = "G:\open-webui"; Destination = Join-Path $DeltaRoot "09_Programme_Settings_Lizenzen\open-webui"; Log = "delta_g_open_webui.log" },
|
||||||
|
@{ Source = "G:\Treiber"; Destination = Join-Path $DeltaRoot "13_Treiber_Windows\G_Treiber"; Log = "delta_g_treiber.log" },
|
||||||
|
@{ Source = "F:\BMW Leasing"; Destination = Join-Path $DeltaRoot "07_Banking_Finanzen\BMW Leasing"; Log = "delta_f_bmw_leasing.log" },
|
||||||
|
@{ Source = "F:\Marina Handy 2025"; Destination = Join-Path $DeltaRoot "03_Bilder\Marina Handy 2025"; Log = "delta_f_marina_handy_2025.log" }
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($Job in $Jobs) {
|
||||||
|
Invoke-RobocopyDelta -Source $Job.Source -Destination $Job.Destination -LogName $Job.Log
|
||||||
|
}
|
||||||
|
|
||||||
|
$ExplicitBankingDir = Join-Path $DeltaRoot "07_Banking_Finanzen\Banking4_Datentresor_explizit"
|
||||||
|
New-Item -ItemType Directory -Force -Path $ExplicitBankingDir | Out-Null
|
||||||
|
|
||||||
|
$BankingFiles = @(
|
||||||
|
(Join-Path $UserProfilePath "Documents\Mein Datentresor.sub"),
|
||||||
|
(Join-Path $UserProfilePath "Documents\.Mein Datentresor.sub.att")
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($File in $BankingFiles) {
|
||||||
|
if (Test-Path $File) {
|
||||||
|
Copy-Item -LiteralPath $File -Destination (Join-Path $ExplicitBankingDir (Split-Path $File -Leaf)) -Force
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$SummaryRoots = Get-ChildItem $DeltaRoot -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -ne "00_Logs" }
|
||||||
|
$Summary = foreach ($Root in $SummaryRoots) {
|
||||||
|
$Files = @(Get-ChildItem $Root.FullName -Recurse -Force -File -ErrorAction SilentlyContinue)
|
||||||
|
$Measure = $Files | Measure-Object Length -Sum
|
||||||
|
[pscustomobject]@{
|
||||||
|
Path = $Root.FullName
|
||||||
|
Files = $Files.Count
|
||||||
|
SizeGB = [math]::Round(($Measure.Sum / 1GB), 4)
|
||||||
|
Newest = ($Files | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty LastWriteTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$Summary | Export-Csv (Join-Path $DeltaLogDir "delta_summary.csv") -NoTypeInformation -Encoding UTF8
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Delta backup finished:"
|
||||||
|
Write-Host " $DeltaRoot"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Summary:"
|
||||||
|
$Summary | Format-Table -AutoSize
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$BackupDir = "H:\Windows-Neuaufsetzen-Backup\12_Exportierte_Listen"
|
||||||
|
$BeforeFile = Join-Path $BackupDir "bcdedit_enum_before_dualboot_cleanup.txt"
|
||||||
|
$AfterFile = Join-Path $BackupDir "bcdedit_enum_after_dualboot_cleanup.txt"
|
||||||
|
|
||||||
|
function Assert-Admin {
|
||||||
|
$Identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
$Principal = [Security.Principal.WindowsPrincipal]::new($Identity)
|
||||||
|
if (-not $Principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||||
|
throw "Dieses Skript muss in PowerShell als Administrator ausgefuehrt werden."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-BcdOsLoaderEntries {
|
||||||
|
$Text = & bcdedit /enum osloader /v
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "bcdedit /enum osloader /v ist fehlgeschlagen."
|
||||||
|
}
|
||||||
|
|
||||||
|
$Entries = @()
|
||||||
|
$Current = $null
|
||||||
|
|
||||||
|
foreach ($Line in $Text) {
|
||||||
|
if ($Line -match "^-{5,}$") {
|
||||||
|
if ($Current -and $Current.Identifier) {
|
||||||
|
$Entries += [pscustomobject]$Current
|
||||||
|
}
|
||||||
|
$Current = @{
|
||||||
|
Identifier = $null
|
||||||
|
Device = $null
|
||||||
|
OsDevice = $null
|
||||||
|
Path = $null
|
||||||
|
Description = $null
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $Current) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Line -match "^\s*(identifier|Bezeichner)\s+(.+)$") { $Current.Identifier = $Matches[2].Trim(); continue }
|
||||||
|
if ($Line -match "^\s*device\s+(.+)$") { $Current.Device = $Matches[1].Trim(); continue }
|
||||||
|
if ($Line -match "^\s*osdevice\s+(.+)$") { $Current.OsDevice = $Matches[1].Trim(); continue }
|
||||||
|
if ($Line -match "^\s*path\s+(.+)$") { $Current.Path = $Matches[1].Trim(); continue }
|
||||||
|
if ($Line -match "^\s*description\s+(.+)$") { $Current.Description = $Matches[1].Trim(); continue }
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Current -and $Current.Identifier) {
|
||||||
|
$Entries += [pscustomobject]$Current
|
||||||
|
}
|
||||||
|
|
||||||
|
return $Entries
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert-Admin
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Force -Path $BackupDir | Out-Null
|
||||||
|
|
||||||
|
& bcdedit /enum all /v | Out-File $BeforeFile -Encoding UTF8
|
||||||
|
|
||||||
|
$Entries = Get-BcdOsLoaderEntries
|
||||||
|
|
||||||
|
Write-Host "Gefundene Windows-Boot-Eintraege:"
|
||||||
|
$Entries | Select-Object Identifier, Description, Device, OsDevice, Path | Format-Table -AutoSize
|
||||||
|
|
||||||
|
$NewWindows = $Entries | Where-Object {
|
||||||
|
$_.OsDevice -eq "partition=D:" -and
|
||||||
|
$_.Path -match "winload\.efi$" -and
|
||||||
|
(Test-Path "D:\Windows\System32\winload.efi")
|
||||||
|
} | Select-Object -First 1
|
||||||
|
|
||||||
|
$OldWindows = $Entries | Where-Object {
|
||||||
|
$_.OsDevice -eq "partition=C:" -and
|
||||||
|
$_.Path -match "winload\.efi$" -and
|
||||||
|
(Test-Path "C:\Windows\System32\winload.efi")
|
||||||
|
} | Select-Object -First 1
|
||||||
|
|
||||||
|
if (-not $NewWindows) {
|
||||||
|
throw "Abbruch: Neuer Windows-Eintrag auf D:\Windows wurde nicht eindeutig gefunden."
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $OldWindows) {
|
||||||
|
throw "Abbruch: Alter Windows-Eintrag auf C:\Windows wurde nicht eindeutig gefunden."
|
||||||
|
}
|
||||||
|
|
||||||
|
$KeepIds = @($NewWindows.Identifier, $OldWindows.Identifier)
|
||||||
|
$DeleteEntries = $Entries | Where-Object { $KeepIds -notcontains $_.Identifier }
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Bleibt erhalten:"
|
||||||
|
Write-Host " NEU: $($NewWindows.Identifier) -> $($NewWindows.OsDevice) -> D:\Windows"
|
||||||
|
Write-Host " ALT: $($OldWindows.Identifier) -> $($OldWindows.OsDevice) -> C:\Windows"
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Wird aus dem Bootmenue entfernt:"
|
||||||
|
if ($DeleteEntries) {
|
||||||
|
$DeleteEntries | Select-Object Identifier, Description, Device, OsDevice, Path | Format-Table -AutoSize
|
||||||
|
} else {
|
||||||
|
Write-Host " Keine ueberfluessigen Eintraege gefunden."
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Es werden nur Bootmenue-Eintraege geloescht, keine Partitionen und keine Windows-Ordner."
|
||||||
|
$Confirmation = Read-Host "Tippe exakt BOOTCLEAN um fortzufahren"
|
||||||
|
if ($Confirmation -ne "BOOTCLEAN") {
|
||||||
|
throw "Abbruch: Bestaetigung wurde nicht eingegeben."
|
||||||
|
}
|
||||||
|
|
||||||
|
& bcdedit /set $NewWindows.Identifier description "Windows 11 Neu"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "Konnte neuen Windows-Eintrag nicht umbenennen." }
|
||||||
|
|
||||||
|
& bcdedit /set $OldWindows.Identifier description "Windows 11 Alt"
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "Konnte alten Windows-Eintrag nicht umbenennen." }
|
||||||
|
|
||||||
|
& bcdedit /default $NewWindows.Identifier
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "Konnte Standard-Boot-Eintrag nicht setzen." }
|
||||||
|
|
||||||
|
& bcdedit /timeout 5
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "Konnte Timeout nicht setzen." }
|
||||||
|
|
||||||
|
foreach ($Entry in $DeleteEntries) {
|
||||||
|
Write-Host "Loesche Boot-Eintrag $($Entry.Identifier) ($($Entry.Description))"
|
||||||
|
& bcdedit /delete $Entry.Identifier /f
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "Konnte Boot-Eintrag $($Entry.Identifier) nicht loeschen."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& bcdedit /enum all /v | Out-File $AfterFile -Encoding UTF8
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Bootmenue bereinigt."
|
||||||
|
Write-Host "Vorher gesichert: $BeforeFile"
|
||||||
|
Write-Host "Nachher gesichert: $AfterFile"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Bitte neu starten. Es sollten nur noch zwei Eintraege erscheinen:"
|
||||||
|
Write-Host " Windows 11 Neu"
|
||||||
|
Write-Host " Windows 11 Alt"
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$TargetDiskNumber = 0
|
||||||
|
$TargetWindowsDrive = "D"
|
||||||
|
$TargetDiskFriendlyName = "INTEL SSDSC2BW180A3L"
|
||||||
|
$TargetDiskSerialNumber = "CVCV3105053K180EGN"
|
||||||
|
$EfiSizeMB = 260
|
||||||
|
$EfiLabel = "SYSTEM"
|
||||||
|
|
||||||
|
function Assert-Admin {
|
||||||
|
$Identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
$Principal = [Security.Principal.WindowsPrincipal]::new($Identity)
|
||||||
|
if (-not $Principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||||
|
throw "Dieses Skript muss in PowerShell als Administrator ausgefuehrt werden."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-FreeDriveLetter {
|
||||||
|
$Used = (Get-Volume | Where-Object DriveLetter | Select-Object -ExpandProperty DriveLetter)
|
||||||
|
foreach ($Letter in "S","T","U","V","W","X","Y","Z") {
|
||||||
|
if ($Used -notcontains $Letter) {
|
||||||
|
return $Letter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw "Kein freier Laufwerksbuchstabe fuer die EFI-Partition gefunden."
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert-Admin
|
||||||
|
|
||||||
|
$Disk = Get-Disk -Number $TargetDiskNumber
|
||||||
|
$TargetWindowsPath = "$TargetWindowsDrive`:\Windows"
|
||||||
|
$TargetWinload = "$TargetWindowsDrive`:\Windows\System32\winload.efi"
|
||||||
|
|
||||||
|
Write-Host "Aktuell gestartetes Windows:"
|
||||||
|
Write-Host " SystemDrive: $env:SystemDrive"
|
||||||
|
Write-Host " windir: $env:windir"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host "Zieldatentraeger:"
|
||||||
|
$Disk | Select-Object Number, FriendlyName, SerialNumber, HealthStatus, OperationalStatus, @{Name="SizeGB";Expression={[math]::Round($_.Size/1GB,2)}}, PartitionStyle | Format-Table -AutoSize
|
||||||
|
|
||||||
|
Write-Host "Partitionen auf Datentraeger ${TargetDiskNumber}:"
|
||||||
|
Get-Partition -DiskNumber $TargetDiskNumber | Sort-Object PartitionNumber | Select-Object DiskNumber, PartitionNumber, DriveLetter, Type, @{Name="SizeGB";Expression={[math]::Round($_.Size/1GB,3)}}, GptType | Format-Table -AutoSize
|
||||||
|
|
||||||
|
if ($Disk.FriendlyName -ne $TargetDiskFriendlyName) {
|
||||||
|
throw "Abbruch: Datentraeger 0 ist nicht die erwartete Intel-SSD."
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Disk.SerialNumber -ne $TargetDiskSerialNumber) {
|
||||||
|
throw "Abbruch: Seriennummer von Datentraeger 0 passt nicht."
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path $TargetWinload)) {
|
||||||
|
throw "Abbruch: Ziel-Windows wurde nicht gefunden: $TargetWinload"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($env:SystemDrive -eq "$TargetWindowsDrive`:") {
|
||||||
|
throw "Abbruch: Du bist bereits im Ziel-Windows. Dieses Skript ist fuer den Zustand gedacht, in dem neues Windows als D: sichtbar ist."
|
||||||
|
}
|
||||||
|
|
||||||
|
$ExistingEfiOnDisk0 = Get-Partition -DiskNumber $TargetDiskNumber | Where-Object {
|
||||||
|
$_.GptType -eq "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ExistingEfiOnDisk0) {
|
||||||
|
Write-Host "EFI-Systempartition auf Datentraeger 0 existiert bereits."
|
||||||
|
$EfiPartition = $ExistingEfiOnDisk0 | Select-Object -First 1
|
||||||
|
} else {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Auf Datentraeger 0 existiert keine EFI-Systempartition."
|
||||||
|
Write-Host "Das Skript verkleinert $TargetWindowsDrive`: um $EfiSizeMB MB und erstellt daraus eine neue EFI-Systempartition."
|
||||||
|
Write-Host ""
|
||||||
|
$Confirmation = Read-Host "Tippe exakt EFI um fortzufahren"
|
||||||
|
if ($Confirmation -ne "EFI") {
|
||||||
|
throw "Abbruch: Bestaetigung wurde nicht eingegeben."
|
||||||
|
}
|
||||||
|
|
||||||
|
$TargetPartition = Get-Partition -DiskNumber $TargetDiskNumber | Where-Object DriveLetter -eq $TargetWindowsDrive
|
||||||
|
if (-not $TargetPartition) {
|
||||||
|
throw "Abbruch: Windows-Partition $TargetWindowsDrive`: wurde auf Datentraeger 0 nicht gefunden."
|
||||||
|
}
|
||||||
|
|
||||||
|
$Supported = Get-PartitionSupportedSize -DiskNumber $TargetDiskNumber -PartitionNumber $TargetPartition.PartitionNumber
|
||||||
|
$NewSize = $TargetPartition.Size - ($EfiSizeMB * 1MB)
|
||||||
|
if ($NewSize -lt $Supported.SizeMin) {
|
||||||
|
throw "Abbruch: Partition kann nicht um $EfiSizeMB MB verkleinert werden."
|
||||||
|
}
|
||||||
|
|
||||||
|
Resize-Partition -DiskNumber $TargetDiskNumber -PartitionNumber $TargetPartition.PartitionNumber -Size $NewSize
|
||||||
|
|
||||||
|
$EfiPartition = New-Partition -DiskNumber $TargetDiskNumber -Size ($EfiSizeMB * 1MB) -GptType "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}"
|
||||||
|
Format-Volume -Partition $EfiPartition -FileSystem FAT32 -NewFileSystemLabel $EfiLabel -Confirm:$false | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
$EfiLetter = Get-FreeDriveLetter
|
||||||
|
Set-Partition -DiskNumber $TargetDiskNumber -PartitionNumber $EfiPartition.PartitionNumber -NewDriveLetter $EfiLetter
|
||||||
|
$EfiDrive = "$EfiLetter`:"
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Schreibe Bootdateien fuer $TargetWindowsPath nach $EfiDrive ..."
|
||||||
|
bcdboot "$TargetWindowsPath" /s $EfiDrive /f UEFI
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
throw "bcdboot ist fehlgeschlagen."
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Setze Bootmenue-Timeout auf 3 Sekunden."
|
||||||
|
bcdedit /timeout 3
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Aktuelle BCD-Eintraege:"
|
||||||
|
bcdedit /enum
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Fertig. Naechster Schritt:"
|
||||||
|
Write-Host "1. Neustarten."
|
||||||
|
Write-Host "2. Im UEFI/BIOS Windows Boot Manager der Intel-SSD/Datentraeger 0 als erste Bootoption waehlen, falls angeboten."
|
||||||
|
Write-Host "3. Im Bootmenue den neuen Windows-11-Eintrag starten."
|
||||||
|
Write-Host "4. Wenn das neue Windows laeuft, sollte SystemDrive C: sein und der alte Windows-Datentraeger einen anderen Buchstaben haben."
|
||||||
@@ -39,12 +39,10 @@ access_control:
|
|||||||
- vault.kaleschke.info
|
- vault.kaleschke.info
|
||||||
- ntfy.kaleschke.info
|
- ntfy.kaleschke.info
|
||||||
- git.kaleschke.info
|
- git.kaleschke.info
|
||||||
- jellyfin.kaleschke.info
|
|
||||||
policy: bypass
|
policy: bypass
|
||||||
|
|
||||||
# Admin-Dienste - 2FA erforderlich
|
# Admin-Dienste - 2FA erforderlich
|
||||||
- domain:
|
- domain:
|
||||||
- uptime.kaleschke.info
|
|
||||||
- files.kaleschke.info
|
- files.kaleschke.info
|
||||||
- scrutiny.kaleschke.info
|
- scrutiny.kaleschke.info
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
@@ -63,7 +61,7 @@ session:
|
|||||||
cookies:
|
cookies:
|
||||||
- domain: kaleschke.info
|
- domain: kaleschke.info
|
||||||
authelia_url: https://auth.kaleschke.info
|
authelia_url: https://auth.kaleschke.info
|
||||||
default_redirection_url: https://home.kaleschke.info
|
default_redirection_url: https://glance.kaleschke.info
|
||||||
|
|
||||||
regulation:
|
regulation:
|
||||||
max_retries: 3
|
max_retries: 3
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ SEND_NTFY="${SEND_NTFY:-1}"
|
|||||||
CLOUDFLARE_TOKEN_FILE="${CLOUDFLARE_TOKEN_FILE:-/mnt/user/appdata/traefik/secrets/cloudflare_dns_api_token}"
|
CLOUDFLARE_TOKEN_FILE="${CLOUDFLARE_TOKEN_FILE:-/mnt/user/appdata/traefik/secrets/cloudflare_dns_api_token}"
|
||||||
WARN_DAYS="${WARN_DAYS:-14}"
|
WARN_DAYS="${WARN_DAYS:-14}"
|
||||||
CRITICAL_DAYS="${CRITICAL_DAYS:-7}"
|
CRITICAL_DAYS="${CRITICAL_DAYS:-7}"
|
||||||
DOMAINS="${DOMAINS:-traefik.kaleschke.info auth.kaleschke.info vault.kaleschke.info git.kaleschke.info cloud.kaleschke.info home.kaleschke.info borg.kaleschke.info monitoring.kaleschke.info ntfy.kaleschke.info}"
|
DOMAINS="${DOMAINS:-traefik.kaleschke.info auth.kaleschke.info vault.kaleschke.info git.kaleschke.info cloud.kaleschke.info glance.kaleschke.info borg.kaleschke.info monitoring.kaleschke.info ntfy.kaleschke.info}"
|
||||||
TMP_DIR="${TMP_DIR:-/tmp/kallilab-cert-token-check}"
|
TMP_DIR="${TMP_DIR:-/tmp/kallilab-cert-token-check}"
|
||||||
|
|
||||||
mkdir -p "$TMP_DIR"
|
mkdir -p "$TMP_DIR"
|
||||||
|
|||||||
Regular → Executable
+1
-1
@@ -800,7 +800,7 @@ collect_log_highlights() {
|
|||||||
while IFS= read -r p; do
|
while IFS= read -r p; do
|
||||||
[ -n "$p" ] || continue
|
[ -n "$p" ] || continue
|
||||||
local pcount
|
local pcount
|
||||||
pcount="$(grep -Eaic -- "$p" "$hits" 2>/dev/null || echo 0)"
|
pcount="$(grep -Eaic -- "$p" "$hits" 2>/dev/null || true)"
|
||||||
if [ "${pcount:-0}" -gt 0 ]; then
|
if [ "${pcount:-0}" -gt 0 ]; then
|
||||||
printf '%d\t%s\n' "$pcount" "$p" >> "$noise_by_pattern"
|
printf '%d\t%s\n' "$pcount" "$p" >> "$noise_by_pattern"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -9,15 +9,79 @@ EVENT_FILTERS="${EVENT_FILTERS:---filter event=die --filter event=oom --filter e
|
|||||||
|
|
||||||
mkdir -p "$(dirname "$OUTPUT_PATH")"
|
mkdir -p "$(dirname "$OUTPUT_PATH")"
|
||||||
|
|
||||||
|
json_value() {
|
||||||
|
local key="$1"
|
||||||
|
local json="$2"
|
||||||
|
|
||||||
|
printf '%s' "$json" | sed -n "s/.*\"$key\":\"\\([^\"]*\\)\".*/\\1/p" | head -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
event_summary() {
|
||||||
|
local event="$1"
|
||||||
|
local action name image exit_code signal
|
||||||
|
|
||||||
|
action="$(json_value "Action" "$event")"
|
||||||
|
name="$(json_value "name" "$event")"
|
||||||
|
image="$(json_value "image" "$event")"
|
||||||
|
exit_code="$(json_value "exitCode" "$event")"
|
||||||
|
signal="$(json_value "signal" "$event")"
|
||||||
|
|
||||||
|
printf 'Container: %s\nAction: %s\nImage: %s\nExit-Code: %s\nSignal: %s\n\nFull event logged in: %s\n' \
|
||||||
|
"${name:-unknown}" \
|
||||||
|
"${action:-unknown}" \
|
||||||
|
"${image:-unknown}" \
|
||||||
|
"${exit_code:-n/a}" \
|
||||||
|
"${signal:-n/a}" \
|
||||||
|
"$OUTPUT_PATH"
|
||||||
|
}
|
||||||
|
|
||||||
|
event_title() {
|
||||||
|
local event="$1"
|
||||||
|
local action name exit_code
|
||||||
|
|
||||||
|
action="$(json_value "Action" "$event")"
|
||||||
|
name="$(json_value "name" "$event")"
|
||||||
|
exit_code="$(json_value "exitCode" "$event")"
|
||||||
|
|
||||||
|
if [ -n "$exit_code" ]; then
|
||||||
|
printf 'Docker critical: %s %s exit=%s' "${name:-unknown}" "${action:-event}" "$exit_code"
|
||||||
|
else
|
||||||
|
printf 'Docker critical: %s %s' "${name:-unknown}" "${action:-event}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
should_send_event() {
|
||||||
|
local event="$1"
|
||||||
|
local action exit_code
|
||||||
|
|
||||||
|
action="$(json_value "Action" "$event")"
|
||||||
|
exit_code="$(json_value "exitCode" "$event")"
|
||||||
|
|
||||||
|
case "$action" in
|
||||||
|
die)
|
||||||
|
[ "${exit_code:-}" != "0" ]
|
||||||
|
;;
|
||||||
|
oom|kill)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
send_event() {
|
send_event() {
|
||||||
local line="$1"
|
local line="$1"
|
||||||
|
local title message
|
||||||
local timestamp
|
local timestamp
|
||||||
timestamp="$(date -Iseconds)"
|
timestamp="$(date -Iseconds)"
|
||||||
|
title="$(event_title "$line")"
|
||||||
|
message="$(event_summary "$line")"
|
||||||
|
|
||||||
printf '%s %s\n' "$timestamp" "$line" | tee -a "$OUTPUT_PATH" >/dev/null
|
printf '%s %s\n' "$timestamp" "$line" | tee -a "$OUTPUT_PATH" >/dev/null
|
||||||
|
|
||||||
if [ "$SEND_NTFY" = "1" ] && [ -f "$NTFY_SCRIPT" ]; then
|
if [ "$SEND_NTFY" = "1" ] && [ -f "$NTFY_SCRIPT" ]; then
|
||||||
bash "$NTFY_SCRIPT" "$NTFY_TOPIC" "Docker critical event" "$line" high || true
|
bash "$NTFY_SCRIPT" "$NTFY_TOPIC" "$title" "$message" high || true
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,5 +93,6 @@ fi
|
|||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
docker events $EVENT_FILTERS --format '{{json .}}' | while IFS= read -r event; do
|
docker events $EVENT_FILTERS --format '{{json .}}' | while IFS= read -r event; do
|
||||||
[ -n "$event" ] || continue
|
[ -n "$event" ] || continue
|
||||||
|
should_send_event "$event" || continue
|
||||||
send_event "$event"
|
send_event "$event"
|
||||||
done
|
done
|
||||||
|
|||||||
Regular → Executable
@@ -41,13 +41,6 @@ monitoring-node-exporter.*mdadm.*Cannot parse /host/proc/mdstat
|
|||||||
# the 403 as a real auth failure signal.
|
# the 403 as a real auth failure signal.
|
||||||
gitea.*user/login/openid.*403 Forbidden
|
gitea.*user/login/openid.*403 Forbidden
|
||||||
|
|
||||||
# Uptime Kuma monitor for legacy domain grafana.kaleschke.info returning 404.
|
|
||||||
# Why: Monitor was not removed when the public Grafana endpoint was
|
|
||||||
# decommissioned.
|
|
||||||
# Re-check: at next Uptime-Kuma cleanup. Action: delete the obsolete monitor
|
|
||||||
# and remove this pattern.
|
|
||||||
uptime-kuma.*grafana\.kaleschke\.info.*status code 404
|
|
||||||
|
|
||||||
# Tailscale PCP port mapping failure (NAT-PMP unsupported by router).
|
# Tailscale PCP port mapping failure (NAT-PMP unsupported by router).
|
||||||
# Why: Tailscale falls back to STUN/DERP transparently; no functional impact.
|
# Why: Tailscale falls back to STUN/DERP transparently; no functional impact.
|
||||||
# Re-check: if Tailscale reports persistent connectivity problems in real
|
# Re-check: if Tailscale reports persistent connectivity problems in real
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ WARNING_TOPIC="${WARNING_TOPIC:-homelab-alerts}"
|
|||||||
CRITICAL_TOPIC="${CRITICAL_TOPIC:-homelab-alerts}"
|
CRITICAL_TOPIC="${CRITICAL_TOPIC:-homelab-alerts}"
|
||||||
SEND_NTFY="${SEND_NTFY:-1}"
|
SEND_NTFY="${SEND_NTFY:-1}"
|
||||||
TMP_DIR="${TMP_DIR:-/tmp/kallilab-posture-check}"
|
TMP_DIR="${TMP_DIR:-/tmp/kallilab-posture-check}"
|
||||||
ALLOW_DISK1_NTFS="${ALLOW_DISK1_NTFS:-1}"
|
ALLOW_DISK1_NTFS="${ALLOW_DISK1_NTFS:-0}"
|
||||||
|
ALERT_STATE_PATH="${ALERT_STATE_PATH:-/mnt/user/services/posture-check/last-alert.state}"
|
||||||
|
ALERT_REPEAT_SECONDS="${ALERT_REPEAT_SECONDS:-86400}"
|
||||||
|
|
||||||
mkdir -p "$TMP_DIR"
|
mkdir -p "$TMP_DIR"
|
||||||
RESULTS_FILE="$TMP_DIR/results.$$"
|
RESULTS_FILE="$TMP_DIR/results.$$"
|
||||||
@@ -63,6 +65,34 @@ check_fstype() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_disk1_fstype() {
|
||||||
|
local actual
|
||||||
|
|
||||||
|
if ! command -v findmnt >/dev/null 2>&1; then
|
||||||
|
add_result "warning" "disk1_fstype" "Cannot check /mnt/disk1 filesystem because findmnt is missing"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! actual="$(findmnt -no FSTYPE "/mnt/disk1" 2>/dev/null)"; then
|
||||||
|
add_result "warning" "disk1_fstype" "Mount not found: /mnt/disk1"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$ALLOW_DISK1_NTFS" = "1" ]; then
|
||||||
|
if [ "$actual" = "ntfs3" ] || [ "$actual" = "fuseblk" ]; then
|
||||||
|
add_result "ok" "disk1_fstype" "/mnt/disk1 filesystem is $actual; temporarily allowed until Disk1 phase 2 migration"
|
||||||
|
else
|
||||||
|
add_result "warning" "disk1_fstype" "/mnt/disk1 filesystem is $actual, expected ntfs3/fuseblk during temporary Disk1 migration exception"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ "$actual" = "xfs" ]; then
|
||||||
|
add_result "ok" "disk1_fstype" "/mnt/disk1 filesystem is $actual"
|
||||||
|
else
|
||||||
|
add_result "critical" "disk1_fstype" "/mnt/disk1 filesystem is $actual, expected xfs"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
check_no_ntfs_on_core_mounts() {
|
check_no_ntfs_on_core_mounts() {
|
||||||
local hits
|
local hits
|
||||||
local pattern="^/mnt/(cache|disk1)(/|$)"
|
local pattern="^/mnt/(cache|disk1)(/|$)"
|
||||||
@@ -80,7 +110,7 @@ check_no_ntfs_on_core_mounts() {
|
|||||||
if [ -n "$hits" ]; then
|
if [ -n "$hits" ]; then
|
||||||
add_result "critical" "no_ntfs_core_mounts" "NTFS-like filesystem on core mount: $hits"
|
add_result "critical" "no_ntfs_core_mounts" "NTFS-like filesystem on core mount: $hits"
|
||||||
elif [ "$ALLOW_DISK1_NTFS" = "1" ]; then
|
elif [ "$ALLOW_DISK1_NTFS" = "1" ]; then
|
||||||
add_result "warning" "no_ntfs_core_mounts" "No NTFS on /mnt/cache; /mnt/disk1 NTFS is temporarily allowed until Disk1 phase 2 migration"
|
add_result "ok" "no_ntfs_core_mounts" "No NTFS on /mnt/cache; /mnt/disk1 NTFS is temporarily allowed until Disk1 phase 2 migration"
|
||||||
else
|
else
|
||||||
add_result "ok" "no_ntfs_core_mounts" "No ntfs3/fuseblk mounts below /mnt/cache or /mnt/disk1"
|
add_result "ok" "no_ntfs_core_mounts" "No ntfs3/fuseblk mounts below /mnt/cache or /mnt/disk1"
|
||||||
fi
|
fi
|
||||||
@@ -122,6 +152,15 @@ check_inode_usage() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_disk1_inode_usage() {
|
||||||
|
if [ "$ALLOW_DISK1_NTFS" = "1" ]; then
|
||||||
|
add_result "ok" "disk1_inode_usage" "/mnt/disk1 inode usage skipped; NTFS transition filesystem does not expose POSIX inode usage"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
check_inode_usage "/mnt/disk1" 80 "disk1_inode_usage"
|
||||||
|
}
|
||||||
|
|
||||||
check_filesystem_usage() {
|
check_filesystem_usage() {
|
||||||
local path="$1"
|
local path="$1"
|
||||||
local max_percent="$2"
|
local max_percent="$2"
|
||||||
@@ -198,6 +237,79 @@ send_ntfy() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alert_fingerprint() {
|
||||||
|
awk -F '\t' '$1 != "ok" { printf "%s|%s|%s\n", $1, $2, $3 }' "$RESULTS_FILE" | cksum | awk '{ print $1 ":" $2 }'
|
||||||
|
}
|
||||||
|
|
||||||
|
alert_summary() {
|
||||||
|
awk -F '\t' '$1 != "ok" { printf "%s:%s; ", $1, $2 }' "$RESULTS_FILE" | sed 's/; $//'
|
||||||
|
}
|
||||||
|
|
||||||
|
should_send_alert() {
|
||||||
|
local fingerprint="$1"
|
||||||
|
local now
|
||||||
|
local last_fingerprint=""
|
||||||
|
local last_sent="0"
|
||||||
|
|
||||||
|
now="$(date +%s)"
|
||||||
|
|
||||||
|
if ! printf '%s' "$ALERT_REPEAT_SECONDS" | grep -Eq '^[0-9]+$'; then
|
||||||
|
ALERT_REPEAT_SECONDS=86400
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$ALERT_STATE_PATH" ]; then
|
||||||
|
IFS="$(printf '\t')" read -r last_fingerprint last_sent < "$ALERT_STATE_PATH" || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$fingerprint" != "$last_fingerprint" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf '%s' "$last_sent" | grep -Eq '^[0-9]+$'; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $((now - last_sent)) -ge "$ALERT_REPEAT_SECONDS" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
remember_alert() {
|
||||||
|
local fingerprint="$1"
|
||||||
|
local now
|
||||||
|
|
||||||
|
now="$(date +%s)"
|
||||||
|
mkdir -p "$(dirname "$ALERT_STATE_PATH")"
|
||||||
|
printf '%s\t%s\n' "$fingerprint" "$now" > "$ALERT_STATE_PATH.tmp"
|
||||||
|
mv "$ALERT_STATE_PATH.tmp" "$ALERT_STATE_PATH"
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_alert_state() {
|
||||||
|
rm -f "$ALERT_STATE_PATH" "$ALERT_STATE_PATH.tmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
send_alert_once() {
|
||||||
|
local severity="$1"
|
||||||
|
local topic="$2"
|
||||||
|
local body="$3"
|
||||||
|
local fingerprint
|
||||||
|
local summary
|
||||||
|
|
||||||
|
fingerprint="$(alert_fingerprint)"
|
||||||
|
summary="$(alert_summary)"
|
||||||
|
|
||||||
|
if [ -n "$summary" ]; then
|
||||||
|
body="$body Checks: $summary"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if should_send_alert "$fingerprint"; then
|
||||||
|
send_ntfy "$severity" "$topic" "$body"
|
||||||
|
remember_alert "$fingerprint"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
write_json() {
|
write_json() {
|
||||||
local timestamp
|
local timestamp
|
||||||
local critical_count
|
local critical_count
|
||||||
@@ -243,13 +355,15 @@ write_json() {
|
|||||||
cat "$OUTPUT_PATH"
|
cat "$OUTPUT_PATH"
|
||||||
|
|
||||||
if [ "$status" = "critical" ]; then
|
if [ "$status" = "critical" ]; then
|
||||||
send_ntfy "critical" "$CRITICAL_TOPIC" "Posture-check critical: $critical_count critical, $warning_count warning. See $OUTPUT_PATH"
|
send_alert_once "critical" "$CRITICAL_TOPIC" "Posture-check critical: $critical_count critical, $warning_count warning. See $OUTPUT_PATH"
|
||||||
return 2
|
return 2
|
||||||
fi
|
fi
|
||||||
if [ "$status" = "warning" ]; then
|
if [ "$status" = "warning" ]; then
|
||||||
send_ntfy "warning" "$WARNING_TOPIC" "Posture-check warning: $warning_count warning. See $OUTPUT_PATH"
|
send_alert_once "warning" "$WARNING_TOPIC" "Posture-check warning: $warning_count warning. See $OUTPUT_PATH"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
clear_alert_state
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
@@ -258,15 +372,11 @@ main() {
|
|||||||
need_cmd awk || true
|
need_cmd awk || true
|
||||||
|
|
||||||
check_fstype "/mnt/cache" "xfs" "critical" "cache_fstype"
|
check_fstype "/mnt/cache" "xfs" "critical" "cache_fstype"
|
||||||
if [ "$ALLOW_DISK1_NTFS" = "1" ]; then
|
check_disk1_fstype
|
||||||
check_fstype "/mnt/disk1" "ntfs3" "warning" "disk1_fstype"
|
|
||||||
else
|
|
||||||
check_fstype "/mnt/disk1" "xfs" "critical" "disk1_fstype"
|
|
||||||
fi
|
|
||||||
check_no_ntfs_on_core_mounts
|
check_no_ntfs_on_core_mounts
|
||||||
check_mover_drift
|
check_mover_drift
|
||||||
check_inode_usage "/mnt/cache" 80 "cache_inode_usage"
|
check_inode_usage "/mnt/cache" 80 "cache_inode_usage"
|
||||||
check_inode_usage "/mnt/disk1" 80 "disk1_inode_usage"
|
check_disk1_inode_usage
|
||||||
check_filesystem_usage "/mnt/cache" 70 "cache_fill_level" "warning"
|
check_filesystem_usage "/mnt/cache" 70 "cache_fill_level" "warning"
|
||||||
|
|
||||||
for share in appdata system domains; do
|
for share in appdata system domains; do
|
||||||
|
|||||||
Regular → Executable
Regular → Executable
@@ -42,6 +42,34 @@ Zeit: taeglich 06:20, Cron `20 6 * * *`.
|
|||||||
bash /mnt/user/services/homelab-infra/services/posture-check/compose-runtime-drift.sh
|
bash /mnt/user/services/homelab-infra/services/posture-check/compose-runtime-drift.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `homelab-operations-report-daily`
|
||||||
|
|
||||||
|
Zeit: taeglich nach Borg und den Morgenchecks, z. B. 07:30, Cron `30 7 * * *`.
|
||||||
|
|
||||||
|
Voraussetzung: SMTP-Passwort liegt **nicht im Repo**, sondern auf dem Host:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /mnt/user/appdata/secrets
|
||||||
|
chmod 700 /mnt/user/appdata/secrets
|
||||||
|
printf '%s' 'SMTP_PASSWORT_HIER_EINTRAGEN' > /mnt/user/appdata/secrets/homelab_smtp_password.txt
|
||||||
|
chmod 600 /mnt/user/appdata/secrets/homelab_smtp_password.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
User Script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
SEND_MAIL=1 \
|
||||||
|
MAIL_MODE=always \
|
||||||
|
MAIL_FROM="michideheld@gmx.de" \
|
||||||
|
MAIL_TO="Mi.Kaleschke@gmx.de" \
|
||||||
|
SMTP_HOST="smtp.gmx.net" \
|
||||||
|
SMTP_PORT="587" \
|
||||||
|
SMTP_USER="michideheld@gmx.de" \
|
||||||
|
SMTP_PASS_FILE="/mnt/user/appdata/secrets/homelab_smtp_password.txt" \
|
||||||
|
bash /mnt/user/services/homelab-infra/services/posture-check/daily-status-report.sh
|
||||||
|
```
|
||||||
|
|
||||||
## `docker-critical-events-at-start`
|
## `docker-critical-events-at-start`
|
||||||
|
|
||||||
Zeit: Array Start. Dieser Job startet einen Hintergrund-Watcher und beendet sich sofort.
|
Zeit: Array Start. Dieser Job startet einen Hintergrund-Watcher und beendet sich sofort.
|
||||||
|
|||||||
Reference in New Issue
Block a user