1 Commits

Author SHA1 Message Date
renovate 0b78f5bb36 chore(deps): update minor-and-patch-updates 2026-06-05 10:20:17 +00:00
87 changed files with 155 additions and 5267 deletions
-2
View File
@@ -28,5 +28,3 @@ Thumbs.db
*.tmp *.tmp
*.log *.log
.serena/ .serena/
.claude/settings.local.json
memory/
+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 - `traefik`: Host-Ports 80/443
- `gitea`: SSH-Port 222 - `gitea`: SSH-Port 222
- `AdGuard Home`: DNS-Port 53 und LAN-Admin-Port 8082 - `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 - `Plex-Media-Server`: historischer Host-Netz-Sonderfall, nicht als Repo-Stack enthalten
- `scrutiny`: `privileged: true` fuer SMART/Laufwerkszugriff - `scrutiny`: `privileged: true` fuer SMART/Laufwerkszugriff
- `Komodo`: Docker-Socket und native Auth ohne pauschale ForwardAuth - `Komodo`: Docker-Socket und native Auth ohne pauschale ForwardAuth
+4 -7
View File
@@ -145,7 +145,6 @@ Diese Dienste sind über echte `*.kaleschke.info`-Domains erreichbar:
- `gitea` (Web) — git.kaleschke.info - `gitea` (Web) — git.kaleschke.info
- `immich_server` — immich.kaleschke.info - `immich_server` — immich.kaleschke.info
- `nextcloud` — cloud.kaleschke.info - `nextcloud` — cloud.kaleschke.info
- `plex` — plex.kaleschke.info (Traefik, native Plex-Auth; Plex Remote Access/Port 32400 bleibt aus)
### 4.2 Nicht öffentlich / nur Tailscale oder Traefik + Middleware ### 4.2 Nicht öffentlich / nur Tailscale oder Traefik + Middleware
Diese Dienste sind **keine Public Apps**: Diese Dienste sind **keine Public Apps**:
@@ -241,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 | | `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 / 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 ### 7.2 Sicherheit / Identity
@@ -274,7 +273,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` | Traefik via `plex.kaleschke.info` + Plex native Auth; LAN direkt `:32400` | Compose-Stack unter `host-services/plex/`; Host-Netz bleibt fuer Discovery / Plex GDM dokumentierte Ausnahme; Traefik routet per File-Provider-Ausnahme auf `http://192.168.178.58:32400`, weil Docker-Labels Host-Netz-Container aus Traefik heraus auf `127.0.0.1` routen wuerden; kein direkter WAN-Port 32400 und Plex Remote Access bleibt aus; Server geclaimt von `Xeridos`; Smart-TVs (Schlafzimmer, Wohnzimmer) ueber WLAN-LAN per mDNS | — | | `plex` | ✅ | `host` | Plex native, **LAN/Tailscale-only** (Remote Access aus seit 2026-05-28) | Compose-Stack unter `host-services/plex/`; Host-Netz bleibt fuer Discovery / Plex GDM dokumentierte Ausnahme; Server geclaimt von `Xeridos`; Smart-TVs (Schlafzimmer, Wohnzimmer) ueber WLAN-LAN per mDNS | — |
| `super-productivity` | ✅ vorbereitet | `frontend_net` | Traefik + Middleware | Persoenliche Task-PWA des Operators; Issues kommen aus Gitea `Micha/mails` via n8n-Mail-Workflow | Deploy + Webhook + DNS-Eintrag offen | | `super-productivity` | ✅ vorbereitet | `frontend_net` | Traefik + Middleware | Persoenliche Task-PWA des Operators; Issues kommen aus Gitea `Micha/mails` via n8n-Mail-Workflow | Deploy + Webhook + DNS-Eintrag offen |
| `n8n` | ✅ vorbereitet | `frontend_net` | Traefik, native Auth (keine pauschale Authelia) | Workflow-Automation; erster Workflow: GMX-Mail -> OpenAI-Extraktion -> Gitea-Issue in `Micha/mails`; `N8N_ENCRYPTION_KEY` ist Stack-ENV-Pflichtsecret | Deploy + Webhook + Owner-Setup offen | | `n8n` | ✅ vorbereitet | `frontend_net` | Traefik, native Auth (keine pauschale Authelia) | Workflow-Automation; erster Workflow: GMX-Mail -> OpenAI-Extraktion -> Gitea-Issue in `Micha/mails`; `N8N_ENCRYPTION_KEY` ist Stack-ENV-Pflichtsecret | Deploy + Webhook + Owner-Setup offen |
@@ -309,7 +308,7 @@ Legende Status:
| Container | Status | Ziel | | Container | Status | Ziel |
|---|---|---| |---|---|---|
| — | — | Plex ist nicht mehr direkt offen: der Dienst ist als Repo-Compose-Stack unter `host-services/plex/` dokumentiert; `host`-Netz bleibt als Discovery-Ausnahme. Externer Zugriff laeuft ausschliesslich ueber Traefik/443 auf `plex.kaleschke.info`; keine direkte 32400-WAN-Freigabe. Technisch nutzt Plex als einzige Host-Netz-Route `traefik/dynamic/plex.yml`, weil Docker-Labels fuer `network_mode: host` in Traefik auf `127.0.0.1:32400` zeigen. | | — | — | 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
@@ -408,7 +407,6 @@ Für den laufenden Betrieb gilt stattdessen:
| `monitoring-influxdb3-core` | Host-Port 8181 auf LAN-IP; `user: "0"` | Home Assistant laeuft in einer VM ausserhalb des Compose-Netzes und muss Metriken schreiben koennen; keine Traefik-Route, kein `frontend_net`, Zugriff nur ueber Token und LAN-IP `INFLUXDB_BIND_IP`; InfluxDB 3 Core benoetigt im aktuellen Container-Setup Root-Rechte fuer den lokalen Object-Store-Pfad im named volume | | `monitoring-influxdb3-core` | Host-Port 8181 auf LAN-IP; `user: "0"` | Home Assistant laeuft in einer VM ausserhalb des Compose-Netzes und muss Metriken schreiben koennen; keine Traefik-Route, kein `frontend_net`, Zugriff nur ueber Token und LAN-IP `INFLUXDB_BIND_IP`; InfluxDB 3 Core benoetigt im aktuellen Container-Setup Root-Rechte fuer den lokalen Object-Store-Pfad im named volume |
| `monitoring-promtail` | Docker-Socket read-only | Docker-Log-Discovery fuer Loki; keine Schreibrechte, keine Appdaten-Persistenz ueber den Socket | | `monitoring-promtail` | Docker-Socket read-only | Docker-Log-Discovery fuer Loki; keine Schreibrechte, keine Appdaten-Persistenz ueber den Socket |
| `n8n` | keine pauschale Authelia-Middleware | Webhook-Endpunkte (`/webhook/*`, `/webhook-test/*`) muessen ohne ForwardAuth erreichbar bleiben; n8n bringt eigene Owner-/Login-Auth mit (analog Komodo/Nextcloud) | | `n8n` | keine pauschale Authelia-Middleware | Webhook-Endpunkte (`/webhook/*`, `/webhook-test/*`) muessen ohne ForwardAuth erreichbar bleiben; n8n bringt eigene Owner-/Login-Auth mit (analog Komodo/Nextcloud) |
| `plex` | Traefik ohne Authelia, File-Provider-Ausnahme trotz Host-Netz | Plex bringt native Konto-/Client-Auth mit; vorgeschaltete ForwardAuth wuerde Plex Web, Apps und Client-Flows stoeren. Docker-Labels sind fuer diesen Host-Netz-Container ungeeignet, weil Traefik sonst `127.0.0.1:32400` nutzt; daher `traefik/dynamic/plex.yml` mit Ziel `192.168.178.58:32400`. Route nur ueber Traefik/443 (`plex.kaleschke.info`), direkter Plex-WAN-Port 32400 und Plex Remote Access bleiben deaktiviert. |
--- ---
@@ -497,8 +495,7 @@ Endstand:
- `PlexOnlineUsername="Xeridos"`, `PlexOnlineMail="michideheld@gmx.de"`, `PlexOnlineHome="1"`. - `PlexOnlineUsername="Xeridos"`, `PlexOnlineMail="michideheld@gmx.de"`, `PlexOnlineHome="1"`.
- Bibliotheken neu angelegt via Plex-Web → Verwalte Mediatheken → `/data/movies`, `/data/Heimatfilme` etc. - Bibliotheken neu angelegt via Plex-Web → Verwalte Mediatheken → `/data/movies`, `/data/Heimatfilme` etc.
- `PublishServerOnPlexOnlineKey="0"` (Remote Access deaktiviert), Plex-Relay aus. - `PublishServerOnPlexOnlineKey="0"` (Remote Access deaktiviert), Plex-Relay aus → Plex bleibt strikt LAN/Tailscale-only, konsistent zum Tailscale-First-Operator-Modell.
- 2026-06-06: Externer Komfortzugriff ueber `https://plex.kaleschke.info` via Traefik ergaenzt. Das ist **kein** Plex-Remote-Access und keine direkte FRITZ!Box-Freigabe auf `32400`; Plex bleibt hinter Traefik/443 und nutzt native Plex-Auth.
Konsequenzen fuer Doku/Betrieb: Konsequenzen fuer Doku/Betrieb:
-11
View File
@@ -34,17 +34,6 @@ services:
container_name: immich_machine_learning container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:release@sha256:a2501141440f10516d329fdfba2c68082e19eb9ba6016c061ac80d23beadf7f3 image: ghcr.io/immich-app/immich-machine-learning:release@sha256:a2501141440f10516d329fdfba2c68082e19eb9ba6016c061ac80d23beadf7f3
restart: unless-stopped restart: unless-stopped
environment:
# Workaround fuer gunicorn-25.1.0-Control-Socket-Bug: der Worker haengt
# nach "Control socket listening at /usr/src/gunicorn.ctl" und erreicht
# nie "Application startup complete" -> Container bleibt dauerhaft
# unhealthy, ML (Gesichtserkennung/CLIP/Smart-Search) ist tot.
# --no-control-socket deaktiviert das fehlerhafte Feature. immich-ml
# startet gunicorn als Subprozess, der GUNICORN_CMD_ARGS aus der Env
# liest und anhaengt. Bestaetigte Upstream-Regression seit Immich 2.6
# (immich#27228, gunicorn#3510). Re-check: bei Immich-Update, das
# gunicorn auf >25.1.0/<25.1.0 mit Fix bringt, wieder entfernen.
GUNICORN_CMD_ARGS: "--no-control-socket"
volumes: volumes:
- model-cache:/cache - model-cache:/cache
networks: networks:
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
mail-archiver: mail-archiver:
image: s1t5/mailarchiver@sha256:4ea7ecc47ad1dd2c523b85c3967574b61e39def1b6fd26edf874e21733c4018c image: s1t5/mailarchiver@sha256:ea7fd8c2e3e0ef0941e8dd9e726e35a8de33296f5c7b9ed811df5168ae6a9714
container_name: mail-archiver container_name: mail-archiver
restart: unless-stopped restart: unless-stopped
environment: environment:
-16
View File
@@ -4,12 +4,6 @@ services:
container_name: mealie container_name: mealie
restart: unless-stopped restart: unless-stopped
# OIDC: Authelia ueber Host-LAN-IP -> Traefik erreichbar (Container-DNS loest
# auth.kaleschke.info sonst nicht; gleiches Muster wie Komodo. SNI bleibt der
# Hostname, Let's-Encrypt-Cert validiert weiter.
extra_hosts:
- "auth.kaleschke.info:192.168.178.58"
environment: environment:
TZ: Europe/Berlin TZ: Europe/Berlin
ALLOW_SIGNUP: "false" ALLOW_SIGNUP: "false"
@@ -24,16 +18,6 @@ services:
BASE_URL: https://mealie.kaleschke.info BASE_URL: https://mealie.kaleschke.info
# --- Authelia OIDC SSO (additiv, 2026-06-06; lokaler Login bleibt) ---
OIDC_AUTH_ENABLED: "true"
OIDC_PROVIDER_NAME: Authelia
OIDC_CONFIGURATION_URL: https://auth.kaleschke.info/.well-known/openid-configuration
OIDC_CLIENT_ID: mealie
OIDC_CLIENT_SECRET: ${MEALIE_OIDC_CLIENT_SECRET}
OIDC_SIGNUP_ENABLED: "true"
OIDC_AUTO_REDIRECT: "false"
OIDC_REMEMBER_ME: "true"
volumes: volumes:
- /mnt/user/appdata/mealie/data:/app/data - /mnt/user/appdata/mealie/data:/app/data
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
n8n: n8n:
image: docker.n8n.io/n8nio/n8n:2.26.2@sha256:61ba01bc5e39304bbc928c9dbecd938c3a5cc1331b68affba6a34d0f654c43d9 image: docker.n8n.io/n8nio/n8n:2.25.4@sha256:5fd37fff98273a00c14f88d36b78a961f822e700801fa1b9509a7af643354531
container_name: n8n container_name: n8n
restart: unless-stopped restart: unless-stopped
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
nextcloud: nextcloud:
image: nextcloud:33.0.5-apache@sha256:56bdc45109067500fd0832fa64832b7c77a167d9394cbf5f0f4b59740b94194d image: nextcloud:33.0.5-apache@sha256:4c8bf9140e07ad0293e32500de47a97647677cd4cace358db3ecdc225dff9856
container_name: nextcloud container_name: nextcloud
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
-8
View File
@@ -3,9 +3,6 @@ services:
image: ghcr.io/paperless-ngx/paperless-ngx:2.20.15@sha256:6c86cad803970ea782683a8e80e7403444c5bf3cf70de63b4d3c8e87500db92f image: ghcr.io/paperless-ngx/paperless-ngx:2.20.15@sha256:6c86cad803970ea782683a8e80e7403444c5bf3cf70de63b4d3c8e87500db92f
container_name: paperless-ngx container_name: paperless-ngx
restart: unless-stopped restart: unless-stopped
# OIDC: Authelia ueber Host-LAN-IP -> Traefik erreichbar (Container-DNS sonst nicht)
extra_hosts:
- "auth.kaleschke.info:192.168.178.58"
security_opt: security_opt:
- no-new-privileges:true - no-new-privileges:true
environment: environment:
@@ -20,11 +17,6 @@ services:
- PAPERLESS_OCR_LANGUAGE=deu+eng - PAPERLESS_OCR_LANGUAGE=deu+eng
- PAPERLESS_URL=https://paperless.kaleschke.info - PAPERLESS_URL=https://paperless.kaleschke.info
# --- Authelia OIDC SSO (additiv, 2026-06-06; lokaler Login bleibt) ---
- PAPERLESS_APPS=allauth.socialaccount.providers.openid_connect
- PAPERLESS_SOCIAL_AUTO_SIGNUP=true
- 'PAPERLESS_SOCIALACCOUNT_PROVIDERS={"openid_connect":{"OAUTH_PKCE_ENABLED":true,"APPS":[{"provider_id":"authelia","name":"Authelia","client_id":"paperless","secret":"${PAPERLESS_OIDC_SECRET}","settings":{"server_url":"https://auth.kaleschke.info"}}]}}'
# Barcode / ASN # Barcode / ASN
- PAPERLESS_CONSUMER_ENABLE_BARCODES=1 - PAPERLESS_CONSUMER_ENABLE_BARCODES=1
- PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE=1 - PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE=1
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
super-productivity: super-productivity:
image: johannesjo/super-productivity:v18.9.1@sha256:773760107344e739f4c29409f7842db66a1b167d50eb2c40248cb5b5b328652e image: johannesjo/super-productivity:v18.8.0@sha256:c739caca8e0c5e83ea4a6289884079ac49e0c3c87c7f95598b5a9fb10cc2d9c4
container_name: super-productivity container_name: super-productivity
restart: unless-stopped restart: unless-stopped
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
unbound: unbound:
image: shaanmajid/unbound:1.25.1@sha256:f140db02a005904802bf5840093e95e675321aa060a00426fdffc2a3ac2eeb6b image: shaanmajid/unbound:1.25.1@sha256:96809ff052e8bd79bba30e067d8b27ed9a2f069b6b2a3484fe1d0eb45aba07c5
container_name: unbound container_name: unbound
restart: unless-stopped restart: unless-stopped
volumes: volumes:
-22
View File
@@ -1,22 +0,0 @@
### VOLUMES ###
DriveLetter Label FS Size_GB Free_GB Health
C (kein Label) NTFS 166.9 59.5 Healthy
D Daten-Projekte NTFS 167.7 148.6 Healthy
E Games NTFS 930.6 714.9 Healthy
G M2 SSD NTFS 930.9 877.5 Healthy
H Externe HDD NTFS 7452.0 3801.3 Healthy
(kein BW) Recovery x5 NTFS diverse diverse Healthy
### DISKS ###
Disk 0 INTEL SSDSC2BW180A3L SATA 167.68 GB GPT Healthy Serial: CVCV3105053K180EGN
Disk 1 INTEL SSDSC2BW180A3L SATA 167.68 GB GPT Healthy Serial: CVCV311302TH180EGN
Disk 2 Samsung SSD 980 PRO 1TB NVMe 931.51 GB GPT Healthy
Disk 3 WDC WDS100T2B0C NVMe 931.51 GB GPT Healthy
Disk 4 asmedia ASM235 USB 7.28 TB GPT Healthy
### PARTITIONS ###
Disk 0: [Reserved 16MB] [C: 166.87 GB Basic] [Recovery 809 MB]
Disk 1: [Reserved 15.98 MB] [D: 167.66 GB Basic]
Disk 2: [Reserved 15.98 MB] [E: 930.63 GB Basic] [Recovery 885 MB] <- F: ist weg
Disk 3: [System 100 MB] [Reserved 16 MB] [G: 930.89 GB Basic] [Recovery 524 MB]
Disk 4: [Reserved 15.98 MB] [H: 7.28 TB Basic]
-52
View File
@@ -1,52 +0,0 @@
### D:\ TOP-LEVEL ###
00_Inbox Directory 2026-06-04
10_Dokumente Directory 2026-06-04
11_Bilder Directory 2026-06-04 [ReadOnly-Attribut gesetzt]
12_Videos Directory 2026-06-04
13_Musik Directory 2026-06-04
14_Downloads Directory 2026-06-04
20_Projekte Directory 2026-06-04
30_Finanzen Directory 2026-06-04
90_Archiv Directory 2026-06-04
Micha Directory 2026-06-05 [Altquelle, noch vorhanden]
WSL Directory 2026-06-04 [nicht in Soll-Doku]
DumpStack.log File
### D:\Micha INHALT ###
Videos Directory 2026-06-05 [1 Datei, 0 MB - fast leer]
(alle anderen Unterordner weg)
### D:\00_Inbox INHALT ###
Desktop Directory 2026-06-05 [ReadOnly - das ist das Known-Folder-Ziel!]
### E:\ TOP-LEVEL ###
BattleNet Directory 2026-06-04 [SOLL]
EA Directory 2026-06-04 [SOLL]
EpicGames Directory 2026-06-04 [SOLL]
Riot Directory 2026-06-04 [SOLL]
Steam Directory 2026-06-05 [SOLL]
Ubisoft Directory 2026-06-04 [SOLL]
_Standalone FEHLT! [SOLL laut Doku]
### G:\ TOP-LEVEL ###
Apps Directory 2026-06-04 [nicht in Soll-Doku]
Gitea_Clone Directory 2026-04-15 [nicht in Soll-Doku - bewusst, homelab-infra]
repos Directory 2026-06-05 [SOLL]
Tools Directory 2026-06-05 [SOLL - Doku schreibt 'tools' lowercase, NTFS case-insensitive]
Workspace Directory 2026-06-04 [nicht in Soll-Doku]
### KNOWN FOLDER REDIRECTS (Ist) ###
Desktop -> D:\00_Inbox\Desktop [ABWEICHUNG! Soll: D:\Micha\Desktop]
Documents -> D:\10_Dokumente [OK]
Downloads -> D:\14_Downloads [OK]
Pictures -> D:\11_Bilder [OK]
Music -> D:\13_Musik [OK]
Videos -> D:\12_Videos [OK]
### DOPPELBESTAND D:\Micha\* vs D:\NN_* ###
D:\Micha\Dokumente : NICHT VORHANDEN | D:\10_Dokumente : 4011 Dateien, 595 MB
D:\Micha\Bilder : NICHT VORHANDEN | D:\11_Bilder : 7789 Dateien, 12367 MB
D:\Micha\Videos : 1 Datei, 0 MB | D:\12_Videos : 1 Datei, 0 MB
D:\Micha\Musik : NICHT VORHANDEN | D:\13_Musik : 0 Dateien
D:\Micha\Downloads : NICHT VORHANDEN | D:\14_Downloads : 2186 Dateien, 2211 MB
D:\Micha\Finanzen : NICHT VORHANDEN | D:\30_Finanzen : 126 Dateien, 123 MB
-63
View File
@@ -1,63 +0,0 @@
### OS BASELINE ###
Caption: Microsoft Windows 11 Pro
Build: 26200
Version: 10.0.26200
Architecture: 64-Bit
InstallDate: 2026-05-10 13:11:27
LastBoot: 2026-06-05 07:57:08
Uptime: 0.04 Tage (~1 Stunde zum Audit-Zeitpunkt)
Manufacturer: Micro-Star International Co., Ltd.
Model: MS-7D32
RAM: 31.79 GB
CPU: Intel Core i5-14600KF, 14 Cores, 20 Threads, 3500 MHz
### AKTIVIERUNG ###
Name: Windows(R), Professional edition
LicenseStatus: 1 (Aktiv)
Channel: OEM_DM
### AUSSTEHENDE UPDATES ###
Windows Update pending: 0
Reboot pending: Nein
### DEFENDER ###
AMProductVersion: 4.18.26040.7
AMServiceEnabled: True
AntivirusEnabled: True
AntispywareEnabled: True
RealTimeProtection: True
TamperProtection: True
SignatureAge: 0 Tage (aktuell)
Exclusions: KEIN ADMIN -> nicht lesbar
ASR Rules: KEIN ADMIN -> nicht lesbar (Get-MpPreference liefert leer)
### FIREWALL ###
Domain: Enabled, DefaultInboundAction: NotConfigured, DefaultOutboundAction: NotConfigured
Private: Enabled, DefaultInboundAction: NotConfigured, DefaultOutboundAction: NotConfigured
Public: Enabled, DefaultInboundAction: NotConfigured, DefaultOutboundAction: NotConfigured
HINWEIS: NotConfigured = Windows-Default (eingehend blockieren, ausgehend erlauben)
### BITLOCKER ###
KEIN ADMIN -> Get-BitLockerVolume verweigert (Access Denied). Status unbekannt.
### SECURE BOOT ###
KEIN ADMIN -> Confirm-SecureBootUEFI verweigert. Status unbekannt.
### TPM ###
KEIN ADMIN -> Get-Tpm liefert alle Felder leer. Status unbekannt.
### UAC ###
EnableLUA: 1 (aktiv)
ConsentPromptBehaviorAdmin: 5 (Nachfrage mit UI, ohne Secure Desktop laut Wert, aber...)
PromptOnSecureDesktop: 1 (Secure Desktop ist AN - Standard-Konfiguration korrekt)
### LOKALE ADMINS ###
Gruppe Administratoren: Administrator, michi
### BCD ###
KEIN ADMIN -> bcdedit /enum verweigert.
Letzte bekannte Aussage (Doku boot-cleanup-plan): Keine partition=F: Referenz nach Cleanup + Neustarttest.
### WinRE ###
KEIN ADMIN -> reagentc /info verweigert.
Letzte bekannte Aussage (Doku): WinRE Disabled.
-58
View File
@@ -1,58 +0,0 @@
### NETZWERK-ADAPTER (UP) ###
Ethernet Intel I225-V MAC: 04-7C-16-53-04-E4 1 Gbps
Tailscale Tunnel 100 Gbps (virtuell)
vEthernet WSL (Hyper-V) MAC: 00-15-5D-F3-5F-C9 10 Gbps (virtuell)
### IP-ADRESSEN ###
Ethernet: 192.168.178.103/24
Tailscale: 100.78.133.37/32
WSL bridge: 172.26.80.1/20
(WLAN, Bluetooth etc.: APIPA 169.254.x.x - nicht konfiguriert/inaktiv)
### DNS ###
Ethernet DNS: 192.168.178.58 (= Kallilabcore AdGuard Home)
WLAN DNS: 192.168.178.58
### TAILSCALE STATUS ###
100.78.133.37 baerchen-1 (dieser Rechner) online
100.105.203.21 baerchen (alter Rechner) offline, last seen 20h ago
100.73.83.55 iphone-14 iOS online
100.112.0.90 kallilab-core linux online
100.80.98.33 kallilabcore linux active; direct 192.168.178.58:49917
### LAUSCHENDE TCP-PORTS ###
Port Adresse Prozess Bemerkung
135 0.0.0.0/:: svchost RPC Endpoint Mapper
139 192.168.178.103 System NetBIOS
445 :: System SMB
3000 ::1/:: wslrelay / docker Docker / WSL lokal
5040 0.0.0.0 svchost WS-Discovery (WDAS)
5357 :: System WSD HTTP
7680 :: svchost WUDO (Delivery Optimization)
11434 127.0.0.1 ollama Ollama API (lokal)
22885 127.0.0.1 Battle.net lokal
26822 127.0.0.1 MSI.TerminalServer MSI Center
27036 0.0.0.0 steam Steam Remote Play (0.0.0.0 - offen!)
27060 127.0.0.1 steam Steam lokal
32683 127.0.0.1 MSI.CentralServer MSI Center
33683 127.0.0.1 MSI.CentralServer MSI Center
38810 fd7a:... tailscaled
49553 100.78.133.37 tailscaled
50123 127.0.0.1 iCUE Corsair lokal
51037 127.0.0.1 RazerAppEngine
55316 127.0.0.1 RazerAppEngine
59686 127.0.0.1 steam
60999 127.0.0.1 Agent Claude Code
### SSH ###
~\.ssh\config: LEER (keine Host-Eintraege)
~\.ssh\id_ed25519: vorhanden (411 Bytes, erstellt 2026-04-04)
~\.ssh\id_ed25519.pub: vorhanden (97 Bytes)
~\.ssh\known_hosts: vorhanden (4719 Bytes, zuletzt 2026-06-04)
~\.ssh\known_hosts.old + .pre-port222-Backup: vorhanden
KEY PERMISSIONS id_ed25519:
NT-AUTORITAET\SYSTEM FullControl Allow
VORDEFINIERT\Administratoren FullControl Allow
baerchen\michi FullControl Allow
BEFUND: Zu viele Berechtigungen - Admins-Gruppe hat FullControl auf Private Key.
-66
View File
@@ -1,66 +0,0 @@
### DEV TOOLCHAIN ###
git: 2.54.0.windows.1
python: 3.13.13
node: 24.16.0 (LTS)
go: 1.26.4 windows/amd64
### GIT CONFIG ###
user.name: michaelkaleschke-spec
user.email: michaelkaleschke@googlemail.com
commit.gpgsign: nicht gesetzt (Commits nicht signiert)
### WSL ###
Ubuntu Stopped Version 2
docker-desktop Running Version 2
### DOCKER CONTEXTS ###
default npipe:////./pipe/docker_engine (nicht aktiv)
desktop-linux* npipe:////./pipe/dockerDesktopLinuxEngine (aktiv)
### KUBECTL ###
Keine Contexts konfiguriert.
### WINGET INVENTAR (158 Pakete, Auswahl) ###
CPUID CPU-Z MSI 2.20.1
CPUID HWMonitor 1.63
CrystalDiskInfo 9.9.1
Docker Desktop 4.76.0
Git 2.54.0
AusweisApp 2.5.1
Node.js LTS 24.16.0
Corsair iCUE5 5.46.67
NVIDIA App 11.0.7.247 / Treiber 610.47
WISO Steuer 2026 33.07.3410
Go 1.26.4
Microsoft Edge 148.0.3967.96
Microsoft OneDrive 23.038 (Update verfuegbar: 26.078)
RivaTuner Statistics Server 7.3.7
Razer Synapse 4.0.683
Steam 2.10.91.91
Banking4 Home
Battle.net / Hearthstone / Overwatch / World of Warcraft
Microsoft 365 16.0.20026.20140
### AUTOSTART ###
HKCU\Run:
BraveSoftware Update -> BraveUpdateCore.exe
Steam -> E:\Steam\steam.exe -silent
RazerAppEngine -> Synapse autoStart
Docker Desktop -> Docker Desktop.exe
HKLM\Run:
SecurityHealth -> SecurityHealthSystray.exe
Corsair iCUE5 -> iCUE Launcher.exe --autorun
RtkAudUService -> Realtek Audio Service
Startup-Ordner (User): Ollama.lnk
Startup-Ordner (Alle): Tailscale.lnk
### GEPLANTE TASKS (nicht-Microsoft, aktiv) ###
OneDrive Reporting Task
OneDrive Startup Task
OneDrive Per-Machine Standalone Update Task
PostponeDeviceSetupToast
BraveSoftwareUpdateTask (2x User-Varianten)
NVIDIA App SelfUpdate
SoftLanding\CreativeManagementTask [UNBEKANNT - pruefen]
-45
View File
@@ -1,45 +0,0 @@
### HARDWARE ###
CPU: Intel Core i5-14600KF, 14 Cores / 20 Threads, 3500 MHz Base
RAM: 31.79 GB
MB: MSI MS-7D32
Energieplan: Ausbalanciert (381b4222) - aktiv
Verfuegbare Plaene: Ausbalanciert, Ultimative Leistung, Hoechstleistung, Energiesparmodus
### PHYSICAL DISKS (SMART) ###
INTEL SSDSC2BW180A3L SSD Healthy OK (Disk 0, C:)
INTEL SSDSC2BW180A3L SSD Healthy OK (Disk 1, D:)
Samsung SSD 980 PRO 1TB SSD Healthy OK (Disk 2, E:)
WDC WDS100T2B0C SSD Healthy OK (Disk 3, G:)
asmedia ASM235 Unspecified Healthy OK (Disk 4, H:)
Get-StorageReliabilityCounter: keine Ausgabe (Wear-Daten nicht via WMI verfuegbar - typisch fuer SATA SSDs und USB)
### GERAETE MIT STATUS "Unknown" (PnP) ###
MyBookLiveDuo (SoftwareDevice) - Netzwerkgeraet, nicht angebunden - erwartet
HID-Tastatur (Keyboard) - ghosted device - harmlos
Dell S2722DGM (DP) (Monitor) - Display-Enumeration Artefakt
Generic Monitor x2 - Display-Enumeration Artefakt
[LG] webOS TV OLED65G48LW x2 - Netzwerkgeraet, nicht lokal - erwartet
Standard-Volumeschattenkopie x3 - VSS Snapshots - erwartet
KEINE echten Fehlercodes (kein gelbes Ausrufezeichen).
### EVENT LOG FEHLER seit Installation (2026-05-10) ###
ID 20 (70x): Defender KB4052623 Installation fehlgeschlagen (0x80240016)
-> Timing-Problem bei Update-Kaskade, harmlos wenn aktuell
ID 10010 (15x): DCOM Server-Timeout {3E11DF0F-...}
-> bekanntes Windows-Hintergrundrauschen, harmlos
ID 7000 (3x): Steam Client Service Start fehlgeschlagen
-> Steam war beim Boot noch nicht bereit, harmlos
ID 7023 (3x): Windows Modules Installer beendet mit Fehler
-> Update-Installationsabbrueche, pruefbar nach Analyse der Zeitstempel
ID 6008 (2x): Unerwartetes Herunterfahren am 2026-05-19 13:56:56
-> Einmaliger Vorfall (BSOD oder Stromausfall) kurz nach Installation
ID 7034 (2x): MSI Center Service unerwartet beendet
-> bekannte Instabilitaet MSI Center, harmlos wenn kein Datenverlust
ID 7043 (1x): Dienst konnte nicht gestoppt werden
ID 1012 (3x): unbekannte ID - weitere Analyse noetig
ID 36 (2x): unbekannte ID - weitere Analyse noetig
### CRASH DUMPS ###
C:\Windows\Minidump: nicht vorhanden
C:\Windows\MEMORY.DMP: nicht vorhanden
Bewertung: kein BSOD-Dump vorhanden (ggf. Dump-Einstellung "automatisch neu starten" ohne Dump-Schreiben)
+2 -20
View File
@@ -1,6 +1,6 @@
# AI Context # AI Context
Stand: 2026-06-05 Stand: 2026-06-02
Kurzer Kontext fuer KI-Agenten. Nicht als Ersatz fuer die echten Runbooks lesen. Kurzer Kontext fuer KI-Agenten. Nicht als Ersatz fuer die echten Runbooks lesen.
@@ -43,32 +43,14 @@ Kurzer Kontext fuer KI-Agenten. Nicht als Ersatz fuer die echten Runbooks lesen.
## Aktuelle Restpunkte ## Aktuelle Restpunkte
Authoritativ: `docs/MASTER_TODO.md`. Authoritativ: `docs/AUDIT_2026-05-25_TODO.md`.
Kurzfassung: Kurzfassung:
- Auth-/OIDC-/CrowdSec-/Hermes-Themen bewusst geparkt - Auth-/OIDC-/CrowdSec-/Hermes-Themen bewusst geparkt
- Wochenend-Sprint 2026-06-05: `docs/WEEKEND_EXECUTION_PLAN_2026-06-05.md`
und `docs/WEEKEND_STATUS_2026-06-05.md`
Letzte Bestaetigung: Letzte Bestaetigung:
- Windows-Image `baerchen`: Veeam Agent Free Job `baerchen-c-image` auf
`\\kallilabcore\backups\windows-images\baerchen`, erster Full-Backup-Lauf
2026-06-05 erfolgreich, GUI-Wert 53,8 GB, Dauer 0:11:31. Recovery-USB ist
erstellt; Boot-/SMB-/Restore-Point-Test ohne Restore ist noch offen.
- Veeam Storage Encryption ist beim ersten Full-Lauf nicht aktiv
(`StorageEncryptionEnabled=False`); nachtraegliche Aktivierung ist eine
Operator-Entscheidung, weil sie Passwort- und Restore-Prozess aendert.
- BitLocker fuer `baerchen` ist bewusst nicht aktiviert und bleibt
Operator-Entscheidung.
- Tailscale-Inventar 2026-06-05 real gemessen: `Kallilabcore`
`100.80.98.33`, IPv6 `fd7a:115c:a1e0::2c01:62b2`, kein Exit Node, aber
aktiver Subnet Router fuer `192.168.178.0/24`. Dadurch ist die Tailnet-ACL
sicherheitsrelevant; Entscheidung Default-Allow vs tag-basierte ACL offen.
- Unraid-Flash-Backup-Artefaktpruefung: `ops/maintenance/check-unraid-flash-backup.sh`
prueft Artefakt, SHA256, Alter und Kern-Configs. Test 2026-06-05 gegen Host
erfolgreich laut `docs/MASTER_TODO.md`.
- Borg-Nachlauf 2026-06-01 erfolgreich: Archiv `Taegliche-Sicherung-2026-06-01T04:30:26.913`, Freshness Critical 0 / Warnings 0. - Borg-Nachlauf 2026-06-01 erfolgreich: Archiv `Taegliche-Sicherung-2026-06-01T04:30:26.913`, Freshness Critical 0 / Warnings 0.
- H:/ Nearline-Pull 2026-06-01 repariert: Borg-Dumps werden kuratiert kopiert, Gitea-Bundles aktuell. - H:/ Nearline-Pull 2026-06-01 repariert: Borg-Dumps werden kuratiert kopiert, Gitea-Bundles aktuell.
- Family-Status-Dashboard liegt als `monitoring/grafana/dashboards/family-status.json` im Repo. - Family-Status-Dashboard liegt als `monitoring/grafana/dashboards/family-status.json` im Repo.
+2 -4
View File
@@ -1,6 +1,6 @@
# Alert Rules # Alert Rules
Stand: 2026-06-05 Stand: 2026-05-31
Diese Datei beschreibt die produktiven Alarmwege und wichtigsten Regeln. Die Diese Datei beschreibt die produktiven Alarmwege und wichtigsten Regeln. Die
Konfiguration selbst liegt in `monitoring/prometheus/alerts.yml` und in den Konfiguration selbst liegt in `monitoring/prometheus/alerts.yml` und in den
@@ -49,6 +49,4 @@ Die Liste der ueberwachten Critical-Container steht in
- Kein Inode-Alarm. Bei Paperless/Immich spaeter sinnvoll, aber aktuell kein - Kein Inode-Alarm. Bei Paperless/Immich spaeter sinnvoll, aber aktuell kein
dokumentierter Vorfall. dokumentierter Vorfall.
- Container-Memory-Limits werden erst nach realen Peak-Daten gesetzt; OOM/kill - Container-Memory-Limits werden erst nach realen Peak-Daten gesetzt; OOM/kill
wird ueber `docker-critical-events.sh` gemeldet, sobald der Host-Watcher per wird bereits ueber `docker-critical-events.sh` gemeldet.
Unraid User Script aktiviert ist. Start/Stop/Status/Smoke laufen ueber
`services/posture-check/docker-critical-events-supervisor.sh`.
+1 -5
View File
@@ -3,14 +3,11 @@
Status: **kompakte Restliste**. Die erledigten Sprint-Tabellen und langen Status: **kompakte Restliste**. Die erledigten Sprint-Tabellen und langen
Audit-Snapshots wurden aus der Arbeitskopie entfernt; Detailhistorie liegt in Git. Audit-Snapshots wurden aus der Arbeitskopie entfernt; Detailhistorie liegt in Git.
Letzter Sync mit `docs/MASTER_TODO.md`: 2026-06-05. Offene Punkte sind deckungsgleich;
neue Restore-Runbook-Stubs (Unraid Flash / AdGuard / Tailscale / Redis 8) wurden
in `docs/RESTORE_MATRIX.md` ergaenzt.
## Aktuell offene Punkte ## Aktuell offene Punkte
| Prioritaet | Punkt | Naechster Schritt | | 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` | | 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) ## Restore-Audit Backlog (Stand 2026-06-03)
@@ -43,7 +40,6 @@ Ergebnis des Restore-Skills-Audits (Session 2026-06-02/03). Die kritischen Bugfi
## Zuletzt geschlossen ## 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`. - 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. - 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. - 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.
-186
View File
@@ -1,186 +0,0 @@
# Authelia OIDC fuer Apps - Plan & Runbook
Stand: 2026-06-06. Authelia-Version: **v4.39.20**.
Ziel: App-uebergreifendes Single-Sign-On ueber Authelia als OpenID-Connect-Provider
(`https://auth.kaleschke.info`). Statt pro App eigener Logins meldet man sich einmal
bei Authelia an (inkl. 2FA) und wird per OIDC an die App durchgereicht.
> **Status:** aktives Runbook. Grafana und Mealie sind seit 2026-06-06 live
> und per Login-Smoke verifiziert. Der weitere Rollout bleibt additiv: lokale
> App-Logins bleiben als Fallback aktiv.
---
## Grundregeln (wichtig)
- **Secrets gehoeren nie ins Repo.** OIDC-Client-Secrets (Klartext und pbkdf2-Hash)
liegen ausschliesslich in der Host-Config `/mnt/user/appdata/authelia/config/configuration.yml`
(Hash) und im jeweiligen App-Stack (Klartext, via Komodo Stack-ENV / Secret-Datei),
plus optional Vaultwarden. Dieses Dokument enthaelt nur Schema und Variablennamen.
- **OIDC-Clients leben host-seitig**, wie der bestehende `beszel`-Client. Die Repo-Baseline
`security/authelia/configuration.yml` haelt nur die nicht-geheime Struktur
(`access_control` etc.); `services/authelia-diff.sh` vergleicht standardmaessig nur
`access_control`, OIDC-Clients auf dem Host loesen also keinen Drift-Alarm aus.
- **Issuer/Endpoints** (Authelia OIDC):
- Issuer: `https://auth.kaleschke.info`
- Authorization: `https://auth.kaleschke.info/api/oidc/authorization`
- Token: `https://auth.kaleschke.info/api/oidc/token`
- Userinfo: `https://auth.kaleschke.info/api/oidc/userinfo`
- JWKS: `https://auth.kaleschke.info/jwks.json`
- Discovery: `https://auth.kaleschke.info/.well-known/openid-configuration`
- **PKCE an, wo moeglich** (`require_pkce: true`, `S256`), wie beim Beszel-Client.
---
## Client-Schema (Authelia v4.39, gespiegelt vom bestehenden `beszel`-Client)
Pro App ein Block unter `identity_providers.oidc.clients` in der **Host-Config**:
```yaml
identity_providers:
oidc:
clients:
- client_id: '<app>'
client_name: '<App-Name>'
client_secret: '<pbkdf2-sha512-Hash - NUR auf dem Host>'
public: false
authorization_policy: 'two_factor' # admin-Apps: two_factor; Familien-Apps: s.u.
require_pkce: true
pkce_challenge_method: 'S256'
redirect_uris:
- 'https://<app>.kaleschke.info/<oidc-callback-pfad>'
scopes:
- 'openid'
- 'profile'
- 'email'
- 'groups'
response_types:
- 'code'
grant_types:
- 'authorization_code'
token_endpoint_auth_method: 'client_secret_basic'
userinfo_signed_response_alg: 'none'
```
### Client-Secret erzeugen (auf dem Host)
```bash
docker exec authelia authelia crypto hash generate pbkdf2 \
--variant sha512 --random --random.length 72 --random.charset rfc3986
```
- Ausgabe: **Random Password** (Klartext) + **Digest** (pbkdf2-Hash).
- **Hash** -> Host-Config `client_secret`.
- **Klartext** -> App-Stack (Komodo Stack-ENV/Secret) + optional Vaultwarden.
- Klartext **nicht** ins Repo, nicht in Logs.
---
## Reihenfolge / Rollout
| Stufe | App | Domain | OIDC-Support | Policy | Risiko | Begruendung |
|---|---|---|---|---|---|---|
| **1 (Proof) ERLEDIGT 2026-06-06** | Grafana (monitoring) | `monitoring.kaleschke.info` | nativ (`generic_oauth`) | `two_factor` | niedrig | **Live + Login verifiziert.** Authelia-Client `grafana` (host), Secret als Datei `/mnt/user/appdata/secrets/grafana_oidc_client_secret` via `__FILE`, ForwardAuth-Middleware durch OIDC ersetzt, lokaler Admin bleibt Fallback |
| 2 | Immich | `immich.kaleschke.info` | nativ (Admin-UI/Config-File) | s. u. (Familie) | mittel | **GEPARKT bis Onboarding (Entscheidung 2026-06-06):** nur `micha` hat Authelia-Account, Familien-SSO-Nutzen entsteht erst mit Familien-Accounts; Immich ist mobil-lastig (hoechste Stoeranfaelligkeit) und braucht UI/Config-File. Erst nach Onboarding gezielt. Runbook bereit. |
| 3 | Nextcloud | `cloud.kaleschke.info` | App `user_oidc` (+occ) | s. u. | mittel | **GEPARKT bis Onboarding (Entscheidung 2026-06-06):** wie Immich; braucht `user_oidc`-App-Install + `occ`. Lokaler Login bleibt. Erst nach Onboarding. Runbook bereit. |
| **4 ERLEDIGT 2026-06-06** | Mealie | `mealie.kaleschke.info` | nativ | `one_factor` | niedrig | **Live + Login verifiziert.** OIDC-Env additiv (lokaler Login bleibt), Secret als Stack-ENV `${MEALIE_OIDC_CLIENT_SECRET}`, `extra_hosts` noetig (s. Gotchas) |
| 5 | Paperless-ngx | `paperless.kaleschke.info` | `django-allauth` (Umgebungsvariablen) | `two_factor` | mittel | dokumentenlastig, Operator-nah |
**Nicht OIDC:** Vaultwarden hat kein Standard-Endnutzer-OIDC (SSO ist Enterprise/Bitwarden-Feature) -> bleibt eigener Login. ntfy bleibt wie gehabt.
### Policy Familien-Apps
- Admin-Apps (Grafana, Paperless): `authorization_policy: two_factor`.
- Familien-Apps (Immich, Nextcloud, Mealie): Start mit `one_factor` und lokalen
App-Logins als Fallback. 2FA fuer Familie erst spaeter, sobald TOTP-Enrollment
pro Person eingerichtet ist; sonst entsteht unnoetiges Lockout-Risiko.
---
## Stufe 1 konkret: Grafana (empfohlener Erststart)
### A) Authelia (Host) - Client anlegen
1. Secret erzeugen (Befehl oben). Klartext + Hash notieren.
2. In `/mnt/user/appdata/authelia/config/configuration.yml` unter
`identity_providers.oidc.clients` neuen Block einfuegen:
```yaml
- client_id: 'grafana'
client_name: 'Grafana'
client_secret: '<HASH>'
public: false
authorization_policy: 'two_factor'
require_pkce: true
pkce_challenge_method: 'S256'
redirect_uris:
- 'https://monitoring.kaleschke.info/login/generic_oauth'
scopes: ['openid', 'profile', 'email', 'groups']
response_types: ['code']
grant_types: ['authorization_code']
token_endpoint_auth_method: 'client_secret_basic'
userinfo_signed_response_alg: 'none'
```
3. `docker restart authelia`, Health + Log pruefen (`Startup complete`, keine Fehler).
### B) Grafana (Komodo Stack-ENV) - generic_oauth
Im `monitoring`-Stack (Grafana) setzen (Klartext-Secret aus Schritt A):
```
GF_AUTH_GENERIC_OAUTH_ENABLED=true
GF_AUTH_GENERIC_OAUTH_NAME=Authelia
GF_AUTH_GENERIC_OAUTH_CLIENT_ID=grafana
GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=<KLARTEXT-SECRET>
GF_AUTH_GENERIC_OAUTH_SCOPES=openid profile email groups
GF_AUTH_GENERIC_OAUTH_AUTH_URL=https://auth.kaleschke.info/api/oidc/authorization
GF_AUTH_GENERIC_OAUTH_TOKEN_URL=https://auth.kaleschke.info/api/oidc/token
GF_AUTH_GENERIC_OAUTH_API_URL=https://auth.kaleschke.info/api/oidc/userinfo
GF_AUTH_GENERIC_OAUTH_USE_PKCE=true
GF_AUTH_GENERIC_OAUTH_ALLOW_SIGN_UP=true
# optional Rollen-Mapping ueber groups:
# GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH=contains(groups[*], 'admins') && 'Admin' || 'Viewer'
```
- `GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET` als Stack-ENV-only (kein `_FILE`-Support) -> in
`docs/SECRETS_MAP.md` als `grafana_oidc_client_secret` (Stack-ENV) nachziehen.
### C) Test + Rollback
- Test: `monitoring.kaleschke.info` -> "Sign in with Authelia" -> Authelia-Login (2FA) -> zurueck in Grafana, eingeloggt.
- **Fallback bleibt:** lokaler Grafana-Admin-Login (`/login`) ist weiter aktiv -> kein Lockout.
- Rollback: `GF_AUTH_GENERIC_OAUTH_ENABLED=false` (Grafana redeploy) und/oder Client-Block in Authelia entfernen + `docker restart authelia`.
---
## Doku-Nachzug bei jedem neuen Client
- `docs/SECRETS_MAP.md`: pro App `<app>_oidc_client_secret` (Stack-ENV) + Hinweis "Hash in Authelia-Host-Config".
- `docs/SERVICE_CATALOG.md`: App-Zeile um "OIDC via Authelia" ergaenzen.
- Dieses Dokument: Rollout-Tabelle abhaken.
- `docs/MASTER_TODO.md`: Fortschritt im OIDC-Punkt nachziehen.
---
## Gotchas (aus dem realen Rollout 2026-06-06)
- **`extra_hosts` ist Pflicht fuer App-Container, die selbst zu Authelia connecten**
(OIDC-Discovery/Token sind Server-zu-Server): Der App-Container loest
`auth.kaleschke.info` per Docker-DNS oft nicht auf -> `httpx.ConnectTimeout` /
500 beim OAuth-Start. Fix wie Komodo:
```yaml
extra_hosts:
- "auth.kaleschke.info:192.168.178.58"
```
Cert validiert weiter (SNI/Hostname bleibt gleich, nur die IP wird gemappt).
Gilt fuer Mealie (bestaetigt) und sehr wahrscheinlich Paperless/Immich/Nextcloud.
- **Additiv heisst additiv:** OIDC als zusaetzlichen Login aktivieren, lokalen
Login NICHT abschalten, `AUTO_REDIRECT`/Force-OIDC aus -> kein Lockout.
- **Account-Linking per E-Mail:** Apps verknuepfen den OIDC-User i. d. R. per
E-Mail-Claim. Stimmt die Authelia-E-Mail mit dem App-Account, wird verknuepft;
sonst legt die App (bei aktivem Signup) einen neuen User an.
- **Secret-Mechanik je App verschieden:** Grafana `__FILE` (Docker-Secret),
Mealie Stack-ENV `${...}`. Hash immer in der Authelia-Host-Config, Klartext nie ins Repo.
## Spaetere Feinschliffe vor breitem Rollout
1. Gruppen/Rollen-Mapping: braucht es Authelia-Gruppen (z. B. `admins`, `family`) fuer
App-Rollen (Grafana Admin/Viewer, Nextcloud-Gruppen)? Wenn ja, in der Authelia
User-Datenbank Gruppen pflegen.
2. Familien-2FA spaeter neu bewerten, nachdem echte Familien-Accounts in Authelia
angelegt und TOTP pro Person verstanden ist.
+1 -44
View File
@@ -290,14 +290,7 @@ Erfolgskriterium: `docker network ls` zeigt `frontend_net`, `backend_net`, `moni
1. `traefik/` 1. `traefik/`
2. `host-services/Adguard/` 2. `host-services/Adguard/`
3. `host-services/tailscale/`
> **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`).
**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. **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.
@@ -529,40 +522,6 @@ Smoke-Test: `hermes-gateway` healthcheck ist gruen, `hermes.kaleschke.info` leit
`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. `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.
### Windows-Workstation `baerchen`
`baerchen` ist die Operator-Workstation und haelt den lokalen Clone unter
`G:\Gitea_Clone\homelab-infra`. Fuer einen schnellen Windows-Bare-Metal-Restore
existiert ein Veeam-Agent-Image-Workflow.
Wichtige Pfade und Artefakte:
- Runbook: `ops/windows-reinstall/docs/windows-image-backup-baseline.md`
- Backup-Ziel: `\\kallilabcore\backups\windows-images\baerchen`
- Host-Pfad: `/mnt/user/backups/windows-images/baerchen/`
- Recovery-Medium: USB-Stick `VEEAMRE`, beschriftet
`baerchen Veeam Recovery - 2026-06-05`
- Veeam Job: `baerchen-c-image`
- Veeam Storage Encryption: erster Full-Lauf 2026-06-05 laut Job-Log
unverschluesselt (`StorageEncryptionEnabled=False`); falls spaeter aktiviert,
Passwort in Vaultwarden Secure Note `Veeam baerchen backup encryption password`
sichern
Restore-Kurzpfad:
1. Von `VEEAMRE` booten.
2. SMB-Ziel `\\kallilabcore\backups\windows-images\baerchen` oeffnen.
3. Mit bestehendem SMB-User `micha` authentifizieren.
4. Restore Point auswaehlen.
5. Falls der Restore Point verschluesselt ist: Veeam-Encryption-Passwort aus
Vaultwarden eingeben.
6. Bare-Metal-Restore nur auf die Windows-Systemdisk ausfuehren.
BitLocker ist am 2026-06-05 bewusst noch nicht aktiv. Falls BitLocker spaeter
aktiviert wird, muss der Recovery-Key vor dem naechsten Restore-Drill in
Vaultwarden, unter `D:\30_Finanzen\BitLocker-RecoveryKey-baerchen-<DATUM>.txt`
und physisch ausserhalb des Rechners abgelegt sein.
--- ---
## 11. Offene Vorbereitungs-To-dos ## 11. Offene Vorbereitungs-To-dos
@@ -572,8 +531,6 @@ und physisch ausserhalb des Rechners abgelegt sein.
- 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
- `baerchen` Recovery-USB-Boot-/SMB-Test nach erfolgreichem erstem Full-Lauf
verifizieren
--- ---
-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: 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 ```bash
cat > ~/dr-smoke.sh <<'EOF' cat > ~/dr-smoke.sh <<'EOF'
#!/bin/bash #!/bin/bash
+1 -2
View File
@@ -21,7 +21,7 @@ Dieses Dokument beschreibt externe Anbieter und Konten, von denen Betrieb, Recov
| OpenAI API | Paperless-GPT LLM und Vision-OCR | mittel | Automatische Dokument-Titel, Tags, Korrespondenten und LLM-OCR fallen aus; Paperless selbst laeuft weiter | OpenAI-Projekt/API-Key ausserhalb Repo | Key in Vaultwarden/Komodo sichern, bei Offenlegung rotieren; Kosten/Usage im OpenAI-Projekt beobachten | | OpenAI API | Paperless-GPT LLM und Vision-OCR | mittel | Automatische Dokument-Titel, Tags, Korrespondenten und LLM-OCR fallen aus; Paperless selbst laeuft weiter | OpenAI-Projekt/API-Key ausserhalb Repo | Key in Vaultwarden/Komodo sichern, bei Offenlegung rotieren; Kosten/Usage im OpenAI-Projekt beobachten |
| Let's Encrypt | TLS-Zertifikate | hoch | Cert-Erneuerung faellt aus | automatisch via Traefik und Cloudflare DNS-Challenge | Cert-Expiry Alert einrichten; Cloudflare-Token und Traefik-Storage pruefen | | Let's Encrypt | TLS-Zertifikate | hoch | Cert-Erneuerung faellt aus | automatisch via Traefik und Cloudflare DNS-Challenge | Cert-Expiry Alert einrichten; Cloudflare-Token und Traefik-Storage pruefen |
| Container Registries | Image Pulls von Docker Hub, GHCR, LSCR, Gitea Registry u. a. | mittel | Redeploy/Update blockiert | ueberwiegend oeffentlich; keine produktiven Registry-Tokens im Repo | Gepinnte Digests und lokale Runtime helfen kurzfristig; Updates geplant und einzeln deployen | | Container Registries | Image Pulls von Docker Hub, GHCR, LSCR, Gitea Registry u. a. | mittel | Redeploy/Update blockiert | ueberwiegend oeffentlich; keine produktiven Registry-Tokens im Repo | Gepinnte Digests und lokale Runtime helfen kurzfristig; Updates geplant und einzeln deployen |
| Plex Konto | Plex native Auth, Claim und Client-Zugriff ueber `plex.kaleschke.info` | mittel | Plex-Web/App-Login und Clients koennen ausfallen; LAN-Medienpfade bleiben lokal | Plex-Konto ausserhalb Repo; `PLEX_CLAIM` nur fuer Setup | Plex Remote Access bleibt aus; externer Zugriff laeuft ueber Traefik/443. Konto-Recovery separat sichern | | Plex Konto/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 | | 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 |
| Operator-DR-Workstation | Bare-Metal-Recovery-Arbeitsplatz (Gaming-PC Windows, lokaler Repo-Clone `G:\Gitea_Clone\homelab-infra`) | kritisch | Ohne Workstation kein Borg-Extract, kein Hetzner-Zugriff, kein Repo-Bootstrap; der Unraid-Host ist im Bare-Metal-Fall gerade weg | Operator-PC, WSL2 + Borg-Client, SSH-Key fuer Hetzner Storage Box, Offline-Kopie der Borg-Passphrase | Setup als bewusste DR-Vorbedingung pflegen (siehe Abschnitt "DR-Workstation Bare-Metal-Kit") | | Operator-DR-Workstation | Bare-Metal-Recovery-Arbeitsplatz (Gaming-PC Windows, lokaler Repo-Clone `G:\Gitea_Clone\homelab-infra`) | kritisch | Ohne Workstation kein Borg-Extract, kein Hetzner-Zugriff, kein Repo-Bootstrap; der Unraid-Host ist im Bare-Metal-Fall gerade weg | Operator-PC, WSL2 + Borg-Client, SSH-Key fuer Hetzner Storage Box, Offline-Kopie der Borg-Passphrase | Setup als bewusste DR-Vorbedingung pflegen (siehe Abschnitt "DR-Workstation Bare-Metal-Kit") |
@@ -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 | 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 | 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-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 |
+13 -59
View File
@@ -25,7 +25,7 @@ Nachteile, ehrlich gesagt: Wenn der Server zuhause aus ist, sind die Apps weg, b
| **Vaultwarden** | Passwoerter sicher speichern und auf jedem Geraet nachschauen | Bitwarden-App (kostenlos), beim ersten Start Server-URL auf `vault.kaleschke.info` aendern lassen | | **Vaultwarden** | Passwoerter sicher speichern und auf jedem Geraet nachschauen | Bitwarden-App (kostenlos), beim ersten Start Server-URL auf `vault.kaleschke.info` aendern lassen |
| **Mealie** | Rezepte sammeln, Wochenplan, Einkaufsliste | Web `mealie.kaleschke.info` oder Mealie-App | | **Mealie** | Rezepte sammeln, Wochenplan, Einkaufsliste | Web `mealie.kaleschke.info` oder Mealie-App |
| **Paperless** | Briefe und wichtige Dokumente scannen, durchsuchen, ablegen | Web `paperless.kaleschke.info`; Scan-Workflow erklaert Michi | | **Paperless** | Briefe und wichtige Dokumente scannen, durchsuchen, ablegen | Web `paperless.kaleschke.info`; Scan-Workflow erklaert Michi |
| **Plex** | Filme und Musik auf Fernseher, Handy und Tablet | Web `https://plex.kaleschke.info` oder Plex-App auf dem Geraet, mit Konto anmelden | | **Plex** | Filme und Musik auf Fernseher, Handy und Tablet | Plex-App auf dem Geraet, mit Konto anmelden |
> Wenn du eine App auf dem Handy installierst und sie fragt nach einer Server-URL, ist das immer eine `...kaleschke.info`-Adresse. Wenn du dir nicht sicher bist, frag bevor du etwas eintippst. > Wenn du eine App auf dem Handy installierst und sie fragt nach einer Server-URL, ist das immer eine `...kaleschke.info`-Adresse. Wenn du dir nicht sicher bist, frag bevor du etwas eintippst.
@@ -193,66 +193,20 @@ Michi laesst es dich wissen, wenn ein Wartungsfenster geplant ist.
--- ---
## Erster Onboarding-Termin - Ablauf fuer Michi ## Onboarding-Checkliste fuer Michi
Diese Sektion ist die konkrete Checkliste fuer den **ersten echten Diese Punkte gehoeren in das erste echte Familien-Onboarding. Keine Secret-Werte
Familien-Onboarding-Termin**. Sie ist als ein zusammenhaengender Termin von in diese Datei schreiben.
ca. 30-45 Minuten pro Person gedacht. Keine Secret-Werte in diese Datei
schreiben.
> Operator-Eingabe vor dem Termin: festlegen, **wer** beim ersten Termin dabei | Status | Aufgabe |
> ist und **welche Geraete** real vorliegen. Die Checkliste funktioniert pro |---|---|
> Person identisch. | offen | Pro Familienmitglied Konto/Start-Passwort persoenlich uebergeben |
| offen | Vaultwarden/Bitwarden-App auf Handy einrichten |
### Vorher bereitlegen (Operator-Vorbereitung) | offen | Testeintrag in Vaultwarden anlegen |
| offen | Immich-App auf jedem Familien-Handy einrichten |
Diese Dinge muessen **vor** dem Termin fertig sein, sonst stockt der Ablauf: | offen | Immich-Backup mit ersten Fotos sichtbar pruefen |
| offen | Mealie mit erstem Rezept und Einkaufsliste praktisch ausprobieren |
- [ ] Pro Teilnehmer ist in **Vaultwarden** ein Benutzerkonto angelegt (Benutzername = Vorname klein). | offen | Danach entscheiden, ob Nextcloud/Paperless/Plex direkt mitkommen oder spaeter |
- [ ] Pro Teilnehmer ist in **Immich** ein Benutzerkonto angelegt.
- [ ] Pro Teilnehmer ist in **Mealie** ein Benutzerkonto angelegt.
- [ ] Start-Passwoerter sind erzeugt und liegen so bereit, dass sie persoenlich uebergeben werden koennen (nicht per Chat, nicht in diese Datei).
- [ ] Die Apps `cloud`, `immich`, `vault`, `mealie` sind erreichbar (kurzer eigener Smoke-Test ueber `https://...kaleschke.info`).
- [ ] Das Familien-Handy/Geraet jedes Teilnehmers ist da, entsperrt und im **Haus-WLAN**.
- [ ] App-Store-/Play-Store-Login auf dem Geraet funktioniert (zum Installieren der Apps).
### Reihenfolge beim Termin (pro Person)
Die Reihenfolge ist bewusst gewaehlt: erst der Passwort-Speicher, dann das, was
am meisten bringt (Fotos), dann das Gemeinsame (Rezepte).
1. **Konto-Uebergabe**: Benutzername + Start-Passwort persoenlich uebergeben, Person aendert das Passwort beim ersten Login.
2. **Vaultwarden / Bitwarden** (Abschnitt "Vaultwarden zuerst"):
- Bitwarden-App installieren, Server-URL `https://vault.kaleschke.info` setzen, anmelden.
- Master-Passwort gemeinsam festlegen (wird **nicht** bei Michi gespeichert).
- Testeintrag "Test KalliLab" anlegen und wiederfinden.
3. **Immich** (Abschnitt "Foto-Backup vom Handy einrichten"):
- Immich-App installieren, Server `https://immich.kaleschke.info`, anmelden.
- Hintergrund-Backup nur ueber WLAN aktivieren, Kamera-Album auswaehlen.
- App offen lassen, bis erste Fotos hochgeladen sind; in der Weboberflaeche sichtbar pruefen.
4. **Mealie** (Abschnitt "Rezepte und Einkaufsliste einrichten"):
- `https://mealie.kaleschke.info` anmelden.
- Gemeinsam ein erstes echtes Rezept anlegen, kategorisieren, Zutaten auf die Einkaufsliste setzen.
- Einkaufsliste auf dem Handy oeffnen und einen Eintrag abhaken.
5. **Abschluss**: kurz zeigen, was bei Problemen zu tun ist (Abschnitt "Was tun, wenn etwas nicht geht"), besonders Passwort-vergessen und 2FA-verloren.
### Erfolgskriterium des ersten Termins
Der Termin gilt als erfolgreich, wenn pro Person **diese drei** Dinge real laufen:
- [ ] Vaultwarden ist eingerichtet und ein Testeintrag wurde gefunden.
- [ ] Immich sichert Handy-Fotos und die ersten Fotos sind in der Weboberflaeche sichtbar.
- [ ] In Mealie existiert ein erstes Rezept mit einer Einkaufslisten-Position.
### Bewusst spaeter (nicht im ersten Termin)
Damit der erste Termin nicht ueberladen wird, kommen diese Punkte bewusst erst
in einem Folgetermin:
- **Nextcloud** (Dateien/Kalender/Adressbuch) - erst wenn die drei Kern-Apps sitzen.
- **Paperless** (Dokumente scannen) - braucht eigenen Scan-Workflow, separater Termin.
- **Plex** (Filme/Musik) - reines Komfort-Thema, kein Onboarding-Kern.
- **App-uebergreifendes Einheits-Login (SSO/OIDC)** - nicht eingerichtet, nur als Idee notiert (siehe "Bewusst nicht versprochen").
## Bewusst nicht versprochen ## Bewusst nicht versprochen
-117
View File
@@ -1,117 +0,0 @@
# Guest / IoT Network Runbook
Stand: 2026-06-06
Dieses Runbook beschreibt den sicheren Weg, das FRITZ!Box-Gastnetz zu aktivieren,
ohne versehentlich Homelab-Admin-Ports aus dem Gastsegment erreichbar zu machen.
## Zielbild
- Normales LAN bleibt `192.168.178.0/24`.
- Kallilabcore bleibt im normalen LAN unter `192.168.178.58`.
- FRITZ!Box-Gast-WLAN darf Internetzugang haben, aber keinen Zugriff auf
`192.168.178.0/24`.
- Homelab-Admin-Pfade bleiben Operator-only:
- Tailscale fuer Admin-Zugriff
- Authelia/2FA fuer geschuetzte Web-UIs
- keine LAN-Admin-Ports aus dem Gastnetz
## Vorbedingungen
Vor dem Einschalten des Gast-WLANs muessen diese Preflights gruen sein:
```powershell
G:\Gitea_Clone\homelab-infra\ops\maintenance\check-guest-iot-isolation.ps1 -Mode LanPreflight
```
Erwartung im normalen LAN:
- `192.168.178.58:8082` ist blockiert (AdGuard Admin nur Tailscale).
- `192.168.178.58:8181` ist blockiert (InfluxDB nicht LAN-exponiert).
- `192.168.178.58:80`, `443`, `222` koennen im normalen LAN erreichbar sein.
Auf Unraid zusaetzlich:
```bash
/mnt/user/services/homelab-infra/ops/maintenance/check-guest-iot-preflight.sh
```
Validierung 2026-06-06: Host-Preflight erfolgreich, Report
`/mnt/user/backups/restore-reports/guest-iot-preflight-2026-06-06-131316.md`.
Ergebnis: FRITZ!Box 7590 per TR-064 erreichbar, `192.168.178.58:8082`
blockiert, `100.80.98.33:8082` erreichbar, `192.168.178.58:8181` blockiert.
Gast-WLAN-Smoke 2026-06-06: Operator hat ein iPhone mit `Fritzi Gastzugang`
verbunden und folgende Ziele getestet; alle waren aus dem Gast-WLAN nicht
erreichbar:
- `http://192.168.178.58:8082`
- `http://192.168.178.58:8181`
- `http://192.168.178.58:222`
- `https://192.168.178.58`
- `http://192.168.178.1`
Damit ist die Gastnetz-Isolation fuer die getesteten Homelab-/Router-Adminpfade
validiert.
## FRITZ!Box Schritte
In der FRITZ!Box UI:
1. `WLAN -> Gastzugang` oeffnen.
2. `Gastzugang aktiv` einschalten.
3. WPA2/WPA3-Verschluesselung aktiv lassen.
4. Eigenen Gast-SSID-Namen setzen, z. B. `Fritzi-Gast`.
5. Starkes Passwort setzen und in Vaultwarden ablegen.
6. Option `Geraete im Gastnetz duerfen miteinander kommunizieren` deaktiviert
lassen, sofern nicht bewusst gebraucht.
7. Option fuer Zugriff auf das Heimnetz / private Netzwerk deaktiviert lassen.
8. Gastzugang speichern.
Wichtig: Die genaue FRITZ!OS-8.25-UI-Beschriftung kann leicht variieren. Der
entscheidende Punkt ist: Gastgeraete duerfen keinen Zugriff auf das Heimnetz
haben.
## Verifikation
Ein Handy oder Laptop mit dem Gast-WLAN verbinden, dann auf diesem Geraet testen:
```powershell
G:\Gitea_Clone\homelab-infra\ops\maintenance\check-guest-iot-isolation.ps1 -Mode Guest
```
Erwartung aus dem Gast-WLAN:
- `192.168.178.58:80` blockiert
- `192.168.178.58:443` blockiert
- `192.168.178.58:222` blockiert
- `192.168.178.58:8082` blockiert
- `192.168.178.58:8181` blockiert
- `192.168.178.1:80` blockiert oder nur Gast-Gateway-Ansicht
Wenn der Test `Risk count: 0` meldet, ist die Isolation fuer die getesteten
Homelab-Admin-Pfade ausreichend.
## Betrieb
- Familien-/Gaestegeraete kommen ins Gast-WLAN, wenn sie keinen direkten Zugriff
auf LAN-Geraete brauchen.
- Homelab-Apps fuer Familie laufen perspektivisch ueber HTTPS/OIDC, nicht ueber
direkten LAN-Zugriff.
- Geraete, die lokale Discovery brauchen (z. B. manche Smart-TV/Plex-Szenarien),
bleiben im normalen LAN oder bekommen eine separate bewusste Entscheidung.
## Rollback
Wenn nach Aktivierung etwas Unerwartetes passiert:
1. FRITZ!Box: `WLAN -> Gastzugang` oeffnen.
2. Gastzugang deaktivieren.
3. Speichern.
4. Normalen LAN-Zugriff pruefen:
```powershell
G:\Gitea_Clone\homelab-infra\ops\maintenance\check-guest-iot-isolation.ps1 -Mode LanPreflight
```
Es werden durch dieses Runbook keine Docker-Stacks, Secrets oder produktiven
Appdaten veraendert.
+8 -29
View File
@@ -3,20 +3,8 @@
Status: Hardware-Baseline erfasst; USV/Power-Loss ist als bewusst akzeptiertes Betreiber-Risiko dokumentiert. Status: Hardware-Baseline erfasst; USV/Power-Loss ist als bewusst akzeptiertes Betreiber-Risiko dokumentiert.
Host: `Kallilabcore` Host: `Kallilabcore`
Letzte Pruefung: 2026-05-26 Letzte Pruefung: 2026-05-26
Doku-Stand Betreiberentscheidungen: 2026-06-05
Naechster Review: 2026-08-26 Naechster Review: 2026-08-26
## Betreiber-Entscheidungen (Stand 2026-06-05)
Diese drei Punkte waren bisher diffuse TBDs und sind jetzt als bewusste
Entscheidungen festgehalten. Details in den jeweiligen Abschnitten unten.
| Thema | Entscheidung | Review-Trigger |
|---|---|---|
| USV / Power Loss | **Bewusst auf Q3/2026 geparkt.** Keine Anschaffung dieses Quartal; Power-Loss bleibt akzeptiertes Risiko. | Naechstes Hardware-Upgrade, erneuter realer Stromausfall mit Datenfolge, oder Q3-Review (ab 2026-07-01) |
| Cold-Backup-Rotation | **Bewusst Hetzner-only.** Off-site bleibt allein das Hetzner-Borg-Repo; keine zweite rotierende Cold-Kopie. | Stark wachsender Datenwert, wiederholte Hetzner-Probleme, oder geaenderte Betreiber-Praeferenz |
| Stromverbrauch messen | **Bewusst ohne Messung (Entscheidung 2026-06-06).** Kein Messgeraet; Werte bleiben dauerhaft offen, kein Beschaffungs-Todo. | Nur falls spaeter doch ein Messgeraet angeschafft wird oder Strom-/Kostenfrage relevant wird |
## Zweck ## 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. 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.
@@ -108,7 +96,7 @@ tailscale ip -4
| Disk1 | `md1p1` / physisch `sdc` | WDC WD60EFAX-68JH4N1 | `WD-WX32D90PC0V0` | 5.5T | XFS auf md1p1 | Array-Daten | 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 | | 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 | | Boot | `sda1` | Samsung Flash Drive | `0375125090000587` | 59.8G | FAT32 | Unraid Boot | aktiv |
| Cold Backup | bewusst keiner | n/a | n/a | n/a | n/a | Externe Rotation | **bewusst Hetzner-only** (Entscheidung 2026-06-05); off-site allein via Hetzner-Borg | | Cold Backup | TBD | TBD | TBD | TBD | TBD | Externe Rotation | offen |
Pruefkommando: Pruefkommando:
@@ -150,27 +138,18 @@ Bewertung:
- Aktueller Befund 2026-05-26: keine funktionierende USV-Absicherung nachgewiesen. - Aktueller Befund 2026-05-26: keine funktionierende USV-Absicherung nachgewiesen.
- `apcupsd` ist zwar auf dem System vorhanden, aber nicht aktiv. - `apcupsd` ist zwar auf dem System vorhanden, aber nicht aktiv.
- **Operator-Entscheidung 2026-06-05: USV-Anschaffung bewusst auf Q3/2026 geparkt.** Keine Beschaffung in diesem Quartal. - Operator-Entscheidung 2026-05-26: aktuell keine USV-Anschaffung.
- Power-Loss bleibt damit ein bewusst akzeptiertes Risiko fuer Docker-/DB-State und laufende Writes. - Power-Loss bleibt damit ein bewusst akzeptiertes Risiko fuer Docker-/DB-State und laufende Writes.
- Review-Trigger (einer reicht): naechstes Hardware-Upgrade, ein erneuter realer Stromausfall mit Datenfolge, oder der Q3-Review ab 2026-07-01. - Review-Ausloeser: Hardware-Erweiterung, wiederholte Stromausfaelle, Datenkorruption oder Veraenderung der Betreiber-Prioritaet.
- Wenn die Entscheidung in Q3 zugunsten einer USV kippt, ist das Mindestkriterium ein USB-HID-faehiges Geraet (~600-900 VA), das von `apcupsd` erkannt wird, damit der bereits vorkonfigurierte Shutdown-Pfad ohne Zusatzsoftware greift.
## Stromverbrauch ## Stromverbrauch
**Bewusst ohne Messung (Operator-Entscheidung 2026-06-06).** Es wird kein
Messgeraet beschafft; Idle/Normal/Backup/Last bleiben dauerhaft offen. Kein
offener Todo. Falls spaeter doch eine Mess-Steckdose angeschafft wird, reicht
ein einziger Messdurchlauf, um die Tabelle zu fuellen.
| Zustand | Verbrauch | Messmethode | Datum | | Zustand | Verbrauch | Messmethode | Datum |
|---|---:|---|---| |---|---:|---|---|
| Idle | offen | schaltbare Mess-Steckdose, 10 min Mittelwert ohne aktive Jobs | nach Beschaffung | | Idle | TBD | externes Messgeraet erforderlich | TBD |
| Normalbetrieb | offen | Mess-Steckdose, typischer Tagbetrieb mit laufenden Apps | nach Beschaffung | | Normalbetrieb | TBD | externes Messgeraet erforderlich | TBD |
| Backup-Lauf | offen | Mess-Steckdose, waehrend naechtlichem Borg-Lauf | nach Beschaffung | | Backup-Lauf | TBD | externes Messgeraet erforderlich | TBD |
| Last | offen | Mess-Steckdose, unter CPU-Last (z. B. Immich-ML/Parity-Check) | nach Beschaffung | | Last | TBD | externes Messgeraet erforderlich | TBD |
Beschaffungs-Trigger: einfache schaltbare Energiemess-Steckdose; danach ein
einziger Messdurchlauf reicht, um diese Tabelle dauerhaft zu fuellen.
## Ersatzteil- und Lifecycle-Plan ## Ersatzteil- und Lifecycle-Plan
@@ -181,7 +160,7 @@ einziger Messdurchlauf reicht, um diese Tabelle dauerhaft zu fuellen.
| Parity | Kleiner als neue groesste Datenplatte | Parity-Upgrade vor Datenplatten-Upgrade | | Parity | Kleiner als neue groesste Datenplatte | Parity-Upgrade vor Datenplatten-Upgrade |
| Boot-USB | Lesefehler oder Alter TBD | Flash-Backup verifizieren, Ersatzstick vorbereiten | | Boot-USB | Lesefehler oder Alter TBD | Flash-Backup verifizieren, Ersatzstick vorbereiten |
| RAM | Swap/OOM oder Immich/Nextcloud-Druck | Ausbau planen | | RAM | Swap/OOM oder Immich/Nextcloud-Druck | Ausbau planen |
| USV | keine funktionierende USV-Abschaltung | Anschaffung 2026-06-05 bewusst auf Q3/2026 geparkt; Trigger: Hardware-Upgrade, realer Stromausfall mit Datenfolge, oder Q3-Review | | USV | keine funktionierende USV-Abschaltung | Risiko am 2026-05-26 bewusst akzeptiert; bei Review erneut bewerten |
## Audit-Kommandos ## Audit-Kommandos
+1 -8
View File
@@ -1,15 +1,8 @@
# Home Assistant -> InfluxDB 3 -> Grafana # 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. 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. - Home Assistant ist per SSH unter `192.168.178.50:22222` erreichbar.
- `ha core check` ist erfolgreich. - `ha core check` ist erfolgreich.
-112
View File
@@ -1,112 +0,0 @@
# Master To-do - KalliLab CORE
Stand: 2026-06-06 (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
haelt Status, naechsten konkreten Schritt und Quelle zusammen.
## Status-Kategorien
- **Aktiv dieses Wochenende** - soll jetzt vorankommen (Claude, Codex oder Operator); konkreter naechster Schritt steht.
- **Operator-Entscheidung** - wartet auf eine bewusste Entscheidung des Betreibers (ja/nein/welche Option).
- **Geparkt** - bewusst nicht jetzt, mit klarem Review-Trigger.
- **Extern blockiert** - wartet auf ein externes Ereignis oder eine Abhaengigkeit (Nachtlauf, zweite Hardware, Geraetebeschaffung).
Owner-Aufteilung fuer das Wochenende: `baerchen`/Veeam/Backup-Verifikation liegt
bei **Codex**; Doku-/Inventar-/Onboarding-Arbeit liegt bei **Claude**;
Host-/Entscheidungsaufgaben beim **Operator**.
---
## Aktiv dieses Wochenende
| Thema | Owner | Naechster konkreter Schritt | Quelle |
|---|---|---|---|
| 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` |
| 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 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" |
| Authelia OIDC fuer Apps | Operator/Claude | **Aktive Phase abgeschlossen 2026-06-06.** Live: Grafana (admin, Login verifiziert) + Mealie (family, verifiziert) + Paperless (family, deployed; Login-Test offen). Muster + Gotchas in `docs/AUTHELIA_OIDC_PLAN.md`. **Immich + Nextcloud bewusst GEPARKT bis Onboarding** (Entscheidung 2026-06-06): nur `micha` hat Authelia-Account, Familien-SSO-Nutzen + UI/occ-Aufwand lohnen erst mit Familien-Accounts. Runbook bereit | `docs/AUTHELIA_OIDC_PLAN.md`, `security/authelia/configuration.yml` |
---
## Operator-Entscheidung
**Stand 2026-06-06: keine offenen Operator-Entscheidungen.** Alle am 2026-06-06
entschieden — Ergebnisse in "Aktiv", "Geparkt" bzw. "Entschieden 2026-06-06".
---
## Geparkt
Bewusst nicht jetzt - mit Review-Trigger.
| Thema | Entscheidung / Trigger | Quelle |
|---|---|---|
| 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` |
| 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 | **Validiert 2026-06-06:** `ops/restore-tests/negative-freshness-alert-test.sh` simuliert fehlende Dumps nur in einem synthetischen Restore-Lab-Pfad und sendet einen Test-Alert nach `homelab-alerts`; Host-Lauf schrieb Report `/mnt/user/backups/restore-reports/freshness-negative-2026-06-06-130320.md` (10 Criticals, produktive Dumps unangetastet). Quartalsweise wiederholen: `ops/restore-tests/run-restore-checks.sh freshness-negative` | `ops/restore-tests/README.md`, `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` |
| Nextcloud 2FA (Operator-TOTP) | **Geparkt (Entscheidung 2026-06-06):** Operator-TOTP fuer Nextcloud erst zusammen mit der app-weiten Familien-/OIDC-Policy entscheiden. Trigger: OIDC-/SSO-Block (jetzt aktiv) erreicht die App-Login-Ebene | `docs/AUDIT_2026-05-25_TODO.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` |
| Scrutiny Privileged-Ausnahme | Nur mit klarer Begruendung aendern; sonst dokumentierte Ausnahme beibehalten | `docs/SERVICE_CATALOG.md` |
| Immich Redis named volume | Anonymes Volume bei passender Wartung auf named volume umstellen oder Ausnahme dokumentieren | `docs/SERVICE_CATALOG.md` |
| Storage-Wachstum | Zweite NVMe, ZFS/BTRFS-Optionen, zweite Array-Disk nur bei Triggern aus Capacity-Doku | `docs/STORAGE_LAYOUT.md`, `docs/CAPACITY_AND_LIFECYCLE.md` |
| Zweites Off-site-Ziel | Bewusst nicht umgesetzt; neu bewerten bei Hetzner-Problemen oder wachsendem Datenwert | `docs/AUDIT_2026-05-25_TODO.md` |
| Borg `append-only` auf Hetzner | Operator-Entscheidung 2026-06-01: nicht umgesetzt (forced-command brach Key-Auth, Nutzen/Risiko unguenstig) | `docs/AUDIT_2026-05-25_TODO.md` |
---
## Extern blockiert
Wartet auf ein externes Ereignis oder eine Abhaengigkeit.
| Thema | Blockiert durch | Naechster Schritt sobald entblockt | Quelle |
|---|---|---|---|
| End-to-end-DR-Drill (Hardware-Teil) | Keine zweite Wegwerf-Hardware verfuegbar | Sobald zweite Hardware da ist: Komplett-Bootstrap Phase 1-5 fahren | `docs/DISASTER_RECOVERY.md` |
---
## Erledigt im Wochenend-Sprint (2026-06-05)
- Restore-Matrix "Naechste Restore-Test-Kandidaten" bereinigt: 5 am 2026-06-03 abgeschlossene Kandidaten entfernt, durch die 4 real offenen Pfade ersetzt; Stand-Datum aktualisiert.
- Restore-Test-Runbook-Stubs fuer Unraid Flash / AdGuard / Tailscale / Redis 8 in `docs/RESTORE_MATRIX.md` ergaenzt.
- Alte Windows-Doku bereinigt: WinRE-/Admin-Check-To-dos in `boot-cleanup-plan-2026-06-04.md` und `laufwerks-neustruktur-2026-06-04.md` als erledigt markiert.
- `docs/HARDWARE_INVENTORY.md`: USV (Q3-Park), Cold-Backup (Hetzner-only) und Stromverbrauch von diffusen TBDs auf bewusste Entscheidungen mit Review-Triggern gehoben.
- `docs/NETWORK_INVENTORY.md`: Tailscale-Inventar am 2026-06-05 **real per read-only SSH gemessen** und eingetragen: IPv6 `fd7a:115c:a1e0::2c01:62b2`, Exit Node `nein`, **Subnet-Router fuer `192.168.178.0/24` aktiv** (widerlegt fruehere Vermutung), Tailnet `taild9fcf2.ts.net`, Geraete-Snapshot + Dubletten-Hinweis. WAN-Failover und Gast-/IoT geschaerft. `zu messen`-Platzhalter entfernt. **`Tailscale-Inventar messen` damit geschlossen.**
- `ops/maintenance/check-unraid-flash-backup.sh` neu: read-only Validierung des Flash-Artefakts (sha256, Frische, Kern-Configs, keine Extraktion). Am 2026-06-05 gegen den Host getestet: Exit 0, sha256 OK, 390 Eintraege, 8/8 Kern-Configs. `docs/RESTORE_MATRIX.md` mit Testdatum/Ergebnis aktualisiert. **Artefakt-Validierung des Unraid-Flash-Backups damit erledigt; nur Stick-Boot-Test offen.**
- `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.
- Restore-Frische-Negativtest validiert: `ops/restore-tests/negative-freshness-alert-test.sh` erstellt und am 2026-06-06 auf Unraid erfolgreich ausgefuehrt. Ergebnis: synthetischer leerer Dump-Pfad erzeugte erwartungsgemaess 10 Criticals, Test-Alert nach `homelab-alerts` gesendet, Report `/mnt/user/backups/restore-reports/freshness-negative-2026-06-06-130320.md`, produktive Dumps unangetastet.
- Gast-/IoT-Netz aktiviert und validiert: FRITZ!Box-Gastzugang `Fritzi Gastzugang` aktiv, Heimnetz-Zugriff aus dem Gastnetz blockiert. LAN- und Host-Preflight gruen; iPhone-Smoke aus dem Gast-WLAN bestaetigt, dass `192.168.178.58:8082`, `:8181`, `:222`, `https://192.168.178.58` und `192.168.178.1` nicht erreichbar sind. Runbook: `docs/GUEST_IOT_NETWORK.md`.
- `baerchen` Veeam-Recovery-Test ohne echten Restore abgeschlossen: Recovery-USB `VEEAMRE` bootet, SMB-Ziel `\\kallilabcore\backups\windows-images\baerchen` ist in der Recovery Environment erreichbar, Restore Point wird angezeigt, Test vor echtem Restore abgebrochen. Runbook: `ops/windows-reinstall/docs/windows-image-backup-baseline.md`.
- **Operator-Entscheidungen 2026-06-06 abgeschlossen** (Liste damit ohne offene Entscheidungen):
- **BitLocker `baerchen`: bewusst deaktiviert.** Recovery laeuft ueber Veeam-Image; kein BitLocker-Key-Management. Restrisiko physischer Diebstahl bewusst akzeptiert.
- **Veeam Storage Encryption: bewusst unverschluesselt.** Erster Full-Lauf bleibt; Image liegt auf dem lokalen SMB-Share `\\kallilabcore\backups`. Neu bewerten bei Off-host-Auslagerung des Images.
- **Stromverbrauch: bewusst ohne Messung.** Kein Messgeraet; Werte bleiben dauerhaft offen, kein Beschaffungs-Todo mehr.
- **Authelia Rest-2FA: KOMPLETT erledigt 2026-06-06.** Catch-all `*.kaleschke.info` -> `two_factor` in Repo **und** Host-Config (chirurgische Einzelzeilen-Aenderung mit Backup, OIDC-Beszel-Client + Secret unangetastet), `docker restart authelia` -> healthy + "Startup complete", Operator-2FA-Login auf einer vorher-1FA-Domain verifiziert. Nebenbei vorbestehenden Drift gefunden+bereinigt (Host-Config war vom 25. Mai, borg/code nie gemerged); Repo-Baseline an Host-Endzustand angeglichen, damit `authelia-diff.sh` clean wird sobald der Host-Mirror nachzieht. Rollback-`.bak` auf dem Host vorhanden.
- **Authelia OIDC: angehen** (neuer aktiver Block) — **Gast-/IoT-Netz: einrichten/planen** (neuer aktiver Block) — **Nextcloud 2FA: geparkt** bis OIDC die App-Login-Ebene erreicht.
---
## Pflege-Regel
- Neue operative To-dos zuerst hier eintragen oder aus Detaildokumenten hierher uebernehmen, immer mit Status-Kategorie.
- Wenn ein Punkt erledigt ist, in der Detaildoku den Beleg/Report eintragen und diese Liste aktualisieren.
- Keine vagen "pruefen"-Eintraege ohne Kommando oder Entscheidung.
- Historische Drill-Reports bleiben Belegmaterial, aber nicht die fuehrende Arbeitsliste.
+12 -275
View File
@@ -1,7 +1,7 @@
# Network Inventory - KalliLab CORE # Network Inventory - KalliLab CORE
Status: Host-Audit erfasst; Router-Baseline und Portfreigaben-UI bereinigt; FRITZ!Box-Remote-Dienste aus; IPv6-Exposure technisch und per UI entschaerft; Tailscale-Inventar am 2026-06-05 real gemessen. Status: Host-Audit erfasst; Router-Baseline und Portfreigaben-UI bereinigt; FRITZ!Box-Remote-Dienste aus; IPv6-Exposure technisch und per UI entschaerft.
Letzte Pruefung: 2026-06-05 (Tailscale-Inventar), 2026-06-01 (Router/Ports) Letzte Pruefung: 2026-06-01
## Zweck ## Zweck
@@ -44,163 +44,23 @@ Dieses Dokument beschreibt Router, DNS, Tailscale, Portfreigaben und Netztrennun
## Tailscale ## Tailscale
Gemessen am 2026-06-05 per read-only SSH auf den Host (`tailscale status`, | Feld | Wert |
`tailscale status --json`, `tailscale ip -4/-6`).
| Feld | Wert / Status |
|---|---| |---|---|
| Node-Name | Kallilabcore | | Node-Name | Kallilabcore |
| Tailnet / MagicDNS | `taild9fcf2.ts.net`; DNSName `kallilabcore.taild9fcf2.ts.net` | | Tailscale IPv4 | 100.80.98.33 |
| Tailscale IPv4 | `100.80.98.33` | | Tailscale IPv6 | TBD |
| Tailscale IPv6 | `fd7a:115c:a1e0::2c01:62b2` (gemessen 2026-06-05) | | Exit Node | TBD |
| 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 | TBD |
| 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 | TBD |
| 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. |
### Tailnet-Geraete (Snapshot 2026-06-05) Pruefkommando:
| Tailscale-IP | Node | OS | Status |
|---|---|---|---|
| `100.80.98.33` | kallilabcore | linux | aktiv (Host, Subnet-Router) |
| `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. |
> **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.
### Subnet-Router-Konsequenz
Weil `Kallilabcore` das LAN `192.168.178.0/24` als Subnet-Route anbietet, kann
**jedes** Tailnet-Geraet mit Zugriff auf diese Route potenziell LAN-Dienste auf
`192.168.178.0/24` erreichen — auch die Admin-Ports, die im LAN bewusst nur auf
die Tailscale-IP gebunden sind, sind ueber die Subnet-Route adressierbar. Genau
deshalb ist die ACL-Policy (unten) der eigentliche Schutzmechanismus und nicht
nur der LAN-Bind.
Pruefkommando (auf dem Unraid-Host, read-only):
```bash ```bash
tailscale status tailscale status
tailscale status --json | jq '{exitNode: .Self.ExitNodeOption, primaryRoutes: .Self.PrimaryRoutes, allowedIPs: .Self.AllowedIPs}'
tailscale ip -4 tailscale ip -4
tailscale ip -6 tailscale ip -6
``` ```
### ACL-Policy — ANGEWENDET 2026-06-06 (restriktive Tag-basierte grants)
**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.
> **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.
**Angewendete Policy (live, kein Secret):**
```json
{
"tagOwners": {
"tag:server": ["autogroup:admin"],
"tag:operator": ["autogroup:admin"],
"tag:family": ["autogroup:admin"]
},
"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"]}
],
"ssh": [
{"action": "check", "src": ["autogroup:member"], "dst": ["autogroup:self"],
"users": ["autogroup:nonroot", "root"]}
]
}
```
**Geraete-Tags (live):** `kallilabcore` = `tag:server`; `baerchen-1` + `iphone-14`
= `tag:operator`; `kallilab-core` (Docker) + alter `baerchen` bewusst untagged ->
isoliert.
**Rollout-Protokoll 2026-06-06 (lockout-sicher, je Schritt read-only verifiziert):**
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.
**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.
**Hintergrund / Designentscheidungen (2026-06-05/06):**
- 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).
**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: ERLEDIGT 2026-06-06** — Node-Eintraege `kallilab-core`
und alter Offline-`baerchen` aus der Admin-Konsole entfernt.
- 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).
## Portfreigaben und Exposure ## Portfreigaben und Exposure
### FRITZ!Box (WAN -> Host) ### FRITZ!Box (WAN -> Host)
@@ -217,7 +77,6 @@ Bewusst **nicht** freigegeben:
|---|---| |---|---|
| `80/tcp` | Cloudflare-DNS-Challenge ersetzt HTTP-01; Traefik macht HTTP->HTTPS-Redirect nur LAN-seitig; WAN-`80` waere zusaetzliche Angriffsflaeche ohne Funktionsnutzen. **2026-05-28 in FRITZ!Box-UI entfernt**, Validierung: Mobilfunk-Test ergibt Timeout auf `http://vault.kaleschke.info`, `https://...` weiter erreichbar. | | `80/tcp` | Cloudflare-DNS-Challenge ersetzt HTTP-01; Traefik macht HTTP->HTTPS-Redirect nur LAN-seitig; WAN-`80` waere zusaetzliche Angriffsflaeche ohne Funktionsnutzen. **2026-05-28 in FRITZ!Box-UI entfernt**, Validierung: Mobilfunk-Test ergibt Timeout auf `http://vault.kaleschke.info`, `https://...` weiter erreichbar. |
| `222/tcp` (Gitea SSH) | bewusst Tailscale-only: Operator-Pfad ist Tailscale, GitHub-Mirror deckt DR-Bootstrap ab, Gitea-Bundles sind off-host. Externe SSH-Brute-Force-Vektoren vermeiden. | | `222/tcp` (Gitea SSH) | bewusst Tailscale-only: Operator-Pfad ist Tailscale, GitHub-Mirror deckt DR-Bootstrap ab, Gitea-Bundles sind off-host. Externe SSH-Brute-Force-Vektoren vermeiden. |
| `32400/tcp` (Plex) | Plex wird extern ausschliesslich ueber `https://plex.kaleschke.info` via Traefik/443 erreicht. Kein direkter WAN-Port fuer Plex, Plex Remote Access bleibt aus. |
### UPnP / Selbstständige Portfreigaben ### UPnP / Selbstständige Portfreigaben
@@ -246,7 +105,6 @@ Historischer UI-Befund vor Bereinigung vom 2026-05-27 (`Internet -> Freigaben ->
| 443/tcp | Traefik | HTTPS | WAN-Freigabe in FRITZ!Box erwartet | | 443/tcp | Traefik | HTTPS | WAN-Freigabe in FRITZ!Box erwartet |
| 222/tcp | Gitea SSH | Git SSH | nur LAN/Tailscale; keine WAN-Freigabe | | 222/tcp | Gitea SSH | Git SSH | nur LAN/Tailscale; keine WAN-Freigabe |
| 53/tcp+udp | AdGuard | DNS | LAN-only, dokumentierte Ausnahme | | 53/tcp+udp | AdGuard | DNS | LAN-only, dokumentierte Ausnahme |
| 32400/tcp | Plex | Medienserver / Plex Web lokal | LAN/Tailscale direkt; extern nur via Traefik `https://plex.kaleschke.info`, keine WAN-Freigabe fuer 32400 |
| 8082/tcp | AdGuard Admin | Admin UI | Bind nur `100.80.98.33:8082` (Tailscale), nicht im LAN exponiert | | 8082/tcp | AdGuard Admin | Admin UI | Bind nur `100.80.98.33:8082` (Tailscale), nicht im LAN exponiert |
| 8181/tcp | InfluxDB 3 Core | Home Assistant / Ecowitt Writer | 2026-05-31 effektiv nur `127.0.0.1:8181`, nicht LAN-exponiert | | 8181/tcp | InfluxDB 3 Core | Home Assistant / Ecowitt Writer | 2026-05-31 effektiv nur `127.0.0.1:8181`, nicht LAN-exponiert |
@@ -263,7 +121,7 @@ docker ps --format "{{.Names}}: {{.Ports}}" | sort
|---|---|---| |---|---|---|
| LAN | 192.168.178.0/24 | Hauptnetz, Host `192.168.178.58`, FRITZ!Box meldet 35 aktive Geraete | | LAN | 192.168.178.0/24 | Hauptnetz, Host `192.168.178.58`, FRITZ!Box meldet 35 aktive Geraete |
| WLAN 2,4 / 5 GHz | aktiv, SSID `Fritzi` | Standard-WLAN, im LAN-Adressbereich, kein eigener Adressraum | | WLAN 2,4 / 5 GHz | aktiv, SSID `Fritzi` | Standard-WLAN, im LAN-Adressbereich, kein eigener Adressraum |
| Gast-WLAN | aktiv, SSID `Fritzi Gastzugang` | FRITZ!Box-Gastnetz ist vom Heimnetz getrennt; Smoke 2026-06-06 vom iPhone bestaetigt keine Erreichbarkeit der getesteten LAN-/Admin-Ziele | | Gast-WLAN | **inaktiv** (FRITZ!Box-UI) | Solange inaktiv: kein Gast-Pfad zu LAN-Diensten; AdGuard-Admin-Trennung primaer ueber Tailscale-Bind statt Netzsegmentierung |
| IoT-Netz | nicht existent | Keine VLAN-Trennung dokumentiert | | IoT-Netz | nicht existent | Keine VLAN-Trennung dokumentiert |
| Tailscale | aktiv | Operator-Zugang, Host-IP `100.80.98.33` | | Tailscale | aktiv | Operator-Zugang, Host-IP `100.80.98.33` |
| VLANs | nicht in Nutzung | FRITZ!Box 7590 kann VLAN-Tagging an einzelnen LAN-Ports; aktuell nicht konfiguriert | | VLANs | nicht in Nutzung | FRITZ!Box 7590 kann VLAN-Tagging an einzelnen LAN-Ports; aktuell nicht konfiguriert |
@@ -288,126 +146,6 @@ docker network inspect frontend_net | jq '.[0].Containers | keys'
docker network inspect backend_net | jq '.[0].Internal' docker network inspect backend_net | jq '.[0].Internal'
``` ```
## SSH-Konfiguration Host
Geprueft 2026-06-06 (read-only), **gehaertet 2026-06-07** via `ssh root@192.168.178.58`.
| Parameter | Ist-Wert (effektiv via `sshd -T`, Stand 2026-06-07) | Soll | Status |
|---|---|---|---|
| `Port` | `22` | 22 | ok |
| `PermitRootLogin` | `prohibit-password` | `prohibit-password` | **gehaertet 2026-06-07** |
| `PasswordAuthentication` | `no` | `no` | **gehaertet 2026-06-07** |
| `KbdInteractiveAuthentication` | `no` | `no` | **gehaertet 2026-06-07** (noetig wegen `UsePAM yes`) |
| `PubkeyAuthentication` | `yes` | `yes` | ok |
| `PermitEmptyPasswords` | `no` | `no` | ok |
| `AuthorizedKeysFile` | `.ssh/authorized_keys` | `.ssh/authorized_keys` | ok |
**Hinterlegte SSH-Keys (root):** 3 Keys vorhanden (persistiert unter `/boot/config/ssh/root/authorized_keys`):
- `root@Kallilabcore` (Host-eigener Key)
- `michi@Baerchen` (Operator-Workstation)
- `hetzner-storagebox-maintenance-2026-06-01` (Hetzner-Maintenance-Key)
**Durchgefuehrte Haertung (2026-06-07):** Root-Login ist jetzt key-only,
Passwort- und Keyboard-Interactive-Auth sind serverseitig abgeschaltet.
Verifiziert: frischer Key-Login `OK`; `ssh -o PreferredAuthentications=none`
meldet `Authentications that can continue: publickey`; reiner Passwort-Versuch
`Permission denied (publickey)`.
**Wichtig — Unraid-Persistenz:** `/etc/ssh/sshd_config` wird beim Boot aus dem
OS-Image regeneriert (`rc.sshd`: `cp -f /boot/config/ssh/* /etc/ssh/`, danach
`sshd_build`, das nur `Port`/`ListenAddress`/`AddressFamily` setzt). Die
Unraid-GUI (**Settings → Management Access → SSH**) bietet nur `Use SSH`/`SSH port`
an — **`PermitRootLogin`/`PasswordAuthentication` sind dort nicht einstellbar.**
Persistiert wird daher **upgrade-sicher** ueber einen idempotenten Hook:
- `/boot/config/ssh-harden.sh` — setzt die drei Direktiven idempotent (bestehende
aktive Zeile entfernen, genau einmal global vor dem ersten `Match`-Block einfuegen),
`sshd -t`-Validierung, Reload nur per `kill -HUP` des Host-`sshd` bei valider Config.
Idempotenz belegt: nach mehreren Laeufen je Direktive exakt 1 aktive Zeile, alte
`PermitRootLogin yes` entfernt.
- `/boot/config/go` — ruft `/bin/bash /boot/config/ssh-harden.sh` bei jedem Boot auf.
**Selbst-Verifikation (Syslog, rein informativ, keine Reparatur):** Das Skript
schreibt nach jedem Lauf die effektiven Auth-Werte (`sshd -T`) nach syslog, z. B.
`ssh-harden: VERIFY permitrootlogin prohibit-password pubkeyauthentication yes
passwordauthentication no kbdinteractiveauthentication no`. Damit ist nach jedem
Boot/Upgrade nachweisbar, ob die Haertung gegriffen hat.
**Post-Upgrade-/Reboot-Check** (manuell, einmal nach jedem Unraid-Upgrade):
```bash
# A) Effektive Werte direkt abfragen (Soll: prohibit-password / no / no / yes)
ssh root@192.168.178.58 "sshd -T | grep -Ei 'permitroot|passwordauth|kbdinteractive|pubkey'"
# B) Oder die automatische VERIFY-Zeile im Syslog lesen (Unraid nutzt rsyslog -> /var/log/syslog, nicht logread)
ssh root@192.168.178.58 "grep 'ssh-harden' /var/log/syslog | tail -3"
```
Dieser Weg editiert die **jeweils aktuelle** von Unraid generierte Config nach und
ueberlebt damit Unraid-Upgrades; findet er die Stock-Zeile nicht (z. B. weil eine
neue Version schon `prohibit-password` ausliefert), macht der `sed` nichts und
bricht den Boot nicht (fail-safe Richtung offen, nicht ausgesperrt). Bewusst
**nicht** der oft empfohlene Weg einer kompletten `/boot/config/ssh/sshd_config`
auf Flash — der wuerde die Stock-Config einfrieren und beim Upgrade neue Defaults
verschlucken.
**Rollback:** `go`-Block + `/boot/config/ssh-harden.sh` entfernen, dann
`cp /boot/config/ssh-harden.sshd_config.bak-20260607 /etc/ssh/sshd_config` und
`kill -HUP $(cat /var/run/sshd.pid)`. Notzugang ueber Unraid-Konsole/GUI bleibt.
**Abgrenzung:** Ein zweiter `sshd` (`-D -e`) laeuft in einem Docker-Container
(s6-overlay, moby-Namespace) und bindet **nicht** den Host-`:22`; eigene Config
im Container, von dieser Haertung unberuehrt.
---
## Post-Upgrade Posture-Recheck — Unraid 7.3.1 (2026-06-07)
Nach dem Major-Upgrade **7.2.4 → 7.3.1** read-only die Host-Listener-Landschaft
(`ss -tlnp`) gegen die dokumentierten Annahmen geprueft.
**Dokumentierte Ausnahmen verifiziert (alle weiterhin gueltig):**
| Dienst | Soll | Ist nach 7.3.1 | Status |
|---|---|---|---|
| InfluxDB 3 | nur `127.0.0.1:8181` | `127.0.0.1:8181` | ✅ |
| AdGuard-Admin | nur Tailscale `100.80.98.33:8082` | `100.80.98.33:8082` | ✅ |
| Gitea-SSH `222` | LAN/Tailscale, keine WAN-Freigabe | `0.0.0.0:222` (LAN/TS), WAN am Router zu | ✅ |
| Traefik `80/443` | einziger Owner | docker-proxy (Traefik) allein | ✅ |
| libvirt `:53` | darf nicht existieren | **weg** (Fix vom 2026-06-07 haelt) | ✅ |
**Docker-Socket (`/var/run/docker.sock`) — C-3-Kontext:**
| Container | Mount | Bewertung |
|---|---|---|
| komodo-periphery | **RW** | dokumentierte Ausnahme (Periphery startet/stoppt Container) |
| traefik | ro | C-3: Direkt-Mount (ro), nicht ueber Socket-Proxy — offener Audit-Punkt, kein Regress |
| glances / monitoring-promtail / glance-docker-socket-proxy | ro | unkritisch |
Keine neue RW-Socket-Exposure durch das Upgrade.
**Vorfall-Notiz AdGuard/DNS (Boot-Race, behoben 2026-06-07):** Das Upgrade hatte das
ungenutzte **libvirt-Default-Netz** auf Autostart gebracht; dessen `dnsmasq` belegte
beim Boot Port `53` **vor** AdGuard → AdGuards erster Start scheiterte am Bind und
liess den Container ohne Netz-Anbindung (`Networks={}`, keine Ports) zurueck. Fix:
`virsh net-autostart default --disable` + `virsh net-destroy default` (kein VM
betroffen, Liste leer) + AdGuard-Container aus der Compose `--force-recreate`
(re-attach `dns_net`, `:53` neu veroeffentlicht). DNS danach verifiziert aufloesend.
`libvirtd` laeuft weiter nur auf `127.0.0.1:16509`.
**Empfehlung (Dauerfix):** Da keine VMs genutzt werden, **Unraid VM Manager → Enable
VMs = No** — dann startet `libvirtd` gar nicht und der `:53`-Konflikt kann prinzipiell
nicht wiederkehren. Bis dahin verhindert der abgeschaltete Autostart die Wiederkehr.
**Beobachtungen (kein Regress, Inventar):** SMB (`:445/:139`) und Plex (`*:32400`)
lauschen auch auf der Tailscale-IP; durch die seit 2026-06-06 tag-restriktive
Tailnet-ACL akzeptabel.
**SSH-Haertung nach Upgrade:** key-only root unveraendert aktiv und verifiziert
(`prohibit-password`/`password no`/`kbd no`), go-Hook genau 1× gefeuert — siehe
Abschnitt „SSH-Konfiguration Host".
---
## Offene Entscheidungen ## Offene Entscheidungen
| Thema | Status | Naechster Schritt | | Thema | Status | Naechster Schritt |
@@ -416,8 +154,7 @@ Abschnitt „SSH-Konfiguration Host".
| FRITZ!Box-Portfreigaben mit Repo-Soll abgleichen | **erledigt 2026-06-01** | Bereinigt: `80/tcp` entfernt (Cloudflare-DNS-Challenge ersetzt HTTP-01; Mobilfunk-Test bestaetigt Timeout auf `http://`, `https://` weiter ok). `222/tcp` bleibt bewusst nicht eingerichtet (Tailscale-only-Linie). UPnP-Selbstfreigaben sind aus. Aktiver Soll-Stand: ausschliesslich `443/tcp -> 192.168.178.58`. | | FRITZ!Box-Portfreigaben mit Repo-Soll abgleichen | **erledigt 2026-06-01** | Bereinigt: `80/tcp` entfernt (Cloudflare-DNS-Challenge ersetzt HTTP-01; Mobilfunk-Test bestaetigt Timeout auf `http://`, `https://` weiter ok). `222/tcp` bleibt bewusst nicht eingerichtet (Tailscale-only-Linie). UPnP-Selbstfreigaben sind aus. Aktiver Soll-Stand: ausschliesslich `443/tcp -> 192.168.178.58`. |
| FRITZ!Box-Dienste aus dem Internet | **erledigt 2026-06-01** | `Internet -> Freigaben -> FRITZ!Box-Dienste`: HTTPS-Zugriff auf die FRITZ!Box aus dem Internet aus; FTP/FTPS auf Speichermedien aus. | | FRITZ!Box-Dienste aus dem Internet | **erledigt 2026-06-01** | `Internet -> Freigaben -> FRITZ!Box-Dienste`: HTTPS-Zugriff auf die FRITZ!Box aus dem Internet aus; FTP/FTPS auf Speichermedien aus. |
| FRITZ!OS Update und Konfig-Backup | **erledigt 2026-06-01** | TR-064 meldet `154.08.25`; Konfig-Export liegt extern/off-system in Vaultwarden, Kennwort und Datei bleiben ausserhalb des Repos. | | FRITZ!OS Update und Konfig-Backup | **erledigt 2026-06-01** | TR-064 meldet `154.08.25`; Konfig-Export liegt extern/off-system in Vaultwarden, Kennwort und Datei bleiben ausserhalb des Repos. |
| Gast-/IoT-Zugriff auf Admin-Ports | **validiert 2026-06-06** | Runbook `docs/GUEST_IOT_NETWORK.md` und Checks `ops/maintenance/check-guest-iot-isolation.ps1` sowie `ops/maintenance/check-guest-iot-preflight.sh` vorhanden. LAN-Preflight von `baerchen` gruen: `192.168.178.58:8082` und `:8181` blockiert. Host-Preflight auf Unraid gruen, Report `/mnt/user/backups/restore-reports/guest-iot-preflight-2026-06-06-131316.md`. Gast-WLAN-Smoke per iPhone: `192.168.178.58:8082`, `:8181`, `:222`, `https://192.168.178.58` und `192.168.178.1` nicht erreichbar. | | Gast-/IoT-Zugriff auf Admin-Ports | aktuell entschaerft | Gast-WLAN ist inaktiv; bei Aktivierung muessen `192.168.178.58:8082`, `192.168.178.58:8181` und ggf. weitere LAN-Ports per FRITZ!Box-Kindersicherung/Netzwerk-Filter abgesichert werden |
| IPv6 Exposure | technisch und per UI entschaerft | Public DNS liefert keine AAAA-Records fuer `*.kaleschke.info`; Host hat keine globale Provider-IPv6. TR-064 meldet IPv6-Firewall aktiv und Pinholes grundsaetzlich erlaubt; FRITZ!Box-UI zeigt keine aktiven IPv6-Freigaben, keine Admin-/SSH-Freigaben. | | IPv6 Exposure | technisch und per UI entschaerft | Public DNS liefert keine AAAA-Records fuer `*.kaleschke.info`; Host hat keine globale Provider-IPv6. TR-064 meldet IPv6-Firewall aktiv und Pinholes grundsaetzlich erlaubt; FRITZ!Box-UI zeigt keine aktiven IPv6-Freigaben, keine Admin-/SSH-Freigaben. |
| WAN-Ausfallschutz | **geparkt: spaeter evaluieren** (Operator-Entscheidung 2026-06-05) | Mobilfunk-Stick-Failover an FRITZ!Box bleibt vorerst inaktiv. Folgen sind bewusst akzeptiert: Internet-Ausfall = ACME/DDNS pausieren, lokale Apps laufen weiter. Review-Trigger: haeufigere oder laengere DSL-Ausfaelle, oder wenn externer Remote-Zugang (statt nur lokalem Betrieb) geschaeftskritisch wird. Erst dann Mobilfunk-Failover technisch bewerten. | | WAN-Ausfallschutz | bewusst nicht eingerichtet | Mobilfunk-Stick-Failover an FRITZ!Box ist nicht aktiv; Internet-Ausfall = ACME/DDNS pausieren, lokale Apps laufen weiter |
| Home Assistant InfluxDB Bind | validiert 2026-05-31 | `docker-proxy` bindet `127.0.0.1:8181`; keine LAN-Exposure. Wenn Home Assistant nicht lokal auf dem Host schreibt, braucht das eine bewusste Bind-Aenderung. | | Home Assistant InfluxDB Bind | validiert 2026-05-31 | `docker-proxy` bindet `127.0.0.1:8181`; keine LAN-Exposure. Wenn Home Assistant nicht lokal auf dem Host schreibt, braucht das eine bewusste Bind-Aenderung. |
| SSH-Haertung Host | **erledigt 2026-06-07** | Root-Login key-only: `PermitRootLogin prohibit-password`, `PasswordAuthentication no`, `KbdInteractiveAuthentication no`. Live gesetzt + verifiziert (Key-Login ok, Passwort-Auth abgelehnt). Persistenz upgrade-sicher ueber `/boot/config/ssh-harden.sh` (idempotent, `sshd -t` vor Reload) aufgerufen aus `/boot/config/go`. GUI bietet diese Optionen nicht. Details im Abschnitt „SSH-Konfiguration Host". |
+2 -7
View File
@@ -1,6 +1,6 @@
# Documentation Index # Documentation Index
Stand: 2026-06-05 Stand: 2026-06-01
Diese Datei trennt aktive Betriebsdokumentation von historischer Arbeitsdoku. Neue operative Dokumente duerfen nur in `docs/` liegen, wenn sie heute als Einstieg, Runbook, Inventar oder offene Arbeitsliste gebraucht werden. Erledigte Audits, Chat-Handoffs, Prompt-Dateien und abgeschlossene Plaene bleiben in der Git-Historie, aber nicht als dauerhafte Arbeitskopie. Diese Datei trennt aktive Betriebsdokumentation von historischer Arbeitsdoku. Neue operative Dokumente duerfen nur in `docs/` liegen, wenn sie heute als Einstieg, Runbook, Inventar oder offene Arbeitsliste gebraucht werden. Erledigte Audits, Chat-Handoffs, Prompt-Dateien und abgeschlossene Plaene bleiben in der Git-Historie, aber nicht als dauerhafte Arbeitskopie.
@@ -31,10 +31,8 @@ Diese Datei trennt aktive Betriebsdokumentation von historischer Arbeitsdoku. Ne
|---|---| |---|---|
| `STORAGE_LAYOUT.md` | verbindliche Storage-/Share-/Pfad-Regeln | | `STORAGE_LAYOUT.md` | verbindliche Storage-/Share-/Pfad-Regeln |
| `SECRETS_MAP.md` | Secret-Namen, Speicherorte und Einbindungsarten ohne Werte | | `SECRETS_MAP.md` | Secret-Namen, Speicherorte und Einbindungsarten ohne Werte |
| `AUTHELIA_OIDC_PLAN.md` | Plan & Runbook fuer app-uebergreifendes SSO via Authelia OIDC |
| `HARDWARE_INVENTORY.md` | Host-, Disk-, SMART-, USV- und Power-Baseline | | `HARDWARE_INVENTORY.md` | Host-, Disk-, SMART-, USV- und Power-Baseline |
| `NETWORK_INVENTORY.md` | Router, DNS, Tailscale, Portfreigaben und Netzthemen | | `NETWORK_INVENTORY.md` | Router, DNS, Tailscale, Portfreigaben und Netzthemen |
| `GUEST_IOT_NETWORK.md` | Sicherer Ablauf fuer FRITZ!Box-Gastnetz / IoT-Isolation |
| `EXTERNAL_DEPENDENCIES.md` | Provider, Konten und externe Abhaengigkeiten | | `EXTERNAL_DEPENDENCIES.md` | Provider, Konten und externe Abhaengigkeiten |
| `EXTERNAL_OPERATOR_RUNBOOK.md` | Hetzner-/Borg-/FRITZ!Box-Betreibercheck | | `EXTERNAL_OPERATOR_RUNBOOK.md` | Hetzner-/Borg-/FRITZ!Box-Betreibercheck |
| `CAPACITY_AND_LIFECYCLE.md` | Kapazitaet, Wachstum und Upgrade-Trigger | | `CAPACITY_AND_LIFECYCLE.md` | Kapazitaet, Wachstum und Upgrade-Trigger |
@@ -45,7 +43,7 @@ Diese Datei trennt aktive Betriebsdokumentation von historischer Arbeitsdoku. Ne
|---|---| |---|---|
| `ALERT_RULES.md` | Prometheus-/ntfy-Regeln und Handlungslogik | | `ALERT_RULES.md` | Prometheus-/ntfy-Regeln und Handlungslogik |
| `RENOVATE.md` | Self-hosted Renovate gegen Gitea | | `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 | | `H_DRIVE_NEARLINE_PULL.md` | Windows-H:/ Nearline-Pull fuer kritische Restore-Artefakte |
## Nutzer- und Planungsdoku ## Nutzer- und Planungsdoku
@@ -54,9 +52,6 @@ Diese Datei trennt aktive Betriebsdokumentation von historischer Arbeitsdoku. Ne
|---|---| |---|---|
| `FAMILY_ONBOARDING.md` | familienverstaendliche Nutzungsdoku | | `FAMILY_ONBOARDING.md` | familienverstaendliche Nutzungsdoku |
| `AUDIT_2026-05-25_TODO.md` | kompakte Restliste aus dem Audit-Zyklus | | `AUDIT_2026-05-25_TODO.md` | kompakte Restliste aus dem Audit-Zyklus |
| `MASTER_TODO.md` | zentrale operative Master-To-do-Liste ueber alle Bereiche |
| `WEEKEND_EXECUTION_PLAN_2026-06-05.md` | Owner-Aufteilung und Wochenendplan fuer Todo-Abschluss |
| `WEEKEND_STATUS_2026-06-05.md` | kurzlebiges Arbeitsboard fuer den laufenden Wochenend-Sprint |
| `AI_CONTEXT.md` | kompakter Kontext fuer KI-Agenten | | `AI_CONTEXT.md` | kompakter Kontext fuer KI-Agenten |
Windows-Neuaufsetzen-Dokumente liegen nicht mehr in `docs/`, sondern im fachlich passenden Ordner `../ops/windows-reinstall/docs/`. Windows-Neuaufsetzen-Dokumente liegen nicht mehr in `docs/`, sondern im fachlich passenden Ordner `../ops/windows-reinstall/docs/`.
+1 -9
View File
@@ -93,15 +93,7 @@ Script: bash /mnt/user/services/homelab-infra/ops/renovate/run-renovate.sh
| Schedule | `extends ["schedule:weekly"]` | Renovate-Engine prueft, aber PRs/Updates folgen Wochen-Profilen wo sinnvoll | | Schedule | `extends ["schedule:weekly"]` | Renovate-Engine prueft, aber PRs/Updates folgen Wochen-Profilen wo sinnvoll |
| Dependency Dashboard | aktiv | Gitea-Issue, die alle ausstehenden Updates auflistet | | Dependency Dashboard | aktiv | Gitea-Issue, die alle ausstehenden Updates auflistet |
| Onboarding-PR | `onboarding: false` | Keine `Configure Renovate`-Onboarding-PR; wir nutzen die Repo-`renovate.json` direkt | | Onboarding-PR | `onboarding: false` | Keine `Configure Renovate`-Onboarding-PR; wir nutzen die Repo-`renovate.json` direkt |
| Ignore-Pfade | `_archive`, `ops/grafana-influxdb`, `ops/loki`, `ops/komodo` | Renovate scant alte/abgeloeste Stacks nicht; `ops/komodo` ist bewusst raus (siehe unten) | | Ignore-Pfade | `_archive`, `ops/grafana-influxdb`, `ops/loki` | Renovate scant alte/abgeloeste Stacks nicht |
## Ausnahme: komodo-Stack ist inline-verwaltet, nicht git-deployed
Der `komodo`-Stack (Komodo-Core/Mongo/Periphery, Datei `ops/komodo/docker-compose.yml`) wird **nicht aus diesem Repo deployed**. In Komodo ist der Stack als **inline `file_contents`** (UI-defined) gespeichert (`repo` leer, `files_on_host=false`, `has_inline_file_contents=true`) und hat bewusst `webhook_enabled=false`, damit Komodo sich nicht selbst per Webhook recreated (Bootstrap-/Henne-Ei-Fall).
Konsequenz: Ein Renovate-PR auf `ops/komodo/docker-compose.yml` wirkt zur Laufzeit **nicht** (Komodo deployt aus seiner Inline-Definition) und erzeugt nur Git↔Komodo-Scheinsicherheit. Deshalb steht `ops/komodo/**` in `ignorePaths`. Die Repo-Datei bleibt als Doku/Spiegel und traegt den aktuell real laufenden Digest.
Befund-Datum 2026-06-10: Renovate-PR #13 (mongo-8.0.23 Digest-Refresh) wurde gemergt, wirkte aber nicht; der Digest wurde im Repo auf den laufenden Stand zurueckgesetzt und der Pfad ausgenommen. Echte Updates des komodo-Stacks laufen bis auf Weiteres manuell ueber Komodo (Inline-Compose anpassen) bzw. spaeter via Migration auf git-backed (eigener Aenderungsblock).
## Aktueller Betriebsstand ## Aktueller Betriebsstand
-1
View File
@@ -35,7 +35,6 @@ Details gilt immer die betroffene Compose-Datei oder das jeweilige Runbook.
| `docs/GITOPS_DRIFT_RUNBOOK.md` | Git/Gitea/Komodo/Docker/Host-Drift | | `docs/GITOPS_DRIFT_RUNBOOK.md` | Git/Gitea/Komodo/Docker/Host-Drift |
| `docs/AUDIT_2026-05-25_TODO.md` | aktuelle Restliste | | `docs/AUDIT_2026-05-25_TODO.md` | aktuelle Restliste |
| `docs/DR_WORKSTATION_SETUP.md` | Schritt-fuer-Schritt-Runbook fuer den DR-Gaming-PC (WSL2 + Borg-Client + SSH-Keys) | | `docs/DR_WORKSTATION_SETUP.md` | Schritt-fuer-Schritt-Runbook fuer den DR-Gaming-PC (WSL2 + Borg-Client + SSH-Keys) |
| `docs/runbooks/komodo-bulk-deploy-dns.md` | Bulk-Deploy-Pulls scheitern an DNS, wenn AdGuard im selben Batch recreated wird |
## Wichtige Skripte ## Wichtige Skripte
+12 -191
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 | | 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; Restore-Smoke am 2026-06-06 erfolgreich | | AdGuard Home | Share / Borg | `/mnt/user/appdata/adguard/conf` | keine | keine zusaetzlichen Repo-Secrets dokumentiert | `dns_net`, `frontend_net` | DNS-Aufloesung funktioniert |
| 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 | | 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` | | 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` | | 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 | | 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 | | 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 |
@@ -40,14 +40,6 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`.
--- ---
## Workstations
| System | Fuehrende Quelle | Datei-Restore | Dump / DB | Secrets / ENV | Abhaengigkeiten | Smoke-Test |
|---|---|---|---|---|---|---|
| `baerchen` Windows 11 | Veeam Agent Image auf Unraid-SMB | `/mnt/user/backups/windows-images/baerchen/` bzw. `\\kallilabcore\backups\windows-images\baerchen` | Veeam Restore Points im Zielordner; erster Full-Lauf 2026-06-05, GUI-Groesse 53,8 GB, Dauer 0:11:31, MetaCheck 0 Fehler/0 Warnungen | SMB-User `micha`; Veeam Job Encryption Password nur noetig, falls Storage Encryption spaeter aktiviert wird; BitLocker-Recovery-Key erst noetig, wenn BitLocker spaeter aktiviert wird | Veeam Recovery USB `VEEAMRE`, SMB auf `kallilabcore`, AdGuard/DNS oder direkte IP | Recovery-Test am 2026-06-06 erfolgreich: USB-Boot, SMB-Ziel erreichbar, Restore Point sichtbar, vor echtem Restore abgebrochen; Runbook `ops/windows-reinstall/docs/windows-image-backup-baseline.md` |
---
## Tier 2 - Wichtige Anwendungen ## Tier 2 - Wichtige Anwendungen
| Dienst | Fuehrende Quelle | Datei-Restore | Dump / DB | Secrets / ENV | Abhaengigkeiten | Smoke-Test | | Dienst | Fuehrende Quelle | Datei-Restore | Dump / DB | Secrets / ENV | Abhaengigkeiten | Smoke-Test |
@@ -135,7 +127,7 @@ Die Dump-Erzeugung ist host-seitig ueber `ops/borg-ui/scripts/pre-backup-dumps.s
## Restore-Test-Reifegrad ## Restore-Test-Reifegrad
Stand 2026-06-06. Pro Dienst auf einen Blick: Wurde der Restore schon einmal real getestet? Stand 2026-06-03. Pro Dienst auf einen Blick: Wurde der Restore schon einmal real getestet?
| Dienst | Tier | Letzter Restore-Test | Typ | Naechster Lauf | | Dienst | Tier | Letzter Restore-Test | Typ | Naechster Lauf |
|---|---|---|---|---| |---|---|---|---|---|
@@ -145,12 +137,12 @@ Stand 2026-06-06. Pro Dienst auf einen Blick: Wurde der Restore schon einmal rea
| Komodo Bootstrap | 1 | 2026-05-30 | Compose + Mongo + HTTP | quartalsweise | | Komodo Bootstrap | 1 | 2026-05-30 | Compose + Mongo + HTTP | quartalsweise |
| Paperless | 2 | 2026-05-31 | File + Dump + Container + HTTP + Doc-Count | zweimonatlich (2. Sa ungerade Mon.) | | Paperless | 2 | 2026-05-31 | File + Dump + Container + HTTP + Doc-Count | zweimonatlich (2. Sa ungerade Mon.) |
| Immich | 2 | 2026-05-27 | Dump + Container + HTTP + Asset-Count | quartalsweise (2. So Feb/Mai/Aug/Nov) | | 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 | | Unraid OS Flash | 1 | - | noch kein Test | - |
| Traefik | 1 | 2026-06-03 | Config + LE-State + File-Provider + Ping 200 | quartalsweise | | 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 | - | | Tailscale | 1 | - | noch kein Test | - |
| PostgreSQL 18 Cluster | 1 | 2026-06-03 | globals + 5 per-DB dumps, 290 Tabellen gesamt | quartalsweise | | 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 | | 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 | | 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 | | Mealie | 2 | 2026-06-03 | File + Dump + Container + HTTP + Recipe-Count (3) | quartalsweise |
@@ -159,184 +151,13 @@ Stand 2026-06-06. Pro Dienst auf einen Blick: Wurde der Restore schon einmal rea
| ntfy | 2 | - | rebuildbar, kein Test noetig | - | | ntfy | 2 | - | rebuildbar, kein Test noetig | - |
| Borg UI | 3 | - | rebuildbar | - | | Borg UI | 3 | - | rebuildbar | - |
| Filebrowser | 3 | - | rebuildbar | - | | Filebrowser | 3 | - | rebuildbar | - |
| baerchen Windows Image | Workstation | 2026-06-06 | Full-Backup geschrieben; Recovery-USB-Boot, SMB-Mount und Restore-Point-Sichtpruefung erfolgreich; vor echtem Restore abgebrochen | nach Image-Aenderungen oder quartalsweise |
--- ---
## Naechste Restore-Test-Kandidaten (priorisiert) ## Naechste Restore-Test-Kandidaten (priorisiert)
Stand 2026-06-06. Die frueheren Kandidaten (Shared PG18, Komodo Mongo, Mailarchiver, Mealie, Traefik) 1. **Shared PostgreSQL 18 Cluster** - globals + per-DB-Dumps, Bootstrap-Konflikt `mailarchiver`
wurden alle am 2026-06-03 abgeschlossen und sind in der Reifegrad-Tabelle belegt. 3. **Komodo Mongo Daten** - echtes `mongorestore` aus `komodo-mongo.archive.gz` (Quelle fuer `KOMODO_*`-Stack-ENVs im DR)
4. **Mailarchiver** - Tier 2, shared Postgres + Authelia ForwardAuth
Verbleibende offene Restore-Pfade ohne vollstaendigen Test: 5. **Mealie** - Tier 2, eigene Postgres
6. **Traefik** - Tier 1, aber komplex (dynamic/, LE-State, CF-Token-Mount)
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
---
## Restore-Test-Runbooks (Entwurf)
Diese Abschnitte sind vorbereitete Checklisten fuer die noch untesteten Restore-Pfade.
Sie sind **nicht** als produktive Anleitungen zu verwenden, bevor ein erster Testlauf
die konkreten Artefaktnamen und Pfade bestaetigt hat.
### Unraid OS Flash
**Voraussetzungen:**
- Borg-Artefakt `unraid-flash-config.tar.gz` und `unraid-flash-config.tar.gz.sha256` unter `/mnt/user/backups/borg/dumps/latest` oder im Hetzner-Borg-Repo verfuegbar
- Neuer leerer USB-Stick (Empfehlung: 16 GB, USB 2.0 kompatibel)
- Unraid USB Flash Creator oder manueller Restore-Pfad
- Offline-gesicherte Borg-Passphrase verfuegbar
**Checkliste Artefakt-Validierung (ohne produktiven Stick):**
Automatisiert via Repo-Skript `ops/maintenance/check-unraid-flash-backup.sh`
(read-only, keine Extraktion). Manuelle Einzelschritte:
1. SHA256-Pruefung: `sha256sum -c unraid-flash-config.tar.gz.sha256`
2. Artefakt-Inhalt pruefen: `tar -tzf unraid-flash-config.tar.gz | head -40` — erwartet `config/` als Prefix
3. Kern-Configs vorhanden: `super.dat`, `disk.cfg`, `ident.cfg`, `share.cfg`, `network.cfg`, `docker.cfg`, `go`, `domain.cfg`
4. Keine produktiven Konfigurationspfade (z. B. `config/ssh/`) ausserhalb des Test-Environments extrahieren
5. Manifest-Datei auf Vollstaendigkeit pruefen
**Validierungsergebnis 2026-06-05 (read-only per SSH):** Artefakt frisch
(2026-06-05 04:00, ~16 h alt beim Test), `sha256sum -c` = OK, 390 Eintraege,
alle 8 Kern-Configs vorhanden. Das Archiv enthaelt erwartungsgemaess
Secret-Material (SSH-Host-Keys, Tailscale-State, `passwd`/`shadow`/`smbpasswd`,
`Trial.key`) und ist wie Secret-Backup zu behandeln. Es wurde nichts extrahiert,
nur Eintragsnamen gelistet. Offen bleibt der physische Ersatzstick-Boot-Test.
**Checkliste vollstaendiger Restore-Test (auf Wegwerf-Stick):**
1. Neuen USB-Stick mit Unraid USB Flash Creator formatieren und Basis-Unraid draufspielen
2. `config/`-Verzeichnis aus `unraid-flash-config.tar.gz` in den `/boot/config`-Pfad des neuen Sticks extrahieren
3. Im Testrahmen booten (kein Array starten, keine Shares mounten)
4. Pruefen: Unraid-Grundkonfiguration (Shares, Hostname, Netzwerk) ist sichtbar
5. Array-Zuordnung lesbar, ohne Drive-Assigns zu bestaetigen
**Smoke-Test-Kriterium:** Unraid bootet, Hostname ist `Kallilabcore`, Share-Konfiguration ist sichtbar, kein Array gestartet.
**Sonderregel:** Das Artefakt enthaelt Host-Konfiguration und SSH-Keys und ist wie Secret-Material zu behandeln. Nicht auf oeffentlichen oder unverschluesselten Testzielen extrahieren.
---
### 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:**
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`):
```yaml
ports:
- "127.0.0.1:15353:53/udp"
- "127.0.0.1:13001:80/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
6. Testcontainer stoppen und Testpfad aufraeumen
**Smoke-Test-Kriterium:** AdGuard-Web-UI laeuft, DNS-Aufloesung antwortet, Filterlisten sind geladen.
**Keine Secrets:** AdGuard Home verwendet keine dokumentierten Repo-Secrets; Login-Credentials liegen in der `AdGuardHome.yaml` im Borg-Archiv.
---
### Tailscale
**Voraussetzungen:**
- Borg-Archiv mit `/mnt/user/appdata/tailscale` zugaenglich
- Testpfad unter `/mnt/user/backups/restore-lab/tailscale` vorbereitet
- Achtung: Der Tailscale-State ist maschinenspezifisch. Ein Restore auf denselben produktiven Host wuerde die laufende Verbindung verdraengen. Nur auf einem Wegwerf- oder Offline-Host testen.
**Checkliste Artefakt-Validierung (ohne produktiven Host):**
1. Borg-Extract nach `/mnt/user/backups/restore-lab/tailscale`
2. State-Verzeichnis auf erwartete Dateien pruefen: `tailscaled.state` vorhanden
3. Dateisystem-Rechte pruefen: `tailscaled.state` muss fuer `root` zugaenglich sein
**Checkliste Reconnect-Test (auf Wegwerf-Host oder VM):**
1. Tailscale-Container mit dem gemounteten State-Pfad starten
2. `tailscale status` zeigt `Connected` oder den erwarteten Hostnamen
3. Tailscale-Admin-Konsole (`login.tailscale.com`) zeigt Geraet als `Online`
4. SSH ueber Tailscale-IP auf den Testhost moeglich
5. Testcontainer stoppen; Wegwerf-Geraet in der Tailscale-Admin-Konsole entfernen
**Smoke-Test-Kriterium:** Container verbindet sich mit bestehendem Tailscale-Account (kein neues Re-Auth noetig), Tailscale-IP ist erreichbar.
**Hinweis:** Falls der State veraltet ist (Key expired), wird Tailscale einen Re-Auth anfordern. Das ist ein valides Testergebnis und belegt, wie lang der Reconnect-Pfad bei abgelaufenem Key ist.
---
### 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:**
1. RDB/AOF-Datei aus dem Backup in den Testpfad kopieren:
```
cp /mnt/user/backups/borg/dumps/latest/shared-redis-pre-redis8-<ts>/dump.rdb \
/mnt/user/backups/restore-lab/redis/
```
(oder Borg-Extract aus dem Appdata-Archiv)
2. Testcontainer starten (kein produktiver Port 6379, stattdessen z. B. `16379`):
```yaml
ports:
- "127.0.0.1:16379:6379"
volumes:
- /mnt/user/backups/restore-lab/redis:/data
command: redis-server --requirepass <aus Secret> --appendonly yes
```
3. Verbindungstest: `redis-cli -p 16379 -a <pass> PING` antwortet `PONG`
4. Redis-Version pruefen: `redis-cli -p 16379 -a <pass> INFO server | grep redis_version` zeigt `8.x`
5. Stichprobe Key-Bestand: `redis-cli -p 16379 -a <pass> DBSIZE` zeigt plausible Zahl (nicht 0)
6. Testcontainer stoppen und Testpfad aufraeumen
**Smoke-Test-Kriterium:** Redis 8 startet mit dem Restore-Datenpfad, `PING` antwortet, `DBSIZE` ist nicht 0.
**Shared Redis Besonderheit:** Shared Redis wird produktiv nur von Paperless genutzt (AOF aktiv). Bei einem echten Restore nach App-Absturz: Erst Redis aus Backup hochziehen, dann Paperless. Nextcloud hat eigene Redis-Instanz ohne Passwort.
+1 -11
View File
@@ -53,16 +53,12 @@ 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 | | 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 | 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 |
| Grafana OIDC (Authelia) | Client Secret | `/mnt/user/appdata/secrets/grafana_oidc_client_secret` (Klartext, chmod 600) -> Docker Secret `/run/secrets/grafana_oidc_client_secret` -> `GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET__FILE`. Zugehoeriger pbkdf2-Hash liegt im Authelia-Host-Config-Client `grafana` (kein Wert im Repo) | aktiv (2026-06-06) | | Home Assistant -> InfluxDB | HA InfluxDB Token | `/homeassistant/secrets.yaml` -> `influxdb3_homeassistant_token` | geplant |
| Mealie OIDC (Authelia) | Client Secret | Stack-ENV `${MEALIE_OIDC_CLIENT_SECRET}` in `/mnt/user/services/stacks/mealie/apps/mealie/.env` (Komodo-Stack-ENV); pbkdf2-Hash im Authelia-Host-Config-Client `mealie` (kein Wert im Repo) | aktiv (2026-06-06) |
| 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`) | | 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 | 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 | | 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 |
| n8n | OpenAI API Key (LLM-Extraktion Workflow) | n8n Credentials Store (Typ `httpHeaderAuth`, Header `Authorization: Bearer ...`) | aktiv | | n8n | OpenAI API Key (LLM-Extraktion Workflow) | n8n Credentials Store (Typ `httpHeaderAuth`, Header `Authorization: Bearer ...`) | aktiv |
| n8n | Gitea PAT fuer `n8n-bot` (Issue-Erstellung Workflow) | n8n Credentials Store (Typ `httpHeaderAuth`, Header `Authorization: token ...`); separater Bot-User mit Scope `write:issue` auf `Micha/mails` | aktiv | | n8n | Gitea PAT fuer `n8n-bot` (Issue-Erstellung Workflow) | n8n Credentials Store (Typ `httpHeaderAuth`, Header `Authorization: token ...`); separater Bot-User mit Scope `write:issue` auf `Micha/mails` | aktiv |
| baerchen Veeam | Veeam Job Encryption Password | Vaultwarden Secure Note `Veeam baerchen backup encryption password`; kein Datei-Secret im Repo | geplant, nur noetig falls Veeam Storage Encryption aktiviert wird |
| baerchen SMB Backup Target | SMB Credential fuer User `micha` | bestehender Unraid-/Vaultwarden-Zugang fuer Share `backups`; wird im Veeam-Job gespeichert, Wert nie dokumentieren | aktiv |
| baerchen BitLocker | BitLocker Recovery Key C: | **bewusst deaktiviert (Entscheidung 2026-06-06):** kein BitLocker, kein Recovery-Key noetig. Falls spaeter aktiviert: Key nach `D:\30_Finanzen\BitLocker-RecoveryKey-baerchen-<DATUM>.txt` + Vaultwarden Secure Note + physischer Ausdruck | nicht aktiv (bewusst) |
--- ---
@@ -112,12 +108,6 @@ Weitere dokumentierte Secret-Pfade:
- Die Borg-Repo-Passphrase liegt zusaetzlich als Host-Secret-Datei fuer Restore-Tests und Notfallzugriff vor. Der Wert ist laut Operator-Bestaetigung vom 2026-05-26 offline gesichert; Ablageort und Wert werden nicht im Repo dokumentiert. - Die Borg-Repo-Passphrase liegt zusaetzlich als Host-Secret-Datei fuer Restore-Tests und Notfallzugriff vor. Der Wert ist laut Operator-Bestaetigung vom 2026-05-26 offline gesichert; Ablageort und Wert werden nicht im Repo dokumentiert.
- 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. - 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.
- `baerchen` nutzt fuer das Veeam-Backup aktuell den bestehenden SMB-User
`micha`. Ein dedizierter SMB-User `veeam-baerchen` ist nur ein spaeteres
Hardening-Ziel, solange keine Unraid-User-/Share-Aenderungen gewuenscht sind.
- Das Veeam-Job-Encryption-Passwort ist restore-kritisch. Ohne diesen Wert ist
das Image unter `\\kallilabcore\backups\windows-images\baerchen` nicht
brauchbar.
--- ---
+3 -3
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 | | `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) | | `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, 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 | | `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,7 +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/postgres18`, archivierter Rollback-Altstand `/mnt/user/appdata/_archive/pg18-immich-rollback-volumes-20260602/nextcloud-postgres17`, `nextcloud_postgres_password.txt` | `nextcloud.dump`, raw DB nicht primaerer Restore-Weg | nein | interne DB; PostgreSQL 18 | | `nextcloud-postgres` | Nextcloud-Datenbank | `apps/nextcloud/docker-compose.yml` | intern | `nextcloud_internal` | `/mnt/user/appdata/nextcloud/postgres18`, archivierter Rollback-Altstand `/mnt/user/appdata/_archive/pg18-immich-rollback-volumes-20260602/nextcloud-postgres17`, `nextcloud_postgres_password.txt` | `nextcloud.dump`, raw DB nicht primaerer Restore-Weg | nein | interne DB; PostgreSQL 18 |
| `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 8.8 | | `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 8.8 |
| `plex` | Medienserver mit LAN-/Client-Discovery | `host-services/plex/docker-compose.yml`, `traefik/dynamic/plex.yml` | `https://plex.kaleschke.info`, LAN `http://192.168.178.58:32400/web`, Remote Access deaktiviert | Host-Netz, Traefik File provider | `/mnt/user/appdata/plex/config`, `/mnt/user/appdata/plex/transcode`, `/mnt/user/media`, `/mnt/user/photos` | Tier 2, Appdata + Medienpfade im Borg-/Share-Scope | ja, native Plex-Auth | Repo-Compose-Stack; `network_mode: host` bleibt dokumentierte Discovery-Ausnahme. Traefik routet via File-Provider-Ausnahme auf `http://192.168.178.58:32400`, weil Docker-Labels Host-Netz-Container aus Traefik heraus auf `127.0.0.1` routen wuerden. Keine FRITZ!Box-Freigabe fuer `32400`. Keine Authelia-ForwardAuth, weil Plex Web/App-Clients native Plex-Auth und eigene Flows nutzen. Server geclaimt von `Xeridos`; Smart-TVs greifen weiter ueber WLAN-LAN per mDNS/Plex-GDM direkt zu. `PublishServerOnPlexOnlineKey=0` (Plex Remote Access aus), `RelayEnabled` ebenfalls aus. | | `plex` | Medienserver mit LAN-/Client-Discovery | `host-services/plex/docker-compose.yml` | Plex native, **LAN/Tailscale-only**, Remote Access deaktiviert | 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. Server geclaimt von `Xeridos` (Reclaim 2026-05-28 nach Preferences-Reset vom 18.05.). Smart-TVs greifen ueber WLAN-LAN per mDNS/Plex-GDM direkt zu. `PublishServerOnPlexOnlineKey=0` (Remote Access aus), `RelayEnabled` ebenfalls aus. |
| `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. **Behalten-Entscheidung 2026-05-28:** Container bleibt aktiv als situatives Tool, auch wenn aktuell keine Traefik-Zugriffe in der Woche. Resource-Footprint vernachlaessigbar (~4 MB RAM). | | `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. **Behalten-Entscheidung 2026-05-28:** Container bleibt aktiv als situatives Tool, auch wenn aktuell keine Traefik-Zugriffe in der Woche. Resource-Footprint vernachlaessigbar (~4 MB RAM). |
| `super-productivity` | Persoenliche Produktivitaets-/Task-PWA (Operator), konsumiert Gitea-Issues aus `Micha/mails` | `apps/super-productivity/docker-compose.yml` | `https://sp.kaleschke.info` | Traefik + Authelia, Gitea `Micha/mails` (Polling vom Client) | statisches Frontend, kein Server-State; Browser-IndexedDB plus optionaler WebDAV-Sync gegen Nextcloud | Tier 3, rebuildbar | ja + Authelia | Reine Static-PWA; SP synchronisiert client-seitig ueber Gitea-API (Scope `assigned`, Repo `Micha/mails`, User `Micha`). | | `super-productivity` | Persoenliche Produktivitaets-/Task-PWA (Operator), konsumiert Gitea-Issues aus `Micha/mails` | `apps/super-productivity/docker-compose.yml` | `https://sp.kaleschke.info` | Traefik + Authelia, Gitea `Micha/mails` (Polling vom Client) | statisches Frontend, kein Server-State; Browser-IndexedDB plus optionaler WebDAV-Sync gegen Nextcloud | Tier 3, rebuildbar | ja + Authelia | Reine Static-PWA; SP synchronisiert client-seitig ueber Gitea-API (Scope `assigned`, Repo `Micha/mails`, User `Micha`). |
@@ -85,7 +85,7 @@ Secret-Werte sind nicht enthalten. Es werden nur Secret-Namen, Env-Key-Namen und
| Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs | | Service | Zweck | Autoritativer Pfad | URL / Zugang | Abhaengigkeiten | Datenpfade | Backup / Restore | Traefik | Besonderheiten / TODOs |
|---|---|---|---|---|---|---|---|---| |---|---|---|---|---|---|---|---|---|
| `posture-check` | Host-Posture-Audit fuer Filesystem, Mover-Drift, NVMe-SMART, Fuellstand und Authelia-Repo<->Host-Drift | `services/posture-check/posture-check.sh` | Unraid User-Script / Cron / Borg Pre-Hook | `findmnt`, `df`, `nvme`, optional `curl` fuer ntfy; ruft `services/authelia-diff.sh` fuer `authelia_config_drift` auf | `/mnt/user/services/posture-check/last.json` | Repo-Skript + letzter JSON-Status | nein | Muss auf dem Unraid-Host bei Boot, stuendlich und vor Borg laufen; Disk1-NTFS ist nach Disk1 Phase 2 nicht mehr erlaubt (`ALLOW_DISK1_NTFS=0` Standard); Warning/Critical alarmieren via ntfy nur bei neuer Ursache oder nach `ALERT_REPEAT_SECONDS`. Authelia-Drift-Check braucht einen Repo-Spiegel unter `/mnt/user/services/homelab-infra/` (siehe `docs/WORKFLOW.md` Sektion "Ausnahme: Authelia configuration.yml") | | `posture-check` | Host-Posture-Audit fuer Filesystem, Mover-Drift, NVMe-SMART, Fuellstand und Authelia-Repo<->Host-Drift | `services/posture-check/posture-check.sh` | Unraid User-Script / Cron / Borg Pre-Hook | `findmnt`, `df`, `nvme`, optional `curl` fuer ntfy; ruft `services/authelia-diff.sh` fuer `authelia_config_drift` auf | `/mnt/user/services/posture-check/last.json` | Repo-Skript + letzter JSON-Status | nein | Muss auf dem Unraid-Host bei Boot, stuendlich und vor Borg laufen; Disk1-NTFS ist nach Disk1 Phase 2 nicht mehr erlaubt (`ALLOW_DISK1_NTFS=0` Standard); Warning/Critical alarmieren via ntfy nur bei neuer Ursache oder nach `ALERT_REPEAT_SECONDS`. Authelia-Drift-Check braucht einen Repo-Spiegel unter `/mnt/user/services/homelab-infra/` (siehe `docs/WORKFLOW.md` Sektion "Ausnahme: Authelia configuration.yml") |
| `docker-critical-events` | Live-Alarmierung fuer Docker `die`/`oom`/`kill` Events | `services/posture-check/docker-critical-events.sh`, Supervisor `services/posture-check/docker-critical-events-supervisor.sh` | Unraid User-Script / Hintergrundprozess | Docker CLI, ntfy | `/mnt/user/services/posture-check/docker-critical-events-last.log`, PID/Outfile unter `/mnt/user/services/posture-check/` | Repo-Skript + letzter Event-Log | nein | Optional als Unraid User-Script `at array start` starten; Supervisor kann `start`, `stop`, `status`, `smoke`; sendet nach `homelab-alerts` | | `docker-critical-events` | Live-Alarmierung fuer Docker `die`/`oom`/`kill` Events | `services/posture-check/docker-critical-events.sh` | 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
-151
View File
@@ -1,151 +0,0 @@
# Weekend Execution Plan - 2026-06-05 bis 2026-06-07
Ziel: Bis Ende des Wochenendes alle offenen To-dos aus `docs/MASTER_TODO.md`
entweder erledigen, verifiziert schliessen, oder bewusst als geparkt/extern
blockiert markieren. Nicht jeder Punkt ist realistisch "fertig" im Sinne von
technisch umgesetzt: Family-Onboarding, zweite Hardware, USV und WAN-Failover
brauchen Operator- oder Hardware-Entscheidungen.
## Arbeitsregeln
- Secrets niemals in Chat, Logs oder Repo schreiben.
- Homelab-Aenderungen nur via GitOps, keine direkten Komodo-/Docker-Hotfixes.
- Destruktive Windows- oder Host-Schritte nur nach expliziter Freigabe.
- Ergebnis jedes abgeschlossenen Punkts in der Detaildoku und in
`docs/MASTER_TODO.md` nachziehen.
- Am Ende: ein sauberer Commit-Block; Push erst nach Freigabe.
## Owner-Aufteilung
| Owner | Fokus | Ergebnis |
|---|---|---|
| Codex | `baerchen` Veeam, Doku-Konsolidierung, lokale Checks, Commit-Vorbereitung | Veeam-Erstbackup geprueft, Recovery-Test dokumentiert, Masterliste aktualisiert |
| Claude | Family-Onboarding-Paket, Network-/Tailscale-Entscheidungen, Hardware-/Todo-Konsolidierung, nicht-destruktive Runbooks | Konkrete Doku-Patches, ausfuehrbare Checklisten, klare Operator-Fragen statt diffuser TBDs |
| Operator | Physische/GUI-Schritte, Secrets, Familie, Hardwareentscheidungen | Recovery-USB booten, Passwoerter/Keys bereitstellen, Family-Onboarding starten/entscheiden |
## Codex-Aufgaben
| Prioritaet | Aufgabe | Abschlusskriterium |
|---|---|---|
| P1 | Veeam-Erstbackup `baerchen-c-image` pruefen | **erledigt 2026-06-05:** Full-Lauf geschrieben, Veeam-GUI 53,8 GB, Dauer 0:11:31, MetaCheck 0 Fehler/0 Warnungen; Storage Encryption war nicht aktiv und ist als Operator-Entscheidung dokumentiert |
| P1 | Recovery-USB-Test begleiten | `VEEAMRE` bootet, SMB-Ziel sichtbar, Restore Point sichtbar, vor Restore abgebrochen |
| P1 | `windows-image-backup-baseline.md` finalisieren | Erster Lauf und Teststatus mit Datum eingetragen |
| P1 | `docs/MASTER_TODO.md` nach jedem Abschluss aktualisieren | erledigte Punkte entfernt oder in "geschlossen" vermerkt |
| P2 | Alte Windows-Reinstall-Doku bereinigen | ueberholte WinRE-/Admin-To-dos als erledigt/ueberholt markiert |
| P2 | Git-Status sortieren | Eigene Aenderungen klar von vorhandenen User-Aenderungen getrennt |
| P2 | Commit vorbereiten | Commit-Message-Vorschlag und Datei-Liste bereit; kein Push ohne Freigabe |
## Claude-Aufgaben
Claude soll parallel nur repo-seitig arbeiten und keine produktiven Host-Aenderungen
ausfuehren. Die Aufgaben sind bewusst als echte Doku-/Planungsarbeit formuliert,
nicht nur als Pruefaufgaben:
| Prioritaet | Aufgabe | Abschlusskriterium |
|---|---|---|
| P1 | `docs/MASTER_TODO.md` gegen Detaildokus gegenpruefen | **erledigt 2026-06-05:** Sync-Notiz in `docs/AUDIT_2026-05-25_TODO.md`, Masterliste aktualisiert |
| P1 | Restore-Backlog aktualisieren | **erledigt 2026-06-05:** erledigte Kandidaten aus `docs/RESTORE_MATRIX.md` bereinigt |
| P1 | Family-Onboarding in ein ausfuehrbares Session-Paket umwandeln | **erledigt 2026-06-05:** `docs/FAMILY_ONBOARDING.md` enthaelt Vorbereitungs-, Termin- und Erfolgskriterien ohne Secret-Werte |
| P1 | `docs/NETWORK_INVENTORY.md` TBDs in Entscheidungen oder konkrete Operator-Fragen verwandeln | **erledigt 2026-06-05:** Tailscale IPv6/Exit Node/Subnet Router/ACL-Policy sind als Messaufgabe/Operator-Entscheidung formuliert; Gast-/WAN-Pfade sind geparkt oder mit Vorbedingungen versehen |
| P2 | Nicht-destruktive Runbooks fuer offene Restore-Tests vorbereiten | **erledigt 2026-06-05:** Runbook-Stubs fuer Unraid Flash, AdGuard, Tailscale, Redis 8 in `docs/RESTORE_MATRIX.md` |
| P2 | `docs/AUDIT_2026-05-25_TODO.md` und `MASTER_TODO.md` synchronisieren | **erledigt 2026-06-05:** keine doppelten oder widerspruechlichen P1/P2-Punkte |
| P2 | Windows-Reinstall-Altdoku auf ueberholte To-dos pruefen | **erledigt 2026-06-05:** WinRE/Admin-Check-Altlasten als erledigt/ueberholt markiert |
| P2 | Hardware-/Betriebsentscheidungen konsolidieren | **teilweise erledigt 2026-06-05:** USV und Cold-Backup-Rotation sind entschieden/geparkt; Masterliste fuehrt sie nicht mehr als aktive Umsetzungsaufgaben |
| P3 | Geparkte Punkte klassifizieren | Family/USV/WAN/CrowdSec/OIDC klar als Entscheidung statt Umsetzungsarbeit markiert |
## Operator-Aufgaben
| Prioritaet | Aufgabe | Abschlusskriterium |
|---|---|---|
| P1 | Veeam-Encryption-Entscheidung treffen | Fuer den ersten Full-Lauf ist kein Veeam-Encryption-Passwort noetig; falls Storage Encryption aktiviert wird, Passwort in Vaultwarden anlegen und neues Full erzeugen |
| P1 | Recovery-USB physisch booten | Boot ins Veeam-Recovery-System gelingt |
| P1 | Keine echten Restore-Ziele bestaetigen | Restore-Test wird vor destruktiver Datentraegerauswahl abgebrochen |
| P2 | BitLocker-Entscheidung treffen | `aktivieren`, `spaeter`, oder `bewusst aus` dokumentiert |
| P2 | Family-Onboarding real starten oder terminieren | konkreter Termin/Personenkreis statt offenem Wunsch |
| P3 | Hardware-Entscheidungen | USV/Cold-Rotation/WAN-Failover als kaufen, spaeter, oder bewusst nein markieren |
## Realistische Wochenend-Ziele
Bis Sonntagabend realistisch fertig:
- `baerchen` Veeam-Erstbackup verifiziert.
- `baerchen` Recovery-USB-Test ohne Restore verifiziert.
- Veeam-/BitLocker-Doku bereinigt.
- Master-To-do-Liste bereinigt.
- Restore-Backlog sortiert.
- Alte/ueberholte To-dos als erledigt/ueberholt markiert.
- Blockierte Punkte explizit als Betreiber-/Hardware-/Familienentscheidung markiert.
Nicht realistisch ohne externe Voraussetzungen:
- End-to-end-DR-Drill ohne zweite Hardware.
- Family-Onboarding ohne Familie/Geraete.
- USV erledigen ohne Kauf.
- WAN-Failover erledigen ohne Mobilfunk-/Router-Entscheidung.
- Dedizierter SMB-User ohne bewusste Unraid-User-/Share-Aenderung.
## Prompt fuer Claude
```text
Du bist Claude im KalliLab CORE Homelab-Repo.
Arbeitsziel fuer dieses Wochenende:
Hilf, alle offenen To-dos aus `docs/MASTER_TODO.md` bis Sonntagabend entweder
zu erledigen, sauber zu dokumentieren, oder bewusst als geparkt/blockiert zu
klassifizieren. Arbeite repo-seitig, keine produktiven Host-Aenderungen.
Pflichtregeln:
- Lies zuerst `CLAUDE.md`.
- Lies danach `HOMELAB_ARCHITECTURE_MASTER_V2.md`, `docs/WORKFLOW.md`,
`docs/README.md`, `docs/REPO_MAP.md`, `docs/MASTER_TODO.md`,
`docs/RESTORE_MATRIX.md`, `docs/DISASTER_RECOVERY.md`,
`docs/SECRETS_MAP.md` und `ops/windows-reinstall/docs/windows-image-backup-baseline.md`.
- Keine Secrets ins Repo. Nur Secret-Namen, Pfade und Ablageorte dokumentieren.
- Keine Komodo-/Docker-/Host-Hotfixes. Keine produktiven Schreibbefehle auf dem Homelab.
- Keine destruktiven Aktionen.
- Beachte vorhandene uncommitted Aenderungen; nichts revertieren, was du nicht selbst gemacht hast.
Konkrete Aufgaben:
1. Wandle `docs/FAMILY_ONBOARDING.md` von einer guten Erklaerseite in ein
ausfuehrbares Session-Paket um:
- 30-Minuten-Ablauf fuer das erste echte Onboarding
- Checkliste pro Geraet/Person ohne Namen oder Secret-Werte
- klare Abschlusskriterien fuer Vaultwarden, Immich und Mealie
- Liste der Operator-Fragen, falls Konten/Startpasswoerter fehlen
2. Bereinige `docs/NETWORK_INVENTORY.md`:
- Tailscale IPv6, Exit Node, Subnet Router und ACL-Policy nicht als
unerklaerte `TBD` stehen lassen
- wenn nicht verifizierbar: als konkrete Operator-Frage oder bewusst offene
Entscheidung formulieren
- Gast-/IoT-Zugriff als Entscheidungspfad dokumentieren, nicht als vage
Altlast
3. Ziehe `docs/MASTER_TODO.md` nach deinen Edits nach:
- echte naechste Schritte in P1/P2
- geparkte Entscheidungen nur im geparkten/geschlossenen Bereich
- keine Duplikate zu `docs/AUDIT_2026-05-25_TODO.md`
4. Falls du weitere diffuse TBDs in Hardware/Network/Family findest: nicht nur
melden, sondern in konkrete Entscheidung, geparkten Punkt oder naechsten
Operator-Schritt umformulieren.
5. Schon erledigte Restore-/Windows-Doku-Aufgaben nicht erneut bearbeiten,
ausser du findest einen klaren Widerspruch.
6. Am Ende liefere:
- geaenderte Dateien
- welche Punkte geschlossen wurden
- welche Punkte blockiert/geparkt bleiben und warum
- welche Operator-Schritte noch noetig sind
Nicht tun:
- Keine Secrets anzeigen oder erfinden.
- Kein Push.
- Kein `docker`, `ssh` oder Host-Schreibzugriff.
- Kein BitLocker, keine Veeam-Aenderung, keine Unraid-User-/Share-Aenderung.
```
## Abschlusskriterien fuer Sonntag
- `docs/MASTER_TODO.md` ist die fuehrende Liste.
- Alle erledigten Punkte haben Beleg in der Detaildoku.
- Alle nicht erledigbaren Punkte sind als blockiert/geparkt mit Grund markiert.
- `git status` ist verstanden: eigene Doku-Aenderungen vs. bestehende
User-Aenderungen sind getrennt.
- Commit ist vorbereitet, Push erfolgt nur nach Operator-Freigabe.
-41
View File
@@ -1,41 +0,0 @@
# Weekend Status - 2026-06-05
Kurzlebiges Arbeitsboard fuer den Wochenend-Sprint. Fuehrende Liste bleibt
`docs/MASTER_TODO.md`; dieses Board haelt nur den aktuellen Arbeitsstand fest.
## Jetzt laufend
| Owner | Aufgabe | Status | Naechster Schritt |
|---|---|---|---|
| Codex | Veeam-Erstbackup `baerchen-c-image` | erledigt | Erster Full-Lauf 2026-06-05 geschrieben; Recovery-Test bleibt offen |
| Codex | Veeam-Verifikationshilfe | erledigt | Hilfsskript bleibt fuer spaetere Checks verfuegbar |
| Claude | Restore-/Altdoku-Bereinigung | erledigt | Keine weitere Arbeit an Veeam/Windows/Restore-Matrix ohne neuen Widerspruch |
| Claude | Family-/Network-Ausfuehrungspaket | erledigt | Masterliste und Weekend-Plan sind nachgezogen |
## Naechste Operator-Schritte
| Zeitpunkt | Aufgabe | Ergebnis, das dokumentiert wird |
|---|---|---|
| Erledigt | Veeam-Erstbackup `baerchen-c-image` pruefen | 2026-06-05 19:46, Full-Lauf erfolgreich, Veeam-GUI 53,8 GB, Dauer 0:11:31 |
| Als naechstes | Recovery-USB `VEEAMRE` booten | Boot OK, Netzwerk OK, SMB-Ziel sichtbar |
| Im Recovery-Test | Restore Point anzeigen; falls spaeter verschluesselt: Passwort testen | Restore Point sichtbar; vor echtem Restore abgebrochen |
| Spaeter | BitLocker-Entscheidung treffen | `aktivieren`, `spaeter`, oder `bewusst aus` in `docs/MASTER_TODO.md`/Baseline nachziehen |
## Heute bereits geschlossen
| Thema | Ergebnis |
|---|---|
| WinRE/Admin-Altlasten | In Windows-Reinstall-Doku als erledigt/ueberholt markiert |
| Restore-Test-Kandidaten | Erledigte Kandidaten aus der aktiven Liste entfernt; Stubs fuer offene Kandidaten ergaenzt |
| Family-Onboarding | Aus der Familien-Doku wurde ein konkreter 30-45-Minuten-Terminablauf mit Vorbereitung und Erfolgskriterien |
| Network-TBDs | Tailscale-/Gastnetz-/WAN-Failover-Punkte wurden in Messaufgaben, Vorbedingungen oder geparkte Entscheidungen umgewandelt |
| Veeam-Erstbackup | Full-Lauf 2026-06-05 erfolgreich geschrieben: Veeam-GUI 53,8 GB, Dauer 0:11:31, MetaCheck 0 Fehler/0 Warnungen, VSS success; Veeam Storage Encryption war nicht aktiv |
| Cold-Backup-Rotation | Bewusst Hetzner-only; kein aktives Todo mehr |
| USV | Bewusst auf Q3/2026 geparkt; Power-Loss bleibt akzeptiertes Risiko |
## Nicht ohne neue Freigabe anfassen
- Keine BitLocker-Aktivierung.
- Keine Aenderung am Veeam-Job oder Encryption-Status.
- Keine Unraid-User-/Share-Aenderung.
- Keine produktiven Host- oder Docker-Schreibbefehle.
@@ -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
```
-303
View File
@@ -1,303 +0,0 @@
# System-Audit 2026-06-05
**Scope:** Windows-Host `baerchen` (frisch aufgesetzt), Read-only
**Referenz-Doku:** `ops/windows-reinstall/docs/laufwerks-neustruktur-2026-06-04.md`, `boot-cleanup-plan-2026-06-04.md`
**Durchgeführt:** 2026-06-05, ohne Admin-Rechte
**Rohdaten:** `audit/raw/01_volumes_partitions.txt` bis `06_events_hardware.txt`
---
## 1. Ordner- und Laufwerksstruktur (Priorität)
### 1.1 Soll-Ist-Vergleich: Ordner-Existenz
| Pfad | Soll | Ist | Status |
|---|---|---|---|
| `D:\00_Inbox` | ✓ | vorhanden | OK |
| `D:\10_Dokumente` | ✓ | vorhanden | OK |
| `D:\11_Bilder` | ✓ | vorhanden | OK, aber ReadOnly-Attribut gesetzt |
| `D:\12_Videos` | ✓ | vorhanden | OK |
| `D:\13_Musik` | ✓ | vorhanden | OK |
| `D:\14_Downloads` | ✓ | vorhanden | OK |
| `D:\20_Projekte\aktiv` | ✓ | vorhanden | OK |
| `D:\20_Projekte\archiv` | ✓ | vorhanden | OK |
| `D:\30_Finanzen\Banking4` | ✓ | vorhanden | OK |
| `D:\30_Finanzen\WISO_Steuer` | ✓ | vorhanden | OK |
| `D:\90_Archiv` | ✓ | vorhanden | OK |
| `E:\Steam\steamapps` | ✓ | vorhanden | OK |
| `E:\BattleNet` | ✓ | vorhanden | OK |
| `E:\EpicGames` | ✓ | vorhanden | OK |
| `E:\EA` | ✓ | vorhanden | OK |
| `E:\Riot` | ✓ | vorhanden | OK |
| `E:\Ubisoft` | ✓ | vorhanden | OK |
| **`E:\_Standalone`** | **✓** | **FEHLT** | **LÜCKE** |
| `G:\repos` | ✓ | vorhanden | OK |
| `G:\tools` | ✓ | vorhanden als `Tools` (Großbuchstabe) | OK (NTFS case-insensitive) |
**Nicht in Soll-Doku, aber vorhanden:**
| Pfad | Beurteilung |
|---|---|
| `D:\Micha\Videos` | Altquelle, fast leer (1 Datei), Rest wurde bereinigt |
| `D:\WSL` | WSL-Datenpfad, nicht in Doku erwähnt, aber logisch |
| `G:\Apps` | Zweck unklar, nicht dokumentiert |
| `G:\Gitea_Clone` | Bewusst so (homelab-infra bleibt laut Doku unangetastet) |
| `G:\Workspace` | Nicht dokumentiert, wahrscheinlich Dev-Workspace |
### 1.2 Known-Folder-Redirects
| Ordner | Soll (Doku) | Ist (gemessen) | Status |
|---|---|---|---|
| Desktop | `D:\Micha\Desktop` | `D:\00_Inbox\Desktop` | **ABWEICHUNG** |
| Dokumente | `D:\10_Dokumente` | `D:\10_Dokumente` | OK |
| Downloads | `D:\14_Downloads` | `D:\14_Downloads` | OK |
| Bilder | `D:\11_Bilder` | `D:\11_Bilder` | OK |
| Musik | `D:\13_Musik` | `D:\13_Musik` | OK |
| Videos | `D:\12_Videos` | `D:\12_Videos` | OK |
**Desktop-Befund (Detail):**
- Soll-Doku schreibt: `D:\Micha\Desktop` (als bewusster Sonderfall ohne nummerierten Ordner).
- Ist: Desktop zeigt auf `D:\00_Inbox\Desktop` — dieser Ordner existiert, enthält 4 Dateien.
- `D:\Micha\Desktop` existiert **nicht**.
- `D:\Micha` enthält nur noch `Videos` (1 Datei, leer).
- Fazit: Das Known-Folder-Ziel wurde nach der Doku-Erstellung nochmals geändert. Die Doku ist in diesem Punkt veraltet. Der Desktop liegt funktional korrekt auf D:, aber das Ziel weicht vom dokumentierten Soll ab. **Doku-Update empfohlen.**
### 1.3 Doppelbestand D:\Micha\* vs. neue Nummernstruktur
| Alt | Dateien | Neu | Dateien | Bewertung |
|---|---|---|---|---|
| `D:\Micha\Dokumente` | NICHT MEHR VORHANDEN | `D:\10_Dokumente` | 4011 / 595 MB | Bereinigt ✓ |
| `D:\Micha\Bilder` | NICHT MEHR VORHANDEN | `D:\11_Bilder` | 7789 / 12,4 GB | Bereinigt ✓ |
| `D:\Micha\Videos` | 1 Datei, ~0 MB | `D:\12_Videos` | 1 Datei, ~0 MB | Quasi-leer, kein Doppelbestand |
| `D:\Micha\Musik` | NICHT MEHR VORHANDEN | `D:\13_Musik` | 0 Dateien | Bereinigt ✓ |
| `D:\Micha\Downloads` | NICHT MEHR VORHANDEN | `D:\14_Downloads` | 2186 / 2,2 GB | Bereinigt ✓ |
| `D:\Micha\Finanzen` | NICHT MEHR VORHANDEN | `D:\30_Finanzen` | 126 / 123 MB | Bereinigt ✓ |
**Fazit:** Der befürchtete Doppelbestand ist weitgehend aufgelöst. Nur `D:\Micha\Videos` ist noch vorhanden, ist aber inhaltlich leer. `D:\Micha` kann nach manueller Prüfung von Videos entfernt werden.
### 1.4 Labels
| Laufwerk | Soll | Ist | Status |
|---|---|---|---|
| D: | `Daten-Projekte` | `Daten-Projekte` | OK ✓ |
| E: | `Games` | `Games` | OK ✓ |
| H: | unveraendert | `Externe HDD` | OK ✓ |
### 1.5 Rollen-Konsistenz und Partitions-Layout
| Laufwerk | Soll-Rolle | Ist | Status |
|---|---|---|---|
| C: | Windows + kleine Programme | Disk 0, 167 GB SATA | OK |
| D: | Daten & Projekte | Disk 1, 168 GB SATA | OK |
| E: | Games | Disk 2, **930 GB** NVMe (nach F-Merge) | OK ✓ |
| F: | Altes Windows (löschen) | **Nicht mehr vorhanden** | Abgeschlossen ✓ |
| G: | Arbeits-SSD, Homelab/Dev | Disk 3, 931 GB NVMe | OK |
| H: | Externe Backup-HDD | Disk 4, 7.28 TB USB | OK |
E: und das ehemalige F: sind jetzt eine einzige 930 GB Partition auf Disk 2. Layout ist sauber.
### 1.6 Fachliche Gesamtbewertung der Struktur
**Stärken:**
- Die Nummernstruktur auf D: ist vollständig angelegt und die Known Folders zeigen bis auf Desktop korrekt dorthin.
- Der Doppelbestand ist fast vollständig bereinigt — das war die größte Risikoquelle.
- F: ist weg, E: ist auf volle Disk-Kapazität gewachsen — die BCD-Bereinigung und Partition-Erweiterung wurde sauber abgeschlossen.
- Label-Benennung konsistent.
- G: ist operational (repos, Tools, Gitea_Clone vorhanden).
**Lücken und Inkonsistenzen:**
1. **Desktop-Redirect weicht von Doku ab** (Ist: `D:\00_Inbox\Desktop`, Doku: `D:\Micha\Desktop`). Da `D:\Micha\Desktop` nicht existiert und der Desktop funktioniert, ist die Doku das Problem, nicht das System.
2. **`E:\_Standalone` fehlt** — laut Doku angelegt, tatsächlich nicht vorhanden. Kein funktionaler Schaden, aber Inkonsistenz zur Rollenbeschreibung.
3. **`D:\11_Bilder` hat ReadOnly-Attribut** auf Ordner-Ebene gesetzt — ungewöhnlich, keine erkennbare Ursache. Kein Showstopper, aber prüfenswert.
4. **`G:\Apps`, `G:\Workspace`** sind nicht in der Soll-Doku definiert. Kein Problem an sich, aber für spätere Audits hilfreich zu dokumentieren.
5. **`D:\WSL`** nicht dokumentiert — WSL-Datenpfade dort gehören explizit erwähnt.
6. **`D:\13_Musik`** ist leer (0 Dateien) — entweder war `D:\Micha\Musik` schon leer, oder die Kopie ist ausgeblieben. Zu prüfen ob Musik aus PostDelta-Backup nachgezogen werden muss.
**Gesamturteil:** Die Struktur ist in sich schlüssig und der Umbau ist zu ~95% abgeschlossen. Die verbleibenden Punkte sind kleine Doku-Lücken und ein fehlender Ordner, kein strukturelles Problem.
---
## 2. OS-Baseline
| Feld | Wert | Bewertung |
|---|---|---|
| Edition | Windows 11 Pro | OK |
| Build | 26200 (Insider/Preview-Build) | Achtung: kein Stable-Channel-Build |
| Aktivierung | OEM_DM, aktiv | OK |
| Installiert | 2026-05-10 | ~25 Tage alt |
| Letzter Boot | 2026-06-05 07:57 | Frisch gebootet |
| Ausstehende Updates | 0 | OK |
| Reboot pending | Nein | OK |
**Befund Build 26200:** Das ist ein Windows Insider/Canary-Channel Build, kein Produktions-Release. Für einen Nerd-Einsatz vertretbar, aber mit dem Wissen verbunden, dass Insider-Builds weniger stabil sind und keine LTS-Garantie haben.
---
## 3. Security
### Defender
- Aktiv, TamperProtection an, Signaturen aktuell. **OK.**
- Ausschlüsse und ASR-Regeln: nur als Admin lesbar — **kein Befund, aber blind spot.**
### Firewall
- Alle drei Profile aktiv. DefaultInboundAction `NotConfigured` bedeutet im Windows-Default: eingehend blockieren, ausgehend erlauben. **OK.**
- Port 27036 (Steam Remote Play) lauscht auf `0.0.0.0` — also LAN-seitig offen. Erwartetes Steam-Verhalten, aber explizit im Bewusstsein halten.
### BitLocker
- Nicht prüfbar ohne Admin. **Blind spot — Empfehlung: BitLocker für C: und D: aktivieren.**
### Secure Boot / TPM
- Nicht prüfbar ohne Admin. Hardware MSI MS-7D32 unterstützt beides. Status unbekannt.
### UAC
- Standard-Konfiguration korrekt (Secure Desktop aktiv). **OK.**
### Lokale Admins
- `Administrator` (Built-in) + `michi`. Zwei Accounts in Admins ist normal für einen Einzel-PC. OK.
### SSH Key Permissions
- `id_ed25519` hat `VORDEFINIERT\Administratoren FullControl` — das ist zu weit offen.
- SSH-Clients unter Windows tolerieren das, aber best practice ist: nur der eigene User darf lesen.
- **Empfehlung:** `icacls` Berechtigungen auf User only setzen (als Admin ausführen).
---
## 4. Storage & Boot
- Alle 5 physischen Disks: **Healthy / OK.**
- Wear-Level via `Get-StorageReliabilityCounter`: keine Ausgabe (SATA-SSDs und USB HDD liefern keine WMI-Daten). CrystalDiskInfo ist installiert — dort manuell prüfen.
- Die zwei Intel SATA SSDs (Disk 0 + 1) sind **180 GB** — typische Einzel-Partition-Auslastung auf C: ~36% und D: ~11%, reichlich Luft.
- **BCD:** ohne Admin nicht lesbar. Doku bestätigt sauberen Zustand nach Cleanup + Neustarttest.
- **WinRE:** ohne Admin nicht lesbar. Doku sagt Disabled — muss vor künftiger Partitionsarbeit aktiviert werden.
---
## 5. Netzwerk
- Ethernet: 192.168.178.103, DNS auf Kallilabcore (AdGuard). **Korrekt.**
- Tailscale: aktiv, dieser Rechner als `baerchen-1` online, direkter Pfad zu `kallilabcore`. **OK.**
- Kein SSH-Config — alle SSH-Verbindungen laufen ohne Host-Aliases. Funktional, aber unpraktisch.
- Lauschende Ports: Keine auffälligen Exposition nach außen außer SMB (139/445 — LAN-normal) und Steam 27036.
---
## 6. Remote-Management / SSH
- Kein `~\.ssh\config` vorhanden. Empfehlung: Host-Aliases anlegen (z.B. `Host kallilabcore`).
- SSH-Key vorhanden und aktuell.
- **Key-Rechte zu weit (s. Security).**
- Docker contexts: `desktop-linux` aktiv. Docker Desktop läuft.
- kubectl: keine Contexts — erwartet (kein k8s im Homelab).
- Tailscale: direkter Pfad zu Homelab aktiv, SSH über Tailscale-IP funktioniert.
---
## 7. Dev-Toolchain
| Tool | Version | Bewertung |
|---|---|---|
| git | 2.54.0 | Aktuell, OK |
| Python | 3.13.13 | Aktuell, OK |
| Node.js | 24.16.0 (LTS) | Aktuell, OK |
| Go | 1.26.4 | Aktuell, OK |
| Commit-Signing | nicht konfiguriert | Optional, aber für Homelab-GitOps empfohlen |
WSL Ubuntu ist installiert aber gestoppt. docker-desktop läuft als WSL2-Backend.
---
## 8. Hardware & Performance
- i5-14600KF, 14C/20T, 31.8 GB RAM — für Homelab-Dev-Rechner gut ausgestattet.
- Energieplan: **Ausbalanciert** — für einen Gaming- und Dev-Rechner suboptimal. `Höchstleistung` oder `Ultimative Leistung` wäre bei dauerhafter Nutzung besser.
- Keine echten Gerätekonflikte in PnP (alle "Unknown" sind erwartet: ghosted devices, Netzwerkgeräte, VSS).
---
## 9. Autostart & Persistenz
Läuft automatisch: Brave Update, Steam, Razer Synapse, Docker Desktop, iCUE, Realtek Audio, Tailscale, Ollama.
**Auffällig:** `SoftLanding\CreativeManagementTask` — unbekannter Scheduled Task, nicht einem Standard-Produkt zuzuordnen. Sollte manuell im Task Scheduler geprüft werden (Quelle, Executable, Publisher).
OneDrive läuft mit drei Tasks (Startup + Update) — falls Daten-Sync nicht gewünscht ist, sollte OneDrive deaktiviert werden, da es Dokumente/Bilder/etc. stummschalten könnte (bekanntes Windows-Verhalten nach Known-Folder-Redirect).
---
## 10. Zuverlässigkeit
| Event ID | Anzahl | Beschreibung | Risiko |
|---|---|---|---|
| 20 | 70 | Defender KB4052623 Update-Fehler (0x80240016) | Niedrig — Timing, Defender aktuell |
| 10010 | 15 | DCOM Server Timeout | Niedrig — Windows-Hintergrund |
| 7000 | 3 | Steam Service Start fehlgeschlagen | Niedrig — Race Condition beim Boot |
| 7023 | 3 | Windows Modules Installer beendet mit Fehler | Mittel — Update-Abbrüche prüfen |
| **6008** | **2** | **Unerwartetes Herunterfahren 2026-05-19 13:56** | **Mittel — einmaliger BSOD/Stromausfall** |
| 7034 | 2 | MSI Center Service Absturz | Niedrig |
- **Kein Crash-Dump** vorhanden (`C:\Windows\Minidump` leer). Entweder ist kein BSOD gewesen (Stromausfall), oder Dump-Einstellungen schreiben nicht.
- Empfehlung: Dump-Einstellungen auf "Kleiner Speicherauszug" oder "Vollständiger Speicherauszug" prüfen.
---
## 11. Homelab-Server (ausstehend)
**Status: NICHT DURCHGEFÜHRT**
SSH-Config ist leer — kein Host-Alias konfiguriert. Tailscale zeigt `kallilabcore` als aktiv auf `100.80.98.33` / `192.168.178.58`.
**Bitte bestätigen:**
- SSH-User für Kallilabcore (wahrscheinlich `root`?)
- Soll ich `ssh root@192.168.178.58` oder über Tailscale-IP verwenden?
Nach Bestätigung wird der Homelab-Teil nachgezogen und dieser Report ergänzt.
---
## 12. Gesamt-Findings (priorisiert)
### Kritisch / Handlungsbedarf vor nächster Partitionsarbeit
| # | Befund | Begründung |
|---|---|---|
| K1 | WinRE ist Disabled (laut Doku) | Ohne WinRE kein automatisches Recovery. Muss aktiviert werden bevor weitere Disk-Ops. |
| K2 | BitLocker-Status unbekannt (kein Admin) | C: und D: sollten verschlüsselt sein — aktuell Blind Spot. |
### Mittel / Zeitnah klären
| # | Befund |
|---|---|
| M1 | Desktop-Redirect zeigt auf `D:\00_Inbox\Desktop`, Doku sagt `D:\Micha\Desktop` — Doku aktualisieren |
| M2 | `E:\_Standalone` fehlt — Ordner anlegen oder aus Doku streichen |
| M3 | SSH Private Key Permissions zu weit (Admins haben FullControl) |
| M4 | Energieplan "Ausbalanciert" — für Gaming/Dev `Höchstleistung` empfohlen |
| M5 | `SoftLanding\CreativeManagementTask` unbekannt — Quelle und Publisher prüfen |
| M6 | Unerwartetes Herunterfahren 2026-05-19 — Ursache klären (Stromausfall? BSOD ohne Dump?) |
| M7 | `D:\11_Bilder` hat ReadOnly-Attribut — Ursache und Auswirkung prüfen |
### Niedrig / Nice-to-have
| # | Befund |
|---|---|
| N1 | SSH-Config leer — Host-Aliases anlegen |
| N2 | Git commit.gpgsign nicht gesetzt — für GitOps-Commits empfohlen |
| N3 | `D:\Micha\Videos` noch vorhanden (1 leere Datei) — bereinigen |
| N4 | `G:\Apps`, `G:\Workspace` nicht in Doku — dokumentieren oder strukturieren |
| N5 | `D:\WSL` nicht in Doku — erwähnen |
| N6 | `D:\13_Musik` leer — Musik aus PostDelta-Backup nachziehen? |
| N7 | OneDrive läuft (3 Tasks) — prüfen ob Sync für D:\10_Dokumente etc. gewünscht |
| N8 | Energiesparmodus-Dump-Einstellungen prüfen (kein Dump für 6008-Event) |
| N9 | `D:\DumpStack.log` ist ein Artefakt aus der alten D:-Nutzung, kann bereinigt werden |
| N10 | Insider-Build 26200 — bewusste Entscheidung, aber dokumentieren |
---
## 13. Nächste Schritte (empfohlen, nicht ausgeführt)
1. **Homelab-SSH-Zugang bestätigen** und Homelab-Audit nachziehen.
2. **WinRE aktivieren** (als Admin: `reagentc /enable`) — Voraussetzung für künftige Disk-Ops.
3. **BitLocker Status prüfen** (als Admin: `Get-BitLockerVolume`) und ggf. für C:/D: aktivieren.
4. **SSH-Key-Permissions straffen**: `icacls $env:USERPROFILE\.ssh\id_ed25519 /inheritance:r /grant:r "$env:USERNAME:F"` (als Admin).
5. **`SoftLanding\CreativeManagementTask` untersuchen** — im Task Scheduler Quelle und Aktion prüfen.
6. **Doku `laufwerks-neustruktur-2026-06-04.md`** unter Abschnitt Desktop-Befund korrigieren: Ist-Ziel `D:\00_Inbox\Desktop`.
7. **`E:\_Standalone`** anlegen falls geplant.
8. **`D:\Micha\Videos`** prüfen und ggf. löschen.
9. **CrystalDiskInfo** für SSD Wear-Level öffnen und Werte dokumentieren.
10. **Energieplan** auf `Höchstleistung` oder `Ultimative Leistung` umstellen.
-228
View File
@@ -1,228 +0,0 @@
# Homelab-Optimierung — Assessment 2026-06-10
Read-only-Analyse des Repos (Stand `master`, lokale Arbeitskopie 2026-06-10).
Keine produktiven Änderungen durchgeführt. Alle Empfehlungen sind Vorschläge
mit Rollback-Plan; nichts wurde deployed.
## Executive Summary
Das KalliLab-CORE-Homelab ist für ein Ein-Host-Setup ungewöhnlich reif:
GitOps mit Gitea+Komodo, sauberes Netzmodell (frontend/backend/app-intern),
Authelia mit 2FA-Catch-all, belegte Restore-Drills für alle Tier-1/2-Dienste,
Off-site-Borg nach Hetzner, DR-Workstation-Kit, Monitoring mit Prometheus/
Loki/Grafana/Alertmanager→ntfy. Die Doku-Disziplin ist das eigentliche Asset.
Die größten realen Lücken liegen nicht in der Architektur, sondern in der
**Container-Betriebsebene**: 20 von 30 Stacks haben keinen Healthcheck, kein
einziger Container hat Memory-/CPU-Limits, und mehrere Images laufen auf
mutablen Tags (`release`, `latest`, `:2`), bei denen Renovate-Digest-Bumps
faktisch unkontrollierte Versionssprünge sind — am kritischsten bei Immich.
Dazu kommen zwei strukturelle Risiken: **AdGuard ist DNS-SPOF ohne Fallback**
(hat bereits einen Teil-Deploy-Ausfall verursacht) und **Borg-Backups sind
vom Host aus löschbar** (append-only bewusst abgelehnt, aber die kostenlose
Kompensation — Hetzner-Storage-Box-Snapshots — ist nicht aktiviert).
## Gesamtbewertung
| Bereich | Note | Begründung |
|---|---|---|
| Architektur | **sehr gut** | klares Netzmodell, dokumentierte Ausnahmen, ein Ingress, Compose-first konsequent |
| Netzwerk/DNS/Proxy | **gut, ein SPOF** | Traefik v3 labelbasiert sauber; AdGuard+Unbound ohne zweiten Resolver — bekannter Vorfall (Bulk-Deploy-DNS-Ausfall, `docs/runbooks/komodo-bulk-deploy-dns.md`) |
| Container-Betrieb | **mittel** | 10/30 Stacks mit Healthcheck, 0 Ressourcen-Limits, mutable Tags hinter Digests versteckt |
| Storage/Backups | **sehr gut** | Borg→Hetzner, Dumps, H:/-Nearline, Restore-Drills mit Reports belegt; offen: Backup-Löschschutz |
| Security/Secrets | **gut** | `_FILE`/Stack-ENV konsequent, 2FA-Catch-all, WAN nur 443/tcp; `no-new-privileges` nur in 10/30 Stacks trotz P8-Pflichtregel |
| Monitoring/Alerting | **gut** | Prometheus/Blackbox/Loki/ntfy-Kette steht; Monitoring-Stack selbst hat keine Healthchecks und überwacht sich nicht selbst |
| Automatisierung/IaC | **sehr gut** | Komodo-Webhooks, Renovate, Posture-Check, Critical-Events-Watcher; manuelle Sync-Ausnahmen (traefik/dynamic, Authelia-Config) sind dokumentiert, aber fehleranfällig |
| Ausfallsicherheit | **bewusst begrenzt** | Ein Host, keine USV (geparkt Q3/2026), kein WAN-Failover — als akzeptiertes Risiko dokumentiert, das ist legitim |
| Strom/Kosten | **keine Daten** | keine Verbrauchsmessung im Repo sichtbar — siehe offene Fragen |
## Top 10 Verbesserungen nach Mehrwert
### 1. Immich vom `release`-Tag auf Versions-Tag pinnen
- **Beobachtung:** `apps/immich/docker-compose.yml:4` nutzt `immich-server:release@sha256:...` (ebenso ML). Renovate aktualisiert Digests — beim `release`-Tag ist ein "Digest-Update" in Wahrheit ein Major-/Minor-Versionssprung, ohne dass es im PR-Titel sichtbar wird. Immich ist berüchtigt für Breaking Changes zwischen Minors.
- **Warum relevant:** Ein gemergter "harmloser" Digest-PR kann Immich unangekündigt auf eine inkompatible Version heben (DB-Migrationen, ML-Modell-Wechsel).
- **Änderung:** Tag auf die konkret laufende Version umstellen (z. B. `immich-server:v2.x.y@sha256:<aktueller Digest>`), gleiche Vorgehensweise wie bei Mealie/Paperless. Laufende Version ermitteln: `docker exec immich_server cat /usr/src/app/package.json | grep version` oder Immich-UI → Version.
- **Verifikation:** Renovate erzeugt danach Versions-PRs statt stiller Digest-PRs; `docker inspect immich_server --format '{{.Config.Image}}'` zeigt den Versionstag.
- **Rollback:** Commit revert; Digest bleibt identisch, kein Redeploy-Zwang.
- **Nebenwirkungen:** keine zur Laufzeit (Digest unverändert). | Nutzen: **hoch** | Risiko: niedrig | Aufwand: klein | sofort
- Gleiches Muster prüfen für: `komodo:2`, `ddns-updater:latest`, `scrutiny:latest-omnibus`, `glances:latest-full` sowie tag-lose digest-only Images (`mail-archiver`, `borg-ui`, `ntfy` — Version im Compose unsichtbar).
### 2. Hetzner-Storage-Box-Snapshots als Ransomware-/Fehlbedienungsschutz aktivieren
- **Beobachtung:** Borg `append-only` wurde am 2026-06-01 bewusst verworfen (forced-command brach Key-Auth). Damit kann jeder mit dem Borg-Key (Host, borg-ui-Container mit `/local/secrets`-Mount) Archive **löschen** — ein kompromittierter Host vernichtet auch das Off-site-Backup.
- **Warum relevant:** Das ist die einzige verbliebene Lücke in einer sonst sehr guten Backup-Kette.
- **Änderung:** In der Hetzner-Robot-Konsole automatische Snapshots der Storage Box aktivieren (z. B. täglich, 714 Tage Retention). Snapshots sind host-seitig nicht löschbar und im Storage-Box-Preis enthalten.
- **Verifikation:** Robot-Konsole zeigt Snapshot-Liste; nach 2 Tagen: zwei Snapshots vorhanden. Restore-Probe: einzelne Datei aus Snapshot über das Snapshot-Verzeichnis lesen.
- **Rollback:** Snapshots deaktivieren — rein additiv, keine Auswirkung auf Borg.
- **Nebenwirkungen:** Snapshots zählen ggf. anteilig aufs Quota (aktuell 65 GB / 1 TB — viel Luft). | Nutzen: **sehr hoch** | Risiko: niedrig | Aufwand: klein (<30 min) | sofort
### 3. DNS-Fallback gegen den AdGuard-SPOF
- **Beobachtung:** AdGuard ist einziger LAN-Resolver. Der dokumentierte Vorfall (Bulk-Deploy: AdGuard-Recreate → Host ohne DNS → Komodo-Pulls scheitern) ist genau dieses Muster; das Runbook behandelt nur das Symptom.
- **Warum relevant:** Jeder AdGuard-Ausfall (Update, OOM, Disk) nimmt LAN + Host-DNS gleichzeitig mit — auch die Reparaturfähigkeit (Image-Pulls!) hängt daran.
- **Änderung (gestuft):**
- a) Host-Ebene: zweiten Nameserver (z. B. `1.1.1.1`) in der Unraid-Netzwerkkonfig als Fallback hinter `192.168.178.58` eintragen. Damit kann der Host immer Images pullen.
- b) LAN-Ebene: in der FRITZ!Box als zweiten lokalen DNS die FRITZ!Box selbst (oder einen Public DNS) hinterlegen — bewusster Trade-off: bei AdGuard-Down kein Ad-Blocking statt kein Internet.
- **Verifikation:** `docker stop adguard` im Wartungsfenster → `nslookup gitea.com` auf dem Host funktioniert weiterhin; danach `docker start adguard`.
- **Rollback:** Nameserver-Eintrag entfernen.
- **Nebenwirkungen:** DNS-Anfragen können am Filter vorbeilaufen, solange AdGuard down ist (gewollt); Fallback-Resolver sieht dann Anfragen (Privacy-Abwägung). | Nutzen: **hoch** | Risiko: niedrig | Aufwand: klein | diese Woche
### 4. Healthchecks für die App-Stacks nachrüsten
- **Beobachtung:** Nur 10 von 30 Compose-Dateien definieren Healthchecks (traefik, gitea, vaultwarden, authelia, postgresql17, redis, komodo, bentopdf, glances, hermes). **Ohne:** Nextcloud (App+DB+Redis), Immich (alle 4), Paperless, Mealie, Mail-Archiver, n8n, AdGuard, Unbound und der komplette Monitoring-Stack (11 Services).
- **Warum relevant:** Ohne Healthcheck meldet Docker "Up", auch wenn die App hängt; der Critical-Events-Watcher sieht nur `die`/`oom`, keine Hänger. Prometheus-Blackbox prüft nur HTTP-Routen von außen.
- **Änderung:** Pro Stack einen minimalen Healthcheck ergänzen, priorisiert: Nextcloud (`curl -f http://localhost/status.php`), Paperless, Mealie, n8n, Unbound (`drill @127.0.0.1 cloudflare.com` bzw. `unbound-control status`), AdGuard. Stackweise deployen, nicht als Bulk (siehe DNS-Runbook!).
- **Verifikation:** `docker ps --format '{{.Names}} {{.Status}}'` zeigt `(healthy)`; cAdvisor/Glance zeigen Health-Status.
- **Rollback:** Healthcheck-Block entfernen, Redeploy — kein Datenrisiko.
- **Nebenwirkungen:** Falsch kalibrierte Checks (zu kurze `start_period`) können Flapping erzeugen; konservativ starten (`interval: 60s`, `retries: 5`). | Nutzen: **hoch** | Risiko: niedrig | Aufwand: mittel | diesen Monat
### 5. Memory-Limits für die größten Verbraucher
- **Beobachtung:** Kein einziger Service hat `mem_limit`/`deploy.resources`. Auf einem Ein-Host-System konkurrieren ~50 Container; ein Speicherleck (Immich-ML, Nextcloud-PHP, Loki) kann den Host-OOM-Killer auslösen, der dann beliebige Tier-1-Container trifft (Postgres!).
- **Warum relevant:** Der OOM-Killer wählt nach Score, nicht nach Wichtigkeit. Limits machen den Blast-Radius deterministisch: die fehlerhafte App stirbt, nicht die Datenbank.
- **Änderung:** Erst messen: `docker stats --no-stream --format '{{.Name}}\t{{.MemUsage}}'` über ein paar Tage (oder cAdvisor-Dashboard `container_memory_working_set_bytes`). Dann Limits = Peak × 1,5 für die Top-5-Verbraucher (typisch: immich-ml, nextcloud, paperless, plex, prometheus) setzen.
- **Verifikation:** `docker inspect <c> --format '{{.HostConfig.Memory}}'`; Grafana-Panel Memory vs. Limit; keine neuen `oom`-Events im Critical-Events-Log.
- **Rollback:** Limit-Zeilen entfernen, Redeploy.
- **Nebenwirkungen:** Zu knappe Limits OOM-killen die App selbst — deshalb messen statt raten, und Limits nur bei unkritischen Apps zuerst. | Nutzen: **hoch** | Risiko: mittel | Aufwand: mittel | diesen Monat
### 6. `no-new-privileges` flächendeckend gemäß P8
- **Beobachtung:** Architektur-Regel P8 verlangt `no-new-privileges:true` standardmäßig; gesetzt ist es nur in 10 von 30 Stacks. Es fehlt u. a. bei allen Apps mit WAN-Exposition (Nextcloud, Immich, Paperless, Mealie, ntfy, n8n).
- **Warum relevant:** Billige Defense-in-Depth gegen Privilege-Escalation nach App-Kompromittierung — genau bei den exponierten Diensten am wertvollsten. Aktuell: dokumentierte Regel ≠ gelebter Stand (Policy-Drift).
- **Änderung:** `security_opt: ["no-new-privileges:true"]` in die fehlenden Stacks, stackweise mit Smoke-Test. Vorsicht bei Images mit s6/sudo-Setup (LSIO-Images wie speedtest/code-server haben es teils schon — prüfen) und bei Plex (Host-Netz, zuerst testen).
- **Verifikation:** `docker inspect <c> --format '{{.HostConfig.SecurityOpt}}'`; Posture-/Policy-Check erweitern, damit Drift künftig alarmiert.
- **Rollback:** Zeile entfernen, Redeploy.
- **Nebenwirkungen:** Container, die intern setuid brauchen (selten: einige Init-Systeme), starten nicht — fällt im Smoke-Test sofort auf. | Nutzen: mittel | Risiko: niedrig | Aufwand: mittel | diesen Monat
### 7. traefik/dynamic-Sync automatisieren statt manuell
- **Beobachtung:** `traefik/dynamic/*` (middlewares, tls, dashboards, plex) wird laut dokumentierter Ausnahme **manuell** auf den Host synchronisiert. Das ist die klassische Quelle für "Repo sagt A, Host macht B" — besonders heikel, weil hier Auth-Middlewares definiert sind.
- **Warum relevant:** Ein vergessener Sync nach einer Middleware-Änderung kann unbemerkt eine Schutzschicht im Live-Zustand alt lassen; auffallen würde es erst beim Audit.
- **Änderung:** Kleines Sync-Skript analog `services/authelia-diff.sh`: Repo-Spiegel `/mnt/user/services/homelab-infra/traefik/dynamic/` per `rsync --checksum --dry-run` gegen `/mnt/user/appdata/traefik/dynamic/` diffen; Diff ≠ leer → ntfy-Warnung über den bestehenden Posture-Check. (Stufe 2 optional: automatisch syncen; erst nur alarmieren.)
- **Verifikation:** Testweise eine Whitespace-Änderung im Repo-Spiegel → Posture-Check meldet `traefik_dynamic_drift`.
- **Rollback:** Check aus dem Posture-Skript entfernen; rein lesend, kein Produktionsrisiko.
- **Nebenwirkungen:** keine (read-only Check). | Nutzen: mittel | Risiko: niedrig | Aufwand: klein | diese Woche
### 8. Watchdog für den Monitoring-Stack selbst (Dead-Man's-Switch)
- **Beobachtung:** Die Alert-Kette ist Prometheus → Alertmanager → Bridge → ntfy. Fällt ein Glied (oder der ganze Monitoring-Stack) aus, kommen schlicht **keine** Alerts mehr — Stille ist nicht von "alles gut" unterscheidbar. Kein Healthcheck im Monitoring-Compose.
- **Warum relevant:** Das Monitoring überwacht alles außer sich selbst.
- **Änderung:** Dauerhaft feuernde `Watchdog`-Alert-Rule in `monitoring/prometheus/alerts.yml` + externen Heartbeat-Empfänger: einfachste Variante ist healthchecks.io (free) — Alertmanager-Route schickt den Watchdog alle 5 min an die Heartbeat-URL; bleibt er aus, alarmiert healthchecks.io per Mail/Push von außen.
- **Verifikation:** `docker stop monitoring-prometheus` im Wartungsfenster → externe Benachrichtigung nach ~10 min; danach Start.
- **Rollback:** Rule + Route entfernen.
- **Nebenwirkungen:** neue (kleine) externe Abhängigkeit — in `docs/EXTERNAL_DEPENDENCIES.md` eintragen. | Nutzen: **hoch** | Risiko: niedrig | Aufwand: klein | diese Woche
### 9. Lokale Arbeitskopie sauber halten (GitOps-Hygiene)
- **Beobachtung:** Die lokale Arbeitskopie hat aktuell 6 modifizierte Dateien und 2 untracked Artefakte (u. a. `docs/KalliLab_CORE_Audit_2026-06-06.pdf`, `ops/h-drive-nearline/README.md`), die nicht committed sind. Bei "Gitea = Quelle der Wahrheit" ist eine dauerhaft schmutzige Arbeitskopie ein Drift-Risiko (Änderungen gehen bei Pull-Konflikten verloren oder landen versehentlich in fremden Commits).
- **Warum relevant:** Genau die Drift-Klasse, vor der `docs/GITOPS_DRIFT_RUNBOOK.md` warnt — nur auf Ebene 2 (lokaler Clone) statt Ebene 4.
- **Änderung:** Modifizierte Doku-Dateien reviewen und committen oder verwerfen; PDF entweder committen (wenn es Referenz ist) oder in `.gitignore`/außerhalb des Repos ablegen; `ops/h-drive-nearline/README.md` committen.
- **Verifikation:** `git status` zeigt clean tree (bis auf bewusste Arbeit).
- **Rollback:** n/a (Aufräumarbeit). | Nutzen: mittel | Risiko: niedrig | Aufwand: klein (<30 min) | sofort
### 10. Doku-Drift-Fixes (klein, aber Vertrauensbasis)
- **Beobachtung:** `HOMELAB_ARCHITECTURE_MASTER_V2.md` nennt "Redis-Caches auf `redis:7.4-alpine` vereinheitlicht" — real laufen alle auf `redis:8.8.0-alpine`. Ebenso "PostgreSQL 17"-Pfade/Servicenamen bei PG 18 (letzteres ist dokumentiert bewusst, ersteres nicht).
- **Warum relevant:** Das Masterdokument ist laut eigener Regel die erste Lesepflicht für jeden (auch KI-)Eingriff; veraltete Fakten dort erzeugen falsche Entscheidungen.
- **Änderung:** Redis-Abschnitt in Sektion 13 auf 8.8 aktualisieren; bei Gelegenheit einen Mini-Check ins Posture-/Audit-Ritual: "stimmen Versionsangaben im Master noch?"
- **Verifikation:** `grep -n "7.4-alpine" HOMELAB_ARCHITECTURE_MASTER_V2.md` → leer.
- **Rollback:** trivial (Doku). | Nutzen: niedrigmittel | Risiko: keiner | Aufwand: klein | sofort
## Top 5 Risiken (zuerst entschärfen)
1. **Löschbare Off-site-Backups** — Host-Kompromittierung oder ein falscher `borg delete` vernichtet auch Hetzner. → Empfehlung 2 (Snapshots). Bis dahin ist das DR-Konzept gegen Ransomware unvollständig.
2. **DNS-SPOF AdGuard** — bereits einmal real eingetreten (Teil-Deploy 2026-06); betrifft auch die Selbstheilungsfähigkeit (Image-Pulls). → Empfehlung 3.
3. **Verdeckte Versionssprünge via `release`/`latest`-Digest-Bumps** — v. a. Immich (DB-Migrationen!). → Empfehlung 1.
4. **OOM-Kaskade ohne Limits** — ein Leck in einer Tier-3-App kann Postgres killen. → Empfehlung 5. (Der Critical-Events-Watcher meldet das nur, verhindert es nicht.)
5. **Blinde Alert-Kette** — Monitoring-Ausfall = Stille statt Alarm. → Empfehlung 8.
Bewusst akzeptierte Risiken (USV geparkt, ein Host, kein WAN-Failover, kein
zweites Off-site-Ziel) sind dokumentiert und werden hier nicht erneut
aufgemacht — die Entscheidungen sind nachvollziehbar.
## Quick Wins unter 30 Minuten
| Quick Win | Wirkung | Kommando/Weg |
|---|---|---|
| Hetzner-Snapshots aktivieren | Backup-Löschschutz | Robot-Konsole → Storage Box → Snapshots (Empf. 2) |
| Host-DNS-Fallback eintragen | Selbstheilung bei AdGuard-Down | Unraid Settings → Network → DNS 2 = `1.1.1.1` (Empf. 3a) |
| Arbeitskopie aufräumen | GitOps-Hygiene | `git status`, committen/verwerfen (Empf. 9) |
| Redis-Doku-Drift fixen | Master-Doku wieder korrekt | Sektion 13 editieren (Empf. 10) |
| Memory-Baseline ziehen | Grundlage für Limits | `docker stats --no-stream` auf dem Host, Output archivieren |
| Watchdog-Rule anlegen | Vorbereitung Dead-Man's-Switch | `alerts.yml` + healthchecks.io-Account (Empf. 8) |
## 30-Tage-Optimierungsplan
**Woche 1 — Risiko-Entschärfung (alles klein):**
Hetzner-Snapshots (Empf. 2) · Host-DNS-Fallback + Stop/Start-Test (Empf. 3a) ·
Immich-Tag-Pinning (Empf. 1) · Arbeitskopie aufräumen (Empf. 9) ·
Memory-Baseline starten.
**Woche 2 — Beobachtbarkeit:**
Dead-Man's-Switch produktiv (Empf. 8) · traefik/dynamic-Drift-Check in den
Posture-Check (Empf. 7) · Healthchecks für Nextcloud, Paperless, Mealie, n8n
(Empf. 4, stackweise).
**Woche 3 — Hardening:**
`no-new-privileges` für alle WAN-exponierten Apps (Empf. 6) · Healthchecks
für AdGuard/Unbound/Monitoring-Kern · restliche Mutable-Tag-Kandidaten pinnen
(komodo, scrutiny, glances, ddns-updater, tag-lose digest-only Images).
**Woche 4 — Stabilität:**
Memory-Limits aus der Baseline für die Top-5-Verbraucher (Empf. 5) ·
FRITZ!Box-DNS-Fallback-Entscheidung (Empf. 3b) · Doku nachziehen
(Master Sektion 13, SERVICE_CATALOG, dieses Dokument abhaken).
## Größere Projekte mit hohem Nutzen (später)
- **End-to-end-DR-Drill** sobald zweite Hardware existiert (bereits geplant,
bleibt der wertvollste offene Beweis).
- **Strom-/Kostentransparenz:** smarte Steckdose mit Messfunktion (z. B.
Shelly Plug S) vor den Unraid-Host, Werte via Home Assistant → InfluxDB 3 →
Grafana. Erst messen, dann ggf. optimieren (Spindown-Policy, CPU-Governor).
Messbarkeit: W-Dauerlast und kWh/Monat als Grafana-Panel.
- **USV-Review Q3/2026** wie geparkt — nach Strommessung lässt sich die
USV-Dimensionierung direkt ableiten.
- **Renovate-Policy verfeinern:** Digest-PRs für mutable Tags entweder
abschalten oder mit Warn-Label versehen, damit Befund 1 strukturell nicht
zurückkommt.
## Konkrete Verifikationskommandos (Sammlung, alle read-only)
```bash
# Health-Status aller Container
docker ps --format '{{.Names}}\t{{.Status}}' | sort
# Memory-Baseline
docker stats --no-stream --format '{{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}' | sort -k3 -hr | head -15
# Welche Container ohne no-new-privileges laufen
docker ps -q | xargs docker inspect --format '{{.Name}} {{.HostConfig.SecurityOpt}}' | grep -v no-new-privileges
# Effektive Image-Referenzen (mutable Tags erkennen)
docker ps --format '{{.Names}}\t{{.Image}}' | grep -E 'latest|release|:2$|:[0-9]+$'
# DNS-Fallback-Test (Wartungsfenster!)
docker stop adguard && nslookup gitea.com && docker start adguard
# Borg-Snapshot-Gegenprobe (nach Aktivierung, von der Storage Box)
ssh -p 23 u565255@u565255.your-storagebox.de ls .snapshots/ 2>/dev/null || echo "via Robot-Konsole prüfen"
```
## Rollback-Hinweise (generell)
- Jede Compose-Änderung: Revert-Commit nach Gitea pushen → Komodo deployed
den Vorzustand; Datenpfade bleiben unberührt (alle Empfehlungen hier sind
config-only, keine Daten-/Volume-Migrationen).
- Healthchecks/Limits/security_opt: Zeilen entfernen + Redeploy genügt.
- Host-DNS/FRITZ!Box-Einträge: Eintrag löschen, sofort wirksam.
- Hetzner-Snapshots und Dead-Man's-Switch sind rein additiv.
- Nichts in diesem Dokument erfordert `push --force`, History-Rewrite oder
Löschoperationen auf Datenpfaden.
## Offene Fragen an den Operator
1. **Strom:** Gibt es eine Messung des Host-Verbrauchs (W idle/last)? Ohne
Zahl ist der Bereich Kosten/Strom nicht bewertbar. → Shelly/Messsteckdose?
2. **RAM-Ausstattung des Hosts:** Wie viel RAM hat Kallilabcore gesamt und
wie ist die aktuelle Auslastung (`free -h`)? Bestimmt, wie aggressiv
Memory-Limits sinnvoll sind.
3. **Renovate-Verhalten gewollt?** Sollen Digest-Bumps auf `release`/`latest`
weiter automatisch als PRs kommen, oder ist die Pinning-Strategie aus
Empfehlung 1 die gewünschte Linie für alle Stacks?
4. **healthchecks.io o. ä. als externe Abhängigkeit akzeptabel?** Alternativ
ginge ein ntfy-basierter Heartbeat von einem zweiten Gerät (z. B. dem
Gaming-PC per Scheduled Task) — null neue Cloud-Abhängigkeit.
5. **FRITZ!Box-DNS-Fallback (3b):** Filterlücke bei AdGuard-Down akzeptieren
oder lieber nur den Host-Fallback (3a) umsetzen?
-58
View File
@@ -1,58 +0,0 @@
# Runbook: Komodo Bulk-Deploy schlaegt mit DNS `connection refused` fehl
Stand: 2026-06-10 · Typ: Runbook / ADR-light · Status: Sofortmassnahme empfohlen, noch nicht umgesetzt
## Symptom
Ein Bulk-Merge (z. B. Renovate-Sammel-PR) loest gleichzeitig viele Komodo-Stack-Webhooks aus. Komodo startet parallele `DeployStack`. Nur ein Teil der Stacks deployt, der Rest bleibt auf dem alten Image. In der Deploy-Stufe **Compose Pull** stehen Fehler wie:
```
Get "https://registry-1.docker.io/v2/": dial tcp: lookup registry-1.docker.io
on 192.168.178.58:53: read udp ...->192.168.178.58:53: read: connection refused
```
Manuelles Re-Deploy der betroffenen Stacks danach funktioniert (AdGuard ist dann wieder oben).
## Ursache
Der Host nutzt **AdGuard Home als einzigen Resolver** (`/etc/resolv.conf` = nur `nameserver 192.168.178.58`, keine `/etc/docker/daemon.json`). AdGuard laeuft selbst als Container auf dem Host und bindet `0.0.0.0:53`. Wird der `adguard`-Stack im selben Batch neu deployt, faellt Port 53 fuer Sekunden aus. Alle parallelen `docker compose pull` der anderen Stacks koennen `registry-1.docker.io` dann nicht aufloesen -> `connection refused` -> Deploy `success=false`.
Es ist **kein** Webhook-, Auth- oder Docker-Hub-Rate-Limit-Problem: Webhooks authentifizieren sauber, `webhook_enabled=true`, Fehlerbild ist `connection refused` auf den eigenen DNS-Port direkt nach AdGuard-Recreate. Fuer den Pull-Pfad zaehlt der Docker-Daemon/Go-Resolver (iteriert ueber die `resolv.conf`-Server und springt bei Socket-Fehlern zum naechsten), nicht der glibc-Client.
## Sofortmassnahme (Schicht 1)
Unraid -> Settings -> Network Settings -> `eth0`:
- DNS server 1: `192.168.178.58` (AdGuard, bleibt)
- **DNS server 2: `192.168.178.1`** (FritzBox) -> Apply
Damit ueberleben Registry-Pulls einen kurzen AdGuard-Ausfall via Resolver-Failover. Im Normalbetrieb wird weiter DNS1 (AdGuard) genutzt, der Filter bleibt aktiv.
Pruefen / Bedingungen:
- **Kein `options rotate`** in `/etc/resolv.conf` (sonst dauerhafter Filter-Bypass). Aktuell nicht gesetzt; nach Apply erneut pruefen.
- Router muss oeffentliche Namen **selbst** aufloesen und nicht intern an AdGuard zurueckleiten.
- Hinweis zur Verifikation: Ein `nslookup registry-1.docker.io 192.168.178.1` bei laufendem AdGuard ist ein gutes Signal, aber **kein letzter Beweis**. Wasserdicht: AdGuard kurz stoppen und `dig @192.168.178.1 registry-1.docker.io`, oder FritzBox-Upstream / AdGuard-Querylog pruefen.
Rollback: DNS server 2 leeren + Apply.
## Betriebsregel (Schicht 2)
- **AdGuard und Unbound nicht gemeinsam mit abhaengigen Stacks im Bulk deployen.** DNS-Infrastruktur immer separat / einzeln deployen, nicht waehrend 20+ parallele Pulls laufen.
- Renovate-PRs gestaffelt mergen (eine Etappe pro Deploy) statt Sammel-Merge. Deckt dieses Problem fuer den Normalbetrieb bereits ab.
## Spaeter optional
- Komodo-Deploys serialisieren: statt vieler paralleler Stack-Webhooks eine **Procedure** (sequenzielle Stages) oder **Resource Sync** mit `after`-Ordering. Trifft die Ursache direkter, ist aber ein groesserer Umbau und **kein Renovate-Blocker**.
- Host-DNS vom AdGuard-Container entkoppeln (AdGuard eigene IP via macvlan, Host-Resolver auf Router/Unbound), damit `:53` am Host nicht exklusiv am Container-Lifecycle haengt.
## Verworfen
- `/etc/docker/daemon.json` mit `"dns": [...]`: wirkt nur fuer Container-DNS, nicht fuer Daemon-eigene Image-Pulls.
- AdGuard `network_mode: host`: beim Recreate ist der DNS-Prozess trotzdem weg; macht aus dem Single Point of Failure keinen HA-Resolver.
## Referenzen
- Diagnose-Zugriff: SSH `root@192.168.178.58`; Komodo-Mongo (`docker exec komodo-mongo`, DB `komodo`, Collections `Stack`/`Update`); Gitea SQLite `/data/gitea/gitea.db` (Tabelle `webhook`, `repo_id=3`).
- Verwandt: `docs/WORKFLOW.md` (DNS-Regeln fuer Container), `docs/GITOPS_DRIFT_RUNBOOK.md`.
</content>
@@ -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
+4 -27
View File
@@ -25,7 +25,7 @@ services:
- cadvisor - cadvisor
alertmanager: alertmanager:
image: prom/alertmanager:v0.32.2@sha256:b85533a2eb45865835315810315f6951331b2dbc8c93a6cf9a51e156a006a706 image: prom/alertmanager:v0.32.1@sha256:51a825c2a40acc3e338fdd00d622e01ec090f72be2b3ea46be0839cd47a4d286
container_name: monitoring-alertmanager container_name: monitoring-alertmanager
restart: unless-stopped restart: unless-stopped
command: command:
@@ -66,18 +66,15 @@ services:
image: prom/blackbox-exporter:v0.28.0@sha256:e753ff9f3fc458d02cca5eddab5a77e1c175eee484a8925ac7d524f04366c2fc image: prom/blackbox-exporter:v0.28.0@sha256:e753ff9f3fc458d02cca5eddab5a77e1c175eee484a8925ac7d524f04366c2fc
container_name: monitoring-blackbox-exporter container_name: monitoring-blackbox-exporter
restart: unless-stopped restart: unless-stopped
# Use AdGuard so *.kaleschke.info resolves to the internal Traefik IP.
# External resolvers (1.1.1.1/8.8.8.8) return the public WAN IP, which
# causes hairpin-NAT timeouts when probing from inside the Docker network.
dns: dns:
- 172.23.0.3 - 1.1.1.1
- 8.8.8.8
command: command:
- --config.file=/etc/blackbox_exporter/blackbox.yml - --config.file=/etc/blackbox_exporter/blackbox.yml
volumes: volumes:
- ./blackbox/blackbox.yml:/etc/blackbox_exporter/blackbox.yml:ro - ./blackbox/blackbox.yml:/etc/blackbox_exporter/blackbox.yml:ro
networks: networks:
- monitoring_net - monitoring_net
- dns_net
expose: expose:
- "9115" - "9115"
security_opt: security_opt:
@@ -132,20 +129,6 @@ services:
GF_USERS_ALLOW_SIGN_UP: "false" GF_USERS_ALLOW_SIGN_UP: "false"
GF_AUTH_ANONYMOUS_ENABLED: "false" GF_AUTH_ANONYMOUS_ENABLED: "false"
GF_PLUGINS_PREINSTALL_DISABLED: "true" GF_PLUGINS_PREINSTALL_DISABLED: "true"
# --- Authelia OIDC SSO (2026-06-06) ---
GF_AUTH_GENERIC_OAUTH_ENABLED: "true"
GF_AUTH_GENERIC_OAUTH_NAME: Authelia
GF_AUTH_GENERIC_OAUTH_CLIENT_ID: grafana
GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET__FILE: /run/secrets/grafana_oidc_client_secret
GF_AUTH_GENERIC_OAUTH_SCOPES: "openid profile email groups"
GF_AUTH_GENERIC_OAUTH_AUTH_URL: https://auth.kaleschke.info/api/oidc/authorization
GF_AUTH_GENERIC_OAUTH_TOKEN_URL: https://auth.kaleschke.info/api/oidc/token
GF_AUTH_GENERIC_OAUTH_API_URL: https://auth.kaleschke.info/api/oidc/userinfo
GF_AUTH_GENERIC_OAUTH_USE_PKCE: "true"
GF_AUTH_GENERIC_OAUTH_ALLOW_SIGN_UP: "true"
# Proof: alle OIDC-Logins als Admin; spaeter ueber groups verfeinern
GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH: "'Admin'"
GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_STRICT: "false"
entrypoint: entrypoint:
- /bin/sh - /bin/sh
- -c - -c
@@ -162,7 +145,6 @@ services:
secrets: secrets:
- monitoring_grafana_admin_password - monitoring_grafana_admin_password
- monitoring_grafana_influxdb_token - monitoring_grafana_influxdb_token
- grafana_oidc_client_secret
expose: expose:
- "3000" - "3000"
security_opt: security_opt:
@@ -178,8 +160,7 @@ services:
- traefik.http.routers.monitoring-grafana.entrypoints=websecure - traefik.http.routers.monitoring-grafana.entrypoints=websecure
- traefik.http.routers.monitoring-grafana.tls=true - traefik.http.routers.monitoring-grafana.tls=true
- traefik.http.routers.monitoring-grafana.tls.certresolver=le - traefik.http.routers.monitoring-grafana.tls.certresolver=le
# ForwardAuth bewusst entfernt 2026-06-06: Grafana macht jetzt eigenes OIDC-SSO gegen Authelia - traefik.http.routers.monitoring-grafana.middlewares=authelia@file,secure-headers@file
- traefik.http.routers.monitoring-grafana.middlewares=secure-headers@file
- traefik.http.services.monitoring-grafana.loadbalancer.server.port=3000 - traefik.http.services.monitoring-grafana.loadbalancer.server.port=3000
grafana-dashboard-importer: grafana-dashboard-importer:
@@ -370,8 +351,6 @@ networks:
driver: bridge driver: bridge
frontend_net: frontend_net:
external: true external: true
dns_net:
external: true
volumes: volumes:
prometheus_data: prometheus_data:
@@ -385,7 +364,5 @@ secrets:
file: /mnt/user/appdata/secrets/monitoring_grafana_admin_password.txt file: /mnt/user/appdata/secrets/monitoring_grafana_admin_password.txt
monitoring_grafana_influxdb_token: monitoring_grafana_influxdb_token:
file: /mnt/user/appdata/secrets/monitoring_grafana_influxdb_token.txt file: /mnt/user/appdata/secrets/monitoring_grafana_influxdb_token.txt
grafana_oidc_client_secret:
file: /mnt/user/appdata/secrets/grafana_oidc_client_secret
influxdb3_admin_token: influxdb3_admin_token:
file: /mnt/user/appdata/secrets/influxdb3_admin_token.json file: /mnt/user/appdata/secrets/influxdb3_admin_token.json
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
borg-ui: borg-ui:
image: ainullcode/borg-ui@sha256:0922157e8f77a1b2bd23cd09366a458ea6de07fd9306aa1485f9cfe623eca17f image: ainullcode/borg-ui@sha256:acb0fbe83dc4a3843abc06f814c5f1061a0701b2cfc574da2e851d17a34ab745
container_name: borg-ui container_name: borg-ui
restart: unless-stopped restart: unless-stopped
security_opt: security_opt:
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
code-server: code-server:
image: lscr.io/linuxserver/code-server:4.123.0@sha256:cb261a7f87674b445e0fd66d87d55900c1b823d276c727ab0d168a75e69e9992 image: lscr.io/linuxserver/code-server:4.123.0@sha256:9dd4555720db04eb92d92cc84e7a34f0862bada5679889446a3004c45b5fa59b
container_name: code-server container_name: code-server
restart: unless-stopped restart: unless-stopped
security_opt: security_opt:
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
filebrowser: filebrowser:
image: filebrowser/filebrowser:v2.63.14@sha256:1ec9b0c68297550c92f4a93feed432850c2993b261706cc3cc2e808f94a95e76 image: filebrowser/filebrowser:v2.63.12@sha256:fc8c3a46c16bbdf97362b20c50164e97de9c1dd5f63230d28a4cb15248b53ec3
container_name: filebrowser container_name: filebrowser
restart: unless-stopped restart: unless-stopped
security_opt: security_opt:
+6
View File
@@ -497,6 +497,12 @@ pages:
description: Upstream Resolver description: Upstream Resolver
category: network category: network
hide: false 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: ddns-updater:
name: DDNS Updater name: DDNS Updater
icon: mdi:cloud-sync icon: mdi:cloud-sync
+1 -1
View File
@@ -1,4 +1,4 @@
FROM nousresearch/hermes-agent:v2026.6.5 FROM nousresearch/hermes-agent:v2026.5.29
USER root USER root
-5
View File
@@ -2,11 +2,6 @@ services:
# ────────────────────────────────────────────────────────────────── # ──────────────────────────────────────────────────────────────────
# MongoDB Datenbank fuer Komodo Core # MongoDB Datenbank fuer Komodo Core
# Netz: komodo_net (internal: true) niemals frontend_net # Netz: komodo_net (internal: true) niemals frontend_net
# ACHTUNG: Dieser Stack wird NICHT aus diesem Repo deployed. Der komodo-Stack
# ist in Komodo inline (file_contents) verwaltet (Bootstrap-/Self-Stack).
# Diese Datei ist nur Doku/Spiegel; Aenderungen hier wirken NICHT zur Laufzeit.
# ops/komodo/** ist in renovate.json ignorePaths. Siehe docs/RENOVATE.md.
# Digest = aktuell real laufender Stand (kein Renovate-Auto-Update).
# ────────────────────────────────────────────────────────────────── # ──────────────────────────────────────────────────────────────────
komodo-mongo: komodo-mongo:
image: mongo:8.0.23@sha256:44aa79ae28ff80b56fe58681b66cda9336706df408a5175a6c04988aa54610d3 image: mongo:8.0.23@sha256:44aa79ae28ff80b56fe58681b66cda9336706df408a5175a6c04988aa54610d3
@@ -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"
@@ -1,127 +0,0 @@
param(
[string]$HostLanIp = "192.168.178.58",
[string]$FritzBoxIp = "192.168.178.1",
[ValidateSet("LanPreflight", "Guest")]
[string]$Mode = "LanPreflight",
[string]$ReportPath = ""
)
$ErrorActionPreference = "Stop"
function Test-TcpPort {
param(
[string]$RemoteHost,
[int]$Port,
[int]$TimeoutMs = 1500
)
$client = [System.Net.Sockets.TcpClient]::new()
try {
$async = $client.BeginConnect($RemoteHost, $Port, $null, $null)
$ok = $async.AsyncWaitHandle.WaitOne($TimeoutMs, $false)
if (-not $ok) {
return $false
}
$client.EndConnect($async)
return $true
} catch {
return $false
} finally {
$client.Close()
}
}
function Add-Result {
param(
[System.Collections.Generic.List[object]]$Results,
[string]$Name,
[string]$Target,
[bool]$Reachable,
[string]$ExpectedGuest,
[string]$Risk
)
$Results.Add([pscustomobject]@{
Name = $Name
Target = $Target
Reachable = $Reachable
ExpectedFromGuest = $ExpectedGuest
RiskIfReachableFromGuest = $Risk
})
}
$adapters = Get-NetIPConfiguration |
Where-Object { $_.IPv4Address -and $_.NetAdapter.Status -eq "Up" } |
Select-Object InterfaceAlias,
@{Name="IPv4";Expression={$_.IPv4Address.IPAddress -join ", "}},
@{Name="Gateway";Expression={$_.IPv4DefaultGateway.NextHop -join ", "}},
@{Name="DnsServer";Expression={$_.DNSServer.ServerAddresses -join ", "}}
$results = [System.Collections.Generic.List[object]]::new()
Add-Result $results "Unraid HTTP/LAN" "${HostLanIp}:80" (Test-TcpPort $HostLanIp 80) "blocked" "Guest can reach LAN web entrypoint directly"
Add-Result $results "Unraid HTTPS/LAN" "${HostLanIp}:443" (Test-TcpPort $HostLanIp 443) "blocked" "Guest can reach LAN HTTPS entrypoint directly"
Add-Result $results "Gitea SSH/LAN" "${HostLanIp}:222" (Test-TcpPort $HostLanIp 222) "blocked" "Guest can reach Git SSH"
Add-Result $results "AdGuard Admin/LAN" "${HostLanIp}:8082" (Test-TcpPort $HostLanIp 8082) "blocked" "Guest can reach AdGuard admin UI"
Add-Result $results "InfluxDB LAN" "${HostLanIp}:8181" (Test-TcpPort $HostLanIp 8181) "blocked" "Guest can reach InfluxDB writer endpoint"
Add-Result $results "FRITZ!Box LAN UI" "${FritzBoxIp}:80" (Test-TcpPort $FritzBoxIp 80) "blocked-or-guest-gateway-only" "Guest can reach main router UI"
$risk = if ($Mode -eq "Guest") {
$results | Where-Object {
$_.ExpectedFromGuest -like "blocked*" -and $_.Reachable
}
} else {
$results | Where-Object {
$_.Name -in @("AdGuard Admin/LAN", "InfluxDB LAN") -and $_.Reachable
}
}
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$lines = [System.Collections.Generic.List[string]]::new()
$lines.Add("# Guest/IoT Isolation Check")
$lines.Add("")
$lines.Add("Timestamp: $timestamp")
$lines.Add("Mode: $Mode")
$lines.Add("Host LAN IP: $HostLanIp")
$lines.Add("FRITZ!Box IP: $FritzBoxIp")
$lines.Add("Risk count: $($risk.Count)")
$lines.Add("")
$lines.Add("## Active Network Adapters")
$lines.Add("")
$lines.Add("| Interface | IPv4 | Gateway | DNS |")
$lines.Add("|---|---|---|---|")
foreach ($adapter in $adapters) {
$lines.Add("| $($adapter.InterfaceAlias) | $($adapter.IPv4) | $($adapter.Gateway) | $($adapter.DnsServer) |")
}
$lines.Add("")
$lines.Add("## Port Tests")
$lines.Add("")
$lines.Add("| Name | Target | Reachable | Expected from guest Wi-Fi | Risk if reachable from guest |")
$lines.Add("|---|---|---:|---|---|")
foreach ($result in $results) {
$lines.Add("| $($result.Name) | $($result.Target) | $($result.Reachable) | $($result.ExpectedFromGuest) | $($result.RiskIfReachableFromGuest) |")
}
$lines.Add("")
$lines.Add("## Interpretation")
$lines.Add("")
$lines.Add("- `LanPreflight`: reachable `80/443/222` can be normal; `8082` and `8181` should still be blocked.")
$lines.Add("- `Guest`: all listed LAN targets should be blocked. Public domains may still work via the internet path.")
$lines.Add("- A non-zero risk count means the selected mode failed.")
$text = $lines -join [Environment]::NewLine
if ($ReportPath) {
$parent = Split-Path -Parent $ReportPath
if ($parent) {
New-Item -ItemType Directory -Force -Path $parent | Out-Null
}
Set-Content -Path $ReportPath -Value $text -Encoding UTF8
}
Write-Output $text
if ($risk.Count -gt 0) {
exit 2
}
exit 0
@@ -1,90 +0,0 @@
#!/bin/bash
set -euo pipefail
HOST_LAN_IP="${HOST_LAN_IP:-192.168.178.58}"
TAILSCALE_IP="${TAILSCALE_IP:-100.80.98.33}"
FRITZBOX_TR064_URL="${FRITZBOX_TR064_URL:-http://192.168.178.1:49000/tr64desc.xml}"
REPORT_ROOT="${REPORT_ROOT:-/mnt/user/backups/restore-reports}"
STAMP="$(date +%F-%H%M%S)"
REPORT_FILE="$REPORT_ROOT/guest-iot-preflight-$STAMP.md"
mkdir -p "$REPORT_ROOT"
tcp_check() {
local host="$1"
local port="$2"
timeout 2 bash -c "cat < /dev/null > /dev/tcp/$host/$port" >/dev/null 2>&1
}
result_row() {
local name="$1"
local target="$2"
local expectation="$3"
local status="$4"
printf '| %s | `%s` | %s | %s |\n' "$name" "$target" "$status" "$expectation"
}
{
echo "# Guest/IoT Preflight"
echo
echo "Timestamp: $(date '+%F %T')"
echo "Scope: host-side read-only checks before enabling FRITZ!Box guest/IoT network"
echo
echo "## FRITZ!Box TR-064"
echo
if curl -fsS --max-time 5 "$FRITZBOX_TR064_URL" >/tmp/guest-iot-fritzbox-tr064.xml 2>/dev/null; then
model="$(grep -o '<friendlyName>[^<]*' /tmp/guest-iot-fritzbox-tr064.xml | head -n1 | sed 's/<friendlyName>//')"
echo "- TR-064 descriptor reachable: yes"
echo "- Model: ${model:-unknown}"
else
echo "- TR-064 descriptor reachable: no"
fi
rm -f /tmp/guest-iot-fritzbox-tr064.xml
echo
echo "## Host listeners"
echo
echo '```text'
ss -ltnp | sort -k4 | grep -E ':(53|80|443|222|8082|8181)[[:space:]]' || true
echo '```'
echo
echo "## Port reachability from host namespace"
echo
echo "| Check | Target | Status | Expectation |"
echo "|---|---|---|---|"
for port in 80 443 222 53; do
if tcp_check "$HOST_LAN_IP" "$port"; then
result_row "LAN service" "$HOST_LAN_IP:$port" "may be reachable from normal LAN; must be blocked from guest Wi-Fi" "reachable"
else
result_row "LAN service" "$HOST_LAN_IP:$port" "not reachable from host namespace or UDP-only" "blocked"
fi
done
if tcp_check "$HOST_LAN_IP" 8082; then
result_row "AdGuard Admin via LAN IP" "$HOST_LAN_IP:8082" "should be blocked" "reachable"
else
result_row "AdGuard Admin via LAN IP" "$HOST_LAN_IP:8082" "should be blocked" "blocked"
fi
if tcp_check "$TAILSCALE_IP" 8082; then
result_row "AdGuard Admin via Tailscale IP" "$TAILSCALE_IP:8082" "operator path should work" "reachable"
else
result_row "AdGuard Admin via Tailscale IP" "$TAILSCALE_IP:8082" "operator path should work" "blocked"
fi
if tcp_check "$HOST_LAN_IP" 8181; then
result_row "InfluxDB via LAN IP" "$HOST_LAN_IP:8181" "should be blocked unless HA LAN writer is reintroduced" "reachable"
else
result_row "InfluxDB via LAN IP" "$HOST_LAN_IP:8181" "should be blocked unless HA LAN writer is reintroduced" "blocked"
fi
echo
echo "## Next operator step"
echo
echo "Enable FRITZ!Box guest Wi-Fi only after confirming LAN isolation is active. Then connect a phone/laptop to guest Wi-Fi and run:"
echo
echo '```powershell'
echo 'G:\Gitea_Clone\homelab-infra\ops\maintenance\check-guest-iot-isolation.ps1 -Mode Guest'
echo '```'
} | tee "$REPORT_FILE"
echo "Guest/IoT preflight report: $REPORT_FILE"
@@ -1,125 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# check-unraid-flash-backup.sh
#
# Read-only Validierung des Unraid-Flash-Backup-Artefakts
# (`unraid-flash-config.tar.gz`) ohne produktive Extraktion.
#
# Prueft:
# 1. Artefakt, Checksumme und Manifest sind vorhanden
# 2. Artefakt ist frisch genug (Standard: <= 36 h)
# 3. `sha256sum -c` ist OK
# 4. Archiv enthaelt die array-/identitaetsdefinierenden Kern-Configs
#
# Es wird NICHTS extrahiert. `tar -tzf` listet nur Eintragsnamen.
# Das Artefakt enthaelt Host-Konfiguration inkl. SSH-Host-Keys, passwd/shadow
# und Tailscale-State und ist wie Secret-Material zu behandeln. Dieses Skript
# gibt bewusst nur Datei-/Eintragsnamen aus, niemals Inhalte.
#
# Exit-Codes:
# 0 alles OK
# 1 Validierung fehlgeschlagen (fehlende Datei, Checksumme falsch,
# fehlende Kern-Config)
# 2 Artefakt aelter als erlaubt (Frische-Warnung)
DUMPS_DIR="${DUMPS_DIR:-/mnt/user/backups/borg/dumps/latest}"
ARTIFACT="${ARTIFACT:-unraid-flash-config.tar.gz}"
MAX_AGE_HOURS="${MAX_AGE_HOURS:-36}"
# Kern-Configs, die ein brauchbares Flash-Restore mindestens enthalten muss.
CRITICAL_FILES=(
"config/super.dat" # Array-/Disk-Zuordnung
"config/disk.cfg" # Array-Einstellungen
"config/ident.cfg" # Hostname/Identitaet
"config/share.cfg" # Share-Grundeinstellungen
"config/network.cfg" # Netzwerk
"config/docker.cfg" # Docker-Settings
"config/go" # Boot-Script
"config/domain.cfg" # VM/Domain-Settings
)
fail=0
artifact_path="$DUMPS_DIR/$ARTIFACT"
sha_path="$artifact_path.sha256"
manifest_path="$DUMPS_DIR/unraid-flash-config.manifest.txt"
echo "## Unraid Flash Backup Validierung"
echo "Verzeichnis: $DUMPS_DIR"
echo
# 1. Existenz
for f in "$artifact_path" "$sha_path" "$manifest_path"; do
if [ -f "$f" ]; then
echo "OK vorhanden: $(basename "$f")"
else
echo "FEHLER fehlt: $(basename "$f")"
fail=1
fi
done
echo
# Wenn das Artefakt fehlt, hat alles Weitere keinen Sinn.
if [ ! -f "$artifact_path" ]; then
echo "Abbruch: Artefakt nicht vorhanden."
exit 1
fi
# 2. Frische
now_epoch="$(date +%s)"
file_epoch="$(stat -c %Y "$artifact_path")"
age_hours=$(( (now_epoch - file_epoch) / 3600 ))
echo "Alter des Artefakts: ${age_hours} h (Grenze: ${MAX_AGE_HOURS} h)"
stale=0
if [ "$age_hours" -gt "$MAX_AGE_HOURS" ]; then
echo "WARNUNG Artefakt ist aelter als ${MAX_AGE_HOURS} h."
stale=1
else
echo "OK Artefakt ist frisch."
fi
echo
# 3. Checksumme
if [ -f "$sha_path" ]; then
if ( cd "$DUMPS_DIR" && sha256sum -c "$(basename "$sha_path")" ) ; then
echo "OK sha256 stimmt."
else
echo "FEHLER sha256-Pruefung fehlgeschlagen."
fail=1
fi
echo
fi
# 4. Kern-Configs (nur Namen, keine Extraktion)
echo "## Kern-Configs im Archiv"
listing="$(tar -tzf "$artifact_path")"
entry_count="$(printf '%s\n' "$listing" | wc -l | tr -d ' ')"
echo "Eintraege im Archiv: $entry_count"
for cf in "${CRITICAL_FILES[@]}"; do
if printf '%s\n' "$listing" | grep -qxF "$cf"; then
echo "OK $cf"
else
echo "FEHLER $cf fehlt im Archiv"
fail=1
fi
done
echo
# Manifest-Kopf zur Orientierung (enthaelt keine Secret-Werte)
if [ -f "$manifest_path" ]; then
echo "## Manifest"
cat "$manifest_path"
echo
fi
if [ "$fail" -ne 0 ]; then
echo "ERGEBNIS: FEHLGESCHLAGEN"
exit 1
fi
if [ "$stale" -ne 0 ]; then
echo "ERGEBNIS: OK, aber Frische-Warnung"
exit 2
fi
echo "ERGEBNIS: OK"
exit 0
-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'))"
-8
View File
@@ -42,17 +42,12 @@ Ziel:
- `authelia-compose.test.yml`: isolierte Testinstanz fuer Authelia inkl. Test-Postgres, Filesystem-Notifier (kein echter SMTP-Versand) - `authelia-compose.test.yml`: isolierte Testinstanz fuer Authelia inkl. Test-Postgres, Filesystem-Notifier (kein echter SMTP-Versand)
- `authelia-plan.md`: konkreter Authelia-Testplan - `authelia-plan.md`: konkreter Authelia-Testplan
- `authelia-runbook.md`: Operator-Runbook fuer den ersten Authelia-Lauf - `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-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 - `nextcloud-compose.test.yml`: isolierte Testinstanz fuer Nextcloud inkl. Test-Postgres und Test-Redis
- `check-restore-freshness.ps1`: woechentlicher Frische-Check fuer Dumps und Reports - `check-restore-freshness.ps1`: woechentlicher Frische-Check fuer Dumps und Reports
- `run-restore-checks.ps1`: einfacher Dispatcher fuer Restore-Jobs - `run-restore-checks.ps1`: einfacher Dispatcher fuer Restore-Jobs
- `check-restore-freshness.sh`: hosttauglicher Frische-Check - `check-restore-freshness.sh`: hosttauglicher Frische-Check
- `negative-freshness-alert-test.sh`: sicherer Negativtest fuer den Frische-Alarmweg; nutzt synthetische leere Testpfade unter `/mnt/user/backups/restore-lab/freshness-negative`, veraendert keine produktiven Dumps und sendet bei erkanntem Fehler einen Test-Alert nach `homelab-alerts`
- `run-restore-checks.sh`: hosttauglicher Dispatcher - `run-restore-checks.sh`: hosttauglicher Dispatcher
- `common.sh`: gemeinsame Host-Helferfunktionen - `common.sh`: gemeinsame Host-Helferfunktionen
- `automation-plan.md`: Host-Job- und Automatisierungsmodell - `automation-plan.md`: Host-Job- und Automatisierungsmodell
@@ -96,12 +91,9 @@ Aktuell ist das erste validierte Muster vorhanden.
- echter Paperless-Restore am 2026-05-07 erfolgreich verifiziert - 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 - 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 - 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 - Bash-Dispatcher und Bash-Restore-Jobs am 2026-05-07 erfolgreich hostseitig verifiziert
- Restore-Lab und Report-Pfade auf dem Host angelegt - Restore-Lab und Report-Pfade auf dem Host angelegt
- `ntfy`-Wrapper ist fuer Host-Jobs verfuegbar - `ntfy`-Wrapper ist fuer Host-Jobs verfuegbar
- Frische-Negativtest ist als sicherer Host-Job verfuegbar und am 2026-06-06 auf Unraid validiert: `ops/restore-tests/run-restore-checks.sh freshness-negative`. Ergebnis: synthetischer leerer Dump-Pfad erzeugte 10 Criticals, Test-Alert ging nach `homelab-alerts`, produktive Dump-Pfade blieben unangetastet. Report: `/mnt/user/backups/restore-reports/freshness-negative-2026-06-06-130320.md`.
- Nextcloud-Restore-Test: Scaffold existiert, aber **blockiert**. Nextcloud 33 fuehrt zur Laufzeit `chmod()` auf Dateien unter `/var/www/html` aus (`OC_Util.php:486`). Auf Unraids FUSE/shfs User-Shares ist `chmod` strukturell nicht moeglich, was zu permanenter 503 fuehrt. Loesungsoptionen: (a) Restore-Lab auf ein Cache-Drive statt User Share legen, (b) Docker-Volumes statt Bind-Mounts verwenden, (c) tmpfs-Mount fuer html/ + `rsync` der Borg-Daten hinein. Bis dahin ist Nextcloud als Backlog-Item dokumentiert. - Nextcloud-Restore-Test: Scaffold existiert, aber **blockiert**. Nextcloud 33 fuehrt zur Laufzeit `chmod()` auf Dateien unter `/var/www/html` aus (`OC_Util.php:486`). Auf Unraids FUSE/shfs User-Shares ist `chmod` strukturell nicht moeglich, was zu permanenter 503 fuehrt. Loesungsoptionen: (a) Restore-Lab auf ein Cache-Drive statt User Share legen, (b) Docker-Volumes statt Bind-Mounts verwenden, (c) tmpfs-Mount fuer html/ + `rsync` der Borg-Daten hinein. Bis dahin ist Nextcloud als Backlog-Item dokumentiert.
- Komodo-Mongo-Daten-Restore am 2026-06-03 erfolgreich: 86904 Dokumente (inkl. 32 Stacks), Report `/mnt/user/backups/restore-reports/komodo-mongo-restore-2026-06-03.md` - Komodo-Mongo-Daten-Restore am 2026-06-03 erfolgreich: 86904 Dokumente (inkl. 32 Stacks), Report `/mnt/user/backups/restore-reports/komodo-mongo-restore-2026-06-03.md`
- naechste grosse Kandidaten sind Mailarchiver und Mealie; Nextcloud bleibt blockiert (shfs-chmod) - naechste grosse Kandidaten sind Mailarchiver und Mealie; Nextcloud bleibt blockiert (shfs-chmod)
@@ -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"
@@ -1,74 +0,0 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
LAB_ROOT="${LAB_ROOT:-/mnt/user/backups/restore-lab/freshness-negative}"
REPORT_ROOT="${REPORT_ROOT:-/mnt/user/backups/restore-reports}"
ALERT_TOPIC="${ALERT_TOPIC:-homelab-alerts}"
INFO_TOPIC="${INFO_TOPIC:-homelab-info}"
SEND_NTFY="${SEND_NTFY:-1}"
stamp="$(date +%F-%H%M%S)"
test_root="$LAB_ROOT/$stamp"
dump_root="$test_root/dumps"
test_report_root="$test_root/reports"
report_file="$REPORT_ROOT/freshness-negative-$stamp.md"
raw_log="$test_root/check-output.md"
mkdir -p "$dump_root" "$test_report_root" "$REPORT_ROOT"
cleanup() {
rm -rf "$test_root"
}
trap cleanup EXIT
set +e
DUMP_ROOT="$dump_root" \
REPORT_ROOT="$test_report_root" \
MAX_DUMP_AGE_HOURS=26 \
MAX_REPORT_AGE_DAYS=45 \
"$SCRIPT_DIR/check-restore-freshness.sh" >"$raw_log" 2>&1
rc=$?
set -e
critical_count="$(awk -F': ' '/^Critical:/ {print $2; exit}' "$raw_log" | tr -d '[:space:]')"
critical_count="${critical_count:-0}"
{
echo "# Restore Freshness Negative Alert Test"
echo
echo "Timestamp: $(date '+%F %T')"
echo "Result: $([ "$rc" -ne 0 ] && [ "$critical_count" -gt 0 ] && echo "ok" || echo "failed")"
echo "Check exit code: $rc"
echo "Critical count: $critical_count"
echo "Synthetic dump root: $dump_root"
echo "Synthetic report root: $test_report_root"
echo "Production dump root touched: no"
echo
echo "## Check Output"
echo
cat "$raw_log"
} >"$report_file"
if [ "$rc" -ne 0 ] && [ "$critical_count" -gt 0 ]; then
if [ "$SEND_NTFY" = "1" ]; then
bash "$SCRIPT_DIR/send-ntfy.sh" \
"$ALERT_TOPIC" \
"TEST: Restore freshness alert path ok" \
"Negativtest erfolgreich: check-restore-freshness.sh meldete ${critical_count} Criticals gegen synthetischen leeren Testpfad. Produktive Dumps wurden nicht veraendert. Report: $report_file" \
high
fi
echo "Negative freshness alert test ok. Report: $report_file"
exit 0
fi
if [ "$SEND_NTFY" = "1" ]; then
bash "$SCRIPT_DIR/send-ntfy.sh" \
"$ALERT_TOPIC" \
"TEST FAILED: Restore freshness alert path" \
"Negativtest fehlgeschlagen: erwarteter Critical-Zustand wurde nicht erkannt. Report: $report_file" \
high || true
fi
echo "Negative freshness alert test failed. Report: $report_file" >&2
exit 1
-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 -16
View File
@@ -10,9 +10,6 @@ case "$MODE" in
freshness) freshness)
exec "$SCRIPT_DIR/check-restore-freshness.sh" exec "$SCRIPT_DIR/check-restore-freshness.sh"
;; ;;
freshness-negative)
exec "$SCRIPT_DIR/negative-freshness-alert-test.sh"
;;
vaultwarden) vaultwarden)
if [ "$WHATIF" = "--what-if" ]; then if [ "$WHATIF" = "--what-if" ]; then
exec "$SCRIPT_DIR/vaultwarden-restore-test.sh" --what-if exec "$SCRIPT_DIR/vaultwarden-restore-test.sh" --what-if
@@ -43,18 +40,6 @@ case "$MODE" in
fi fi
exec "$SCRIPT_DIR/authelia-restore-test.sh" 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) nextcloud)
if [ "$WHATIF" = "--what-if" ]; then if [ "$WHATIF" = "--what-if" ]; then
exec "$SCRIPT_DIR/nextcloud-restore-test.sh" --what-if exec "$SCRIPT_DIR/nextcloud-restore-test.sh" --what-if
@@ -98,7 +83,7 @@ case "$MODE" in
exec "$SCRIPT_DIR/shared-pg-cluster-restore-test.sh" exec "$SCRIPT_DIR/shared-pg-cluster-restore-test.sh"
;; ;;
*) *)
echo "Usage: $0 {freshness|freshness-negative|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 exit 1
;; ;;
esac esac
+1 -5
View File
@@ -28,8 +28,6 @@ Quartalsweise:
- Restore-/DR-Sanity-Check - Restore-/DR-Sanity-Check
- `immich` Restore-Smoke-Test (DB + UI, ohne produktive Foto-Mounts; Erstlauf 2026-05-27 erfolgreich) - `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: - pruefen:
- Restore-Lab-Struktur - Restore-Lab-Struktur
- Reports - Reports
@@ -47,9 +45,7 @@ Quartals-Belegung:
Bestaetigte Mini-Restores: Vaultwarden, Gitea und Paperless am 2026-05-07; Bestaetigte Mini-Restores: Vaultwarden, Gitea und Paperless am 2026-05-07;
Immich am 2026-05-27; Paperless erneut am 2026-05-31; Authelia am 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-03 (Config-Smoke ohne produktiven Dump-Restore).
2026-06-06 (Config + HTTP/DNS-Smoke); Redis 8 am 2026-06-06
(Pre-Cutover-Artefakt + PING/INFO/DBSIZE-Smoke).
## Konkreter Kalender ## Konkreter Kalender
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
scrutiny: scrutiny:
image: ghcr.io/starosdev/scrutiny:latest-omnibus@sha256:228483f16a6236d2fa9b2fbfca2e76dc861e648fbc6ae6e680d23e5d00211a5d image: ghcr.io/starosdev/scrutiny:latest-omnibus@sha256:bf5a07f583998b1b0a690d4b06be0378baa6f6f0140b4c815bd4fa55268df223
container_name: scrutiny container_name: scrutiny
restart: unless-stopped restart: unless-stopped
privileged: true privileged: true
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
speedtest-tracker: speedtest-tracker:
image: lscr.io/linuxserver/speedtest-tracker:1.14.3@sha256:c3750c40948a9360000ce62d694da92e85584b4ab6d3d9a9d1432d76fa5e0726 image: lscr.io/linuxserver/speedtest-tracker:1.14.3@sha256:79c00631575dec6d91c10ed904c211224f00813013a305c2284324e195a538bb
container_name: speedtest-tracker container_name: speedtest-tracker
restart: unless-stopped restart: unless-stopped
security_opt: security_opt:
-1
View File
@@ -6,6 +6,5 @@ Diese Skripte sind bewusst versionierte Operator-Hilfen fuer den Windows-Neuaufs
- `repair-disk0-boot-to-new-windows.ps1` repariert EFI/Bootdateien fuer das neue Windows auf der Intel-SSD und verlangt Adminrechte. - `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. - `cleanup-dualboot-bcd.ps1` bereinigt BCD-Bootmenueeintraege und verlangt eine explizite Textbestaetigung.
- `ops/windows-reinstall/docs/windows-neuaufsetzen-masterplan.md` und `ops/windows-reinstall/docs/postinstall-erstes-ziel-codex.md` enthalten die zugehoerigen Operator-Notizen. - `ops/windows-reinstall/docs/windows-neuaufsetzen-masterplan.md` und `ops/windows-reinstall/docs/postinstall-erstes-ziel-codex.md` enthalten die zugehoerigen Operator-Notizen.
- `ops/windows-reinstall/docs/postdelta-2026-06-04.md` dokumentiert den PostDelta-Stand vom 2026-06-04: aktuellster Banking4-Tresor, WISO-Ergaenzungen, Overwatch-2-Config, iCUE/Corsair-Maussettings und `D:\Users\michi`-Admincheck.
Die Skripte enthalten keine Secrets, arbeiten aber mit lokalen Windows-Datentraegern und duerfen nur interaktiv und mit vorheriger Sichtpruefung ausgefuehrt werden. Die Skripte enthalten keine Secrets, arbeiten aber mit lokalen Windows-Datentraegern und duerfen nur interaktiv und mit vorheriger Sichtpruefung ausgefuehrt werden.
@@ -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,81 +0,0 @@
param(
[string]$BackupPath = "\\kallilabcore\backups\windows-images\baerchen",
[string]$JobName = "baerchen-c-image",
[string]$LogRoot = "C:\ProgramData\Veeam\Endpoint"
)
$ErrorActionPreference = "Stop"
function Write-Section {
param([string]$Title)
Write-Host ""
Write-Host "== $Title =="
}
$result = [ordered]@{
ComputerName = $env:COMPUTERNAME
Timestamp = (Get-Date).ToString("s")
JobName = $JobName
BackupPath = $BackupPath
VeeamService = $null
BackupPathReachable = $false
BackupFiles = @()
LatestBackupFile = $null
LatestJobEvidence = @()
}
Write-Section "Veeam service"
$service = Get-Service -Name VeeamEndpointBackupSvc -ErrorAction SilentlyContinue
if ($service) {
$result.VeeamService = $service.Status.ToString()
$service | Select-Object Status,Name,DisplayName | Format-Table -AutoSize
} else {
$result.VeeamService = "missing"
Write-Warning "VeeamEndpointBackupSvc not found"
}
Write-Section "Backup target"
if (Test-Path -LiteralPath $BackupPath) {
$result.BackupPathReachable = $true
$files = Get-ChildItem -LiteralPath $BackupPath -File -Recurse |
Sort-Object LastWriteTime -Descending |
Select-Object FullName,Length,LastWriteTime
$result.BackupFiles = $files
$result.LatestBackupFile = $files | Select-Object -First 1
$files | Select-Object -First 20 | Format-Table -AutoSize
} else {
Write-Warning "Backup path is not reachable from this Windows session. This can be normal when Veeam uses stored job credentials and the interactive user has no active SMB session."
}
Write-Section "Veeam job evidence"
if (Test-Path -LiteralPath $LogRoot) {
$logFiles = @(
(Join-Path $LogRoot "Svc.VeeamEndpointBackup.log"),
(Join-Path $LogRoot "UI.BackupJobWizard.log"),
(Join-Path $LogRoot "UI.Tray-michi.log")
) | Where-Object { Test-Path -LiteralPath $_ }
$patterns = @(
[regex]::Escape("Job [$JobName] was started"),
'Session result: "Success", job type: "EndpointBackup"',
[regex]::Escape("Name: [$JobName]"),
'Result: \[Success\]',
'StorageEncryptionEnabled:\s+0',
[regex]::Escape("Starting endpoint backup, job name = $JobName")
) -join "|"
$evidence = Select-String -Path $logFiles -Pattern $patterns -CaseSensitive:$false -ErrorAction SilentlyContinue |
Sort-Object Path,LineNumber |
Select-Object -Last 30 @{Name = "File"; Expression = { Split-Path -Leaf $_.Path } },LineNumber,Line
$result.LatestJobEvidence = $evidence
$evidence | Format-List
if (-not ($evidence | Where-Object { $_.Line -match 'Result: \[Success\]|Session result: "Success"' })) {
Write-Warning "No successful EndpointBackup session was found in the selected Veeam logs."
}
} else {
Write-Warning "Veeam log root not found: $LogRoot"
}
Write-Section "Summary"
[pscustomobject]$result | Select-Object ComputerName,Timestamp,JobName,BackupPath,VeeamService,BackupPathReachable,LatestBackupFile | Format-List
@@ -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.
@@ -1,132 +0,0 @@
# Boot-Cleanup-Plan 2026-06-04
## Ziel
`F:` ist das alte Windows und soll spaeter verschwinden. Vor Loeschen/Formatieren/Resize muss das neue Windows beweisen, dass es ohne `F:` bootet und keine BCD-/Resume-Abhaengigkeit mehr auf `F:` zeigt.
Noch keine Partition wird geloescht, formatiert oder erweitert.
## Aktueller Befund
- Neues Windows: `C:\WINDOWS`
- Alter Loader: `Windows 11 Alt`
- Alter Loader zeigt auf `partition=F:`
- Alter Resume-Eintrag zeigt auf `partition=F:` und `F:\hiberfil.sys`
- Boot Manager referenziert aktuell noch den alten Resume-Eintrag.
- Aktives Pagefile ist nur `C:\pagefile.sys`.
- `D:\pagefile.sys` und `E:\pagefile.sys` sind inaktive Altlasten, lassen sich aber ohne Adminrechte nicht entfernen.
## Vorbereitete Skripte
Im Arbeitsordner `C:\Users\michi\Documents\Neues Windows`:
- `boot-cleanup-freigabe-f-vorbereitung.ps1`
- `start-boot-cleanup-admin.cmd`
Der Syntaxcheck des PowerShell-Skripts wurde ausgefuehrt. Es laedt korrekt und stoppt ohne Adminrechte erwartbar mit:
```text
Dieses Skript muss als Administrator laufen.
```
## Geplanter Admin-Block
Das Skript fuehrt mit Adminrechten aus:
1. Backupordner unter `C:\Temp\boot_cleanup_<timestamp>` anlegen.
2. BCD, WinRE, Volumes, Partitionen und Pagefiles vor der Aenderung protokollieren.
3. BCD exportieren nach `BCD-before-cleanup.bak`.
4. `{bootmgr}` `resumeobject` auf den aktuellen C:-Resume-Eintrag setzen.
5. Alten Loader `Windows 11 Alt` aus der Boot-Anzeige entfernen.
6. Alten Loader loeschen.
7. Alten F:-Resume-Eintrag loeschen.
8. Inaktive Alt-Pagefiles `D:\pagefile.sys` und `E:\pagefile.sys` entfernen.
9. BCD, WinRE und Pagefiles danach erneut protokollieren.
## Nicht enthalten
- Kein Loeschen von `F:`.
- Kein Formatieren von `E:`.
- Kein Resize von Partitionen.
- Kein Entfernen von Recovery-Partitionen.
- Kein Veraendern von `G:` / Homelab / EFI-Systempartition.
## Danach notwendig
1. Neustart.
2. Pruefen, ob Windows sauber bootet.
3. `bcdedit /enum all` pruefen: keine `partition=F:` Referenz mehr.
4. Pagefiles pruefen: nur `C:\pagefile.sys` aktiv, `D:\pagefile.sys` und `E:\pagefile.sys` weg.
5. Erst danach `F:` als technisch freigegeben markieren.
## Ausgefuehrt 2026-06-04 17:25
Der Admin-Block wurde ausgefuehrt. Log-/Backup-Ordner:
- `C:\Temp\boot_cleanup_20260604_172547`
- BCD-Backup: `C:\Temp\boot_cleanup_20260604_172547\BCD-before-cleanup.bak`
- Log: `C:\Temp\boot_cleanup_20260604_172547\boot_cleanup_log.txt`
Ergebnis laut Admin-Log:
- `{bootmgr}` `resumeobject` wurde auf den aktuellen C:-Resume-Eintrag `{f6daf1c6-6f16-11f0-992f-bc6ee2f9d6ec}` gesetzt.
- `Windows 11 Alt` wurde aus `displayorder` entfernt.
- Alter Loader `{f6daf1bd-6f16-11f0-992f-bc6ee2f9d6ec}` wurde geloescht.
- Alter F:-Resume-Eintrag `{f6daf1bc-6f16-11f0-992f-bc6ee2f9d6ec}` war nach dem Loader-Cleanup bereits nicht mehr auffindbar.
- `D:\pagefile.sys` wurde entfernt.
- `E:\pagefile.sys` wurde entfernt.
After-BCD aus Admin-Log:
- `displayorder` enthaelt nur noch `{current}`.
- `Windows 11 Neu` zeigt auf `device partition=C:` und `osdevice partition=C:`.
- Resume zeigt auf `partition=C:`.
- Im After-BCD-Log sind keine `partition=F:`-Eintraege mehr sichtbar.
After-Pagefiles:
- Aktiv: `C:\pagefile.sys`
- Vorhanden: `C:\hiberfil.sys`, `C:\pagefile.sys`, `C:\swapfile.sys`
- Alte Dateien auf `D:` und `E:` sind weg.
- Auf `F:` liegen weiterhin alte `hiberfil.sys`/`swapfile.sys` des alten Windows; diese bleiben bis zur finalen F:-Bereinigung unangetastet.
Unabhaengige Nachpruefung aus normaler Codex-Shell:
- `D:` frei: ca. 126.3 GB
- `E:` frei: ca. 629.5 GB
- `Get-CimInstance Win32_PageFileUsage` meldet nur `C:\pagefile.sys`
Verbleibend nach Neustarttest:
- `F:` ist nach erfolgreichem Neustarttest technisch von der Boot-Konfiguration entkoppelt.
- WinRE war nach dem Cleanup `Disabled`. **Erledigt 2026-06-05:** WinRE wurde im Admin-Nachlauf (siehe `laufwerks-neustruktur-2026-06-04.md` Abschnitt "Admin-Nachlauf 2026-06-05") mit `reagentc /setreimage` und `reagentc /enable` repariert und aktiviert. `Windows RE-Status: Enabled`, Version `10.0.26100.8455`.
## Neustarttest 2026-06-04 17:27
Nach dem Cleanup wurde Windows erfolgreich neu gestartet.
Post-Reboot-Status:
- `LastBootUpTime`: 2026-06-04 17:27:56
- Neues Windows laeuft weiter von `C:\WINDOWS`.
- `D:\pagefile.sys` und `E:\pagefile.sys` sind weiterhin weg.
- Aktives Pagefile: `C:\pagefile.sys`
Post-Reboot-Bootreport:
- `C:\Temp\bcd_post_reboot_latest.txt`
- `C:\Temp\winre_post_reboot_latest.txt`
Ergebnis:
- Keine sichtbare `partition=F:`-Referenz im BCD-Post-Reboot-Report.
- `displayorder` enthaelt nur `{current}`.
- `Windows 11 Neu` zeigt auf `device partition=C:` und `osdevice partition=C:`.
- Resume zeigt auf `partition=C:`.
- Historischer Post-Reboot-Stand war: WinRE blieb `Disabled`. Nachlauf
2026-06-05: WinRE ist `Enabled`; siehe Erledigt-Hinweis oben.
Bewertung:
- `F:` ist aus Boot-/Resume-Sicht technisch freigegeben.
- Partitionen wurden weiterhin nicht geloescht, formatiert oder erweitert.
@@ -1,325 +0,0 @@
# Laufwerks-Neustruktur 2026-06-04
## Ausgangslage
Neues Windows:
- SystemDrive: `C:`
- Windows: `C:\WINDOWS`
- Benutzer: `baerchen\michi`
Backup-Pfade wurden gefunden:
- `H:\Windows-Neuaufsetzen-Backup`
- `H:\Windows-Neuaufsetzen-Backup\_PostDelta_2026-06-04_100146`
## Zielbild
| Laufwerk | Rolle |
|---|---|
| `C:` | Windows + kleine Programme |
| `D:` | Daten & Projekte |
| `E:` | Games |
| `F:` | Altes Windows, spaeter loeschen und Platz an `E:` anhaengen |
| `G:` | Arbeits-SSD, Homelab/Dev bleibt |
| `H:` | Externe Backup-HDD, unveraendert |
## Phase 0: Read-only Status
Volumestatus nach nicht-destruktivem Umbau:
| Laufwerk | Label | Dateisystem | Groesse | Frei |
|---|---|---|---:|---:|
| `C:` | | NTFS | 166.9 GB | 63.1 GB |
| `D:` | `Daten-Projekte` | NTFS | 167.7 GB | 116.8 GB |
| `E:` | `Games` | NTFS | 638.5 GB | 602.5 GB |
| `F:` | | NTFS | 292.1 GB | 45.6 GB |
| `G:` | `M2 SSD` | NTFS | 930.9 GB | 121.5 GB |
| `H:` | `Externe HDD` | NTFS | 7452.0 GB | 3801.3 GB |
Physische Datentraeger:
| Disk | Modell | Bus | Groesse | Partitionsstil | Status |
|---:|---|---|---:|---|---|
| 0 | INTEL SSDSC2BW180A3L | SATA | 168 GB | GPT | Healthy |
| 1 | INTEL SSDSC2BW180A3L | SATA | 168 GB | GPT | Healthy |
| 2 | Samsung SSD 980 PRO 1TB | NVMe | 932 GB | GPT | Healthy |
| 3 | WDC WDS100T2B0C | NVMe | 932 GB | GPT | Healthy |
| 4 | asmedia ASM235 | USB | 7452 GB | GPT | Healthy |
Partitionen:
| Disk | Partition | Laufwerk | Groesse | Typ |
|---:|---:|---|---:|---|
| 0 | 1 | | 0.0 GB | Reserved |
| 0 | 2 | `C:` | 166.9 GB | Basic |
| 0 | 3 | | 0.8 GB | Recovery |
| 1 | 1 | | 0.0 GB | Reserved |
| 1 | 2 | `D:` | 167.7 GB | Basic |
| 2 | 1 | | 0.0 GB | Reserved |
| 2 | 2 | `E:` | 638.5 GB | Basic |
| 2 | 3 | `F:` | 292.1 GB | Basic |
| 2 | 4 | | 0.9 GB | Recovery |
| 3 | 1 | | 0.1 GB | System |
| 3 | 2 | | 0.0 GB | Reserved |
| 3 | 3 | `G:` | 930.9 GB | Basic |
| 3 | 4 | | 0.5 GB | Recovery |
| 4 | 1 | | 0.0 GB | Reserved |
| 4 | 2 | `H:` | 7452.0 GB | Basic |
## Phase 1: Gate `F -> E`
Technische Lage:
- `E:` und `F:` liegen beide auf Disk 2, Samsung SSD 980 PRO 1TB.
- `F:` ist Partition 3 und liegt direkt hinter `E:` Partition 2.
- Danach folgt noch eine Recovery-Partition.
- Rein vom Layout her waere `F loeschen, E erweitern` grundsaetzlich moeglich.
Blocker:
- BCD enthaelt noch den Booteintrag `Windows 11 Alt` mit `device partition=F:` und `osdevice partition=F:`.
- BCD enthaelt auch einen Resume-Eintrag fuer `F:`.
Entscheidung:
- `F:` wurde nicht geloescht.
- Keine Formatierung, kein Resize, kein BCD-Cleanup.
- Destruktive Phase bleibt gesperrt bis zur ausdruecklichen Bestaetigung und einem separaten Boot-Cleanup-Plan.
## Phase 2: Spiele-/Launcher-Bestand
| Name | Quelle | Groesse | Dateien |
|---|---|---:|---:|
| Battle.net | `D:\Battle.net` | 1.99 GB | 1162 |
| Hearthstone | `D:\Hearthstone` | 11.75 GB | 5148 |
| Overwatch | `D:\Overwatch` | 0.20 GB | 22 |
| Steam2 | `D:\Steam2` | 22.83 GB | 14992 |
| World of Warcraft II | `D:\World of Warcraft II` | 0.13 GB | 7882 |
| WOW INTERFACE | `D:\WOW INTERFACE` | 0.46 GB | 28198 |
| Games | `G:\Games` | 298.06 GB | 20675 |
| SteamLibrary | `G:\SteamLibrary` | 446.26 GB | 39228 |
Noch nicht verschoben:
- Battle.net / Overwatch wird spaeter separat behandelt.
- Docker Desktop wird spaeter separat behandelt.
- `G:\Gitea_Clone\homelab-infra` bleibt unangetastet.
## Phase 3: Nicht-destruktiver Umbau
Angelegt auf `D:`:
- `D:\00_Inbox`
- `D:\10_Dokumente`
- `D:\11_Bilder`
- `D:\12_Videos`
- `D:\13_Musik`
- `D:\14_Downloads`
- `D:\20_Projekte\aktiv`
- `D:\20_Projekte\archiv`
- `D:\30_Finanzen\Banking4`
- `D:\30_Finanzen\WISO_Steuer`
- `D:\90_Archiv`
Angelegt auf `E:`:
- `E:\Steam\steamapps`
- `E:\BattleNet`
- `E:\EpicGames`
- `E:\EA`
- `E:\Riot`
- `E:\Ubisoft`
- `E:\_Standalone`
Angelegt auf `G:`:
- `G:\repos`
- `G:\tools`
Windows-Bekanntordner:
| Known Folder | Ziel |
|---|---|
| Desktop | `D:\00_Inbox\Desktop` |
| Dokumente | `D:\10_Dokumente` |
| Downloads | `D:\14_Downloads` |
| Bilder | `D:\11_Bilder` |
| Musik | `D:\13_Musik` |
| Videos | `D:\12_Videos` |
Kopiert, ohne Quellen zu loeschen:
- `D:\Micha\Dokumente` nach `D:\10_Dokumente`
- `D:\Micha\Bilder` nach `D:\11_Bilder`
- `D:\Micha\Videos` nach `D:\12_Videos`
- `D:\Micha\Musik` nach `D:\13_Musik`
- `D:\Micha\Downloads` nach `D:\14_Downloads`
- `D:\Micha\Finanzen` nach `D:\30_Finanzen`
- Aktueller Banking4-Tresor aus PostDelta nach `D:\30_Finanzen\Banking4`
Labels:
- `D:` von `Blizzard Games` auf `Daten-Projekte`
- `E:` von `980SSD` auf `Games`
- `H:` unveraendert
## Log-Dateien
- `C:\Temp\disk_report.txt`
- `C:\Temp\disk_report_after_latest.txt`
- `C:\Temp\bcd_report.txt`
- `C:\Temp\bcd_report_after_latest.txt`
- `C:\Temp\winre_report_after_latest.txt`
- `C:\Temp\spiele_inventory_2026-06-04.csv`
- `C:\Temp\laufwerks_neustruktur_log_2026-06-04.txt`
## Review-Abgleich nach Claude-Hinweis
Am 2026-06-04 wurde nach dem externen Review ein frischer Nachher-Snapshot erzeugt:
- `C:\Temp\disk_report_after_20260604_143928.txt`
- `C:\Temp\disk_report_after_latest.txt`
- `C:\Temp\bcd_report_after_latest.txt`
- `C:\Temp\winre_report_after_latest.txt`
Damit ist die vorherige Doku-Luecke geschlossen: Der urspruengliche `disk_report.txt` war vor der Label-Aenderung entstanden. Der neue Report bestaetigt:
- `D:` Label `Daten-Projekte`
- `E:` Label `Games`
- `C:`, `D:`, `E:`, `F:`, `G:` und `H:` sind `Healthy`
- Known Folders zeigen auf die neue Struktur:
- Desktop: `D:\00_Inbox\Desktop`
- Dokumente: `D:\10_Dokumente`
- Downloads: `D:\14_Downloads`
- Bilder: `D:\11_Bilder`
- Musik: `D:\13_Musik`
- Videos: `D:\12_Videos`
Desktop-Befund:
- Der Desktop wurde nach dem Review bewusst auf `D:\00_Inbox\Desktop` gelegt.
- `D:\Micha\Desktop` ist veraltet und soll nicht mehr als Known-Folder-Ziel verwendet werden.
Zusatzbefund Boot/Resume:
- `F:` haengt nicht nur am Loader `Windows 11 Alt`, sondern auch am Resume-Eintrag `{f6daf1bc-6f16-11f0-992f-bc6ee2f9d6ec}`.
- Der Windows-Start-Manager referenziert diesen alten Resume-Eintrag ebenfalls als `resumeobject`.
- Der Stop fuer alle destruktiven Schritte an `F:` bleibt dadurch noch staerker begruendet.
WinRE-Befund:
- `reagentc /info` meldet: `Windows RE-Status: Disabled`.
- WinRE wird separat in der Windows-Idealkonfiguration behandelt, bevor Boot-/Partitionsbereinigung freigegeben wird.
Kopier-/Doppelbestand:
- Die Inhalte aus `D:\Micha\*` wurden abgeglichen und in die neue Nummernstruktur ueberfuehrt.
- Es besteht kein gewollter Doppelbestand mehr.
- `D:\Micha\Videos\NVIDIA` kann von NVIDIA/Overlay-Komponenten leer neu angelegt werden und ist kein Datenbestand.
## Offene Punkte
- ~~WinRE/Secure Boot/TPM Admin-Check~~ **Erledigt 2026-06-05** (siehe Abschnitt "Admin-Nachlauf 2026-06-05"): WinRE aktiviert, Secure Boot `True`, TPM ready/enabled.
- **BitLocker-Entscheidung offen:** Alle Laufwerke `FullyDecrypted`, Protection `Off`. Vor Aktivierung: Recovery Keys fuer mindestens `C:` und `D:` an drei Orten sichern (Vaultwarden, `D:\30_Finanzen\BitLocker-RecoveryKey-baerchen-<DATUM>.txt`, physisch). Verweis: `docs/MASTER_TODO.md` Abschnitt "Windows / Workstation baerchen".
- Optional: `D:\11_Bilder` ReadOnly-Attribut beobachten; fuer Windows-Shell-Ordner ist das in der Praxis meist unkritisch.
- Optional: `D:\13_Musik` bleibt leer, solange aus dem Backup keine Musikdaten nachgezogen werden muessen.
- Optional: `G:\Apps`, `G:\Workspace`, `D:\WSL` in der Homelab-/Dev-Doku ergaenzen.
## E/F-Finalisierung 2026-06-04 17:33
Nach erfolgreichem Boot-Cleanup und Neustarttest wurde die E/F-Phase ausgefuehrt.
Vor dem Formatieren von `E:` wurden dort gefundene Nicht-Games-Daten nach `D:` gesichert:
- Quelle: `E:\BMW Leasing`
- Quelle: `E:\Marina Handy 2025`
- Quelle: `E:\Marina Handy Backup`
- Ziel: `D:\90_Archiv\E_vor_Format_20260604_173209`
Der Datei-/Groessenabgleich war stimmig:
- `BMW Leasing`: 9 Dateien, ca. 0.01 GB
- `Marina Handy 2025`: 7119 Dateien, ca. 8.83 GB
- `Marina Handy Backup`: 0 Dateien
Admin-Log:
- `C:\Temp\final_ef_partition_20260604_173349\final_ef_partition_log.txt`
Durchgefuehrt:
- `E:` schnell formatiert als NTFS mit Label `Games`
- `F:` Partition entfernt
- `E:` auf den frei gewordenen Bereich erweitert
- Games-Struktur auf `E:` neu angelegt
Finaler Stand:
| Laufwerk | Label | Groesse | Frei | Status |
|---|---|---:|---:|---|
| `E:` | `Games` | 930.6 GB | 930.5 GB | Healthy |
`F:` ist nicht mehr als Volume vorhanden.
Aktuelles Disk-2-Layout:
| Disk | Partition | Laufwerk | Groesse | Typ |
|---:|---:|---|---:|---|
| 2 | 1 | | 0.0 GB | Reserved |
| 2 | 2 | `E:` | 930.6 GB | Basic |
| 2 | 4 | | 0.9 GB | Recovery |
Overwatch-Config wurde zurueckkopiert:
- Quelle: `H:\Windows-Neuaufsetzen-Backup\_PostDelta_2026-06-04_100146\16_Overwatch2_Config\Documents_Overwatch`
- Ziel: `D:\10_Dokumente\Overwatch`
- `D:\10_Dokumente\Overwatch\Settings\Settings_v0.ini` vorhanden
Battle.net:
- Offizieller Blizzard-Installer wurde geladen nach `D:\Software\Installer\BattleNet\Battle.net-Setup.exe`
- Authenticode-Signatur: gueltig, Herausgeber `Blizzard Entertainment, Inc.`
- Installer wurde gestartet; Installation/Anmeldung laeuft per GUI.
## Abschluss-Audit 2026-06-05
Nach dem Abschluss-Audit wurden folgende Punkte bereinigt oder festgehalten:
- `E:\_Standalone` wurde angelegt.
- `D:\Micha` enthaelt keinen migrierten Datenbestand mehr. Ein leerer Pfad `D:\Micha\Videos\NVIDIA` kann durch NVIDIA/Overlay-Komponenten neu entstehen.
- Known Folders:
- Desktop: `D:\00_Inbox\Desktop`
- Dokumente: `D:\10_Dokumente`
- Downloads: `D:\14_Downloads`
- Bilder: `D:\11_Bilder`
- Musik: `D:\13_Musik`
- Videos: `D:\12_Videos`
- Energieplan wurde auf `Ultimative Leistung` gesetzt.
- SSH-Key-Rechte fuer `C:\Users\michi\.ssh\id_ed25519` wurden auf `BAERCHEN\michi:(F)` reduziert.
- OneDrive-Benutzer-Tasks `Reporting` und `Startup` wurden deaktiviert. Der globale Per-Machine-Updater benoetigt Adminrechte.
- `SoftLanding\CreativeManagementTask` war beim Nachcheck nicht vorhanden.
- Docker Desktop laeuft; Docker-WSL-Daten liegen physisch unter `G:\tools\ai\docker\wsl`. Der alte Pfad `C:\Users\michi\AppData\Local\Docker\wsl` ist eine Junction auf diesen Zielpfad.
- Ollama nutzt `G:\tools\ai\ollama`; Open WebUI-Daten liegen unter `G:\tools\ai\open-webui`.
- Open WebUI laeuft unter `http://localhost:3000` und sieht die Ollama-Modelle.
Admin-Nachlauf 2026-06-05:
- WinRE wurde repariert und aktiviert:
- `Winre.wim` gefunden unter `C:\Windows\System32\Recovery\Winre.wim`
- `reagentc /setreimage /path C:\Windows\System32\Recovery /target C:\Windows`
- `reagentc /enable`
- Ergebnis: `Windows RE-Status: Enabled`, Version `10.0.26100.8455`
- Secure Boot: `True`
- TPM: vorhanden, ready, enabled, activated, owned
- BitLocker-Status geprueft:
- `C:`, `D:`, `E:`, `G:`, `H:` sind `FullyDecrypted`, Protection `Off`
- BitLocker wurde nicht automatisch aktiviert, weil dafuer eine bewusste Recovery-Key- und Lockout-Entscheidung noetig ist.
- OneDrive Per-Machine Standalone Update Task wurde deaktiviert.
- SSH-Aliases angelegt und getestet:
- `kallilabcore` -> `root@100.80.98.33`
- `kallilabcore-lan` -> `root@192.168.178.58`
Weiter offen:
- BitLocker-Entscheidung fuer mindestens `C:` und `D:` treffen. Vor Aktivierung Recovery Keys extern sichern.
@@ -1,148 +0,0 @@
# PostDelta 2026-06-04
Diese Datei dokumentiert das Delta, das nach dem urspruenglichen Windows-Neuaufsetzen-Plan und nach `_Delta_2026-05-19` entstanden ist.
Sie ist die Git-Repo-Kopie von:
```text
H:\Windows-Neuaufsetzen-Backup\HANDOFF_2026-06-04.md
```
## Kontext
- Der Benutzer war am 2026-06-04 noch im alten Windows gebootet.
- Aus Sicht des alten Windows war:
- altes Windows = `C:\WINDOWS`
- neues Windows vermutlich = `D:\Windows`
- Nach Boot ins neue Windows koennen sich Laufwerksbuchstaben aendern.
- Vor jedem Restore oder Cleanup zuerst `Get-Volume`, `Get-Disk`, `Get-Partition`, `$env:SystemDrive` und `$env:windir` pruefen.
## Relevante Backup-Schichten
```text
H:\Windows-Neuaufsetzen-Backup
H:\Windows-Neuaufsetzen-Backup\_Delta_2026-05-19
H:\Windows-Neuaufsetzen-Backup\_PostDelta_2026-06-04_100146
```
Das PostDelta ist fuer aktuelle Daten zwingend mitzuberuecksichtigen.
## PostDelta-Inhalte
PostDelta-Ziel:
```text
H:\Windows-Neuaufsetzen-Backup\_PostDelta_2026-06-04_100146
```
Wichtige Unterordner:
- `00_Kritisch_Direkt`
- `01_Desktop`
- `02_Dokumente`
- `03_Bilder`
- `05_Downloads`
- `09_Programme_Settings_Lizenzen`
- `16_Overwatch2_Config`
- `17_Maus_Settings`
- `18_D_Users_michi_AdminCheck`
## Banking4
Aktuellster bekannter Tresor:
```text
H:\Windows-Neuaufsetzen-Backup\_PostDelta_2026-06-04_100146\00_Kritisch_Direkt\Mein Datentresor.sub
H:\Windows-Neuaufsetzen-Backup\_PostDelta_2026-06-04_100146\00_Kritisch_Direkt\.Mein Datentresor.sub.att
```
Hash-verifiziert:
```text
Mein Datentresor.sub
SHA256 F22224B7A765046D4B76D71C1E296DA59D8D8A849A41A12E5C10254DF0EC71AD
.Mein Datentresor.sub.att
SHA256 3FC5D0BD8B673975F9C42F4ED53278CFF434ED21E266B8B60589288A2FF9F4D8
```
Der aeltere Banking4-Tresor aus Hauptbackup/Delta ist nicht mehr der neueste Stand.
Lizenz:
```text
H:\Windows-Neuaufsetzen-Backup\09_Programme_Settings_Lizenzen\keys_exporte\banking4_license_private.txt
```
## WISO Steuer
Hauptbackup:
```text
H:\Windows-Neuaufsetzen-Backup\07_Banking_Finanzen\WISO_Steuer_Dokumente
```
PostDelta:
```text
H:\Windows-Neuaufsetzen-Backup\_PostDelta_2026-06-04_100146\02_Dokumente\steuer
```
PostDelta enthaelt 8 Steuerdateien inklusive einer Pia-Marie-Datei.
## Overwatch 2
PostDelta:
```text
H:\Windows-Neuaufsetzen-Backup\_PostDelta_2026-06-04_100146\16_Overwatch2_Config
```
Wichtig:
```text
16_Overwatch2_Config\Documents_Overwatch\Settings\Settings_v0.ini
```
Beim Backup war nur `Overwatch.log` gesperrt; das ist eine Logdatei.
## Maus / iCUE
PostDelta:
```text
H:\Windows-Neuaufsetzen-Backup\_PostDelta_2026-06-04_100146\17_Maus_Settings
```
Enthaelt Corsair/iCUE-bezogene AppData/ProgramData, Registry-Exports und Windows-Mauswerte.
## D:\Users\michi Admincheck
Bericht:
```text
H:\Windows-Neuaufsetzen-Backup\_PostDelta_2026-06-04_100146\18_D_Users_michi_AdminCheck
```
Ergebnis:
- 6565 Dateien
- 2950 Ordner
- ca. 1.58 GB
- 0 rekursive Zugriffsfehler
- Standardordner praktisch leer
- fast alles AppData/Windows-Package-Kram
Interpretation: aus alter Windows-Sicht keine wichtigen persoenlichen Daten in `D:\Users\michi`.
## Reihenfolge im neuen Windows
1. Laufwerksbuchstaben und gebootetes Windows pruefen.
2. Alle drei Backup-Schichten pruefen.
3. Banking4 mit PostDelta-Tresor wiederherstellen.
4. WISO mit Hauptbackup plus PostDelta pruefen.
5. Dokumente/Desktop/Bilder/Downloads migrieren.
6. Overwatch 2 und iCUE/Corsair gezielt wiederherstellen.
7. SSH/Git/Homelab wiederherstellen.
8. Erst danach Windows-Idealkonfiguration, Bootcleanup und Formatierungen.
@@ -1,255 +0,0 @@
# Programme-Entscheidungs-Checkliste fuer den Windows-Wiederaufbau
Stand: 2026-06-04
Quelle: `H:\Windows-Neuaufsetzen-Backup\12_Exportierte_Listen\installierte_programme.csv` (161 Eintraege, Registry-Export vom 2026-05-07)
Winget-Abdeckung: `H:\Windows-Neuaufsetzen-Backup\12_Exportierte_Listen\winget-export.json`
Uebergeordneter Kontext: [HANDOFF_2026-06-04.md](../../../../H:/Windows-Neuaufsetzen-Backup/HANDOFF_2026-06-04.md) sowie [postdelta-2026-06-04.md](postdelta-2026-06-04.md)
## Wofuer ist diese Datei
Vorgefilterte Sortierung aller 161 installierten Programme in drei Toepfe, damit der Wiederaufbau im neuen Windows nicht 161 Einzelentscheidungen braucht.
**So nutzt sie der neue Codex/Claude:**
1. **Auto-Ja** (18 Eintraege): alle winget-IDs werden in einem Skript-Lauf installiert, ein User-OK reicht.
2. **Einzelfrage** (38 Eintraege): jeden Eintrag mit Micha durchgehen, pro Eintrag `j` / `n` / `spaeter`. Hier sitzen die Lizenzen, Logins und Config-Restores.
3. **Auto-Skip** (105 Eintraege): nur Sichtkontrolle, default ueberspringen. Sind Treiber-Bundles, Runtimes, Duplikate, Bloatware.
Summe: 161 == 161, keine Duplikate doppelt klassifiziert (Registry-Hive-Duplikate landen automatisch in Auto-Skip).
---
## 1. Auto-Ja (18)
Standard-Tools ohne Login/Lizenz, alle ueber `winget` installierbar. Im neuen Windows als Sammel-Befehl ausfuehrbar:
```powershell
winget install --exact --id Brave.Brave --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id CPUID.CPU-Z.MSI --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id CPUID.HWMonitor --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id CrystalDewWorld.CrystalDiskInfo --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id Futuremark.3DMark --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id Git.Git --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id GoLang.Go --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id Governikus.AusweisApp --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id Guru3D.RTSS --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id HulubuluSoftware.AdvancedRenamer --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id LimeTechnology.UnraidUSBCreator --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id MaxCut.MaxCut --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id OpenJS.NodeJS.LTS --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id Python.Launcher --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id Python.Python.3.13 --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id buchen.portfolio --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id den4b.ReNamer --source winget --accept-package-agreements --accept-source-agreements
winget install --exact --id fjsoft.MyPhoneExplorer --source winget --accept-package-agreements --accept-source-agreements
```
| DisplayName | Version | Publisher | winget-ID | Begruendung |
|---|---|---|---|---|
| 3DMark | 2.22.7359.0 | UL | Futuremark.3DMark | Benchmark, kein Login |
| Advanced Renamer | 3.95 | Hulubulu Software | HulubuluSoftware.AdvancedRenamer | Free Rename-Tool |
| AusweisApp | 2.4.0 | Governikus GmbH & Co. KG | Governikus.AusweisApp | Online-Ausweis, kein Login |
| Brave | 147.1.89.145 | Die Brave-Autoren | Brave.Brave | Browser (Sync optional spaeter) |
| CPUID CPU-Z MSI 2.03 | 2.03 | CPUID, Inc. | CPUID.CPU-Z.MSI | System-Info-Tool |
| CPUID HWMonitor 1.60 | 1.60 | CPUID, Inc. | CPUID.HWMonitor | Hardware-Monitor |
| CrystalDiskInfo 9.8.0 | 9.8.0 | Crystal Dew World | CrystalDewWorld.CrystalDiskInfo | SSD/HDD-Health-Check |
| Git | 2.53.0.2 | The Git Development Community | Git.Git | Dev-Tool (SSH-Key wird separat aus Backup zurueckgeholt) |
| Go Programming Language amd64 go1.26.1 | 1.26.1 | https://go.dev | GoLang.Go | Dev-Tool |
| MaxCut | 2.9.3.9 | MaxCut Software Ltd | MaxCut.MaxCut | Plattenoptimierungs-Tool, free |
| MyPhoneExplorer | 2.3 | F.J. Wechselberger | fjsoft.MyPhoneExplorer | Android-Sync-Tool |
| Node.js | 24.15.0 | Node.js Foundation | OpenJS.NodeJS.LTS | Dev-Tool |
| Portfolio Performance | 0.76.3 | Andreas Buchen | buchen.portfolio | Open Source, Daten aus Backup |
| Python 3.13.3 Core Interpreter (64-bit) | 3.13.3150.0 | Python Software Foundation | Python.Python.3.13 | Python 3.13 — Python.Python.3.13 deckt das gesamte Bundle ab |
| Python Launcher | 3.13.3150.0 | Python Software Foundation | Python.Launcher | Dev-Tool |
| ReNamer | 7.7.0.0 | den4b Team | den4b.ReNamer | Free Rename-Tool |
| RivaTuner Statistics Server 7.3.7 | 7.3.7 | Unwinder | Guru3D.RTSS | OSD fuer Spiele (kommt mit MSI Afterburner) |
| Unraid USB Creator | 1.1.0 | Lime Technology, Inc | LimeTechnology.UnraidUSBCreator | Homelab-Tool |
---
## 2. Einzelfrage (38)
Brauchen Lizenz, Login, Konfig-Restore oder explizite Ja/Nein-Entscheidung. **Pro Eintrag mit Micha klaeren.** Die "KRITISCH"-Eintraege haben Vorrang.
Reihenfolge-Empfehlung (aus `HANDOFF_2026-06-04.md`):
1. NVIDIA App (zuerst — bringt alle NVIDIA-Komponenten im Bundle)
2. Microsoft 365 (M-Konto)
3. Banking4 + Tresor aus PostDelta
4. WISO Steuer 2026 + Steuerdateien aus PostDelta
5. WSL + Distros (Ubuntu.tar / docker-desktop.tar)
6. Tailscale (Login)
7. Browser (Google Chrome / Brave mit Sync)
8. Corsair iCUE (Mausprofile aus PostDelta)
9. Battle.net + Overwatch 2 Config aus PostDelta
10. Rest in beliebiger Reihenfolge
| DisplayName | Version | Publisher | winget-ID | Begruendung |
|---|---|---|---|---|
| Adobe Acrobat (64-bit) | 26.001.21529 | Adobe | Adobe.Acrobat.Pro | Adobe-Lizenz/Subscription pruefen |
| Adobe Refresh Manager | 1.8.0 | Adobe Systems Incorporated | | Adobe-Komponente, nur falls Acrobat installiert |
| AIDA64 Extreme v6.85 | 6.85 | FinalWire Ltd. | FinalWire.AIDA64.Extreme | Kostenpflichtige Lizenz |
| Android Studio | 2024.3 | Google LLC | Google.AndroidStudio | Sehr gross — nur falls Android-Dev gebraucht |
| Ant Movie Catalog | 4.2.2.2 | Ant Software | | Spezial-Tool, kein winget |
| Banking4 Home | | Subsembly GmbH | | KRITISCH: Lizenz aus 09_Programme_Settings_Lizenzen\keys_exporte\banking4_license_private.txt + Tresor aus _PostDelta_2026-06-04_100146\00_Kritisch_Direkt |
| Battle.net | | Blizzard Entertainment | | Launcher fuer Blizzard-Spiele (Overwatch 2 + WoW + Hearthstone). Overwatch-Config aus _PostDelta\16_Overwatch2_Config |
| Corsair iCUE5 Software | 5.44.55 | Corsair | Corsair.iCUE.5 | Mausprofile aus _PostDelta_2026-06-04_100146\17_Maus_Settings |
| Docker Desktop | 4.67.0 | Docker Inc. | Docker.DockerDesktop | WSL2-basiert, viele Configs. Bewusste Entscheidung ob noetig |
| EMDB Version 3.72 | 3.72 | Wicked & Wild Inc | | Spezial-Tool, kein winget |
| Epic Games Launcher | 1.3.155.0 | Epic Games, Inc. | EpicGames.EpicGamesLauncher | Login + Spiele-Bibliothek |
| FileBot | 5.1.5 | Point Planck Limited | PointPlanck.FileBot | Kostenpflichtige Lizenz |
| Google Chrome | 147.0.7727.138 | Google LLC | Google.Chrome.EXE | Sync-Login (Lesezeichen/Passwords/Profile aus Backup) |
| HP Scan - Grundlegende Software für das Gerät | 63.6.6364.25288 | HP Inc. | | HP-Drucker-Software (LAN/Netzwerk-Setup) — nur installieren wenn HP-Drucker noch da |
| Microsoft 365 - de-de | 16.0.19929.20136 | Microsoft Corporation | Microsoft.Office | KRITISCH: ueber Microsoft-Konto / Office.com installieren |
| Microsoft OneDrive | 26.063.0405.0002 | Microsoft Corporation | Microsoft.OneDrive | Microsoft-Konto-Login; vorher Sync-Konflikte mit altem OneDrive bedenken |
| Movienizer 10.3 | | Movienizer.com | | Kostenpflichtig, kein winget |
| MSI Afterburner 4.6.6 | 4.6.6 | MSI Co., LTD | Guru3D.Afterburner | Nur falls GPU-OC/Monitoring noch gebraucht wird |
| NVIDIA App 11.0.7.237 | 11.0.7.237 | NVIDIA Corporation | Nvidia.GeForceExperience | KRITISCH ZUERST: NVIDIA-App-Bundle installiert ALLE NVIDIA-Komponenten in einem Rutsch (Treiber + Container) |
| Octoparse 8.7.2 | 8.7.2 | Octopus Data Inc. | OctopusData.Octoparse | Web-Scraper, Account-bezogen, optional |
| PingPlotter 5 | 5.18.0.7997 | Pingman Tools, LLC | Pingman.PingPlotter | Kostenpflichtige Lizenz, optional |
| Plex Media Server | 1.40.3555 | Plex, Inc. | Plex.PlexMediaServer | Server-Komponente + Plex-Konto. Hinweis: am Homelab laeuft separater Plex; Desktop-Installation nur falls bewusst gewollt |
| Razer Chroma | 4.0.662 | Razer Inc. | | Razer-Komponente — kommt mit Synapse |
| Razer Synapse | 4.0.662 | Razer Inc. | | Hardware-Konfig (Mauspad/Beleuchtung) |
| Rename Expert 5.31.6 | 5.31.6 | Gillmeister Software | | Kostenpflichtig, manuelle Installation |
| Tailscale | 1.96.3 | Tailscale Inc. | Tailscale.Tailscale | Login + Tailscale-Konto (gleicher Account wie Homelab) |
| Tesseract-OCR - open source OCR engine | 5.5.0.20241111 | Tesseract-OCR community | UB-Mannheim.TesseractOCR | OCR-Engine, open source — entscheiden ob noch genutzt |
| WD Discovery | 4.4.407 | Western Digital Technologies, Inc. | | NAS-Discovery-Tool, nur falls WD NAS noch in Nutzung |
| WD Drive Utilities | 2.1.0.142 | Western Digital Technologies, Inc. | | WD-HDD-Tool, nur falls WD-Platte noch in Nutzung |
| WD My Cloud | 1.0.2.34 | Western Digital Technologies, Inc. | | WD My Cloud Login, nur falls Geraet noch in Nutzung |
| Windows Subsystem for Linux | 2.6.3.0 | Microsoft Corporation | Microsoft.WSL | KRITISCH: WSL aktivieren, dann Distros per `wsl --import` aus 09_Programme_Settings_Lizenzen\Ubuntu.tar + docker-desktop.tar |
| WinRAR 7.11 (64-Bit) | 7.11.0 | win.rar GmbH | RARLab.WinRAR | Lizenz (technisch Shareware) |
| WISO Steuer 2023 | 30.10.3890 | Buhl Data Service GmbH | | Alte Version — nur falls noch reaktiviert werden soll. Steuerdateien aus 07_Banking_Finanzen\WISO_Steuer_Dokumente |
| WISO Steuer 2024 | 31.02.3430 | Buhl Data Service GmbH | | Alte Version — nur falls noch reaktiviert werden soll |
| WISO Steuer 2025 | 32.03.2120 | Buhl Data Service GmbH | | Alte Version — nur falls noch reaktiviert werden soll |
| WISO Steuer 2026 | 33.05.3220 | Buhl Data Service GmbH | | KRITISCH aktuellste Version: Buhl-Konto + Steuerdateien aus _PostDelta\02_Dokumente\steuer (8 Dateien) |
| Wondershare Recoverit(Build 14.0.13.3) | 14.0.13.3 | Wondershare Software Co.,Ltd. | | Kostenpflichtig, Datenrettungs-Tool |
| WoodWorks 1.8.7 | 1.8.7 | Robert Denk | | Spezial-Tool, manuelle Installation |
---
## 3. Auto-Skip (105)
Treiber-Bundles, Runtimes, Dependencies, Duplikate, Bloatware, Spiele (kommen ueber Launcher). **Nur Sichtkontrolle, default ueberspringen.** Wenn Micha ein Item hier wider Erwarten doch will, in Einzelfrage verschieben.
| DisplayName | Version | Publisher | winget-ID | Begruendung |
|---|---|---|---|---|
| 3DMark | 2.22.7359.0 | UL | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| Apex Legends | | Respawn Entertainment | | Game — Reinstall ueber Battle.net/Steam/Epic Launcher (Einzelfrage betrifft nur den Launcher) |
| ARC Raiders | | Embark Studios | | Game — Reinstall ueber Battle.net/Steam/Epic Launcher (Einzelfrage betrifft nur den Launcher) |
| Bonjour | 2.0.2.0 | Apple Inc. | | Apple Bonjour — Dependency (z. B. HP), wird bei Bedarf nachgezogen |
| Bonjour-Druckdienste | 2.0.2.0 | Apple Inc. | | Apple Bonjour — Dependency (z. B. HP), wird bei Bedarf nachgezogen |
| Documentation Manager | 23.40.0.4 | Intel Corporation | | Intel Treiber/Engine — kommt mit Intel Chipset-Driver-Bundle |
| Dynamic Application Loader Host Interface Service | 1.0.0.0 | Intel Corporation | | Intel Treiber/Engine — kommt mit Intel Chipset-Driver-Bundle |
| Epic Games Launcher Prerequisites (x64) | 1.0.0.0 | Epic Games, Inc. | | Epic Games Prerequisite — kommt mit Epic Games Launcher |
| Epic Online Services | 4.0.1 | Epic Games, Inc. | | Epic Online Services — kommt mit Epic Games Launcher |
| Futuremark SystemInfo | 5.49.1085.0 | Futuremark | | Dependency von 3DMark — wird mit installiert |
| Hearthstone | | Blizzard Entertainment | | Game — Reinstall ueber Battle.net/Steam/Epic Launcher (Einzelfrage betrifft nur den Launcher) |
| HELLDIVERS™ 2 | | Arrowhead Game Studios | | Game — Reinstall ueber Battle.net/Steam/Epic Launcher (Einzelfrage betrifft nur den Launcher) |
| HP EmailSMTP Plugin | 56.0.517.0 | HP | | HP Drucker-Plugin — kommt mit HP-Treiber/HP Smart |
| HP OCR | 1.0.1020.0 | HP Inc. | | HP Drucker-Plugin — kommt mit HP-Treiber/HP Smart |
| HP SFTP Plugin | 56.0.517.0 | HP Inc. | | HP Drucker-Plugin — kommt mit HP-Treiber/HP Smart |
| HP SharePoint Plugin | 56.0.517.0 | HP | | HP Drucker-Plugin — kommt mit HP-Treiber/HP Smart |
| Intel(R) Chipset Device Software | 10.1.19899.8597 | Intel(R) Corporation | | Intel Treiber/Engine — kommt mit Intel Chipset-Driver-Bundle |
| Intel(R) Chipset Device Software | 10.1.19899.8597 | Intel Corporation | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| Intel(R) Icls | 1.0.0.0 | Intel Corporation | | Intel Treiber/Engine — kommt mit Intel Chipset-Driver-Bundle |
| Intel(R) Management Engine Components | 1.0.0.0 | Intel Corporation | | Intel Treiber/Engine — kommt mit Intel Chipset-Driver-Bundle |
| Intel(R) Management Engine Components | 2425.6.26.0 | Intel Corporation | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| Intel(R) Management Engine Driver | 1.0.0.0 | Intel Corporation | | Intel Treiber/Engine — kommt mit Intel Chipset-Driver-Bundle |
| Intel(R) ME WMI Provider | 1.0.0.0 | Intel Corporation | | Intel Treiber/Engine — kommt mit Intel Chipset-Driver-Bundle |
| Intel(R) Serial IO | 30.100.2131.26 | Intel Corporation | | Intel Treiber/Engine — kommt mit Intel Chipset-Driver-Bundle |
| Intel(R) Serial IO | 30.100.2131.26 | Intel Corporation | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| Intel(R) Wireless Bluetooth(R) | 23.40.0.2 | Intel Corporation | | Intel Treiber/Engine — kommt mit Intel Chipset-Driver-Bundle |
| Intel® Software Installer | 23.40.0.4 | Intel Corporation | | Intel Treiber/Engine — kommt mit Intel Chipset-Driver-Bundle |
| Launcher Prerequisites (x64) | 1.0.0.0 | Epic Games, Inc. | | Epic Games Prerequisite — kommt mit Epic Games Launcher |
| MaxCut | 2.9.3.9 | MaxCut Software (Pty) Ltd | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| Microsoft Edge | 147.0.3912.98 | Microsoft Corporation | | Microsoft Edge / WebView2 — Bestandteil von Windows 11 |
| Microsoft Edge WebView2-Laufzeit | 147.0.3912.98 | Microsoft Corporation | | Microsoft Edge / WebView2 — Bestandteil von Windows 11 |
| Microsoft Teams Meeting Add-in for Microsoft Office | 1.26.08901 | Microsoft | | Office-Add-in — kommt mit Microsoft 365 / Teams |
| Microsoft Update Health Tools | 5.72.0.0 | Microsoft Corporation | | Windows-Component — Windows Update |
| Microsoft Visual C++ 2008 Redistributable - x64 9.0.30729.6161 | 9.0.30729.6161 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2008 Redistributable - x86 9.0.30729.6161 | 9.0.30729.6161 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2010 x86 Redistributable - 10.0.30319 | 10.0.30319 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2012 Redistributable (x64) - 11.0.61030 | 11.0.61030.0 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2012 Redistributable (x86) - 11.0.61030 | 11.0.61030.0 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2012 x64 Additional Runtime - 11.0.61030 | 11.0.61030 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2012 x64 Minimum Runtime - 11.0.61030 | 11.0.61030 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2012 x86 Additional Runtime - 11.0.61030 | 11.0.61030 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2012 x86 Minimum Runtime - 11.0.61030 | 11.0.61030 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2013 Redistributable (x64) - 12.0.30501 | 12.0.30501.0 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2013 Redistributable (x86) - 12.0.30501 | 12.0.30501.0 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2013 x64 Additional Runtime - 12.0.21005 | 12.0.21005 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2013 x64 Minimum Runtime - 12.0.21005 | 12.0.21005 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2013 x86 Additional Runtime - 12.0.21005 | 12.0.21005 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2013 x86 Minimum Runtime - 12.0.21005 | 12.0.21005 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2015-2022 Redistributable (x64) - 14.44.35211 | 14.44.35211.0 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2015-2022 Redistributable (x86) - 14.44.35211 | 14.44.35211.0 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2022 X64 Additional Runtime - 14.44.35211 | 14.44.35211 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2022 X64 Minimum Runtime - 14.44.35211 | 14.44.35211 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2022 X86 Additional Runtime - 14.44.35211 | 14.44.35211 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| Microsoft Visual C++ 2022 X86 Minimum Runtime - 14.44.35211 | 14.44.35211 | Microsoft Corporation | | VC++ Redistributable/Runtime — Dependency, wird mit Apps gezogen |
| MSI Center SDK | 3.2026.0123.01 | MSI | | MSI Komponente — wird mit MSI Center nachgezogen |
| NvCpl | 1.0 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA AIUser Container | 1.48 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA Backend | 11.0.7.237 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA Container | 1.48 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA FrameView SDK 1.7.12227.37421622 | 1.7.12227.37421622 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA Grafiktreiber 596.21 | 596.21 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA HD-Audiotreiber 1.4.5.7 | 1.4.5.7 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA Install Application | 2.1002.442.0 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA LocalSystem Container | 1.48 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA MessageBus 3 for NvApp | 3.21 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA NvDLISR | 1.0 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA PhysX-Systemsoftware 9.23.1019 | 9.23.1019 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA Session Container | 1.48 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA ShadowPlay 11.0.7.0 | 11.0.7.0 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA Telemetry Client | 19.5.13.0 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA User Container | 1.48 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA Virtual Audio 4.65.0.12 | 4.65.0.12 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| NVIDIA Watchdog Plugin for NvContainer | 1.48 | NVIDIA Corporation | | NVIDIA Driver-Komponente — wird mit dem NVIDIA-App-/Treiber-Bundle installiert |
| Office 16 Click-to-Run Extensibility Component | 16.0.19929.20136 | Microsoft Corporation | | Office-Komponente — kommt mit Microsoft 365 |
| Office 16 Click-to-Run Localization Component | 16.0.19929.20062 | Microsoft Corporation | | Office-Komponente — kommt mit Microsoft 365 |
| Overwatch | | Blizzard Entertainment | | Game — Reinstall ueber Battle.net/Steam/Epic Launcher (Einzelfrage betrifft nur den Launcher) |
| PingPlotter 5 | 5.18.0.7997 | Pingman Tools, LLC | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| Plex Media Server | 1.40.3.8555 | Plex, Inc. | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| PUBG: BATTLEGROUNDS | | KRAFTON, Inc. | | Game — Reinstall ueber Battle.net/Steam/Epic Launcher (Einzelfrage betrifft nur den Launcher) |
| Python 3.13.3 Add to Path (64-bit) | 3.13.3150.0 | Python Software Foundation | | Sub-Komponente von Python 3.13 — wird mit dem Python-Hauptpaket nachgezogen |
| Python 3.13.3 Development Libraries (64-bit) | 3.13.3150.0 | Python Software Foundation | | Sub-Komponente von Python 3.13 — wird mit dem Python-Hauptpaket nachgezogen |
| Python 3.13.3 Documentation (64-bit) | 3.13.3150.0 | Python Software Foundation | | Sub-Komponente von Python 3.13 — wird mit dem Python-Hauptpaket nachgezogen |
| Python 3.13.3 Executables (64-bit) | 3.13.3150.0 | Python Software Foundation | | Sub-Komponente von Python 3.13 — wird mit dem Python-Hauptpaket nachgezogen |
| Python 3.13.3 pip Bootstrap (64-bit) | 3.13.3150.0 | Python Software Foundation | | Sub-Komponente von Python 3.13 — wird mit dem Python-Hauptpaket nachgezogen |
| Python 3.13.3 Standard Library (64-bit) | 3.13.3150.0 | Python Software Foundation | | Sub-Komponente von Python 3.13 — wird mit dem Python-Hauptpaket nachgezogen |
| Python 3.13.3 Tcl/Tk Support (64-bit) | 3.13.3150.0 | Python Software Foundation | | Sub-Komponente von Python 3.13 — wird mit dem Python-Hauptpaket nachgezogen |
| Python 3.13.3 Test Suite (64-bit) | 3.13.3150.0 | Python Software Foundation | | Sub-Komponente von Python 3.13 — wird mit dem Python-Hauptpaket nachgezogen |
| Realtek USB Audio | 6.4.0.2422 | Realtek Semiconductor Corp. | | Realtek Audio-Treiber — kommt mit Chipset-/Audio-Driver-Bundle |
| Stopping Plex | 1.40.3555 | Plex, Inc. | | Artefakt, kein echtes Programm |
| THX Spatial Audio USB 1532-0555 | 3.2.3.0 | THX | | THX Audio-Komponente — kommt mit Audio-/Headset-Treiber |
| THX Spatial Audio USB 1532-0555 | 3.2.3.0 | THX | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| THX V3 APO Presets | 3.2.11.0 | THX | | THX Audio-Komponente — kommt mit Audio-/Headset-Treiber |
| THX V3 APO Presets | 3.2.11.0 | THX | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| THX V3 APO Presets | 3.2.14.0 | THX | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| THX V3 APO Presets | 3.2.12.0 | THX | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| THX V3 APO Presets | 3.2.12.0 | THX | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| THX V3 APO Presets | 3.2.11.0 | THX | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| THX V3 APO Presets | 3.2.14.0 | THX | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| THX V3 APO Presets | 3.2.11.0 | THX | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| WD Desktop App 2.1.0.335 | 2.1.0.335 | Western Digital Corporation | | WD Desktop App (legacy) — durch WD Discovery abgeloest |
| WD Desktop App 2.1.0.335 (x64) | 2.1.0.335 | Western Digital Corporation | | WD Desktop App (legacy) — durch WD Discovery abgeloest |
| WD Drive Utilities | 2.1.0.142 | Western Digital Technologies, Inc. | | Duplikat (zweiter Registry-Hive-Eintrag desselben Programms, siehe oben) |
| WD SES Driver Setup | 2.1.0 | Western Digital | | Altes WD-Driver-Setup — wird durch aktuelle WD-Tools ersetzt |
| World of Warcraft | | Blizzard Entertainment | | Game — Reinstall ueber Battle.net/Steam/Epic Launcher (Einzelfrage betrifft nur den Launcher) |
| World of Warcraft Classic Era | | Blizzard Entertainment | | Game — Reinstall ueber Battle.net/Steam/Epic Launcher (Einzelfrage betrifft nur den Launcher) |
---
## Verifikations-Block
- Eintraege total: 161
- Auto-Ja: 18
- Einzelfrage: 38
- Auto-Skip: 105
- Quelle: 161 Eintraege in `installierte_programme.csv`
- Stimmt: 18 + 38 + 105 == 161 == 161
## Aenderungs-Workflow
Falls Micha vor der Neuinstallation noch Eintraege umsortieren will: direkt in diesem Markdown-File die Zeile zwischen den Sektionen verschieben und Begruendung anpassen. Diese Datei ist die Wahrheit fuer den Wiederaufbau-Schritt.
@@ -1,184 +0,0 @@
# Windows Image Backup Baseline - baerchen
Stand: 2026-06-05
Dieses Runbook beschreibt den Windows-Image-Backup-Workflow fuer den frisch
aufgesetzten Windows-11-Rechner `baerchen`. Ziel ist ein schneller Bare-Metal-
Restore von C: ueber Veeam Recovery Media und ein Image auf dem bestehenden
Unraid-SMB-Share `backups`.
## Zielbild
| Feld | Wert |
|---|---|
| Workstation | `baerchen` |
| OS | Windows 11 Pro, Build 26200 |
| Backup-Tool | Veeam Agent for Microsoft Windows |
| Installierte Version | `13.0.2.1102` |
| Backup-Art | Volume-level image backup |
| Gesicherte Volumes | C: plus EFI-/Recovery-Partitionen, keine Datenlaufwerke D:/G:/H: |
| Primaeres Ziel | `\\kallilabcore\backups\windows-images\baerchen` |
| Linux-Pfad auf Unraid | `/mnt/user/backups/windows-images/baerchen/` |
| Share-Modell | bestehender Unraid-Share `backups`, kein neuer Share |
| SMB-User | `micha` (bestehender Unraid-User mit Read/Write auf `backups`) |
| Veeam Job | `baerchen-c-image` |
| Verschluesselung | Stand erster Lauf: Veeam Storage Encryption **nicht aktiv** (`StorageEncryptionEnabled=False` im Job-Log); optional separat aktivieren und neues Full-Backup erzeugen |
| Recovery Media | USB-Stick `VEEAMRE` auf Laufwerk F: erstellt |
## Bewusste Entscheidungen
- Es wurde kein neuer Unraid-Share angelegt. Der Zielpfad lebt unter dem
bestehenden Share `backups`, weil dieser laut `docs/STORAGE_LAYOUT.md` fuer
lokale Backup-Daten vorgesehen ist.
- Es wurde vorerst kein dedizierter SMB-User `veeam-baerchen` angelegt, um
keine Unraid-Share-/User-Aenderung zu erzwingen. Der produktive Job nutzt
den bestehenden User `micha`.
- BitLocker wurde am 2026-06-05 nicht aktiviert. TPM, Secure Boot und WinRE
wurden geprueft; BitLocker bleibt ein separater Security-Schritt.
- Der Recovery-Stick ist Teil des Restore-Pfads und muss getrennt vom Rechner
aufbewahrt werden.
## Vorab-Pruefungen 2026-06-05
| Check | Ergebnis |
|---|---|
| SMB-Port `445` auf `kallilabcore` | erreichbar |
| SMB-Zielordner | angelegt |
| SMB-Schreibtest | erfolgreich, Testdatei wieder geloescht |
| Veeam Installation | erfolgreich via `winget` |
| Veeam Dienst | `VeeamEndpointBackupSvc` running |
| WinRE | Enabled |
| TPM | Present/Ready/Enabled/Activated/Owned |
| Secure Boot | True |
| BitLocker C: | FullyDecrypted, Protection Off |
## Backup-Job
Veeam Agent -> Job `baerchen-c-image`
- Backup Mode: `Volume level backup`
- Included items:
- Lokaler Datentraeger C:
- EFI System Partition
- Recovery-Partitionen
- Destination: `Shared folder`
- Shared folder: `\\kallilabcore\backups\windows-images\baerchen`
- Credentials: bestehender Unraid-SMB-User `micha`
- Compression: `Optimal`
- Storage encryption: Stand erster Lauf **nicht aktiv**
- Schedule: Workstation-Schedule in Veeam; Stand 2026-06-05: taeglich nachts
eingerichtet.
Wenn Veeam Storage Encryption spaeter aktiviert wird, ist das
Veeam-Job-Passwort nicht aus dem Repo wiederherstellbar. Es muss dann in
Vaultwarden als eigener Eintrag/Secure Note liegen und vor dem ersten
verschluesselten Full-Backup getestet werden.
## Secrets und Ablageorte
Keine Secret-Werte in dieses Repository schreiben.
| Secret | Ablage |
|---|---|
| Veeam Job Encryption Password | nur noetig, falls Veeam Storage Encryption aktiviert wird; Ziel: Vaultwarden Secure Note `Veeam baerchen backup encryption password` |
| SMB Credential fuer Backup-Ziel | bestehender Unraid/Vaultwarden-Eintrag fuer User `micha` |
| BitLocker Recovery Key | noch nicht aktiv; Ziel bei Aktivierung: `D:\30_Finanzen\BitLocker-RecoveryKey-baerchen-<DATUM>.txt`, Vaultwarden Secure Note, physischer Ausdruck |
## Recovery Media
Der USB-Stick wurde mit Veeam Recovery Media erstellt.
Erwarteter Zustand:
- Laufwerk beim Erstellen: F:
- Label: `VEEAMRE`
- Dateisystem: FAT32
- Boot-Dateien vorhanden: `boot/`, `efi/`, `sources/`, `bootmgr`,
`bootmgr.efi`, `BMRAnswerFile.xml`
Aufbewahrung: Stick beschriften mit `baerchen Veeam Recovery - 2026-06-05`
und nicht als Alltags-USB verwenden.
## Restore-Prozedur
1. Wenn C: defekt oder leer ist, `baerchen` ausschalten.
2. Veeam-Recovery-USB `VEEAMRE` einstecken.
3. Rechner vom USB-Stick booten.
4. In der Veeam Recovery Environment Netzwerk pruefen.
5. Shared Folder als Backup-Quelle oeffnen:
`\\kallilabcore\backups\windows-images\baerchen`
6. Mit SMB-User `micha` authentifizieren.
7. Veeam-Backup auswaehlen.
8. Falls der Restore Point verschluesselt ist: Veeam-Job-Encryption-Passwort
aus Vaultwarden eingeben. Der erste Full-Lauf vom 2026-06-05 ist laut Log
nicht verschluesselt.
9. Bare Metal Recovery starten und Ziel-Disk fuer Windows C: auswaehlen.
10. Vor dem finalen Restore pruefen, dass nicht D:/G:/H: als Ziel ausgewaehlt
sind.
11. Restore ausfuehren.
12. Nach erstem Boot:
- Windows startet
- Veeam Agent startet
- Laufwerksbuchstaben C:/D:/G: plausibel
- Netzwerk/DNS/Tailscale plausibel
- `reagentc /info` zeigt WinRE Enabled
## Restore-Test ohne echten Restore
Der Test wurde am 2026-06-06 erfolgreich abgeschlossen
(Operator-Bestaetigung):
1. Vom USB-Stick booten.
2. Veeam Recovery Environment starten.
3. Netzwerk pruefen.
4. SMB-Ziel `\\kallilabcore\backups\windows-images\baerchen` mounten.
5. Restore Point anzeigen lassen.
6. Wenn der Restore Point verschluesselt ist: Veeam-Encryption-Passwort testen.
7. Vor Auswahl eines echten Ziel-Datentraegers abbrechen.
8. Windows normal von der internen SSD booten.
Ergebnis: Recovery-Umgebung bootet, SMB-Ziel
`\\kallilabcore\backups\windows-images\baerchen` ist erreichbar, Restore Point
wird angezeigt, und der Test wurde vor einem echten Restore abgebrochen.
## Erster Full-Lauf 2026-06-05
Der erste manuelle Full-Lauf wurde am 2026-06-05 gestartet.
Beleg aus `C:\ProgramData\Veeam\Endpoint\baerchen-c-image\Job.baerchen-c-image.Backup.log`:
- Start: 2026-06-05 19:46:01
- Ziel-Datei: `\\kallilabcore\backups\windows-images\baerchen\baerchen-c-image\baerchen-c-image2026-06-05T194605.vbk`
- Veeam-Storage-Statistik: `BackupSize 57801080832` Bytes; Veeam-GUI zeigt
`Total backup size: 53,8 GB`
- Veeam-GUI: Restore Point Size `53,8 GB`, Backup duration `0:11:31`,
`Full backup created`
- MetaChecker: `0 errors`, `0 warnings`
- VSS Finalisierung: `job: success`
- Repository-Speicher danach: ca. 5.99 TB total, ca. 3.99 TB frei
- Job-Konfiguration im Log: `StorageEncryptionEnabled=False`
Damit ist der erste Image-Lauf technisch erfolgreich geschrieben und der
Recovery-USB-/SMB-/Restore-Point-Test wurde am 2026-06-06 erfolgreich
abgeschlossen.
Hilfsskript fuer die Windows-Seite:
```powershell
.\ops\windows-reinstall\check-veeam-baerchen.ps1
```
Wenn der SMB-Pfad in der normalen PowerShell nicht erreichbar ist, aber Veeam
den Job erfolgreich schreibt, liegt das meist an getrennten Credentials:
Veeam nutzt gespeicherte Job-Credentials, waehrend die interaktive Windows-
Sitzung zusaetzlich per `net use` authentifiziert werden muss.
## Offene Punkte
- Entscheiden, ob Veeam Storage Encryption nachtraeglich aktiviert werden soll.
Wenn ja: Passwort in Vaultwarden anlegen, Job umstellen und ein neues Full-
Backup erzeugen.
- Optional: BitLocker C: separat aktivieren und Recovery-Key an den drei
vorgesehenen Orten sichern.
- Optional: spaeter dedizierten SMB-User `veeam-baerchen` anlegen, falls die
Unraid-User-/Share-Policy wieder angefasst wird.
@@ -43,19 +43,12 @@ Erledigt:
Noch offen: Noch offen:
- Manuelle Screenshots in `H:\Windows-Neuaufsetzen-Backup\14_Screenshots` ablegen. - Manuelle Screenshots in `H:\Windows-Neuaufsetzen-Backup\14_Screenshots` ablegen.
- BitLocker-Status mit Adminrechten pruefen. **Nachlauf 2026-06-05:** Status - BitLocker-Status mit Adminrechten pruefen.
wurde geprueft; C:/D:/E:/G:/H: sind `FullyDecrypted`, Protection `Off`. - Passwortmanager, 2FA-Recovery-Codes und Browser-Sync manuell pruefen.
Offen bleibt nur die bewusste BitLocker-Entscheidung. - Banking4-Speicherort explizit pruefen.
- Passwortmanager, 2FA-Recovery-Codes und Browser-Sync manuell 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.
- Banking4-Speicherort explizit pruefen. **Erledigt 2026-06-06 laut Operator-Bestaetigung.** - WISO Steuer 2026 oeffnen und Lizenz/Buhl-Konto sowie Speicherorte der Steuerdateien bestaetigen.
- 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.** - Microsoft-Konto fuer M365 pruefen: Office-Webkonto/Abonnement, Installationsrecht, OneDrive-Sync.
- 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").
- Optional Keyfinder-Lauf durchfuehren und Ergebnisse lokal auf H: speichern. - Optional Keyfinder-Lauf durchfuehren und Ergebnisse lokal auf H: speichern.
- `G:\Ollama` bewusst entscheiden: nicht gesichert, ca. 40,9 GB lokale Modell-/Cache-Daten. - `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. - D:, F: und G: vor dem spaeteren Loeschen noch einmal in Ruhe final bestaetigen.
@@ -535,22 +528,16 @@ Zielstruktur:
## Finale Checkliste vor dem Löschen ## Finale Checkliste vor dem Löschen
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").
- [ ] Backup-Struktur auf H: erstellt - [ ] Backup-Struktur auf H: erstellt
- [ ] Programmliste exportiert - [ ] Programmliste exportiert
- [ ] Laufwerksliste exportiert - [ ] Laufwerksliste exportiert
- [ ] Windows-Aktivierung dokumentiert - [ ] Windows-Aktivierung dokumentiert
- [ ] Benutzerordner gesichert - [ ] Benutzerordner gesichert
- [ ] Browser-Lesezeichen exportiert oder Sync geprüft - [ ] Browser-Lesezeichen exportiert oder Sync geprüft
- [x] Passwortmanager geprüft - [ ] Passwortmanager geprüft
- [x] 2FA-Recovery-Codes gesichert - [ ] 2FA-Recovery-Codes gesichert
- [ ] SSH/API/GPG/Zertifikate gesichert - [ ] SSH/API/GPG/Zertifikate gesichert
- [x] Banking4-Speicherort geprüft und gesichert - [ ] Banking4-Speicherort geprüft und gesichert
- [ ] Homelab-/NAS-Doku gesichert - [ ] Homelab-/NAS-Doku gesichert
- [ ] D:, F: und G: analysiert - [ ] D:, F: und G: analysiert
- [ ] Unklare Daten nach `99_Unsortiert_von_D_F_G` kopiert - [ ] Unklare Daten nach `99_Unsortiert_von_D_F_G` kopiert
+1 -15
View File
@@ -38,19 +38,6 @@
"automerge": false, "automerge": false,
"labels": ["dependencies", "minor-patch"] "labels": ["dependencies", "minor-patch"]
}, },
{
"description": "Kritische Kerninfra (Traefik=Public-Entrypoint, Unbound=DNS, n8n, Nextcloud): nicht im Sammel-PR, eigene einzeln reviewbare PRs, kein Auto-Merge",
"matchManagers": ["docker-compose", "dockerfile"],
"matchPackageNames": [
"traefik",
"shaanmajid/unbound",
"docker.n8n.io/n8nio/n8n",
"nextcloud"
],
"groupName": null,
"automerge": false,
"labels": ["dependencies", "core-critical"]
},
{ {
"description": "Stateful Tier-1 (Postgres, Mongo, Redis): keine Auto-Group, einzelne PRs, kein Auto-Merge", "description": "Stateful Tier-1 (Postgres, Mongo, Redis): keine Auto-Group, einzelne PRs, kein Auto-Merge",
"matchPackageNames": [ "matchPackageNames": [
@@ -112,7 +99,6 @@
"ignorePaths": [ "ignorePaths": [
"**/_archive/**", "**/_archive/**",
"ops/grafana-influxdb/**", "ops/grafana-influxdb/**",
"ops/loki/**", "ops/loki/**"
"ops/komodo/**"
] ]
} }
+5 -8
View File
@@ -41,21 +41,18 @@ access_control:
- git.kaleschke.info - git.kaleschke.info
policy: bypass policy: bypass
# Expliziter 2FA-Block (Tier-1-Operator-UIs). Redundant, seit die Catch-all # Admin-Dienste - 2FA erforderlich (Operator-UIs mit Host-/Backup-Zugriff)
# unten ohnehin alles auf two_factor zwingt - bleibt nur zur Lesbarkeit.
# borg/code brauchen hier keinen Eintrag mehr: sie sind via Catch-all 2FA.
- domain: - domain:
- files.kaleschke.info - files.kaleschke.info
- scrutiny.kaleschke.info - scrutiny.kaleschke.info
- borg.kaleschke.info
- code.kaleschke.info
policy: two_factor policy: two_factor
# Alles andere mit Authelia-Middleware - 2FA (Haertung 2026-06-06). # Alles andere mit Authelia-Middleware - 1FA.
# Vorher one_factor; alle restlichen Admin-UIs (monitoring, glances, glance,
# speedtest, pdf, mail, paperless-gpt, hermes, sp, borg, code ...) sind damit two_factor.
# Voraussetzung: Operator-Account hat Authelia-TOTP enrolled (Tier-1-UIs nutzen es bereits).
# Komodo hat bewusst keine ForwardAuth-Middleware und wird hier nicht ausgewertet. # Komodo hat bewusst keine ForwardAuth-Middleware und wird hier nicht ausgewertet.
- domain: "*.kaleschke.info" - domain: "*.kaleschke.info"
policy: two_factor policy: one_factor
session: session:
name: authelia_session name: authelia_session
+19 -126
View File
@@ -11,7 +11,6 @@ SINCE="${SINCE:-24h}"
MAX_LOG_LINES="${MAX_LOG_LINES:-80}" MAX_LOG_LINES="${MAX_LOG_LINES:-80}"
CERT_MAX_ROWS="${CERT_MAX_ROWS:-12}" CERT_MAX_ROWS="${CERT_MAX_ROWS:-12}"
IMAGE_AGE_WARN_DAYS="${IMAGE_AGE_WARN_DAYS:-180}" IMAGE_AGE_WARN_DAYS="${IMAGE_AGE_WARN_DAYS:-180}"
IMAGE_AGE_ALLOW_FILE="${IMAGE_AGE_ALLOW_FILE:-/mnt/user/services/homelab-infra/services/posture-check/image-age-allow.patterns}"
LOG_VOLUME_TOP_N="${LOG_VOLUME_TOP_N:-10}" LOG_VOLUME_TOP_N="${LOG_VOLUME_TOP_N:-10}"
DISK_USAGE_WARN_PCT="${DISK_USAGE_WARN_PCT:-85}" DISK_USAGE_WARN_PCT="${DISK_USAGE_WARN_PCT:-85}"
CERT_WARN_DAYS="${CERT_WARN_DAYS:-21}" CERT_WARN_DAYS="${CERT_WARN_DAYS:-21}"
@@ -29,7 +28,6 @@ TRAEFIK_ACME_PATH="${TRAEFIK_ACME_PATH:-/mnt/user/appdata/traefik/letsencrypt/ac
NOISE_PATTERNS_FILE="${NOISE_PATTERNS_FILE:-/mnt/user/services/homelab-infra/services/posture-check/log-noise.patterns}" NOISE_PATTERNS_FILE="${NOISE_PATTERNS_FILE:-/mnt/user/services/homelab-infra/services/posture-check/log-noise.patterns}"
NORMALIZE_NOISE_SCRIPT="${NORMALIZE_NOISE_SCRIPT:-/mnt/user/services/homelab-infra/services/posture-check/lib/normalize-noise-patterns.sh}" NORMALIZE_NOISE_SCRIPT="${NORMALIZE_NOISE_SCRIPT:-/mnt/user/services/homelab-infra/services/posture-check/lib/normalize-noise-patterns.sh}"
NOISE_ESCALATION_THRESHOLD="${NOISE_ESCALATION_THRESHOLD:-500}" NOISE_ESCALATION_THRESHOLD="${NOISE_ESCALATION_THRESHOLD:-500}"
NOISE_ESCALATION_EXEMPT_FILE="${NOISE_ESCALATION_EXEMPT_FILE:-/mnt/user/services/homelab-infra/services/posture-check/noise-escalation-exempt.patterns}"
NOISE_BREAKDOWN_TOP_N="${NOISE_BREAKDOWN_TOP_N:-10}" NOISE_BREAKDOWN_TOP_N="${NOISE_BREAKDOWN_TOP_N:-10}"
POSTURE_CHECK_FILE="${POSTURE_CHECK_FILE:-/mnt/user/services/posture-check/last.json}" POSTURE_CHECK_FILE="${POSTURE_CHECK_FILE:-/mnt/user/services/posture-check/last.json}"
LOCK_FILE="${LOCK_FILE:-/tmp/homelab-daily-report.lock}" LOCK_FILE="${LOCK_FILE:-/tmp/homelab-daily-report.lock}"
@@ -461,10 +459,6 @@ with open("/acme.json", "r", encoding="utf-8") as handle:
data = json.load(handle) data = json.load(handle)
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
# Deduplicate: for each unique set of domains keep only the longest-lived cert.
# Traefik stores both the old and the newly-issued cert in acme.json during
# the renewal window, which would otherwise produce a false warning.
best = {} # frozenset(domains) -> (days, expire_date_iso, names)
for resolver in data.values(): for resolver in data.values():
for cert in resolver.get("Certificates", []): for cert in resolver.get("Certificates", []):
domain = cert.get("domain", {}).get("main") or "-" domain = cert.get("domain", {}).get("main") or "-"
@@ -480,11 +474,7 @@ for resolver in data.values():
not_after = datetime.strptime(decoded["notAfter"], "%b %d %H:%M:%S %Y %Z").replace(tzinfo=timezone.utc) not_after = datetime.strptime(decoded["notAfter"], "%b %d %H:%M:%S %Y %Z").replace(tzinfo=timezone.utc)
days = (not_after - now).days days = (not_after - now).days
names = ", ".join([domain, *sans]) names = ", ".join([domain, *sans])
key = frozenset([domain, *sans]) print(f"{days}\t{not_after.date().isoformat()}\t{names}")
if key not in best or days > best[key][0]:
best[key] = (days, not_after.date().isoformat(), names)
for days, expires, names in best.values():
print(f"{days}\t{expires}\t{names}")
PY PY
then then
if [ ! -s "$cert_file" ]; then if [ ! -s "$cert_file" ]; then
@@ -583,36 +573,13 @@ collect_image_freshness() {
local image_file="$TMP_DIR/images.tsv" local image_file="$TMP_DIR/images.tsv"
local image_warnings=0 local image_warnings=0
local image_allowed=0
local now_epoch local now_epoch
: > "$image_file" : > "$image_file"
now_epoch="$(date +%s)" now_epoch="$(date +%s)"
# Parse the image-age allowlist: container deliberately pinned to a stable or
# upstream-recommended image. Each entry carries a recheck date; once that
# date has passed the suppression lapses, so a pin gets re-reviewed instead
# of silently aging forever.
local allow_file="$TMP_DIR/image-allow.tsv"
: > "$allow_file"
if [ -f "$IMAGE_AGE_ALLOW_FILE" ]; then
while IFS= read -r line; do
line="${line%%#*}"
line="$(printf '%s' "$line" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')"
[ -n "$line" ] || continue
local a_name a_date a_epoch
a_name="$(printf '%s' "$line" | awk '{ print $1 }')"
a_date="$(printf '%s' "$line" | awk '{ print $2 }')"
[ -n "$a_name" ] && [ -n "$a_date" ] || continue
a_epoch="$(date -d "$a_date" +%s 2>/dev/null || echo 0)"
if [ "$a_epoch" -ge "$now_epoch" ]; then
printf '%s\t%s\n' "$a_name" "$a_date" >> "$allow_file"
fi
done < "$IMAGE_AGE_ALLOW_FILE"
fi
while IFS= read -r name; do while IFS= read -r name; do
[ -n "$name" ] || continue [ -n "$name" ] || continue
local image_id created_iso created_epoch age_days image_tag note recheck local image_id created_iso created_epoch age_days image_tag
image_id="$(docker inspect --format '{{.Image}}' "$name" 2>/dev/null || true)" image_id="$(docker inspect --format '{{.Image}}' "$name" 2>/dev/null || true)"
[ -n "$image_id" ] || continue [ -n "$image_id" ] || continue
created_iso="$(docker image inspect --format '{{.Created}}' "$image_id" 2>/dev/null || true)" created_iso="$(docker image inspect --format '{{.Created}}' "$image_id" 2>/dev/null || true)"
@@ -621,46 +588,33 @@ collect_image_freshness() {
created_epoch="$(date -d "$created_iso" +%s 2>/dev/null || echo 0)" created_epoch="$(date -d "$created_iso" +%s 2>/dev/null || echo 0)"
[ "$created_epoch" -gt 0 ] || continue [ "$created_epoch" -gt 0 ] || continue
age_days=$(( (now_epoch - created_epoch) / 86400 )) age_days=$(( (now_epoch - created_epoch) / 86400 ))
note="" printf '%d\t%s\t%s\n' "$age_days" "$name" "$image_tag" >> "$image_file"
if [ "$age_days" -ge "$IMAGE_AGE_WARN_DAYS" ]; then if [ "$age_days" -ge "$IMAGE_AGE_WARN_DAYS" ]; then
recheck="$(awk -F '\t' -v n="$name" '$1 == n { print $2; found = 1 } END { exit !found }' "$allow_file" || true)" image_warnings=$((image_warnings + 1))
if [ -n "$recheck" ]; then
note="bewusst gepinnt (recheck $recheck)"
image_allowed=$((image_allowed + 1))
else
note="ueberaltert"
image_warnings=$((image_warnings + 1))
fi
fi fi
printf '%d\t%s\t%s\t%s\n' "$age_days" "$name" "$image_tag" "$note" >> "$image_file"
done < <(docker ps --format '{{.Names}}') done < <(docker ps --format '{{.Names}}')
set_summary "image_warnings" "$image_warnings" set_summary "image_warnings" "$image_warnings"
set_summary "image_allowed" "$image_allowed"
if [ ! -s "$image_file" ]; then if [ ! -s "$image_file" ]; then
append "- Keine Image-Daten verfuegbar." append "- Keine Image-Daten verfuegbar."
record_section_error "images" "Keine Image-Daten ermittelt" record_section_error "images" "Keine Image-Daten ermittelt"
else else
append "- Schwelle Warnung: Image aelter als $IMAGE_AGE_WARN_DAYS Tage" append "- Schwelle Warnung: Image aelter als $IMAGE_AGE_WARN_DAYS Tage"
append "- Container mit ueberaltertem Image (gewarnt): $image_warnings" append "- Container mit Image >= $IMAGE_AGE_WARN_DAYS Tage: $image_warnings"
append "- Davon bewusst gepinnt (von Warnung ausgenommen): $image_allowed"
append "- Allowlist-Quelle: \`$IMAGE_AGE_ALLOW_FILE\`"
append "" append ""
append "### Aelteste Images (Top 10)" append "### Aelteste Images (Top 10)"
append "" append ""
append "| Alter Tage | Container | Image | Hinweis |" append "| Alter Tage | Container | Image |"
append "|---:|---|---|---|" append "|---:|---|---|"
sort -nr "$image_file" | head -n 10 | while IFS="$(printf '\t')" read -r age name img note; do sort -nr "$image_file" | head -n 10 | while IFS="$(printf '\t')" read -r age name img; do
append "| $age | $name | $img | ${note:-} |" append "| $age | $name | $img |"
done done
append "" append ""
if [ "$image_warnings" -eq 0 ] && [ "$image_allowed" -eq 0 ]; then if [ "$image_warnings" -eq 0 ]; then
append "Bewertung: Keine Container mit ueberalterten Images. CVE-Hygiene aus dieser Sicht ok." append "Bewertung: Keine Container mit ueberalterten Images. CVE-Hygiene aus dieser Sicht ok."
elif [ "$image_warnings" -eq 0 ]; then
append "Bewertung: Keine ungeprueft ueberalterten Images. $image_allowed Container sind bewusst gepinnt und mit Recheck-Datum dokumentiert."
else else
append "Bewertung: $image_warnings Container nutzen ueberalterte Images (nicht in der Allowlist). Update-Pipeline und CVE-Status pruefen." append "Bewertung: $image_warnings Container nutzen Images aelter als $IMAGE_AGE_WARN_DAYS Tage. Update-Pipeline und CVE-Status pruefen."
fi fi
fi fi
append "" append ""
@@ -701,31 +655,6 @@ collect_container_events() {
collect_container_state() { collect_container_state() {
append "## Container-Zustand" append "## Container-Zustand"
append "" append ""
append "### Unhealthy Container"
local unhealthy_file="$TMP_DIR/unhealthy.log"
docker ps --filter health=unhealthy --format '{{.Names}}' > "$unhealthy_file"
if [ ! -s "$unhealthy_file" ]; then
append "- Keine."
else
append "| Container | FailingStreak | Letzter Healthcheck |"
append "|---|---:|---|"
while IFS= read -r name; do
[ -n "$name" ] || continue
local streak hc
streak="$(docker inspect "$name" --format '{{.State.Health.FailingStreak}}' 2>/dev/null || echo '?')"
# Letzten nicht-leeren Health-Log-Eintrag holen, einzeilig machen und
# Pipe-Zeichen escapen, damit die Markdown-Tabelle nicht bricht.
hc="$(docker inspect "$name" --format '{{range .State.Health.Log}}exit={{.ExitCode}} out={{.Output}}~~~{{end}}' 2>/dev/null \
| tr '\n' ' ' \
| awk -F '~~~' '{ for (i = NF - 1; i >= 1; i--) { if ($i != "") { print $i; break } } }' \
| sed -E 's/[[:space:]]+/ /g; s/\|/\\|/g' \
| cut -c1-160)"
append "| \`$name\` | ${streak:-?} | ${hc:-(kein Output)} |"
done < "$unhealthy_file"
fi
append ""
append "### Nicht laufende Container" append "### Nicht laufende Container"
local stopped_file="$TMP_DIR/stopped.log" local stopped_file="$TMP_DIR/stopped.log"
docker ps -a --filter status=exited --filter status=dead --filter status=created --format '{{.Names}}\t{{.Status}}' > "$stopped_file" docker ps -a --filter status=exited --filter status=dead --filter status=created --format '{{.Names}}\t{{.Status}}' > "$stopped_file"
@@ -881,35 +810,12 @@ collect_log_highlights() {
fi fi
fi fi
# Escalation-exempt patterns: known noise that is also permanently very loud # Threshold escalation: how many patterns produced more than the threshold?
# (e.g. Unraid mdadm parse spam). Without this, such a pattern would keep the local noise_threshold_exceeded=0
# report stuck at >= WARNUNG forever and devalue the OK/WARNUNG/KRITISCH
# signal. Exempt patterns are still counted/shown as noise, but do NOT count
# toward noise_threshold_exceeded. New/unexpected loud patterns still escalate.
local noise_exempt="$TMP_DIR/noise-escalation-exempt.normalized"
: > "$noise_exempt"
if [ -f "$NOISE_ESCALATION_EXEMPT_FILE" ]; then
grep -Ev '^[[:space:]]*(#|$)' "$NOISE_ESCALATION_EXEMPT_FILE" 2>/dev/null \
| sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//' \
| grep -v '^$' > "$noise_exempt" || : > "$noise_exempt"
fi
# Threshold escalation: how many NON-exempt patterns exceeded the threshold?
local noise_threshold_exceeded=0 noise_threshold_exempt=0
if [ -s "$noise_by_pattern" ]; then if [ -s "$noise_by_pattern" ]; then
noise_threshold_exceeded="$(awk -F '\t' -v t="$NOISE_ESCALATION_THRESHOLD" ' noise_threshold_exceeded="$(awk -v t="$NOISE_ESCALATION_THRESHOLD" '$1 > t { n++ } END { print n + 0 }' "$noise_by_pattern")"
NR == FNR { exempt[$0] = 1; next }
$1 > t && !($2 in exempt) { n++ }
END { print n + 0 }
' "$noise_exempt" "$noise_by_pattern")"
noise_threshold_exempt="$(awk -F '\t' -v t="$NOISE_ESCALATION_THRESHOLD" '
NR == FNR { exempt[$0] = 1; next }
$1 > t && ($2 in exempt) { n++ }
END { print n + 0 }
' "$noise_exempt" "$noise_by_pattern")"
fi fi
set_summary "noise_threshold_exceeded" "$noise_threshold_exceeded" set_summary "noise_threshold_exceeded" "$noise_threshold_exceeded"
set_summary "noise_threshold_exempt" "$noise_threshold_exempt"
local hit_count attention_count known_noise_count local hit_count attention_count known_noise_count
hit_count="$(count_lines < "$hits")" hit_count="$(count_lines < "$hits")"
@@ -930,9 +836,6 @@ collect_log_highlights() {
if [ "$noise_threshold_exceeded" -gt 0 ]; then if [ "$noise_threshold_exceeded" -gt 0 ]; then
append "- WARNUNG: $noise_threshold_exceeded Pattern ueberschreit(en) die Schwelle - bitte pruefen ob noch wirklich Noise." append "- WARNUNG: $noise_threshold_exceeded Pattern ueberschreit(en) die Schwelle - bitte pruefen ob noch wirklich Noise."
fi fi
if [ "${noise_threshold_exempt:-0}" -gt 0 ]; then
append "- Hinweis: $noise_threshold_exempt laute(s) Pattern ist/sind als bewusst eskalations-befreit markiert (siehe \`$NOISE_ESCALATION_EXEMPT_FILE\`) und loesen keine WARNUNG aus."
fi
append "" append ""
if [ "$attention_count" -eq 0 ]; then if [ "$attention_count" -eq 0 ]; then
@@ -982,32 +885,22 @@ collect_log_highlights() {
if [ -s "$noise_by_pattern" ]; then if [ -s "$noise_by_pattern" ]; then
append "#### Pattern mit den meisten Treffern" append "#### Pattern mit den meisten Treffern"
append "" append ""
append "| Pattern | Anzahl | Hinweis |" append "| Pattern | Anzahl |"
append "|---|---:|---|" append "|---|---:|"
head -n "$NOISE_BREAKDOWN_TOP_N" "$noise_by_pattern" \ head -n "$NOISE_BREAKDOWN_TOP_N" "$noise_by_pattern" \
| while IFS="$(printf '\t')" read -r cnt pat; do | while IFS="$(printf '\t')" read -r cnt pat; do
local short="$pat" note="" local short="$pat"
# Mark patterns that are deliberately exempt from escalation.
if [ -s "$noise_exempt" ] && grep -Fxq -- "$pat" "$noise_exempt"; then
if [ "$cnt" -gt "$NOISE_ESCALATION_THRESHOLD" ]; then
note="eskalations-befreit"
fi
elif [ "$cnt" -gt "$NOISE_ESCALATION_THRESHOLD" ]; then
note="ueber Schwelle"
fi
if [ "${#short}" -gt 80 ]; then if [ "${#short}" -gt 80 ]; then
short="${short:0:77}..." short="${short:0:77}..."
fi fi
# Escape pipe characters that would break the markdown table. # Escape pipe characters that would break the markdown table.
short="${short//|/\\|}" short="${short//|/\\|}"
append "| \`$short\` | $cnt | $note |" append "| \`$short\` | $cnt |"
done done
append "" append ""
fi fi
if [ "$noise_threshold_exceeded" -gt 0 ]; then if [ "$noise_threshold_exceeded" -gt 0 ]; then
append "Bewertung: $noise_threshold_exceeded nicht-befreite(s) Pattern ueberschreit(en) die Eskalations-Schwelle ($NOISE_ESCALATION_THRESHOLD). Bitte pruefen, ob die als Noise eingeordneten Meldungen noch fachlich Noise sind oder ob sich ein echter Vorfall darunter versteckt." append "Bewertung: $noise_threshold_exceeded Pattern ueberschreit(en) die Eskalations-Schwelle ($NOISE_ESCALATION_THRESHOLD). Bitte pruefen, ob die als Noise eingeordneten Meldungen noch fachlich Noise sind oder ob sich ein echter Vorfall darunter versteckt."
elif [ "${noise_threshold_exempt:-0}" -gt 0 ]; then
append "Bewertung: Kein nicht-befreites Pattern ueberschreitet die Eskalations-Schwelle ($NOISE_ESCALATION_THRESHOLD). $noise_threshold_exempt lautes Pattern ist bewusst eskalations-befreit und mit Begruendung dokumentiert."
else else
append "Bewertung: Kein Pattern ueberschreitet die Eskalations-Schwelle ($NOISE_ESCALATION_THRESHOLD)." append "Bewertung: Kein Pattern ueberschreitet die Eskalations-Schwelle ($NOISE_ESCALATION_THRESHOLD)."
fi fi
@@ -1,142 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_DIR="${BASE_DIR:-/mnt/user/services/posture-check}"
WATCHER_SCRIPT="${WATCHER_SCRIPT:-/mnt/user/services/homelab-infra/services/posture-check/docker-critical-events.sh}"
PID_FILE="${PID_FILE:-$BASE_DIR/docker-critical-events.pid}"
OUT_FILE="${OUT_FILE:-$BASE_DIR/docker-critical-events.out}"
EVENT_LOG="${EVENT_LOG:-$BASE_DIR/docker-critical-events-last.log}"
NTFY_SCRIPT="${NTFY_SCRIPT:-/mnt/user/services/homelab-infra/ops/restore-tests/send-ntfy.sh}"
NTFY_TOPIC="${NTFY_TOPIC:-homelab-alerts}"
usage() {
cat >&2 <<EOF
Usage: $0 start|stop|restart|status|smoke
start Start Docker critical-events watcher in the background.
stop Stop the watcher by pidfile.
restart Stop and start the watcher.
status Print watcher status and recent log tail.
smoke Send one ntfy test message through the same alert path.
EOF
}
is_running() {
[ -s "$PID_FILE" ] || return 1
local pid
pid="$(cat "$PID_FILE")"
[ -n "$pid" ] || return 1
kill -0 "$pid" >/dev/null 2>&1
}
start_watcher() {
mkdir -p "$BASE_DIR"
if is_running; then
echo "docker-critical-events watcher already running (pid $(cat "$PID_FILE"))"
return 0
fi
if [ ! -r "$WATCHER_SCRIPT" ]; then
echo "Watcher script not readable: $WATCHER_SCRIPT" >&2
return 1
fi
NTFY_SCRIPT="$NTFY_SCRIPT" \
NTFY_TOPIC="$NTFY_TOPIC" \
OUTPUT_PATH="$EVENT_LOG" \
nohup bash "$WATCHER_SCRIPT" >"$OUT_FILE" 2>&1 </dev/null &
echo "$!" > "$PID_FILE"
sleep 1
if is_running; then
echo "docker-critical-events watcher started (pid $(cat "$PID_FILE"))"
else
echo "docker-critical-events watcher failed to stay running; see $OUT_FILE" >&2
return 1
fi
}
stop_watcher() {
if ! is_running; then
rm -f "$PID_FILE"
echo "docker-critical-events watcher is not running"
return 0
fi
local pid
pid="$(cat "$PID_FILE")"
kill "$pid" >/dev/null 2>&1 || true
sleep 1
if kill -0 "$pid" >/dev/null 2>&1; then
echo "watcher still running after SIGTERM; sending SIGKILL"
kill -9 "$pid" >/dev/null 2>&1 || true
fi
rm -f "$PID_FILE"
echo "docker-critical-events watcher stopped"
}
status_watcher() {
if is_running; then
echo "status=running pid=$(cat "$PID_FILE")"
else
echo "status=stopped"
[ -e "$PID_FILE" ] && echo "stale_pidfile=$PID_FILE"
fi
echo "watcher_script=$WATCHER_SCRIPT"
echo "event_log=$EVENT_LOG"
echo "out_file=$OUT_FILE"
if [ -s "$EVENT_LOG" ]; then
echo
echo "Recent critical events:"
tail -n 20 "$EVENT_LOG"
fi
if [ -s "$OUT_FILE" ]; then
echo
echo "Recent watcher output:"
tail -n 20 "$OUT_FILE"
fi
}
smoke_ntfy() {
if [ ! -r "$NTFY_SCRIPT" ]; then
echo "ntfy helper not readable: $NTFY_SCRIPT" >&2
return 1
fi
bash "$NTFY_SCRIPT" \
"$NTFY_TOPIC" \
"Docker critical watcher smoke" \
"Smoke test from $(hostname) at $(date -Iseconds). No container was stopped." \
default
echo "smoke notification sent to $NTFY_TOPIC"
}
case "${1:-}" in
start)
start_watcher
;;
stop)
stop_watcher
;;
restart)
stop_watcher
start_watcher
;;
status)
status_watcher
;;
smoke)
smoke_ntfy
;;
*)
usage
exit 2
;;
esac
@@ -4,9 +4,7 @@ set -euo pipefail
TEXTFILE_DIR="${TEXTFILE_DIR:-/mnt/user/services/posture-check/textfile}" TEXTFILE_DIR="${TEXTFILE_DIR:-/mnt/user/services/posture-check/textfile}"
OUTPUT_FILE="${OUTPUT_FILE:-$TEXTFILE_DIR/homelab.prom}" OUTPUT_FILE="${OUTPUT_FILE:-$TEXTFILE_DIR/homelab.prom}"
BORG_CONTAINER="${BORG_CONTAINER:-borg-ui}" BORG_CONTAINER="${BORG_CONTAINER:-borg-ui}"
CRITICAL_CONTAINERS="${CRITICAL_CONTAINERS:-traefik authelia postgresql17 gitea komodo-core komodo-mongo komodo-periphery vaultwarden borg-ui ntfy adguard unbound monitoring-alertmanager monitoring-alertmanager-ntfy-bridge monitoring-blackbox-exporter monitoring-cadvisor monitoring-grafana monitoring-loki monitoring-node-exporter monitoring-promtail immich_server immich_postgres immich_redis paperless-ngx nextcloud nextcloud-postgres nextcloud-redis mealie mealie-postgres}" CRITICAL_CONTAINERS="${CRITICAL_CONTAINERS:-traefik authelia postgresql17 gitea komodo-core komodo-mongo komodo-periphery vaultwarden borg-ui ntfy adguard unbound Tailscale-Docker monitoring-alertmanager monitoring-alertmanager-ntfy-bridge monitoring-blackbox-exporter monitoring-cadvisor monitoring-grafana monitoring-loki monitoring-node-exporter monitoring-promtail immich_server immich_postgres immich_redis paperless-ngx nextcloud nextcloud-postgres nextcloud-redis mealie mealie-postgres}"
# Hinweis: Tailscale laeuft als natives Unraid-Plugin (kein Docker-Container) und
# wird daher hier bewusst NICHT als kritischer Container gefuehrt (Stand 2026-06-06).
mkdir -p "$TEXTFILE_DIR" mkdir -p "$TEXTFILE_DIR"
tmp="$(mktemp "$TEXTFILE_DIR/homelab.prom.XXXXXX")" tmp="$(mktemp "$TEXTFILE_DIR/homelab.prom.XXXXXX")"
@@ -1,30 +0,0 @@
# image-age-allow.patterns - Daily Operations Report
#
# Container, die bewusst auf einem aelteren, aber aktuellen/empfohlenen Image
# gepinnt sind, sollen nicht jeden Tag als "Image ueberaltert" warnen.
#
# Format pro Zeile:
# <container-name> <YYYY-MM-DD recheck> # Begruendung
#
# - Spalte 1: exakter Container-Name (docker ps {{.Names}}).
# - Spalte 2: Recheck-Datum. NACH diesem Datum greift die Ausnahme NICHT
# mehr und der Container taucht wieder als Warnung auf -> erzwingt eine
# menschliche Neubewertung statt stillen Alterns.
# - Alles nach '#' ist Kommentar. Leerzeilen werden ignoriert.
#
# Eine Ausnahme heisst NICHT "Image egal", sondern "am Datum X erneut pruefen,
# ob es noch die empfohlene/aktuelle Version ist".
#
# Last reviewed: 2026-06-10
# immich_postgres: exakt das von Immich offiziell empfohlene, per Digest
# gepinnte DB-Image (14-vectorchord0.4.3-pgvectors0.2.0). Immichs eigene
# docker-compose auf main pinnt am 2026-06-10 denselben Tag inkl. identischem
# Digest. Kein Update, solange Immich nichts Neueres empfiehlt.
# Re-check: ob Immich ein neueres Postgres-Image empfiehlt.
immich_postgres 2026-09-10
# monitoring-blackbox-exporter: v0.28.0 ist am 2026-06-10 die NEUESTE Release
# (Dez 2025). Das Image-Alter ist nur Build-Alter, keine veraltete Version.
# Re-check: ob eine blackbox_exporter-Version > v0.28.0 erschienen ist.
monitoring-blackbox-exporter 2026-09-10
+1 -16
View File
@@ -18,7 +18,7 @@
# Removing a pattern: replace with a fresh attention example in the next # Removing a pattern: replace with a fresh attention example in the next
# daily report and consult before reintroducing. # daily report and consult before reintroducing.
# #
# Last reviewed: 2026-06-10 # Last reviewed: 2026-05-21
# Loki internal query cancellations / scheduler chatter. # Loki internal query cancellations / scheduler chatter.
# Why: Loki cancels internal queries continuously when downstream Promtails # Why: Loki cancels internal queries continuously when downstream Promtails
@@ -72,18 +72,3 @@ authelia.*Request timeout occurred.*status_code=408
# noise becomes overwhelming, add a *narrow* pattern restricted to # noise becomes overwhelming, add a *narrow* pattern restricted to
# push contexts only (e.g. `vaultwarden.*push.*(ResolveError|...)`). # push contexts only (e.g. `vaultwarden.*push.*(ResolveError|...)`).
vaultwarden.*(Token has expired|Invalid refresh token|Failed to decode.*refresh_token|POST /identity/connect/token => 401 Unauthorized) vaultwarden.*(Token has expired|Invalid refresh token|Failed to decode.*refresh_token|POST /identity/connect/token => 401 Unauthorized)
# AdGuard: Fritz!Box sends malformed SOA queries for myfritz.net / myfritz.link.
# Why: AVM Fritz!Box devices send multi-question DNS SOA queries that violate
# RFC 1035 ("only 1 question allowed"). AdGuard rejects them with an error
# but they have no operational impact.
# Re-check: if the same error appears for non-AVM domains, or if rate spikes
# well above 1000/day without a Fritz!Box reboot explaining it.
adguard.*bad question section.*only 1 question allowed
# Grafana: usage-stats collector looks for the Amazon Prometheus plugin, which
# is not installed in this setup. The error is emitted once per stats cycle.
# Why: GF_PLUGINS_PREINSTALL_DISABLED=true keeps the plugin list minimal;
# this lookup is harmless and does not affect any dashboard.
# Re-check: only if Amazon Prometheus is added as a datasource.
monitoring-grafana.*grafana-amazonprometheus-datasource not found
@@ -1,32 +0,0 @@
# noise-escalation-exempt.patterns - Daily Operations Report
#
# Pattern, die als Rauschen bekannt UND dauerhaft sehr laut sind, sollen die
# Eskalations-Schwelle (NOISE_ESCALATION_THRESHOLD) nicht in eine WARNUNG
# uebersetzen. Ohne diese Ausnahme haengt der Report-Status strukturell auf
# >= WARNUNG fest (z. B. mdadm-Noise auf Unraid feuert dauerhaft > 5000/Tag),
# was die OK/WARNUNG/KRITISCH-Ampel entwertet.
#
# Wirkung: Ein hier gelistetes Pattern wird weiterhin als Noise gezaehlt und
# in der Breakdown-Tabelle gezeigt (mit Markierung "eskalations-befreit"),
# zaehlt aber NICHT mehr zu noise_threshold_exceeded. Neue/unerwartete laute
# Patterns loesen weiterhin eine WARNUNG aus.
#
# Format:
# - Exakte Pattern-Zeile wie in log-noise.patterns (nach Normalisierung:
# getrimmt, ohne Kommentar). Muss zeichengenau dem Eintrag entsprechen.
# - Zeilen mit '#' sind Kommentare, Leerzeilen werden ignoriert.
#
# Eine Befreiung heisst NICHT "ignorieren", sondern "Volumen ist als Noise
# akzeptiert; nur die ESKALATION ist abgeschaltet".
#
# Last reviewed: 2026-06-10
# node-exporter kann /proc/mdstat auf Unraid nicht parsen (eigener Array-
# Treiber, kein Linux-mdadm). Dauerhaft > 5000/Tag, rein kosmetisch.
# Re-check: nur bei Migration auf echtes mdadm-RAID.
monitoring-node-exporter.*mdadm.*Cannot parse /host/proc/mdstat
# Fritz!Box sendet RFC-1035-widrige Multi-Question-SOA-Queries fuer
# myfritz.net/myfritz.link; AdGuard lehnt sie ab. ~1000+/Tag, kein Impact.
# Re-check: falls derselbe Fehler fuer Nicht-AVM-Domains auftaucht.
adguard.*bad question section.*only 1 question allowed
@@ -1,55 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WATCHER="$SCRIPT_DIR/../docker-critical-events.sh"
if [ ! -r "$WATCHER" ]; then
echo "FAIL: watcher not readable at $WATCHER" >&2
exit 1
fi
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
mkdir -p "$tmp/bin"
cat > "$tmp/bin/docker" <<'EOF'
#!/usr/bin/env bash
if [ "${1:-}" != "events" ]; then
echo "unexpected docker command: $*" >&2
exit 1
fi
cat <<'EVENTS'
{"Type":"container","Action":"die","Actor":{"Attributes":{"name":"ok-container","image":"example:latest","exitCode":"0"}}}
{"Type":"container","Action":"die","Actor":{"Attributes":{"name":"bad-container","image":"example:latest","exitCode":"137"}}}
{"Type":"container","Action":"oom","Actor":{"Attributes":{"name":"oom-container","image":"example:latest"}}}
EVENTS
EOF
chmod +x "$tmp/bin/docker"
PATH="$tmp/bin:$PATH" \
SEND_NTFY=0 \
OUTPUT_PATH="$tmp/events.log" \
bash "$WATCHER"
fail() {
echo "FAIL: $*" >&2
echo "--- events.log ---" >&2
cat "$tmp/events.log" >&2 || true
exit 1
}
[ -s "$tmp/events.log" ] || fail "expected critical event log to be written"
if grep -q 'ok-container' "$tmp/events.log"; then
fail "exitCode 0 die event should not alert"
fi
grep -q 'bad-container' "$tmp/events.log" || fail "non-zero die event missing"
grep -q 'oom-container' "$tmp/events.log" || fail "oom event missing"
line_count="$(wc -l < "$tmp/events.log" | tr -d ' ')"
[ "$line_count" = "2" ] || fail "expected 2 logged critical events, got $line_count"
echo "OK - docker critical events filter test passed"
+5 -22
View File
@@ -93,29 +93,12 @@ bash /mnt/user/services/homelab-infra/services/posture-check/daily-status-report
## `docker-critical-events-at-start` ## `docker-critical-events-at-start`
Zeit: Array Start. Dieser Job startet einen Hintergrund-Watcher und beendet sich Zeit: Array Start. Dieser Job startet einen Hintergrund-Watcher und beendet sich sofort.
sofort. Der Supervisor schreibt PID, stdout/stderr und Event-Log nach
`/mnt/user/services/posture-check/`.
```bash ```bash
#!/bin/bash #!/bin/bash
exec /mnt/user/services/homelab-infra/services/posture-check/docker-critical-events-supervisor.sh start ps -ef | grep -F -- "docker events --filter event=die --filter event=oom --filter event=kill" | grep -v grep >/dev/null && exit 0
``` mkdir -p /mnt/user/services/posture-check
nohup bash /mnt/user/services/homelab-infra/services/posture-check/docker-critical-events.sh >/mnt/user/services/posture-check/docker-critical-events.out 2>&1 </dev/null &
Status pruefen: exit 0
```bash
/mnt/user/services/homelab-infra/services/posture-check/docker-critical-events-supervisor.sh status
```
Stoppen:
```bash
/mnt/user/services/homelab-infra/services/posture-check/docker-critical-events-supervisor.sh stop
```
ntfy-Smoke-Test ohne Container-Stopp:
```bash
/mnt/user/services/homelab-infra/services/posture-check/docker-critical-events-supervisor.sh smoke
``` ```
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
traefik: traefik:
image: traefik:v3.7@sha256:d6858791f9e74df44ca4014166647c41cdc2abd3bf2a71b832ca4e1c6a91b257 image: traefik:v3.7@sha256:25cd7b175a493ea66a40329e23a649b59eda38b7e2a570493bf63fc4d74fd1c1
container_name: traefik container_name: traefik
restart: unless-stopped restart: unless-stopped
security_opt: security_opt:
-41
View File
@@ -1,41 +0,0 @@
# traefik/dynamic/plex.yml
#
# Plex runs with network_mode: host for LAN discovery/GDM. The Docker provider
# resolves host-network containers to 127.0.0.1 from inside Traefik, which causes
# Bad Gateway. Keep this one host-network route as a documented File provider
# exception and point Traefik at the Unraid LAN IP.
http:
routers:
plex-web:
rule: Host(`plex.kaleschke.info`) && (Path(`/`) || Path(`/web`))
entryPoints:
- websecure
middlewares:
- plex-web-redirect
- secure-headers@file
tls:
certResolver: le
service: plex
plex:
rule: Host(`plex.kaleschke.info`)
entryPoints:
- websecure
middlewares:
- secure-headers@file
tls:
certResolver: le
service: plex
middlewares:
plex-web-redirect:
redirectRegex:
regex: "^https://plex\\.kaleschke\\.info(/web)?/?$"
replacement: "https://plex.kaleschke.info/web/index.html"
permanent: true
services:
plex:
loadBalancer:
servers:
- url: http://192.168.178.58:32400