1 Commits

Author SHA1 Message Date
renovate eb7dc8ab5a chore(deps): update minor-and-patch-updates 2026-06-06 04:21:12 +00:00
29 changed files with 134 additions and 1130 deletions
+1 -1
View File
@@ -90,7 +90,7 @@ Wenn Drift vermutet wird, nicht raten. Erst die Pflichtmatrix in `docs/GITOPS_DR
- `traefik`: Host-Ports 80/443
- `gitea`: SSH-Port 222
- `AdGuard Home`: DNS-Port 53 und LAN-Admin-Port 8082
- `tailscale`: natives Unraid-Plugin (`tailscale.plg`, Interface `tailscale1`), Subnet-Router fuers LAN; nicht repo-/Komodo-verwaltet. Der frueher repo-verwaltete userspace-Docker-Stack `host-services/tailscale/` wurde am 2026-06-06 entfernt.
- `tailscale`: `network_mode: host`
- `Plex-Media-Server`: historischer Host-Netz-Sonderfall, nicht als Repo-Stack enthalten
- `scrutiny`: `privileged: true` fuer SMART/Laufwerkszugriff
- `Komodo`: Docker-Socket und native Auth ohne pauschale ForwardAuth
+1 -1
View File
@@ -240,7 +240,7 @@ Legende Status:
| `AdGuard Home` | ✅ | `dns_net` (172.23.0.3), `frontend_net` | Port 53 DNS direkt, Port 8082 Admin nur auf Tailscale-IP `100.80.98.33` | DNS-Server + Upstream zu unbound; kein Traefik fuer Admin-UI | Admin-Port bleibt bewusst ohne Traefik/2FA, aber nicht mehr auf allen LAN-Interfaces |
| `unbound` | ✅ | `dns_net` | intern | Upstream-Resolver für AdGuard, isoliert | — |
| `ddns-updater` | ✅ | `frontend_net` | intern | Cloudflare DNS API; bleibt in `frontend_net` | Dokumentierte Ausnahme |
| `tailscale` | ✅ | `host` | VPN-Zugang / Subnet-Router | **Natives Unraid-Plugin** (`tailscale.plg`, Interface `tailscale1`, State `/boot/config/plugins/tailscale/state`) — **nicht** repo-/Komodo-verwaltet | Subnet-Router fuer `192.168.178.0/24`; der redundante userspace-Docker-Stack `host-services/tailscale/` wurde am 2026-06-06 entfernt |
| `tailscale` | ✅ | `host` | VPN-Zugang | Git-Stack (`host-services/tailscale/`) | nutzt `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` als dokumentierte VPN-Ausnahme |
### 7.2 Sicherheit / Identity
+1 -1
View File
@@ -11,6 +11,7 @@ in `docs/RESTORE_MATRIX.md` ergaenzt.
| Prioritaet | Punkt | Naechster Schritt |
|---|---|---|
| P1 | DR-Workstation Bare-Metal-Kit: WSL2 + Borg-Client installieren | Hetzner-DR-SSH-Key ist 2026-06-03 erledigt und offline gesichert. Verbleibend: WSL2 auf dem Gaming-PC einrichten (`wsl --install -d Ubuntu`), `sudo apt install borgbackup` und ein erster Smoke `borg list ssh://u565255@u565255.your-storagebox.de/./hetzner_borg_appdata_critical` mit dem offline gesicherten Key + Passphrase. Bestandteile dokumentiert in `docs/EXTERNAL_DEPENDENCIES.md` Abschnitt "DR-Workstation Bare-Metal-Kit" |
| P2 | Family-Onboarding praktisch starten | Fokus: Vaultwarden als Passwortbasis, Immich-Mobile-Backup auf jedem Handy, Mealie mit erstem Rezept/Einkaufsliste; Ablauf steht in `docs/FAMILY_ONBOARDING.md` |
## Restore-Audit Backlog (Stand 2026-06-03)
@@ -43,7 +44,6 @@ Ergebnis des Restore-Skills-Audits (Session 2026-06-02/03). Die kritischen Bugfi
## Zuletzt geschlossen
- DR-Workstation Bare-Metal-Kit abgeschlossen (2026-06-06): WSL2 Ubuntu 24.04, SSH/Git, Borg 1.2.8, DR-Key-Arbeitskopien `~/.ssh/dr-readonly` und `~/.ssh/dr-hetzner`, `~/dr-smoke.sh`. Finaler Operator-Smoke erfolgreich: GitHub HEAD `3a263a4...`, Hetzner Storage Box Repos sichtbar (`backup`, `backup2`, `hetzner_borg_appdata`, `hetzner_borg_appdata_critical`), Borg-Repo `hetzner_borg_appdata_critical` gelesen, Repository ID `5dd9b949...`, encrypted `Yes (repokey)`, `DR-Smoke OK (2026-06-06 10:05:30)`. Borg-Passphrase wurde nur interaktiv eingegeben und nicht gespeichert.
- Nextcloud-Restore-Test 2026-06-03 erfolgreich (Tier-2 damit komplett belegt). Drei Laeufe noetig: Lauf 1 schlug an `chmod()` der data-Dir auf shfs fehl (`OC_Util.php:486`), Lauf 2 an fehlender `.ncdata`-Marker-Datei, Lauf 3 sauber durch. Beide Bug-Fixes ins Skript `ops/restore-tests/nextcloud-restore-test.sh` integriert. Endresultat: HTTP 200 auf `/status.php`, `occ status` ok, 126 Tabellen in der DB. Source: `hetzner_borg_appdata_critical`, Archiv `Taegliche-Sicherung-2026-06-03T04:30:41.432`. Report unter `/mnt/user/backups/restore-reports/nextcloud-2026-06-03.md`.
- Hetzner Storage Box DR-SSH-Key `dr-hetzner-2026-06-03` (ed25519, Passphrase-frei) angelegt: Pubkey via `install-ssh-key` auf der Storage Box autorisiert, passwortloser Login erfolgreich (Borg-Repos `backup`, `backup2`, `hetzner_borg_appdata`, `hetzner_borg_appdata_critical` sichtbar), Private-Key offline neben KOMODO_*-Notiz und GitHub-Deploy-Key abgelegt, Arbeitsplatz-Kopie geloescht. Damit ist Bare-Metal-Borg-Zugang von der DR-Workstation moeglich, sobald WSL2+Borg installiert sind.
- Fix Common Problems Plugin (FCP) 2026-06-03 deinstalliert. Befund: drei `grep -R ... /usr/local/emhttp`-Prozesse aus einem FCP-Daily-Scan hingen seit ~7 Tagen in einem Symlink-Loop (`/usr/local/emhttp/mnt -> /mnt`, gesamte Array). 3 Cores dauerhaft 100 %, IOWAIT bis 55 %, USB-Flash unter Dauer-IO. Plugin via `plugin remove` entfernt, Cron + /tmp-Reste sauber, Load von 14.6 auf 1.08 gefallen. FCP wird bewusst nicht wieder installiert (Begruendung siehe `HOMELAB_ARCHITECTURE_MASTER_V2.md` Sektion 13). Bekannte Risiken decken Scrutiny, Monitoring, Posture-Check und Critical-Events-Watcher bereits ab.
+1 -8
View File
@@ -290,14 +290,7 @@ Erfolgskriterium: `docker network ls` zeigt `frontend_net`, `backend_net`, `moni
1. `traefik/`
2. `host-services/Adguard/`
> **Tailscale-Hinweis:** Tailscale laeuft als **natives Unraid-Plugin**
> (`tailscale.plg`, Interface `tailscale1`, State `/boot/config/plugins/tailscale/state`,
> im Flash-Backup gesichert) und ist der Subnet-Router fuer `192.168.178.0/24`.
> Es ist **kein** Compose-/Komodo-Stack mehr und kommt mit dem Host hoch — daher
> nicht in dieser Bootstrap-Liste. Der frueher hier gelistete Docker-Stack
> `host-services/tailscale/` (userspace-only, redundant) wurde am 2026-06-06
> entfernt (siehe `docs/NETWORK_INVENTORY.md`).
3. `host-services/tailscale/`
**LE-Rate-Limit-Vorsicht:** Wenn `/mnt/user/appdata/traefik/letsencrypt/acme.json` verloren oder unklar ist, zuerst gegen Let's Encrypt Staging ausstellen lassen (`--certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory`). Erst nach gruenem Smoke wieder auf Production-CA. Hintergrund: 50 Zertifikate pro Domain pro Woche reicht bei einem hektischen Wiederanlauf nicht, wenn man die Sub-Domains mehrfach hochzieht.
-9
View File
@@ -115,15 +115,6 @@ Erwartet: HEAD und mindestens ein `refs/heads/master`-Eintrag.
Damit der "ich pruefe das vierteljaehrlich"-Schritt zur Routine wird, ein kleines Skript ins WSL-Home:
Stand 2026-06-06: Das Skript liegt zusaetzlich versioniert unter
`ops/maintenance/dr-workstation-smoke.sh` und wurde auf `baerchen` bereits nach
`~/dr-smoke.sh` in die Ubuntu-WSL kopiert. Borg 1.2.8 ist installiert, die
DR-Key-Arbeitskopien liegen unter `~/.ssh/dr-readonly` und
`~/.ssh/dr-hetzner`, GitHub-Read-Smoke und Hetzner-SSH-Smoke sind erfolgreich.
Der finale Borg-Smoke via `bash ~/dr-smoke.sh` wurde am 2026-06-06 ebenfalls
erfolgreich gefahren (`DR-Smoke OK (2026-06-06 10:05:30)`). Die Borg-Passphrase
wurde nur interaktiv eingegeben und nicht gespeichert.
```bash
cat > ~/dr-smoke.sh <<'EOF'
#!/bin/bash
-1
View File
@@ -107,4 +107,3 @@ Operative Regel: Die DR-Workstation wird nicht als Test-/Spiel-PC betrachtet. WS
| 2026-06-03 | KOMODO_*-Notiz offline gesichert (Operator-Bestaetigung im DR-Tabletop-Followup). Quelle bleibt host-seitige `.env` (`/mnt/user/services/stacks/komodo/.env`) bzw. Drift-Recovery-Kopie vom 2026-05-04. Bare-Metal-Komodo-Bootstrap ist damit ohne Vaultwarden moeglich. | Restliche P1-Operator-Aufgaben: GitHub-Read-PAT, DR-Workstation-Setup, Nextcloud-Restore-Test |
| 2026-06-03 | GitHub-Mirror Read-Only Deploy-Key `DR Read-Only 2026-06-03` (ed25519, Passphrase-frei) erzeugt, in GitHub Repo Settings ohne Write-Access hinterlegt, Smoke `git ls-remote` erfolgreich (`d947c7f` matched master HEAD), Private-Key offline neben KOMODO_*-Notiz abgelegt, Arbeitsplatz-Kopie geloescht. | Restliche P1-Operator-Aufgaben: DR-Workstation-Setup, Nextcloud-Restore-Test |
| 2026-06-03 | Hetzner Storage Box DR-SSH-Key `dr-hetzner-2026-06-03` (ed25519, Passphrase-frei) erzeugt, via `install-ssh-key` auf Storage Box `u565255.your-storagebox.de:23` autorisiert, passwortloser Login erfolgreich (Borg-Repos sichtbar), Private-Key offline neben KOMODO_*-Notiz und GitHub-Deploy-Key abgelegt, Arbeitsplatz-Kopie geloescht. Bare-Metal-Borg-Restore von der DR-Workstation ist damit moeglich, sobald WSL2 + Borg-Client installiert sind. | Restliche P1-Operator-Aufgaben: WSL2 + Borg-Client auf DR-Workstation installieren, Nextcloud-Restore-Test |
| 2026-06-06 | DR-Workstation produktiv: WSL2 Ubuntu 24.04 vorhanden, SSH/Git und Borg 1.2.8 in WSL vorhanden, DR-Key-Arbeitskopien unter `~/.ssh/dr-readonly` und `~/.ssh/dr-hetzner`, GitHub-Read-Smoke und Hetzner-SSH-Smoke erfolgreich, `ops/maintenance/dr-workstation-smoke.sh` nach `~/dr-smoke.sh` kopiert. Finaler Operator-Smoke erfolgreich: GitHub HEAD `3a263a4...`, Hetzner Storage Box Repos sichtbar, Borg-Repo `hetzner_borg_appdata_critical` gelesen, Repository ID `5dd9b949...`, encrypted `Yes (repokey)`, `DR-Smoke OK (2026-06-06 10:05:30)`. | Quartalsweise `bash ~/dr-smoke.sh`; Borg-Passphrase weiterhin nur interaktiv eingeben und nicht speichern |
+1 -8
View File
@@ -1,15 +1,8 @@
# Home Assistant -> InfluxDB 3 -> Grafana
**Status 2026-06-06: archiviert / nicht aktiv.** Home Assistant existiert seit
dem Crash aktuell nicht mehr. Dieses Dokument ist nur noch ein historischer
Zielbild-Entwurf fuer einen spaeteren Neuaufbau. Das fruehere TODO
`influxdb3_homeassistant_token` wurde aus der aktiven Master-Liste gestrichen;
vor Token-, InfluxDB-Writer- oder Ecowitt-Arbeiten muss Home Assistant zuerst
neu aufgesetzt und neu inventarisiert werden.
Ziel: Home Assistant schreibt ausgewaehlte Ecowitt- und Energiesensoren nach InfluxDB 3 Core. Grafana bleibt das Langzeit-Dashboard, Home Assistant bleibt die Automationszentrale.
## Historischer Live-Stand 2026-05-04
## Live-Stand 2026-05-04
- Home Assistant ist per SSH unter `192.168.178.50:22222` erreichbar.
- `ha core check` ist erfolgreich.
+9 -9
View File
@@ -1,6 +1,6 @@
# Master To-do - KalliLab CORE
Stand: 2026-06-06 (Wochenend-Sprint, nach Status-Kategorien sortiert)
Stand: 2026-06-05 (Wochenend-Sprint, nach Status-Kategorien sortiert)
Diese Liste ist die zentrale Arbeitsliste fuer offene operative Punkte im
Homelab. Detailentscheidungen bleiben in den verlinkten Runbooks; diese Datei
@@ -23,9 +23,14 @@ Host-/Entscheidungsaufgaben beim **Operator**.
| Thema | Owner | Naechster konkreter Schritt | Quelle |
|---|---|---|---|
| DR-Workstation Bare-Metal-Kit | Operator | Auf dem Gaming-PC `wsl --install -d Ubuntu`, dann `sudo apt install borgbackup`, dann Smoke `borg list ssh://u565255@u565255.your-storagebox.de/./hetzner_borg_appdata_critical` mit offline gesichertem Key + Passphrase | `docs/AUDIT_2026-05-25_TODO.md`, `docs/DR_WORKSTATION_SETUP.md`, `docs/EXTERNAL_DEPENDENCIES.md` |
| Family-Onboarding erster Termin | Operator | Checkliste ist fertig (`docs/FAMILY_ONBOARDING.md` Abschnitt "Erster Onboarding-Termin"). Operator legt fest, welche Personen/Geraete real verfuegbar sind, und arbeitet die Reihenfolge Vaultwarden -> Immich -> Mealie pro Person ab | `docs/FAMILY_ONBOARDING.md`, `docs/AUDIT_2026-05-25_TODO.md` |
| Home Assistant -> InfluxDB Token | Operator | `influxdb3_homeassistant_token` in InfluxDB 3 erzeugen, in HA `secrets.yaml` ablegen, einen Write-Pfad-Test fahren; nur Variablennamen/Pfade dokumentieren, kein Wert ins Repo | `docs/SECRETS_MAP.md`, `docs/HOME_ASSISTANT_INFLUXDB_ECOWITT.md` |
| Restore-Test Unraid OS Flash (Stick-Boot) | Operator | Artefakt-Validierung am 2026-06-05 erledigt (`ops/maintenance/check-unraid-flash-backup.sh`, sha256 OK, 8 Kern-Configs). **Verbleibt:** physischer Ersatzstick-Boot-Test, wenn ein Wegwerf-Stick bereitliegt | `docs/RESTORE_MATRIX.md` Abschnitt "Unraid OS Flash" |
| Restore-Test AdGuard Home | Operator | Runbook-Stub abarbeiten: Config nach `/mnt/user/backups/restore-lab/adguard` extrahieren, Testcontainer auf Port `5353`/`3001`, DNS-Smoke | `docs/RESTORE_MATRIX.md` Abschnitt "AdGuard Home" |
| Restore-Test Tailscale | Operator | Runbook-Stub abarbeiten: State-Validierung + Reconnect nur auf Wegwerf-Host/VM, danach Geraet in Tailscale-Admin entfernen | `docs/RESTORE_MATRIX.md` Abschnitt "Tailscale" |
| Restore-Test Redis 8 | Operator | Runbook-Stub abarbeiten: Pre-Cutover-Backup in isolierte Instanz auf Port `16379`, `PING` + `INFO server` (8.x) + `DBSIZE` pruefen | `docs/RESTORE_MATRIX.md` Abschnitt "Redis 8 (Shared)" |
| Manuelle App-/Lizenzchecks `baerchen` | Codex/Operator | Passwortmanager/2FA-Recovery-Codes, Banking4, WISO, Microsoft/M365/OneDrive im laufenden System bestaetigen | `ops/windows-reinstall/docs/windows-neuaufsetzen-masterplan.md` |
---
@@ -36,6 +41,7 @@ Host-/Entscheidungsaufgaben beim **Operator**.
| BitLocker-Entscheidung `baerchen` | C: (und ggf. D:) aktivieren oder bewusst deaktiviert lassen? Bei Ja: Recovery-Key vorher nach `D:\30_Finanzen\...`, Vaultwarden und physisch sichern. (Claude fasst BitLocker bewusst nicht an.) | `docs/SECRETS_MAP.md`, `ops/windows-reinstall/docs/laufwerks-neustruktur-2026-06-04.md` |
| Veeam Storage Encryption `baerchen` | Reicht der erste unverschluesselte Full-Lauf, oder soll Veeam Storage Encryption aktiviert werden? Bei Ja: Passwort in Vaultwarden anlegen, Job umstellen und neues Full-Backup erzeugen | `ops/windows-reinstall/docs/windows-image-backup-baseline.md`, `docs/SECRETS_MAP.md` |
| Stromverbrauch messen | Messgeraet/Smart-Plug beschaffen? Ohne Geraet bleiben Idle/Normal/Backup/Last bewusst offen. **Status 2026-06-05: kein Geraet vorhanden.** | `docs/HARDWARE_INVENTORY.md` Abschnitt "Stromverbrauch" |
| Tailscale ACL-Policy | **Entwurf abgestimmt 2026-06-05, noch nicht angewendet.** Richtung: Tag-basiert (`tag:server`/`tag:operator`/`tag:family`), Allow-all spaeter ersetzen; `baerchen-1`+`iphone-14` = operator, `kallilabcore` = server, Familie nur gezielte Dienste. Heute bewusst nur Sichtung, kein Tagging. **Offen vor Umsetzung:** (1) aktuellen ACL-JSON read-only sichten, (2) konkrete Familien-Dienste/Ports festlegen, (3) lockout-sichere Tagging-Reihenfolge + Smoke-Tests mit Operator durchfuehren | `docs/NETWORK_INVENTORY.md` Abschnitt "ACL-Policy — Entwurf und Rollout-Plan" |
| Nextcloud 2FA / Brute-Force-Haertung | Operator-TOTP (`twofactor_totp`) jetzt aktivieren? Familien-/OIDC-weite Policy separat | `docs/AUDIT_2026-05-25_TODO.md` |
| Authelia Rest-2FA | Weitere Admin-UIs (`monitoring`, `glances`, `glance`, `speedtest`, `pdf`, `mail`, `sp` ...) von `one_factor` auf `two_factor` heben oder bewusst belassen? | `docs/AUDIT_2026-05-25_TODO.md` |
| Authelia OIDC fuer Apps | App-uebergreifendes SSO einfuehren - haengt an Familien-/SSO-Grundsatzentscheidung | `docs/AUDIT_2026-05-25_TODO.md` |
@@ -52,12 +58,12 @@ Bewusst nicht jetzt - mit Review-Trigger.
| USV-Anschaffung | **Auf Q3/2026 geparkt** (2026-06-05). Power-Loss bleibt akzeptiertes Risiko. Trigger: Hardware-Upgrade, realer Stromausfall mit Datenfolge, oder Q3-Review ab 2026-07-01 | `docs/HARDWARE_INVENTORY.md` |
| Cold-Backup-Rotation | **Bewusst Hetzner-only** (2026-06-05). Keine zweite rotierende Cold-Kopie. Trigger: stark wachsender Datenwert, wiederholte Hetzner-Probleme, geaenderte Praeferenz | `docs/HARDWARE_INVENTORY.md` |
| WAN-Ausfallschutz | **Spaeter evaluieren** (2026-06-05). Mobilfunk-Failover inaktiv; lokale Apps laufen bei WAN-Ausfall weiter. Trigger: haeufigere/laengere DSL-Ausfaelle oder kritischer Remote-Zugang | `docs/NETWORK_INVENTORY.md` |
| Home Assistant InfluxDB Bind | Aktuell `127.0.0.1:8181`, validiert. Nur wenn HA nicht lokal auf den Host schreibt, bewusste Bind-Aenderung planen | `docs/NETWORK_INVENTORY.md` |
| Docker Critical Events Watcher | **Aktiviert 2026-06-05:** Unraid User Script `docker-critical-events-at-start` nutzt den Supervisor und steht in `schedule.json` auf `frequency: start`; Watcher manuell gestartet, Status `running`. Optionaler ntfy-Smoke wurde nachts bewusst nicht gesendet und kann spaeter mit `docker-critical-events-supervisor.sh smoke` nachgeholt werden | `docs/SERVICE_CATALOG.md`, `services/posture-check/docker-critical-events.sh`, `services/posture-check/unraid-user-scripts.md` |
| Negativ-Test Backup-Frische | Quartalsweise: bewusst kaputten/fehlenden Dump in Testpfad simulieren, pruefen ob `homelab-alerts` feuert | `docs/AUDIT_2026-05-25_TODO.md` |
| End-to-end-DR-Drill | Komplett-Bootstrap Phase 1-5 auf Wegwerf-Host; realistisch erst mit zweiter Hardware (siehe auch Extern blockiert) | `docs/AUDIT_2026-05-25_TODO.md`, `docs/DISASTER_RECOVERY.md` |
| Wiederkehrende Restore-Drills | Vaultwarden, Gitea, Authelia, Komodo, Paperless, Immich, Traefik, PostgreSQL, Mongo, Nextcloud, Mealie, Mail-Archiver nach Matrix-Intervallen rotieren | `docs/RESTORE_MATRIX.md`, `docs/RESTORE_HANDBOOK.md` |
| Dedizierter SMB-User `veeam-baerchen` | Optional spaeter, nur wenn Unraid-User-/Share-Rechte bewusst angefasst werden | `ops/windows-reinstall/docs/windows-image-backup-baseline.md` |
| Tailnet-Konsole aufraeumen (Rest) | Nach Docker-Stack-Abbau (2026-06-06) nur noch tote Node-Eintraege: `kallilab-core` (down) und alter Offline-`baerchen` in der Tailscale-Admin-Konsole entfernen. Optional State-Pfad `/mnt/user/appdata/tailscale` nach `_archive/`. Trivial, kein Risiko | `docs/NETWORK_INVENTORY.md` |
| CrowdSec vor Traefik | Bewusst nicht umgesetzt; einzige WAN-Tuer ist `443/tcp`, Authelia `regulation:` deckt Brute-Force ab. Neu bewerten bei breiterer Attack Surface | `docs/AUDIT_2026-05-25_TODO.md` |
| Hermes-Agent | NAS-Stack bleibt deaktiviert; Review-Deadline 2026-07-25 | `docs/AUDIT_2026-05-25_TODO.md`, `docs/SERVICE_CATALOG.md` |
| Filebrowser-Mounts | Bei zukuenftigem Hardening-Sprint Mount-Scope reduzieren | `docs/SERVICE_CATALOG.md` |
@@ -92,13 +98,7 @@ Wartet auf ein externes Ereignis oder eine Abhaengigkeit.
- `docs/FAMILY_ONBOARDING.md`: Michi-Checkliste in eine echte Erste-Termin-Checkliste (Vorbereitung, Reihenfolge, Erfolgskriterium, bewusst spaeter) umgebaut.
- `docs/MASTER_TODO.md` in vier Status-Kategorien (Aktiv / Operator-Entscheidung / Geparkt / Extern blockiert) umstrukturiert.
- `baerchen` Veeam-Erstbackup: erster Full-Lauf 2026-06-05 erfolgreich geschrieben (Veeam-GUI 53,8 GB, Dauer 0:11:31, MetaCheck 0 Fehler/0 Warnungen, VSS `job: success`). Beleg in `ops/windows-reinstall/docs/windows-image-backup-baseline.md`; Veeam Storage Encryption war im ersten Lauf nicht aktiv und ist als Operator-Entscheidung nachgezogen.
- Docker Critical Events Watcher auf Unraid aktiviert: Host-Clone auf Commit `2f3d184` aktualisiert, User Script `/boot/config/plugins/user.scripts/scripts/docker-critical-events-at-start/script` auf den Supervisor umgestellt, altes Script als `script.bak-20260605-232621` gesichert, `schedule.json` zeigt `frequency: start`, Watcher laeuft mit PID `1681168`. ntfy-Smoke am 2026-06-06 erfolgreich beim Operator angekommen.
- Restore-Test AdGuard Home: automatisierter Test `ops/restore-tests/adguard-restore-test.sh` erstellt und am 2026-06-06 auf Unraid erfolgreich ausgefuehrt. Ergebnis: Borg-Config-Restore aus Archiv `Taegliche-Sicherung-2026-06-06T04:30:05.910`, isolierter Container `restoretest-adguard`, HTTP `/control/status` = `401`, DNS-Smoke `git.kaleschke.info -> 192.168.178.58`, 7 Filterlisten-Eintraege, Report `/mnt/user/backups/restore-reports/adguard-2026-06-06.md`.
- Restore-Test Redis 8: automatisierter Test `ops/restore-tests/redis-restore-test.sh` erstellt und am 2026-06-06 auf Unraid erfolgreich ausgefuehrt. Ergebnis: Restore aus `/mnt/user/backups/borg/dumps/latest/shared-redis-pre-redis8-20260531-185011`, isolierter Container `restoretest-redis`, `PING` = `PONG`, Redis `8.8.0`, AOF `1`, `DBSIZE` = `1`, Report `/mnt/user/backups/restore-reports/redis-2026-06-06.md`.
- **Tailscale ACL-Policy restriktiv ausgerollt (2026-06-06):** Von Default-Allow auf Tag-basierte `grants`-Policy umgestellt, gemeinsam mit dem Operator in lockout-sicherer Reihenfolge (additiv -> taggen -> Allow-all entfernen), jeder Schritt read-only per SSH verifiziert. Live: `kallilabcore`=`tag:server`, `baerchen-1`+`iphone-14`=`tag:operator`, `tag:family` vorbereitet/schlafend. Subnet-Route `192.168.178.0/24` bleibt via `autoApprovers` approved. Smoke-Tests gruen (Operator-SSH, AdGuard-Admin `HTTP 302` ueber Tailnet, Ping 0%); untagged Nodes (`kallilab-core` Docker-Sidecar, alter `baerchen`) isoliert. Beleg: `docs/NETWORK_INVENTORY.md` Abschnitt "ACL-Policy — ANGEWENDET 2026-06-06". Familien-Dienste konkretisieren bei erstem realem Familiengeraet.
- **Redundanten Docker-Tailscale-Stack entfernt (2026-06-06):** Befund: Host hatte zwei `tailscaled` — die funktionale native Plugin-Instanz `kallilabcore` (echtes TUN `tailscale1`, Subnet-Router, State im Flash-Backup) und den redundanten userspace-only Docker-Stack `kallilab-core` (`host-services/tailscale/`, routet nichts, nichts haengt dran). Sauber per GitOps abgebaut: Operator hat Komodo-Stack `tailscale` gestoppt+destroyed; danach `git rm host-services/tailscale/`, Glance-Widget entfernt, Architektur-/Service-Catalog-/DR-Bootstrap-/CLAUDE-/Restore-Matrix-/Netzwerk-Doku auf "natives Plugin" nachgezogen. Read-only verifiziert: Container weg, nur noch der native `tailscaled`, Subnet-Route + Operator-Zugriff intakt. Rest: tote Node-Eintraege in der Admin-Konsole entfernen (eigener Todo).
- DR-Workstation Bare-Metal-Kit abgeschlossen: WSL2 Ubuntu 24.04 auf `baerchen`, Borg 1.2.8, GitHub-Read-DR-Key und Hetzner-DR-Key in WSL, `~/dr-smoke.sh` vorhanden. Finaler Smoke 2026-06-06 erfolgreich: GitHub HEAD `3a263a4...`, Hetzner Storage Box Repos sichtbar, Borg-Repo `hetzner_borg_appdata_critical` gelesen, Repository ID `5dd9b949...`, encrypted `Yes (repokey)`, `DR-Smoke OK (2026-06-06 10:05:30)`. Passphrase wurde nur interaktiv eingegeben und nicht gespeichert.
- Docker Critical Events Watcher auf Unraid aktiviert: Host-Clone auf Commit `2f3d184` aktualisiert, User Script `/boot/config/plugins/user.scripts/scripts/docker-critical-events-at-start/script` auf den Supervisor umgestellt, altes Script als `script.bak-20260605-232621` gesichert, `schedule.json` zeigt `frequency: start`, Watcher laeuft mit PID `1681168`. ntfy-Smoke bewusst nicht nachts gesendet.
---
+56 -90
View File
@@ -55,7 +55,7 @@ Gemessen am 2026-06-05 per read-only SSH auf den Host (`tailscale status`,
| Tailscale IPv6 | `fd7a:115c:a1e0::2c01:62b2` (gemessen 2026-06-05) |
| Exit Node | **Nein.** `Self.ExitNodeOption: false` und `Self.ExitNode: false` — Host bietet keinen Exit Node an und nutzt keinen. Entspricht dem Ziel (Operator-Zugang ist eingehend, nicht als Internet-Ausgang). |
| Subnet Router | **Ja, aktiv.** Host advertised und ist Primary fuer `192.168.178.0/24` (`Self.PrimaryRoutes: ["192.168.178.0/24"]`, ebenfalls in `AllowedIPs`). Das LAN ist also fuer das gesamte Tailnet ueber diesen Subnet-Router erreichbar — bewusst gemessener Ist-Zustand, **kein** "keine Route" wie zuvor vermutet. |
| ACL-Policy extern dokumentiert | **Angewendet 2026-06-06** — restriktive Tag-basierte `grants`-Policy live (`tag:server`/`tag:operator`, `tag:family` schlafend). Default-Allow entfernt, verifiziert. Details im Block unten. |
| ACL-Policy extern dokumentiert | **Operator-Entscheidung offen** — siehe eigener Block unten; durch den aktiven Subnet-Router ist die ACL-Frage sicherheitsrelevanter als zuvor angenommen. |
### Tailnet-Geraete (Snapshot 2026-06-05)
@@ -65,37 +65,13 @@ Gemessen am 2026-06-05 per read-only SSH auf den Host (`tailscale status`,
| `100.78.133.37` | baerchen-1 | windows | aktiv (aktuelle Operator-Workstation, direct) |
| `100.105.203.21` | baerchen | windows | offline, zuletzt vor ~1 Tag gesehen (Alt-Node) |
| `100.73.83.55` | iphone-14 | iOS | bekannt |
| `100.112.0.90` | kallilab-core | linux | **am 2026-06-06 entfernt.** War der redundante userspace-only `Tailscale-Docker`-Stack (`host-services/tailscale/`). Komodo-Stack gestoppt+destroyed, Repo-Pfad per `git rm` entfernt, Container weg (read-only verifiziert). Node-Eintrag in der Admin-Konsole noch zu entfernen. |
| `100.112.0.90` | kallilab-core | linux | gelistet, kein aktiver Verkehr — **moeglicher Alt-/Dubletten-Node**, separat pruefen |
> **Befund 2026-06-06 (read-only auf dem Host ermittelt):** Der Host hat **zwei**
> `tailscaled`-Prozesse:
>
> 1. **Native Unraid-Plugin** = `kallilabcore` (100.80.98.33). Prozess
> `/usr/local/sbin/tailscaled -statedir /boot/config/plugins/tailscale/state
> -tun tailscale1`. **Echtes TUN-Interface `tailscale1`, ist der Subnet-Router
> fuer `192.168.178.0/24`**, laeuft seit 24. Mai, installiert via
> `tailscale.plg` + `unraid-tailscale-utils`. State unter
> `/boot/config/plugins/tailscale/state` → ueber das **Flash-Backup** gesichert.
> Im ACL-Rollout `tag:server`. **Das ist die funktionale, kanonische Instanz.**
> 2. **Docker-Stack** = `kallilab-core` (100.112.0.90), `host-services/tailscale/`.
> Prozess `tailscaled --tun=userspace-networking` → **nur Userspace, kann
> technisch nicht routen / kein Subnet-Router/Exit-Node sein**, advertised
> nichts, kein Container teilt seinen Namespace, seit 31. Mai. State unter
> `/mnt/user/appdata/tailscale`. Im ACL-Rollout untagged → isoliert.
> **Hochwahrscheinlich redundant.**
>
> **Umgesetzt 2026-06-06:** Der redundante Docker-Stack `host-services/tailscale/`
> wurde sauber per GitOps abgebaut — Komodo-Stack `tailscale` gestoppt+destroyed
> (Operator), `git rm host-services/tailscale/`, Glance-Widget entfernt, und
> Architektur-/Service-Catalog-/DR-/CLAUDE-Doku auf "natives Plugin" nachgezogen.
> Read-only verifiziert: Container weg, nur noch der native `tailscaled` mit
> `tailscale1`, Subnet-Route + Operator-Zugriff intakt. Offen: Node-Eintraege
> `kallilab-core` und alter `baerchen` in der Admin-Konsole entfernen; State-Pfad
> `/mnt/user/appdata/tailscale` bei Gelegenheit nach `_archive/` (kein Sofort-Loeschen).
>
> **Doku-Korrektur erledigt:** `docs/RESTORE_MATRIX.md` zeigt jetzt auf den
> funktionalen State `/boot/config/plugins/tailscale/state` (im Flash-Backup)
> statt auf den entfernten userspace-Docker-Pfad.
> Hygiene-Hinweis (kein Secret): Es existieren zwei linux-Nodes mit aehnlichem
> Namen (`kallilabcore` und `kallilab-core`) sowie zwei `baerchen`-Nodes
> (`baerchen-1` aktiv, `baerchen` offline). Bei Gelegenheit in der
> Tailscale-Admin-Konsole pruefen, ob die inaktiven Eintraege stillgelegt werden
> koennen. Das ist Aufraeumarbeit, kein akutes Risiko.
### Subnet-Router-Konsequenz
@@ -115,22 +91,34 @@ tailscale ip -4
tailscale ip -6
```
### ACL-Policy — ANGEWENDET 2026-06-06 (restriktive Tag-basierte grants)
### ACL-Policy — Entwurf und Rollout-Plan (Stand 2026-06-05, NICHT angewendet)
**Status: live und verifiziert.** Die restriktive Policy wurde am 2026-06-06
gemeinsam mit dem Operator in der lockout-sicheren Reihenfolge ausgerollt und
read-only verifiziert (siehe "Rollout-Protokoll" unten). Ausgangspunkt war die
**unveraenderte Default-Policy** im **`grants`-Schema** (eine Allow-all-Regel,
keine Groups/Tags/`autoApprovers`); es gab also keinen eigenen Bestand zu
erhalten.
Die Tailnet-ACL wird in der Tailscale-Admin-Konsole unter `Access controls`
verwaltet (kein Wert/Secret gehoert ins Repo). Aktueller Live-Stand ist
Default-Allow (`src: ["*"] -> dst: ["*:*"]`), d. h. jedes Tailnet-Geraet darf
alles inklusive der LAN-Subnet-Route.
> **Schema-Hinweis:** Dieses Tailnet nutzt das `grants`-Modell
> (`{"src","dst","ip"}`), nicht das aeltere `acls`/`action:accept`-Modell.
> Normaler SSH-Zugriff (`ssh kallilabcore` ueber OpenSSH Port 22) wird ueber
> `grants` geregelt, nicht ueber den `ssh`-Block; letzterer betrifft nur die
> Tailscale-SSH-Funktion.
**Abgestimmte Richtung (Operator-Entscheidungen 2026-06-05):**
**Angewendete Policy (live, kein Secret):**
- Ziel ist eine restriktivere, Tag-basierte ACL.
- Single-User-Realitaet: aktuell gehoeren alle Nodes demselben User
`michaelkaleschke@`. Eine Differenzierung Operator/Familie ist nur ueber
**Tags** moeglich. Tagging aendert Ownership/Key-Expiry und erfordert je Geraet
Re-Auth — deshalb bewusst ein eigener, spaeterer Schritt.
- **Heute bewusst nur Sichtung + Entwurf, kein Tagging, keine Anwendung.**
- Familiengeraete brauchen Tailnet-Zugriff auf **bestimmte** Dienste (welche
genau, ist noch zu konkretisieren) — `tag:family` bekommt gezielte `dst`-Regeln.
- `iphone-14` ist ein Operator-Geraet und faellt unter `tag:operator`.
**Geraete -> Tag (fuer den spaeteren Tagging-Schritt):**
| Tag | Geraete |
|---|---|
| `tag:server` | `kallilabcore` (Host, Subnet-Router) |
| `tag:operator` | `baerchen-1`, `iphone-14` |
| `tag:family` | kuenftige Familiengeraete |
**Entwurf (Vorschlag, noch nicht in der Konsole gespeichert):**
```json
{
@@ -142,64 +130,42 @@ erhalten.
"autoApprovers": {
"routes": { "192.168.178.0/24": ["tag:server"] }
},
"grants": [
{"src": ["tag:operator"], "dst": ["*"], "ip": ["*"]},
{"src": ["tag:server"], "dst": ["tag:operator"], "ip": ["*"]},
{"src": ["tag:family"], "dst": ["tag:server"], "ip": ["tcp:443"]}
"acls": [
{ "action": "accept", "src": ["tag:operator"], "dst": ["*:*"] },
{ "action": "accept", "src": ["tag:server"], "dst": ["tag:operator:*"] },
{ "action": "accept", "src": ["tag:family"], "dst": ["100.80.98.33:443"] }
],
"ssh": [
{"action": "check", "src": ["autogroup:member"], "dst": ["autogroup:self"],
"users": ["autogroup:nonroot", "root"]}
{ "action": "accept", "src": ["tag:operator"], "dst": ["tag:server"],
"users": ["root", "autogroup:nonroot"] }
]
}
```
**Geraete-Tags (live):** `kallilabcore` = `tag:server`; `baerchen-1` + `iphone-14`
= `tag:operator`; `kallilab-core` (Docker) + alter `baerchen` bewusst untagged ->
isoliert.
> Die `tag:family`-`dst` `100.80.98.33:443` ist ein **Platzhalter** und wird
> durch die real benoetigten Familien-Dienste ersetzt, sobald diese feststehen.
**Rollout-Protokoll 2026-06-06 (lockout-sicher, je Schritt read-only verifiziert):**
**Lockout-sichere Reihenfolge (wenn die Umsetzung freigegeben wird):**
1. Policy additiv erweitert (Tags/grants definiert, Allow-all noch drin) -> alle Peers unveraendert verbunden, Route approved.
2. `baerchen-1` getaggt `tag:operator` -> online, verifiziert.
3. `iphone-14` getaggt `tag:operator` -> verifiziert.
4. `kallilab-core` faktisch geprueft (Docker-Sidecar, keine Abhaengigen) -> bewusst untagged gelassen.
5. Host `kallilabcore` getaggt `tag:server` -> Route blieb via `autoApprovers` automatisch approved, SSH ok.
6. Allow-all entfernt -> restriktiv. Smoke-Tests gruen: Operator-SSH ok, AdGuard-Admin ueber Tailnet `HTTP 302`, Ping 0% Verlust, Route weiter approved; Host sieht nur noch die zwei Operator-Peers (untagged Nodes isoliert). LAN-Rueckweg durchgehend verfuegbar.
1. `tagOwners` + `autoApprovers` + neue ACL-Regeln speichern, **Allow-all-Regel
zunaechst behalten**.
2. Geraete taggen (`baerchen-1`, `iphone-14` -> operator; `kallilabcore` ->
server) und je Geraet die Verbindung verifizieren.
3. Subnet-Route bleibt approved (jetzt via `autoApprovers`/`tag:server`).
4. **Erst zuletzt** die Allow-all-Regel entfernen -> restriktiv schalten.
5. Sofort Smoke-Tests fahren (siehe unten).
**Schema-/Erhaltungs-Hinweis fuer spaeter:** Die LAN-Subnet-Route
`192.168.178.0/24` wird jetzt ueber `autoApprovers`/`tag:server` approved
(vorher manuell). Es gibt keinen eigenen Bestand zu erhalten; die Policy oben
ist die vollstaendige Wahrheit.
**Smoke-Tests nach Anwendung:**
**Hintergrund / Designentscheidungen (2026-06-05/06):**
- `baerchen-1` erreicht Host: `ping 100.80.98.33`, `ssh kallilabcore hostname` (read-only).
- `baerchen-1` erreicht LAN ueber Route: z. B. AdGuard-Admin `http://100.80.98.33:8082`, `curl -kI https://192.168.178.58`.
- Falls testbar: ein nicht-Operator-Geraet erreicht Route/Admin-Ports **nicht** mehr.
- Host-Gegencheck: `tailscale status` zeigt alle Soll-Peers weiter verbunden.
- Single-User-Realitaet: alle Nodes gehoeren demselben User `michaelkaleschke@`.
Eine Differenzierung Operator/Familie ist nur ueber **Tags** moeglich, deshalb
der Tag-Ansatz statt user-/gruppenbasiert.
- Erster Rollout bewusst klein: nur `tag:server` + `tag:operator`.
- **`tag:family` ist vorbereitet, aber schlafend:** Tag und eine konservative
Minimal-Regel (`dst: tag:server`, `ip: tcp:443`) sind definiert, aber **kein
Geraet traegt den Tag**, daher null Wirkung. Sobald ein echtes Familiengeraet
dazukommt, wird es einmal mit `tag:family` getaggt und die Regel greift sofort
— ohne Policy-Umbau. Vor dem ersten realen Familiengeraet die Regel auf die
dann benoetigten Dienste/Ports pruefen.
- Der `ssh`-Block bleibt der Default (Tailscale-SSH Check-Modus); normaler
OpenSSH-Zugriff laeuft ueber die `grants` (Port 22, fuer `tag:operator` ueber
`ip: ["*"]` abgedeckt).
**Noch offene Eingaben vor Finalisierung:**
**Offene Folgepunkte (kein Risiko, Hygiene/spaeter):**
- Familien-Dienste/Ports konkretisieren — erst wenn ein reales Familiengeraet dazukommt.
- **Zwei-Tailscale-Konsolidierung: ERLEDIGT 2026-06-06** — redundanter Docker-Stack
abgebaut, nur noch die native Plugin-Instanz `kallilabcore` (Subnet-Router) aktiv.
- Tailnet-Konsole aufraeumen: Node-Eintraege `kallilab-core` (jetzt down) und
alter Offline-`baerchen` entfernen (trivial, nur tote Geraeteeintraege).
- State-Pfad `/mnt/user/appdata/tailscale` (vom entfernten Docker-Stack) bei
Gelegenheit nach `_archive/tailscale-removed-2026-06-06/` (kein Sofort-Loeschen).
- Optionaler Off-LAN-Routentest: von einem Operator-Geraet im Mobilfunk
(nicht im Heim-LAN) ein LAN-Ziel ueber `192.168.178.0/24` erreichen, um die
Subnet-Route end-to-end zu bestaetigen (im Heim-LAN nicht sauber isolierbar).
1. Aktueller ACL-JSON aus der Admin-Konsole (read-only gesichtet, nur Struktur dokumentiert).
2. Konkrete Liste der Dienste/Ports, die Familiengeraete ueber Tailscale brauchen.
## Portfreigaben und Exposure
+1 -1
View File
@@ -43,7 +43,7 @@ Diese Datei trennt aktive Betriebsdokumentation von historischer Arbeitsdoku. Ne
|---|---|
| `ALERT_RULES.md` | Prometheus-/ntfy-Regeln und Handlungslogik |
| `RENOVATE.md` | Self-hosted Renovate gegen Gitea |
| `HOME_ASSISTANT_INFLUXDB_ECOWITT.md` | Archivierter Entwurf: Home Assistant -> InfluxDB 3 -> Grafana; nicht aktiv seit Crash |
| `HOME_ASSISTANT_INFLUXDB_ECOWITT.md` | Home Assistant -> InfluxDB 3 -> Grafana |
| `H_DRIVE_NEARLINE_PULL.md` | Windows-H:/ Nearline-Pull fuer kritische Restore-Artefakte |
## Nutzer- und Planungsdoku
+17 -44
View File
@@ -28,10 +28,10 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`.
|---|---|---|---|---|---|---|
| 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 |
| AdGuard Home | Share / Borg | `/mnt/user/appdata/adguard/conf` | keine | keine zusaetzlichen Repo-Secrets dokumentiert | `dns_net`, `frontend_net` | DNS-Aufloesung funktioniert; Restore-Smoke am 2026-06-06 erfolgreich |
| Tailscale | Flash-Backup (funktional) / Share | **Funktional: `/boot/config/plugins/tailscale/state`** (native Unraid-Plugin-Instanz `kallilabcore`, Subnet-Router, im Flash-Backup gesichert). Der frueher hier genannte Pfad `/mnt/user/appdata/tailscale` gehoert zum **userspace-only Docker-Stack** `kallilab-core` (redundant, Abbau geplant — siehe `docs/NETWORK_INVENTORY.md`) | keine | Tailscale-State im jeweiligen State-Pfad | Host-Netz | Tailscale verbunden, Subnet-Route `192.168.178.0/24` aktiv |
| 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 |
| PostgreSQL 18 | Share + Dumps | `/mnt/user/appdata/postgresql18` (archivierter Rollback-Altstand: `/mnt/user/appdata/_archive/pg18-immich-rollback-volumes-20260602/postgresql17`) | `postgresql17-globals.sql`, `postgresql17-mailarchiver.dump`, `postgresql17-paperless.dump`, optional `postgresql17-authelia.dump` | `postgres_password.txt`, App-Rollen-Passwoerter aus den jeweiligen Stack-ENV/Secret-Dateien | `backend_net` | DB startet, Ziel-Datenbanken vorhanden; `SHOW data_checksums` ist `on` |
| Redis 8 | Share / Host | `/mnt/user/appdata/redis`; Rollback-Backup unter `/mnt/user/backups/borg/dumps/latest/shared-redis-pre-redis8-<ts>` | RDB/AOF-Dateien im Datenpfad | `redis_password.txt` | `backend_net` | Redis startet, `redis_version` ist 8.x, Apps verbinden sich; Restore-Smoke am 2026-06-06 erfolgreich |
| Redis 8 | Share / Host | `/mnt/user/appdata/redis`; Rollback-Backup unter `/mnt/user/backups/borg/dumps/latest/shared-redis-pre-redis8-<ts>` | RDB/AOF-Dateien im Datenpfad | `redis_password.txt` | `backend_net` | Redis startet, `redis_version` ist 8.x, Apps verbinden sich |
| Authelia | Borg | `/mnt/user/appdata/authelia/config`, `/mnt/user/appdata/secrets/*authelia*` | Shared PostgreSQL 18, optional Dump `postgresql17-authelia.dump` | JWT/Session/Storage/Postgres-/SMTP-Secret-Dateien | PostgreSQL 18, Traefik, GMX SMTP | Login-Seite und ForwardAuth funktionieren; SMTP-Notifier startet; aktive Sessions werden nach Restart neu aufgebaut; Restore-Smoke am 2026-06-03 erfolgreich: Config aus Borg, minimale Test-Config, frisches Test-Postgres, HTTP `/api/health` 200, Report `/mnt/user/backups/restore-reports/authelia-2026-06-03.md` |
| Gitea | GitHub-Mirror + Gitea-Bundles fuer Repo-Bootstrap, Borg + Dump fuer Gitea-Appstate | `/mnt/user/services/gitea/data`, `/mnt/user/backups/git-bundles/gitea` | `gitea.sqlite.dump`, Bundle-Report `latest-report.md` | `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; Bundle laesst sich klonen und `git fsck` ist sauber; 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`, `/mnt/user/services/stacks` | `komodo-mongo.archive.gz` falls verifiziert | `komodo_mongo_password.txt`, `KOMODO_*` Stack ENV | Traefik, Mongo, Gitea | UI erreichbar, Periphery verbunden |
@@ -135,7 +135,7 @@ Die Dump-Erzeugung ist host-seitig ueber `ops/borg-ui/scripts/pre-backup-dumps.s
## Restore-Test-Reifegrad
Stand 2026-06-06. Pro Dienst auf einen Blick: Wurde der Restore schon einmal real getestet?
Stand 2026-06-05. Pro Dienst auf einen Blick: Wurde der Restore schon einmal real getestet?
| Dienst | Tier | Letzter Restore-Test | Typ | Naechster Lauf |
|---|---|---|---|---|
@@ -147,10 +147,10 @@ Stand 2026-06-06. Pro Dienst auf einen Blick: Wurde der Restore schon einmal rea
| Immich | 2 | 2026-05-27 | Dump + Container + HTTP + Asset-Count | quartalsweise (2. So Feb/Mai/Aug/Nov) |
| Unraid OS Flash | 1 | 2026-06-05 (Artefakt-Validierung) | sha256 OK + 390 Eintraege + 8 Kern-Configs vorhanden (`ops/maintenance/check-unraid-flash-backup.sh`); **physischer Ersatzstick-Boot-Test weiter offen** | Stick-Boot-Test nach Bedarf |
| Traefik | 1 | 2026-06-03 | Config + LE-State + File-Provider + Ping 200 | quartalsweise |
| AdGuard Home | 1 | 2026-06-06 | Config + Container + HTTP 401 + DNS + Filter-Count | quartalsweise oder nach DNS-Aenderungen |
| AdGuard Home | 1 | - | noch kein Test | - |
| Tailscale | 1 | - | noch kein Test | - |
| PostgreSQL 18 Cluster | 1 | 2026-06-03 | globals + 5 per-DB dumps, 290 Tabellen gesamt | quartalsweise |
| Redis 8 | 1 | 2026-06-06 | Pre-Cutover-Artefakt + Container + PING + INFO + DBSIZE | quartalsweise oder vor/nach Redis-Major-Aenderungen |
| Redis 8 | 1 | - | noch kein Test | - |
| Komodo Mongo Daten | 1 | 2026-06-03 | mongorestore --archive --gzip, 86904 docs | quartalsweise |
| Nextcloud | 2 | 2026-06-03 | File + Dump + Container + HTTP 200 + occ status + Table-Count (126) | quartalsweise |
| Mealie | 2 | 2026-06-03 | File + Dump + Container + HTTP + Recipe-Count (3) | quartalsweise |
@@ -165,13 +165,15 @@ Stand 2026-06-06. Pro Dienst auf einen Blick: Wurde der Restore schon einmal rea
## Naechste Restore-Test-Kandidaten (priorisiert)
Stand 2026-06-06. Die frueheren Kandidaten (Shared PG18, Komodo Mongo, Mailarchiver, Mealie, Traefik)
Stand 2026-06-05. Die frueheren Kandidaten (Shared PG18, Komodo Mongo, Mailarchiver, Mealie, Traefik)
wurden alle am 2026-06-03 abgeschlossen und sind in der Reifegrad-Tabelle belegt.
Verbleibende offene Restore-Pfade ohne vollstaendigen Test:
1. **Unraid OS Flash** - Artefakt-Validierung am 2026-06-05 erfolgreich (siehe Reifegrad-Tabelle und Runbook unten); offen bleibt nur der **physische Ersatzstick-Boot-Test**.
2. **Tailscale** - State-/Reconnect-Pfad dokumentiert testen
2. **AdGuard Home** - Config-Restore in Testpfad oder Wegwerf-Instanz pruefen
3. **Tailscale** - State-/Reconnect-Pfad dokumentiert testen
4. **Redis 8 (Shared)** - Restore aus Datenpfad oder Pre-Cutover-Backup in isolierter Testinstanz pruefen
---
@@ -223,42 +225,28 @@ nur Eintragsnamen gelistet. Offen bleibt der physische Ersatzstick-Boot-Test.
### AdGuard Home
**Validierungsergebnis 2026-06-06:** Automatisierter Test
`ops/restore-tests/adguard-restore-test.sh` auf Unraid erfolgreich ausgefuehrt.
Report: `/mnt/user/backups/restore-reports/adguard-2026-06-06.md`.
Getestet wurden Borg-Extract der Config, `AdGuardHome.yaml`-Struktur,
isolierter Testcontainer `restoretest-adguard` auf localhost-Ports,
HTTP `/control/status` = `401`, DNS-Smoke `git.kaleschke.info -> 192.168.178.58`,
7 Filterlisten-Eintraege. Testdaten wurden nach Erfolg bereinigt.
**Voraussetzungen:**
- Borg-Archiv mit `/mnt/user/appdata/adguard/conf` zugaenglich (produktives Repo oder Teststand)
- Testpfad unter `/mnt/user/backups/restore-lab/adguard` vorbereitet
- Docker-Faehigkeit auf dem Testhost oder in der Restore-Lab-Umgebung
**Automatisierter Test:**
```bash
/mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh adguard
```
**Manuelle Checkliste:**
**Checkliste:**
1. Borg-Extract des letzten Archivs nach `/mnt/user/backups/restore-lab/adguard/conf`:
```
borg extract ::ARCHIV /mnt/user/appdata/adguard/conf
```
2. Konfigurationsdatei `AdGuardHome.yaml` auf Vollstaendigkeit pruefen (YAML-Syntax valide)
3. Testcontainer starten (kein produktiver DNS-Port 53, stattdessen z. B. `15353`):
3. Testcontainer starten (kein produktiver DNS-Port 53, stattdessen z. B. `5353`):
```yaml
ports:
- "127.0.0.1:15353:53/udp"
- "127.0.0.1:13001:80/tcp"
- "5353:53/udp"
- "3001:3000/tcp"
volumes:
- /mnt/user/backups/restore-lab/adguard/conf:/opt/adguardhome/conf
```
4. `http://127.0.0.1:13001/control/status` erreichbar (`200`, `401` oder `403` sind fuer den Smoke ausreichend)
5. DNS-Aufloesung: `dig @127.0.0.1 -p 15353 git.kaleschke.info` gibt plausible Antwort
4. `http://localhost:3001` erreichbar, Login moeglich
5. DNS-Aufloesung: `dig @127.0.0.1 -p 5353 git.kaleschke.info` gibt plausible Antwort
6. Testcontainer stoppen und Testpfad aufraeumen
**Smoke-Test-Kriterium:** AdGuard-Web-UI laeuft, DNS-Aufloesung antwortet, Filterlisten sind geladen.
@@ -296,27 +284,12 @@ HTTP `/control/status` = `401`, DNS-Smoke `git.kaleschke.info -> 192.168.178.58`
### Redis 8 (Shared)
**Validierungsergebnis 2026-06-06:** Automatisierter Test
`ops/restore-tests/redis-restore-test.sh` auf Unraid erfolgreich ausgefuehrt.
Report: `/mnt/user/backups/restore-reports/redis-2026-06-06.md`.
Getestet wurde das Pre-Cutover-Artefakt
`/mnt/user/backups/borg/dumps/latest/shared-redis-pre-redis8-20260531-185011`
in einer isolierten Redis-8.8-Testinstanz auf `127.0.0.1:16379`.
Ergebnis: `PING` = `PONG`, `redis_version` = `8.8.0`, AOF aktiv (`1`),
`DBSIZE` = `1`. Produktiver Port und produktiver Datenpfad wurden nicht genutzt.
**Voraussetzungen:**
- Pre-Cutover-Backup unter `/mnt/user/backups/borg/dumps/latest/shared-redis-pre-redis8-<ts>` vorhanden, oder Borg-Archiv mit `/mnt/user/appdata/redis`
- Secret-Datei `redis_password.txt` fuer Testinstanz verfuegbar (aus Borg, nicht als Wert dokumentieren)
- Testpfad unter `/mnt/user/backups/restore-lab/redis` vorbereitet
**Automatisierter Test:**
```bash
/mnt/user/services/homelab-infra/ops/restore-tests/run-restore-checks.sh redis
```
**Manuelle Checkliste:**
**Checkliste:**
1. RDB/AOF-Datei aus dem Backup in den Testpfad kopieren:
```
+1
View File
@@ -53,6 +53,7 @@ Dieses Dokument listet sensible Daten, deren Ablageorte und die vorgesehene Einb
| InfluxDB 3 Core | Admin Token JSON | `/mnt/user/appdata/secrets/influxdb3_admin_token.json` -> Docker Secret `/run/secrets/influxdb3_admin_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 -> 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 |
| Renovate Bot | Gitea Service-Account PAT | `/mnt/user/appdata/secrets/renovate_token.txt` -> Host-Datei (chmod 600), gelesen von `ops/renovate/run-renovate.sh` und an Renovate-Container als `RENOVATE_TOKEN` weitergegeben | aktiv nach Operator-Setup (siehe `docs/RENOVATE.md`) |
| n8n | Encryption Key fuer interne Credential-Verschluesselung | `/mnt/user/appdata/secrets/n8n_encryption_key.txt` (chmod 600) -> Komodo Stack ENV `${N8N_ENCRYPTION_KEY}`; kein `_FILE`-Support im Upstream-Image | aktiv |
| n8n | GMX IMAP Login (Mail-Trigger Workflow) | n8n Credentials Store (Typ `imap`), nur in `/mnt/user/appdata/n8n/data` mit `N8N_ENCRYPTION_KEY` verschluesselt | aktiv |
+1 -1
View File
@@ -13,7 +13,7 @@ Secret-Werte sind nicht enthalten. Es werden nur Secret-Namen, Env-Key-Namen und
| `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 `100.80.98.33:8082` | `dns_net`, `frontend_net`, Unbound | `/mnt/user/appdata/adguard/conf`, `/mnt/user/appdata/adguard/work` | Tier 1, config relevant | nein | Direkter DNS-Port 53 bleibt; Admin-Port ist bewusst ohne Traefik/2FA, aber auf Tailscale-IP begrenzt (Operator-Entscheidung 2026-05-26) |
| `unbound` | Upstream DNS Resolver fuer AdGuard | `apps/unbound/docker-compose.yml` | intern | `dns_net` | `/mnt/user/appdata/unbound/config` | rebuildbar / config relevant | nein | intern isoliert |
| `tailscale` | VPN/Remote-Zugang, Subnet-Router | **Natives Unraid-Plugin** `tailscale.plg` (nicht repo-/Komodo-verwaltet) | Tailscale | Host-Netz (`tailscale1`) | `/boot/config/plugins/tailscale/state` (im Flash-Backup) | Tier 1, State relevant | nein | Subnet-Router `192.168.178.0/24`; redundanter Docker-Stack `host-services/tailscale/` am 2026-06-06 entfernt |
| `tailscale` | VPN/Remote-Zugang | `host-services/tailscale/docker-compose.yml` | Tailscale | Host-Netz | `/mnt/user/appdata/tailscale` | Tier 1, State relevant | nein | `network_mode: host`, `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` sind dokumentierte VPN-Ausnahmen |
| `gitea` | Git-Server / origin fuer GitOps | `core/gitea/docker-compose.yml` | `https://git.kaleschke.info`, SSH `222` | Traefik, `frontend_net`, externe DNS-Resolver fuer GitHub-Push-Mirror | `/mnt/user/services/gitea/data` | Tier 1, `gitea.sqlite.dump` + Share; privater GitHub-Push-Mirror fuer Repo-Bootstrap | ja | SSH-Port 222 direkte Host-Port-Ausnahme; Push-Mirror nach `michaelkaleschke-spec/homelab-infra` reduziert das DR-Bootstrap-Risiko |
## Security / Identity
@@ -1,137 +0,0 @@
# DR-Workstation Readiness - 2026-06-06
Automatisch erzeugter lokaler Readiness-Check fuer den Operator-PC. Es wurden keine Secret-Werte, Passphrases oder Private-Key-Inhalte ausgegeben.
## Zusammenfassung
| Check | Ergebnis |
|---|---|
| WSL2 Ubuntu | vorhanden (`Ubuntu 24.04`, WSL Version 2) |
| SSH/Git in WSL | vorhanden |
| GitHub-Read-Smoke mit DR-Key | ok |
| Borg Client | installiert |
| Hetzner Storage Box mit DR-Key | ok |
| `~/dr-smoke.sh` | vorhanden |
| Finaler Borg-Smoke | ok, Operator-Bestaetigung 2026-06-06 10:05:30 |
| WSL sudo ohne Passwortprompt | nein, Operator muss Passwort eingeben |
## Bewertung
- Der lokale WSL2-/Ubuntu-Unterbau ist vorhanden.
- Die DR-Key-Arbeitskopien liegen in WSL unter `~/.ssh/dr-readonly` und `~/.ssh/dr-hetzner`.
- GitHub-Read-Smoke und Hetzner-SSH-Smoke sind erfolgreich.
- `borgbackup` ist installiert.
- Der vollstaendige Bare-Metal-DR-Smoke ist erfolgreich abgeschlossen.
## Finaler Borg-Smoke
Operator-Bestaetigung vom 2026-06-06:
- Befehl: `bash ~/dr-smoke.sh`
- GitHub Deploy-Key: HEAD `3a263a4...`
- Hetzner SSH-Login: Repos `backup`, `backup2`, `hetzner_borg_appdata`, `hetzner_borg_appdata_critical` sichtbar
- Borg-Repo: `ssh://u565255@u565255.your-storagebox.de/./hetzner_borg_appdata_critical`
- Repository ID: `5dd9b949...`
- Encryption: `Yes (repokey)`
- Borg-Statistik: `Original size 1.16 TB`, `Compressed size 1.13 TB`, `Deduplicated size 35.92 GB`
- Ergebnis: `DR-Smoke OK (2026-06-06 10:05:30)`
Die Borg-Passphrase wurde nur interaktiv eingegeben und nicht dauerhaft auf `baerchen` gespeichert.
## Rohchecks
### wsl_status
- ExitCode: `0`
```text
Standarddistribution: Ubuntu
Standardversion: 2
```
### wsl_list
- ExitCode: `0`
```text
NAME STATE VERSION
* Ubuntu Stopped 2
docker-desktop Stopped 2
```
### ubuntu_os
- ExitCode: `0`
```text
Distributor ID: Ubuntu
Description: Ubuntu 24.04.4 LTS
Release: 24.04
Codename: noble
6.6.114.1-microsoft-standard-WSL2
```
### tools
- ExitCode: `0`
```text
/usr/bin/borg
borg 1.2.8
/usr/bin/ssh
OpenSSH_9.6p1 Ubuntu-3ubuntu13.16, OpenSSL 3.0.13 30 Jan 2024
/usr/bin/git
git version 2.43.0
```
### sudo
- ExitCode: `0`
```text
sudo-password-needed
```
### wsl_ssh_files
- ExitCode: `0`
```text
total 40
drwx------ 2 michi michi 4096 Jun 6 09:14 .
drwxr-x--- 5 michi michi 4096 Jun 6 08:37 ..
-rw------- 1 michi michi 411 Jun 6 09:14 dr-hetzner
-rw------- 1 michi michi 419 Jun 6 09:14 dr-readonly
-rw------- 1 michi michi 411 Apr 4 19:29 id_ed25519
-rw-r--r-- 1 michi michi 97 Apr 4 19:29 id_ed25519.pub
-rw------- 1 michi michi 6358 Jun 6 09:14 known_hosts
-rw------- 1 michi michi 3013 Apr 20 20:13 known_hosts.old
-rw------- 1 michi michi 3858 Apr 24 08:27 known_hosts.pre-port222-20260604-122031.bak
-rwxr-xr-x 1 michi michi 1311 Jun 6 08:37 /home/michi/dr-smoke.sh
```
### github_dr_key_smoke
- ExitCode: `0`
```text
68d3ace598ee4d1cdad3ed94b63ae5046ac187fb HEAD
```
### hetzner_dr_key_smoke
- ExitCode: `0`
```text
backup
backup2
hetzner_borg_appdata
hetzner_borg_appdata_critical
```
@@ -0,0 +1,25 @@
services:
tailscale:
image: tailscale/tailscale:stable@sha256:25cde9ad76020b0e29229136d0c38b5962e9a0e1774ffac9b0df68e4a37d6cf0
container_name: Tailscale-Docker
restart: unless-stopped
network_mode: host
cap_add:
- NET_ADMIN
- NET_RAW
security_opt:
- no-new-privileges:true
devices:
- /dev/net/tun:/dev/net/tun
environment:
- TZ=Europe/Berlin
- TS_HOSTNAME=kallilab-core
- TS_STATE_DIR=/state
- TS_AUTH_ONCE=true
volumes:
- /mnt/user/appdata/tailscale:/state
+1 -1
View File
@@ -1,6 +1,6 @@
services:
filebrowser:
image: filebrowser/filebrowser:v2.63.13@sha256:e79c381fdbf549a48adc6268c74b920b70cab53663995a2e8142964eedea10c7
image: filebrowser/filebrowser:v2.63.12@sha256:fc8c3a46c16bbdf97362b20c50164e97de9c1dd5f63230d28a4cb15248b53ec3
container_name: filebrowser
restart: unless-stopped
security_opt:
+6
View File
@@ -497,6 +497,12 @@ pages:
description: Upstream Resolver
category: network
hide: false
Tailscale-Docker:
name: Tailscale
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/tailscale.svg
description: VPN
category: network
hide: false
ddns-updater:
name: DDNS Updater
icon: mdi:cloud-sync
@@ -1,95 +0,0 @@
param(
[string]$ReportPath = "G:\Gitea_Clone\homelab-infra\docs\audit\dr-workstation-readiness-2026-06-06.md"
)
$ErrorActionPreference = "Stop"
function Invoke-Capture {
param([string]$Command)
$output = & cmd.exe /c $Command 2>&1
[pscustomobject]@{
Command = $Command
ExitCode = $LASTEXITCODE
Output = ($output | ForEach-Object { ([string]$_).Replace("`0", "") })
}
}
function Invoke-WslCapture {
param([string]$Bash)
Invoke-Capture -Command ('wsl -d Ubuntu -- bash -lc ' + '"' + ($Bash.Replace('"', '\"')) + '"')
}
$checks = [ordered]@{}
$checks["wsl_status"] = Invoke-Capture -Command "wsl --status"
$checks["wsl_list"] = Invoke-Capture -Command "wsl --list --verbose"
$checks["ubuntu_os"] = Invoke-WslCapture -Bash "lsb_release -a 2>/dev/null || cat /etc/os-release; uname -r"
$checks["tools"] = Invoke-WslCapture -Bash "command -v borg || true; borg --version 2>/dev/null || true; command -v ssh || true; ssh -V 2>&1 || true; command -v git || true; git --version 2>/dev/null || true"
$checks["sudo"] = Invoke-WslCapture -Bash "sudo -n true >/dev/null 2>&1 && echo sudo-noprompt-ok || echo sudo-password-needed"
$checks["wsl_ssh_files"] = Invoke-WslCapture -Bash "ls -la ~/.ssh 2>/dev/null || true; test -f ~/dr-smoke.sh && ls -la ~/dr-smoke.sh || true"
$checks["github_dr_key_smoke"] = Invoke-WslCapture -Bash "GIT_SSH_COMMAND='ssh -i ~/.ssh/dr-readonly -o BatchMode=yes -o IdentitiesOnly=yes -o ConnectTimeout=8' git ls-remote git@github.com:michaelkaleschke-spec/homelab-infra.git HEAD 2>&1 | sed -n '1,5p'"
$checks["hetzner_dr_key_smoke"] = Invoke-WslCapture -Bash "ssh -i ~/.ssh/dr-hetzner -o BatchMode=yes -o IdentitiesOnly=yes -o ConnectTimeout=8 -p 23 u565255@u565255.your-storagebox.de 'ls' 2>&1 | sed -n '1,10p'"
$borgInstalled = ($checks["tools"].Output -match "borg \d")
$githubOk = ($checks["github_dr_key_smoke"].Output -match "HEAD")
$hetznerOk = ($checks["hetzner_dr_key_smoke"].Output -match "hetzner_borg_appdata_critical")
$sudoNeedsPassword = ($checks["sudo"].Output -match "sudo-password-needed")
$drSmokeExists = ($checks["wsl_ssh_files"].Output -match "dr-smoke.sh")
$lines = @()
$lines += "# DR-Workstation Readiness - 2026-06-06"
$lines += ""
$lines += "Automatisch erzeugter lokaler Readiness-Check fuer den Operator-PC. Es wurden keine Secret-Werte, Passphrases oder Private-Key-Inhalte ausgegeben."
$lines += ""
$lines += "## Zusammenfassung"
$lines += ""
$lines += "| Check | Ergebnis |"
$lines += "|---|---|"
$lines += '| WSL2 Ubuntu | vorhanden (`Ubuntu 24.04`, WSL Version 2) |'
$lines += "| SSH/Git in WSL | vorhanden |"
$lines += "| GitHub-Read-Smoke mit DR-Key | " + ($(if ($githubOk) { "ok" } else { "nicht ok" })) + " |"
$lines += "| Borg Client | " + ($(if ($borgInstalled) { "installiert" } else { "fehlt" })) + " |"
$lines += "| Hetzner Storage Box mit DR-Key | " + ($(if ($hetznerOk) { "ok" } else { "nicht ok" })) + " |"
$lines += '| `~/dr-smoke.sh` | ' + ($(if ($drSmokeExists) { "vorhanden" } else { "fehlt" })) + ' |'
$lines += "| WSL sudo ohne Passwortprompt | " + ($(if ($sudoNeedsPassword) { "nein, Operator muss Passwort eingeben" } else { "ja" })) + " |"
$lines += ""
$lines += "## Bewertung"
$lines += ""
$lines += "- Der lokale WSL2-/Ubuntu-Unterbau ist vorhanden."
$lines += '- Die DR-Key-Arbeitskopien liegen in WSL unter `~/.ssh/dr-readonly` und `~/.ssh/dr-hetzner`.'
$lines += "- GitHub-Read-Smoke und Hetzner-SSH-Smoke sind erfolgreich."
$lines += '- `borgbackup` ist installiert.'
$lines += "- Der vollstaendige Bare-Metal-DR-Smoke wartet nur noch auf die interaktive Borg-Passphrase."
$lines += ""
$lines += "## Naechste Operator-Schritte"
$lines += ""
$lines += "In Ubuntu ausfuehren:"
$lines += ""
$lines += '```bash'
$lines += "bash ~/dr-smoke.sh"
$lines += '```'
$lines += ""
$lines += 'Borg fragt dabei interaktiv nach der Borg-Repo-Passphrase. Diese Passphrase wurde nicht auf `baerchen` gefunden und wird bewusst nicht dauerhaft gespeichert.'
$lines += ""
$lines += "## Rohchecks"
$lines += ""
foreach ($name in $checks.Keys) {
$check = $checks[$name]
$lines += "### $name"
$lines += ""
$lines += '- ExitCode: `' + $check.ExitCode + '`'
$lines += ""
$lines += '```text'
$lines += ($check.Output | ForEach-Object {
$_ -replace ([regex]::Escape($env:USERPROFILE)), '%USERPROFILE%'
})
$lines += '```'
$lines += ""
}
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $ReportPath) | Out-Null
while ($lines.Count -gt 0 -and $lines[-1] -eq "") {
$lines = $lines[0..($lines.Count - 2)]
}
$lines -join "`r`n" | Set-Content -LiteralPath $ReportPath -Encoding UTF8
Write-Host "Report written: $ReportPath"
-41
View File
@@ -1,41 +0,0 @@
#!/bin/bash
# DR-Workstation Quartals-Smoke
#
# Prueft Git-Read, Hetzner-SSH und Borg-Repo-Erreichbarkeit vom Operator-PC.
# Speichert keine Passphrase. Borg fragt interaktiv nach der Repo-Passphrase.
set -euo pipefail
GITHUB_KEY="${GITHUB_KEY:-$HOME/.ssh/dr-readonly}"
HETZNER_KEY="${HETZNER_KEY:-$HOME/.ssh/dr-hetzner}"
GITHUB_REPO="${GITHUB_REPO:-git@github.com:michaelkaleschke-spec/homelab-infra.git}"
BORG_REPO="${BORG_REPO:-ssh://u565255@u565255.your-storagebox.de/./hetzner_borg_appdata_critical}"
echo "=== Tooling ==="
command -v ssh
command -v git
command -v borg
borg --version
echo
echo "=== Key files ==="
test -r "$GITHUB_KEY" || { echo "Missing GitHub key: $GITHUB_KEY" >&2; exit 1; }
test -r "$HETZNER_KEY" || { echo "Missing Hetzner key: $HETZNER_KEY" >&2; exit 1; }
ls -l "$GITHUB_KEY" "$HETZNER_KEY"
echo
echo "=== GitHub Deploy-Key ==="
GIT_SSH_COMMAND="ssh -i $GITHUB_KEY -o IdentitiesOnly=yes -o BatchMode=yes" \
git ls-remote "$GITHUB_REPO" HEAD
echo
echo "=== Hetzner SSH-Login ==="
ssh -i "$HETZNER_KEY" -o IdentitiesOnly=yes -o BatchMode=yes -p 23 \
u565255@u565255.your-storagebox.de "ls" | head -5
echo
echo "=== Borg-Repo ==="
export BORG_RSH="ssh -i $HETZNER_KEY -o IdentitiesOnly=yes -p 23"
borg info "$BORG_REPO" | head -12
echo
echo "DR-Smoke OK ($(date '+%F %T'))"
-6
View File
@@ -42,10 +42,6 @@ Ziel:
- `authelia-compose.test.yml`: isolierte Testinstanz fuer Authelia inkl. Test-Postgres, Filesystem-Notifier (kein echter SMTP-Versand)
- `authelia-plan.md`: konkreter Authelia-Testplan
- `authelia-runbook.md`: Operator-Runbook fuer den ersten Authelia-Lauf
- `adguard-restore-test.sh`: AdGuard-Home-Restore-Job (Config + isolierter Container + HTTP/DNS-Smoke; Erstlauf 2026-06-06 erfolgreich)
- `adguard-compose.test.yml`: isolierte AdGuard-Testinstanz auf localhost-Ports `13001` und `15353`
- `redis-restore-test.sh`: Redis-8-Restore-Job (Pre-Cutover-Artefakt + isolierter Container + PING/INFO/DBSIZE; Erstlauf 2026-06-06 erfolgreich)
- `redis-compose.test.yml`: isolierte Redis-8-Testinstanz auf localhost-Port `16379`
- `nextcloud-restore-test.sh`: Nextcloud-Restore-Job (Scaffold; **blockiert** durch Unraid shfs-chmod-Inkompatibilitaet - siehe unten)
- `nextcloud-compose.test.yml`: isolierte Testinstanz fuer Nextcloud inkl. Test-Postgres und Test-Redis
@@ -95,8 +91,6 @@ Aktuell ist das erste validierte Muster vorhanden.
- echter Paperless-Restore am 2026-05-07 erfolgreich verifiziert
- Immich-Restore-Test am 2026-05-27 erfolgreich verifiziert; Test-Postgres wurde nach der VectorChord-Migration am 2026-05-31 auf das produktive Immich-Postgres-Image umgestellt
- Authelia-Restore-Smoke am 2026-06-03 erfolgreich verifiziert; bewusst ohne produktiven Dump-Restore wegen Storage-Encryption-Key-Kopplung
- AdGuard-Home-Restore-Smoke am 2026-06-06 erfolgreich verifiziert; Borg-Config-Restore, HTTP `/control/status` 401, DNS-Smoke ok, 7 Filterlisten-Eintraege, Report `/mnt/user/backups/restore-reports/adguard-2026-06-06.md`
- Redis-8-Restore-Smoke am 2026-06-06 erfolgreich verifiziert; Pre-Cutover-Artefakt, Redis 8.8, PING ok, AOF aktiv, DBSIZE 1, Report `/mnt/user/backups/restore-reports/redis-2026-06-06.md`
- Bash-Dispatcher und Bash-Restore-Jobs am 2026-05-07 erfolgreich hostseitig verifiziert
- Restore-Lab und Report-Pfade auf dem Host angelegt
- `ntfy`-Wrapper ist fuer Host-Jobs verfuegbar
@@ -1,14 +0,0 @@
services:
restoretest-adguard:
image: adguard/adguardhome:v0.107.76@sha256:7157eb1dc3b26c7af1d6898759a7b3f7d0fa09891fbd2d3caa6abc1057a9179b
container_name: restoretest-adguard
restart: "no"
ports:
- "127.0.0.1:15353:53/tcp"
- "127.0.0.1:15353:53/udp"
- "127.0.0.1:13001:80/tcp"
volumes:
- /mnt/user/backups/restore-lab/adguard/work:/opt/adguardhome/work
- /mnt/user/backups/restore-lab/adguard/conf:/opt/adguardhome/conf
security_opt:
- no-new-privileges:true
-181
View File
@@ -1,181 +0,0 @@
#!/bin/bash
set -euo pipefail
# AdGuard Home Restore Smoke Test
#
# Scope:
# - Borg-Extract von /local/appdata/adguard/conf
# - YAML-/Strukturcheck fuer AdGuardHome.yaml
# - Start einer isolierten Testinstanz auf localhost-Ports
# - HTTP-Smoke gegen Admin-UI/API
# - DNS-Smoke gegen localhost:15353, falls ein passender Resolver-Client vorhanden ist
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
. "$SCRIPT_DIR/common.sh"
WHATIF=0
KEEP_DATA=0
for arg in "$@"; do
case "$arg" in
--what-if) WHATIF=1 ;;
--keep-data) KEEP_DATA=1 ;;
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
esac
done
RESTORE_ROOT="/mnt/user/backups/restore-lab/adguard"
REPORT_ROOT="/mnt/user/backups/restore-reports"
EXTRACT_DIR="$BORG_RESTORE_HOST_ROOT/adguard-extract"
COMPOSE_FILE="$SCRIPT_DIR/adguard-compose.test.yml"
REPORT_FILE="$REPORT_ROOT/adguard-$(date +%F).md"
TEST_HTTP="http://127.0.0.1:13001"
TEST_DNS_PORT="15353"
if [ "$WHATIF" -eq 1 ]; then
cat <<EOF
AdGuard Home restore test
Mode: WhatIf
RestoreRoot: $RESTORE_ROOT
Borg source: local/appdata/adguard/conf
Test HTTP endpoint: $TEST_HTTP
Test DNS endpoint: 127.0.0.1:$TEST_DNS_PORT
Scope: Config-Restore + isolated AdGuard boot + HTTP/DNS smoke
EOF
exit 0
fi
require_cmd docker
require_cmd curl
require_path "$BORG_PASSPHRASE_FILE_DEFAULT"
require_path "$COMPOSE_FILE"
RESTORE_SUCCESS=0
cleanup() {
cleanup_compose "$COMPOSE_FILE"
if [ "$RESTORE_SUCCESS" -ne 1 ]; then
preserve_on_failure "adguard" "$RESTORE_ROOT"
rm -rf "$EXTRACT_DIR"
return
fi
if [ "$KEEP_DATA" -ne 1 ]; then
rm -rf "$RESTORE_ROOT"
fi
rm -rf "$EXTRACT_DIR"
}
trap cleanup EXIT
rm -rf "$EXTRACT_DIR" "$RESTORE_ROOT"
mkdir -p "$RESTORE_ROOT/conf" "$RESTORE_ROOT/work"
archive="$(latest_archive_name)"
repo="$(borg_repo_url)"
if [ -z "$archive" ] || [ -z "$repo" ]; then
echo "Could not resolve Borg repo/archive from borg-ui database" >&2
exit 1
fi
borg_extract "/restore/adguard-extract" "local/appdata/adguard/conf"
if [ ! -d "$EXTRACT_DIR/local/appdata/adguard/conf" ]; then
echo "AdGuard conf path missing in Borg archive" >&2
exit 1
fi
cp -a "$EXTRACT_DIR/local/appdata/adguard/conf/." "$RESTORE_ROOT/conf/"
chmod -R a+rX "$RESTORE_ROOT/conf"
chmod -R a+rwX "$RESTORE_ROOT/work"
config_file="$RESTORE_ROOT/conf/AdGuardHome.yaml"
if [ ! -s "$config_file" ]; then
echo "Missing restored AdGuardHome.yaml" >&2
exit 1
fi
if command -v ruby >/dev/null 2>&1; then
ruby -e 'require "yaml"; YAML.load_file(ARGV.fetch(0))' "$config_file"
yaml_check="ruby-yaml-ok"
else
grep -q '^dns:' "$config_file"
grep -q '^http:' "$config_file"
yaml_check="basic-structure-ok"
fi
filter_count="$(grep -c '^[[:space:]]*-[[:space:]]*enabled:' "$config_file" 2>/dev/null || true)"
docker compose -f "$COMPOSE_FILE" up -d restoretest-adguard >/dev/null
http_status=""
for _ in $(seq 1 60); do
http_status="$(curl -s -o /tmp/adguard-body.html -w '%{http_code}' \
"$TEST_HTTP/control/status" || true)"
if [ "$http_status" = "200" ] || [ "$http_status" = "401" ] || [ "$http_status" = "403" ]; then
break
fi
sleep 2
done
if [ "$http_status" != "200" ] && [ "$http_status" != "401" ] && [ "$http_status" != "403" ]; then
echo "AdGuard HTTP smoke failed: status=$http_status" >&2
docker logs --tail 80 restoretest-adguard >&2 || true
exit 1
fi
dns_status="not-run"
dns_detail="no dig/drill command available"
if command -v dig >/dev/null 2>&1; then
if dig @127.0.0.1 -p "$TEST_DNS_PORT" git.kaleschke.info A +time=3 +tries=1 >/tmp/adguard-dig.out 2>&1; then
dns_status="ok"
dns_detail="$(grep -E '^[[:alnum:].-]+[[:space:]]+[0-9]+[[:space:]]+IN[[:space:]]+A[[:space:]]+' /tmp/adguard-dig.out | head -1 || true)"
else
dns_status="failed"
dns_detail="$(tail -20 /tmp/adguard-dig.out | tr '\n' ' ')"
fi
elif command -v drill >/dev/null 2>&1; then
if drill -p "$TEST_DNS_PORT" git.kaleschke.info @127.0.0.1 >/tmp/adguard-drill.out 2>&1; then
dns_status="ok"
dns_detail="$(grep -E '^[[:alnum:].-]+\\.[[:space:]]+[0-9]+[[:space:]]+IN[[:space:]]+A[[:space:]]+' /tmp/adguard-drill.out | head -1 || true)"
else
dns_status="failed"
dns_detail="$(tail -20 /tmp/adguard-drill.out | tr '\n' ' ')"
fi
fi
if [ "$dns_status" = "failed" ]; then
echo "AdGuard DNS smoke failed: $dns_detail" >&2
docker logs --tail 80 restoretest-adguard >&2 || true
exit 1
fi
write_report "$REPORT_FILE" <<EOF
# AdGuard Home Restore Test Report - $(date +%F)
- Service: \`adguard\`
- Source repo: \`$repo\`
- Archive: \`$archive\`
- Restore root: \`$RESTORE_ROOT\`
- Test container: \`restoretest-adguard\`
- Test HTTP endpoint: \`$TEST_HTTP/control/status\`
- Test DNS endpoint: \`127.0.0.1:$TEST_DNS_PORT\`
- Result: \`SUCCESS\`
## Checks
- Borg extract of conf: \`ok\`
- Restored config file: \`AdGuardHome.yaml\`
- Config check: \`$yaml_check\`
- Filter-list-like entries counted: \`$filter_count\`
- HTTP status from /control/status: \`$http_status\`
- DNS smoke: \`$dns_status\`
- DNS detail: \`$dns_detail\`
## Notes
- Productive AdGuard DNS port 53 and admin port 8082 were NOT used.
- Test ports were bound to localhost only: \`127.0.0.1:15353\` and \`127.0.0.1:13001\`.
- Login credentials are part of the restored AdGuardHome.yaml and were not printed.
- Test data was cleaned after success: \`$([ "$KEEP_DATA" -eq 1 ] && echo no || echo yes)\`
EOF
RESTORE_SUCCESS=1
echo "AdGuard restore test ok -> $REPORT_FILE"
-16
View File
@@ -1,16 +0,0 @@
services:
restoretest-redis:
image: redis:8.8.0-alpine@sha256:09160599abd229764c0fb44cb6be640294e1d360a54b19985ab4843dcf2d90f1
container_name: restoretest-redis
restart: "no"
command:
- sh
- -c
- exec redis-server --appendonly yes --requirepass "$$(cat /run/secrets/redis_password)"
ports:
- "127.0.0.1:16379:6379/tcp"
volumes:
- /mnt/user/backups/restore-lab/redis/data:/data
- /mnt/user/backups/restore-lab/redis/secrets/redis_password.txt:/run/secrets/redis_password:ro
security_opt:
- no-new-privileges:true
-152
View File
@@ -1,152 +0,0 @@
#!/bin/bash
set -euo pipefail
# Redis 8 Restore Smoke Test
#
# Scope:
# - Restore aus dem dokumentierten shared-redis-pre-redis8-Artefakt
# - Start einer isolierten Redis-8-Testinstanz auf localhost:16379
# - PING, INFO server und DBSIZE ohne Ausgabe des Passworts
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
. "$SCRIPT_DIR/common.sh"
WHATIF=0
KEEP_DATA=0
for arg in "$@"; do
case "$arg" in
--what-if) WHATIF=1 ;;
--keep-data) KEEP_DATA=1 ;;
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
esac
done
RESTORE_ROOT="/mnt/user/backups/restore-lab/redis"
REPORT_ROOT="/mnt/user/backups/restore-reports"
PRE_CUTOVER_ROOT="/mnt/user/backups/borg/dumps/latest"
SECRET_FILE="/mnt/user/appdata/secrets/redis_password.txt"
COMPOSE_FILE="$SCRIPT_DIR/redis-compose.test.yml"
REPORT_FILE="$REPORT_ROOT/redis-$(date +%F).md"
if [ "$WHATIF" -eq 1 ]; then
cat <<EOF
Redis 8 restore test
Mode: WhatIf
RestoreRoot: $RESTORE_ROOT
Restore source: newest $PRE_CUTOVER_ROOT/shared-redis-pre-redis8-*
Secret source: $SECRET_FILE
Test endpoint: 127.0.0.1:16379
Scope: Data restore + isolated Redis boot + PING/INFO/DBSIZE smoke
EOF
exit 0
fi
require_cmd docker
require_path "$PRE_CUTOVER_ROOT"
require_path "$SECRET_FILE"
require_path "$COMPOSE_FILE"
RESTORE_SUCCESS=0
cleanup() {
cleanup_compose "$COMPOSE_FILE"
if [ "$RESTORE_SUCCESS" -ne 1 ]; then
preserve_on_failure "redis" "$RESTORE_ROOT"
return
fi
if [ "$KEEP_DATA" -ne 1 ]; then
rm -rf "$RESTORE_ROOT"
fi
}
trap cleanup EXIT
rm -rf "$RESTORE_ROOT"
mkdir -p "$RESTORE_ROOT/data" "$RESTORE_ROOT/secrets"
restore_source="$(find "$PRE_CUTOVER_ROOT" -maxdepth 1 -type d -name 'shared-redis-pre-redis8-*' | sort | tail -1)"
if [ -z "$restore_source" ]; then
echo "No shared-redis-pre-redis8-* restore source found under $PRE_CUTOVER_ROOT" >&2
exit 1
fi
if [ ! -d "$restore_source" ]; then
echo "Redis restore source is not a directory: $restore_source" >&2
exit 1
fi
cp -a "$restore_source/." "$RESTORE_ROOT/data/"
cp "$SECRET_FILE" "$RESTORE_ROOT/secrets/redis_password.txt"
chmod -R a+rwX "$RESTORE_ROOT/data"
chmod a+r "$RESTORE_ROOT/secrets/redis_password.txt"
data_files="$(find "$RESTORE_ROOT/data" -type f | wc -l | tr -d ' ')"
data_bytes="$(du -sb "$RESTORE_ROOT/data" | awk '{print $1}')"
docker compose -f "$COMPOSE_FILE" up -d restoretest-redis >/dev/null
ping_result=""
for _ in $(seq 1 60); do
ping_result="$(docker exec restoretest-redis sh -lc \
'p=$(cat /run/secrets/redis_password); redis-cli -a "$p" --no-auth-warning PING' 2>/dev/null || true)"
if [ "$ping_result" = "PONG" ]; then
break
fi
sleep 1
done
if [ "$ping_result" != "PONG" ]; then
echo "Redis PING smoke failed: $ping_result" >&2
docker logs --tail 80 restoretest-redis >&2 || true
exit 1
fi
redis_version="$(docker exec restoretest-redis sh -lc \
'p=$(cat /run/secrets/redis_password); redis-cli -a "$p" --no-auth-warning INFO server | awk -F: "/^redis_version:/ {gsub(/\r/, \"\", \$2); print \$2}"')"
dbsize="$(docker exec restoretest-redis sh -lc \
'p=$(cat /run/secrets/redis_password); redis-cli -a "$p" --no-auth-warning DBSIZE' | tr -d '\r')"
aof_enabled="$(docker exec restoretest-redis sh -lc \
'p=$(cat /run/secrets/redis_password); redis-cli -a "$p" --no-auth-warning INFO persistence | awk -F: "/^aof_enabled:/ {gsub(/\r/, \"\", \$2); print \$2}"')"
case "$redis_version" in
8.*) ;;
*)
echo "Unexpected Redis version: $redis_version" >&2
exit 1
;;
esac
if [ "${dbsize:-0}" -lt 1 ]; then
echo "Unexpected Redis DBSIZE: $dbsize" >&2
exit 1
fi
write_report "$REPORT_FILE" <<EOF
# Redis 8 Restore Test Report - $(date +%F)
- Service: \`redis\`
- Restore source: \`$restore_source\`
- Restore root: \`$RESTORE_ROOT\`
- Test container: \`restoretest-redis\`
- Test endpoint: \`127.0.0.1:16379\`
- Result: \`SUCCESS\`
## Checks
- Data restore from pre-Redis8 artifact: \`ok\`
- Secret file mounted from host secret path: \`ok\`
- Restored data files: \`$data_files\`
- Restored data bytes: \`$data_bytes\`
- PING: \`$ping_result\`
- Redis version: \`$redis_version\`
- AOF enabled: \`$aof_enabled\`
- DBSIZE: \`$dbsize\`
## Notes
- Productive Redis port 6379 and productive data path were NOT used.
- Test port was bound to localhost only: \`127.0.0.1:16379\`.
- Redis password value was used from the restored secret file and was not printed.
- Test data was cleaned after success: \`$([ "$KEEP_DATA" -eq 1 ] && echo no || echo yes)\`
EOF
RESTORE_SUCCESS=1
echo "Redis restore test ok -> $REPORT_FILE"
+1 -13
View File
@@ -40,18 +40,6 @@ case "$MODE" in
fi
exec "$SCRIPT_DIR/authelia-restore-test.sh"
;;
adguard)
if [ "$WHATIF" = "--what-if" ]; then
exec "$SCRIPT_DIR/adguard-restore-test.sh" --what-if
fi
exec "$SCRIPT_DIR/adguard-restore-test.sh"
;;
redis)
if [ "$WHATIF" = "--what-if" ]; then
exec "$SCRIPT_DIR/redis-restore-test.sh" --what-if
fi
exec "$SCRIPT_DIR/redis-restore-test.sh"
;;
nextcloud)
if [ "$WHATIF" = "--what-if" ]; then
exec "$SCRIPT_DIR/nextcloud-restore-test.sh" --what-if
@@ -95,7 +83,7 @@ case "$MODE" in
exec "$SCRIPT_DIR/shared-pg-cluster-restore-test.sh"
;;
*)
echo "Usage: $0 {freshness|vaultwarden|gitea|paperless|immich|authelia|adguard|redis|nextcloud|komodo-bootstrap|komodo-mongo-restore|traefik|mailarchiver|mealie|shared-pg-cluster} [--what-if]" >&2
echo "Usage: $0 {freshness|vaultwarden|gitea|paperless|immich|authelia|nextcloud|komodo-bootstrap|komodo-mongo-restore|shared-pg-cluster} [--what-if]" >&2
exit 1
;;
esac
+1 -5
View File
@@ -28,8 +28,6 @@ Quartalsweise:
- Restore-/DR-Sanity-Check
- `immich` Restore-Smoke-Test (DB + UI, ohne produktive Foto-Mounts; Erstlauf 2026-05-27 erfolgreich)
- `adguard` Restore-Smoke-Test (Config + HTTP/DNS, nach DNS-Aenderungen auch ausserhalb des Quartals)
- `redis` Restore-Smoke-Test (Pre-Cutover-Artefakt + Redis 8, vor/nach Major-Aenderungen auch ausserhalb des Quartals)
- pruefen:
- Restore-Lab-Struktur
- Reports
@@ -47,9 +45,7 @@ Quartals-Belegung:
Bestaetigte Mini-Restores: Vaultwarden, Gitea und Paperless am 2026-05-07;
Immich am 2026-05-27; Paperless erneut am 2026-05-31; Authelia am
2026-06-03 (Config-Smoke ohne produktiven Dump-Restore); AdGuard Home am
2026-06-06 (Config + HTTP/DNS-Smoke); Redis 8 am 2026-06-06
(Pre-Cutover-Artefakt + PING/INFO/DBSIZE-Smoke).
2026-06-03 (Config-Smoke ohne produktiven Dump-Restore).
## Konkreter Kalender
@@ -1,194 +0,0 @@
param(
[string]$ReportPath = "G:\Gitea_Clone\homelab-infra\ops\windows-reinstall\docs\baerchen-app-license-readiness-2026-06-06.md"
)
$ErrorActionPreference = "Stop"
function Get-InstalledProgram {
param([string[]]$NamePattern)
$roots = @(
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
"HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*",
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*"
)
Get-ItemProperty $roots -ErrorAction SilentlyContinue |
Where-Object {
$display = $_.DisplayName
if (-not $display) { return $false }
foreach ($pattern in $NamePattern) {
if ($display -like $pattern) { return $true }
}
return $false
} |
Sort-Object DisplayName |
Select-Object DisplayName,DisplayVersion,Publisher,InstallDate
}
function Test-PathSummary {
param([string]$Path)
$item = Get-Item -LiteralPath $Path -ErrorAction SilentlyContinue
if (-not $item) {
return [pscustomobject]@{
Path = $Path
Exists = $false
Type = ""
LastWriteTime = ""
Bytes = ""
}
}
$bytes = ""
if ($item.PSIsContainer) {
$measure = Get-ChildItem -LiteralPath $Path -Recurse -Force -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum
$bytes = [int64]($measure.Sum)
} else {
$bytes = [int64]$item.Length
}
[pscustomobject]@{
Path = $Path
Exists = $true
Type = $(if ($item.PSIsContainer) { "Directory" } else { "File" })
LastWriteTime = $item.LastWriteTime.ToString("s")
Bytes = $bytes
}
}
function ConvertTo-MarkdownTable {
param(
[Parameter(ValueFromPipeline = $true)]$InputObject,
[string[]]$Columns
)
begin {
$rows = @()
}
process {
$rows += $InputObject
}
end {
if (-not $rows -or $rows.Count -eq 0) {
return "_Keine Treffer._"
}
$lines = @()
$lines += "| " + ($Columns -join " | ") + " |"
$lines += "| " + (($Columns | ForEach-Object { "---" }) -join " | ") + " |"
foreach ($row in $rows) {
$values = foreach ($column in $Columns) {
$value = [string]$row.$column
$value.Replace("|", "\|")
}
$lines += "| " + ($values -join " | ") + " |"
}
$lines -join "`n"
}
}
$programGroups = [ordered]@{
"Passwortmanager / Browser" = @("*Bitwarden*", "*Vaultwarden*", "*1Password*", "*KeePass*", "*KeePassXC*", "*Chrome*", "*Microsoft Edge*", "*Brave*", "*Firefox*")
"Banking4 / Subsembly" = @("*Banking4*", "*Subsembly*")
"WISO / Buhl" = @("*WISO*", "*Buhl*")
"Microsoft 365 / Office / OneDrive" = @("*Microsoft 365*", "*Microsoft Office*", "*Office 16*", "*OneDrive*")
}
$pathChecks = @(
"C:\Users\michi\AppData\Local\Subsembly",
"C:\Users\michi\AppData\Local\Buhl",
"C:\Users\michi\AppData\Local\Buhl Data Service GmbH",
"C:\ProgramData\Buhl Data Service GmbH",
"C:\Users\michi\Documents\steuer",
"C:\Users\michi\Desktop\Banking",
"C:\Users\michi\OneDrive",
"D:\30_Finanzen",
"D:\30_Finanzen\Recovery-Codes",
"D:\30_Finanzen\BitLocker-RecoveryKey-baerchen-2026-06-06.txt"
)
$oneDriveProcess = Get-Process OneDrive -ErrorAction SilentlyContinue |
Select-Object ProcessName,Id,StartTime
$oneDriveAccounts = Get-ChildItem "HKCU:\Software\Microsoft\OneDrive\Accounts" -ErrorAction SilentlyContinue |
Select-Object PSChildName
$officeCscript = @(
"$env:ProgramFiles\Microsoft Office\Office16\OSPP.VBS",
"${env:ProgramFiles(x86)}\Microsoft Office\Office16\OSPP.VBS"
) | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1
$officeStatus = @()
if ($officeCscript) {
$raw = & cscript.exe //Nologo $officeCscript /dstatus 2>$null
$officeStatus = $raw |
Where-Object { $_ -match "LICENSE NAME|LICENSE STATUS|ERROR CODE|Last 5 characters" } |
ForEach-Object { $_.Trim() }
}
$lines = @()
$lines += "# baerchen App-/Lizenz-Readiness - 2026-06-06"
$lines += ""
$lines += "Automatisch erzeugter lokaler Check. Keine Lizenzkeys, Passwoerter, Tokens oder Recovery-Code-Werte wurden ausgelesen oder ins Repo geschrieben."
$lines += ""
$lines += "## Ergebnis"
$lines += ""
$lines += "- Technische Inventarisierung: erledigt"
$lines += "- Manuelle Konto-/Recovery-Bestaetigung: weiterhin Operator-Schritt"
$lines += ""
$lines += "## Installierte Programme"
$lines += ""
foreach ($group in $programGroups.Keys) {
$lines += "### $group"
$lines += ""
$programs = Get-InstalledProgram -NamePattern $programGroups[$group]
$lines += ($programs | ConvertTo-MarkdownTable -Columns DisplayName,DisplayVersion,Publisher,InstallDate)
$lines += ""
}
$lines += "## Relevante Datenpfade"
$lines += ""
$pathResults = $pathChecks | ForEach-Object { Test-PathSummary $_ }
$lines += ($pathResults | ConvertTo-MarkdownTable -Columns Path,Exists,Type,LastWriteTime,Bytes)
$lines += ""
$lines += "## OneDrive / Microsoft 365 Indikatoren"
$lines += ""
$lines += "### OneDrive Prozess"
$lines += ""
$lines += ($oneDriveProcess | ConvertTo-MarkdownTable -Columns ProcessName,Id,StartTime)
$lines += ""
$lines += "### OneDrive Accounts Registry"
$lines += ""
$lines += ($oneDriveAccounts | ConvertTo-MarkdownTable -Columns PSChildName)
$lines += ""
$lines += "### Office Aktivierungsindikatoren"
$lines += ""
if ($officeStatus.Count -gt 0) {
$lines += '```text'
$lines += $officeStatus
$lines += '```'
} else {
$lines += "_Keine Office-OSPP-Aktivierungsdaten gefunden oder Office nicht klassisch installiert._"
}
$lines += ""
$lines += "## Manuell noch zu bestaetigen"
$lines += ""
$lines += "- [ ] Passwortmanager laesst sich oeffnen und enthaelt Homelab-/Banking-/Provider-Eintraege."
$lines += "- [ ] 2FA-Recovery-Codes fuer Microsoft, Hetzner, Cloudflare, Tailscale, Gitea/GitHub und Banken sind offline oder in Vaultwarden auffindbar."
$lines += "- [ ] Banking4 oeffnet den aktuellen Datentresor; ein frischer Backup-/Exportpfad ist bekannt."
$lines += '- [ ] WISO Steuer 2026 oeffnet, Buhl-Konto/Lizenz ist aktiv, Steuerdateien unter `C:\Users\michi\Documents\steuer` bzw. neuem Zielpfad sind sichtbar.'
$lines += "- [ ] Microsoft-Konto zeigt aktives M365/Office-Installationsrecht."
$lines += "- [ ] OneDrive-Sync ist angemeldet und synchronisiert die erwarteten Ordner."
$lines += ""
$lines += "## Bewertung"
$lines += ""
$lines += 'Dieses Dokument ersetzt nicht die manuelle Kontoanmeldung. Es belegt nur, welche lokalen Programme, Datenpfade und Aktivierungsindikatoren auf `baerchen` sichtbar waren.'
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $ReportPath) | Out-Null
$lines -join "`r`n" | Set-Content -LiteralPath $ReportPath -Encoding UTF8
Write-Host "Report written: $ReportPath"
@@ -1,86 +0,0 @@
# baerchen App-/Lizenz-Readiness - 2026-06-06
Automatisch erzeugter lokaler Check. Keine Lizenzkeys, Passwoerter, Tokens oder Recovery-Code-Werte wurden ausgelesen oder ins Repo geschrieben.
## Ergebnis
- Technische Inventarisierung: erledigt
- Manuelle Konto-/Recovery-Bestaetigung: erledigt laut Operator-Bestaetigung 2026-06-06 ("alle Dienste laufen")
## Installierte Programme
### Passwortmanager / Browser
| DisplayName | DisplayVersion | Publisher | InstallDate |
| --- | --- | --- | --- |
| Brave | 149.1.91.168 | Die Brave-Autoren | 20260604 |
| Google Chrome | 149.0.7827.54 | Google LLC | 20260604 |
| Microsoft Edge | 148.0.3967.96 | Microsoft Corporation | 20260604 |
| Microsoft Edge WebView2-Laufzeit | 148.0.3967.96 | Microsoft Corporation | 20260604 |
### Banking4 / Subsembly
| DisplayName | DisplayVersion | Publisher | InstallDate |
| --- | --- | --- | --- |
| Banking4 Home | | Subsembly GmbH | |
### WISO / Buhl
| DisplayName | DisplayVersion | Publisher | InstallDate |
| --- | --- | --- | --- |
| WISO Steuer 2026 | 33.07.3410 | Buhl Data Service GmbH | 20260604 |
### Microsoft 365 / Office / OneDrive
| DisplayName | DisplayVersion | Publisher | InstallDate |
| --- | --- | --- | --- |
| Microsoft 365 - de-de | 16.0.20026.20140 | Microsoft Corporation | |
| Microsoft 365 - en-us | 16.0.20026.20140 | Microsoft Corporation | |
| Microsoft OneDrive | 23.038.0219.0001 | Microsoft Corporation | |
| Office 16 Click-to-Run Extensibility Component | 16.0.20026.20076 | Microsoft Corporation | 20260604 |
| Office 16 Click-to-Run Localization Component | 16.0.20026.20140 | Microsoft Corporation | 20260604 |
## Relevante Datenpfade
| Path | Exists | Type | LastWriteTime | Bytes |
| --- | --- | --- | --- | --- |
| C:\Users\michi\AppData\Local\Subsembly | True | Directory | 2026-06-04T12:23:43 | 43360359 |
| C:\Users\michi\AppData\Local\Buhl | True | Directory | 2026-06-04T12:55:57 | 680833 |
| C:\Users\michi\AppData\Local\Buhl Data Service GmbH | False | | | |
| C:\ProgramData\Buhl Data Service GmbH | True | Directory | 2026-06-04T12:57:08 | 6037194 |
| C:\Users\michi\Documents\steuer | True | Directory | 2026-01-26T11:21:44 | 13069132 |
| C:\Users\michi\Desktop\Banking | False | | | |
| C:\Users\michi\OneDrive | True | Directory | 2026-06-04T12:39:24 | 39370265 |
| D:\30_Finanzen | True | Directory | 2026-06-04T20:14:26 | 128994854 |
| D:\30_Finanzen\Recovery-Codes | False | | | |
| D:\30_Finanzen\BitLocker-RecoveryKey-baerchen-2026-06-06.txt | False | | | |
## OneDrive / Microsoft 365 Indikatoren
### OneDrive Prozess
_Keine Treffer._
### OneDrive Accounts Registry
| PSChildName |
| --- |
| Business1 |
| Personal |
### Office Aktivierungsindikatoren
_Keine Office-OSPP-Aktivierungsdaten gefunden oder Office nicht klassisch installiert._
## Manuell noch zu bestaetigen
- [x] Passwortmanager laesst sich oeffnen und enthaelt Homelab-/Banking-/Provider-Eintraege.
- [x] 2FA-Recovery-Codes fuer Microsoft, Hetzner, Cloudflare, Tailscale, Gitea/GitHub und Banken sind offline oder in Vaultwarden auffindbar.
- [x] Banking4 oeffnet den aktuellen Datentresor; ein frischer Backup-/Exportpfad ist bekannt.
- [x] WISO Steuer 2026 oeffnet, Buhl-Konto/Lizenz ist aktiv, Steuerdateien unter `C:\Users\michi\Documents\steuer` bzw. neuem Zielpfad sind sichtbar.
- [x] Microsoft-Konto zeigt aktives M365/Office-Installationsrecht.
- [x] OneDrive-Sync ist angemeldet und synchronisiert die erwarteten Ordner.
## Bewertung
Dieses Dokument belegt die technische Inventarisierung und die Operator-Bestaetigung vom 2026-06-06. Secret-Werte, Lizenzkeys und Recovery-Code-Werte wurden nicht dokumentiert.
@@ -46,16 +46,11 @@ Noch offen:
- BitLocker-Status mit Adminrechten pruefen. **Nachlauf 2026-06-05:** Status
wurde geprueft; C:/D:/E:/G:/H: sind `FullyDecrypted`, Protection `Off`.
Offen bleibt nur die bewusste BitLocker-Entscheidung.
- Passwortmanager, 2FA-Recovery-Codes und Browser-Sync manuell pruefen. **Erledigt 2026-06-06 laut Operator-Bestaetigung.**
- Banking4-Speicherort explizit pruefen. **Erledigt 2026-06-06 laut Operator-Bestaetigung.**
- Banking4 im Programm selbst oeffnen und aktuellen Datentresor/Backup-Export bestaetigen. Der Key und der Datentresor sind bereits lokal auf H: gesichert. **Erledigt 2026-06-06 laut Operator-Bestaetigung.**
- WISO Steuer 2026 oeffnen und Lizenz/Buhl-Konto sowie Speicherorte der Steuerdateien bestaetigen. **Erledigt 2026-06-06 laut Operator-Bestaetigung.**
- Microsoft-Konto fuer M365 pruefen: Office-Webkonto/Abonnement, Installationsrecht, OneDrive-Sync. **Erledigt 2026-06-06 laut Operator-Bestaetigung.**
- Technisches Nachinstallations-Inventar 2026-06-06 erledigt:
`baerchen-app-license-readiness-2026-06-06.md`. Banking4, WISO Steuer
2026, Microsoft 365/OneDrive und relevante Datenpfade sind lokal sichtbar;
Konto-/App-Oeffnen-Checks wurden am 2026-06-06 durch den Operator
bestaetigt ("alle Dienste laufen").
- Passwortmanager, 2FA-Recovery-Codes und Browser-Sync manuell pruefen.
- Banking4-Speicherort explizit pruefen.
- Banking4 im Programm selbst oeffnen und aktuellen Datentresor/Backup-Export bestaetigen. Der Key und der Datentresor sind bereits lokal auf H: gesichert.
- WISO Steuer 2026 oeffnen und Lizenz/Buhl-Konto sowie Speicherorte der Steuerdateien bestaetigen.
- Microsoft-Konto fuer M365 pruefen: Office-Webkonto/Abonnement, Installationsrecht, OneDrive-Sync.
- Optional Keyfinder-Lauf durchfuehren und Ergebnisse lokal auf H: speichern.
- `G:\Ollama` bewusst entscheiden: nicht gesichert, ca. 40,9 GB lokale Modell-/Cache-Daten.
- D:, F: und G: vor dem spaeteren Loeschen noch einmal in Ruhe final bestaetigen.
@@ -538,8 +533,8 @@ Zielstruktur:
Status 2026-06-05: Diese Checkliste ist historisch fuer die Freigabe der
Neuinstallation. Die technische Neuinstallation, Laufwerksbereinigung,
WinRE-Pruefung und Veeam-Baseline sind in neueren Dokumenten nachgezogen.
Status 2026-06-06: Passwortmanager/2FA, Banking4, WISO und Microsoft/M365
wurden durch den Operator bestaetigt ("alle Dienste laufen").
Als offene manuelle Pruefungen bleiben vor allem Passwortmanager/2FA,
Banking4, WISO und Microsoft/M365.
- [ ] Backup-Struktur auf H: erstellt
- [ ] Programmliste exportiert
@@ -547,10 +542,10 @@ wurden durch den Operator bestaetigt ("alle Dienste laufen").
- [ ] Windows-Aktivierung dokumentiert
- [ ] Benutzerordner gesichert
- [ ] Browser-Lesezeichen exportiert oder Sync geprüft
- [x] Passwortmanager geprüft
- [x] 2FA-Recovery-Codes gesichert
- [ ] Passwortmanager geprüft
- [ ] 2FA-Recovery-Codes gesichert
- [ ] SSH/API/GPG/Zertifikate gesichert
- [x] Banking4-Speicherort geprüft und gesichert
- [ ] Banking4-Speicherort geprüft und gesichert
- [ ] Homelab-/NAS-Doku gesichert
- [ ] D:, F: und G: analysiert
- [ ] Unklare Daten nach `99_Unsortiert_von_D_F_G` kopiert