Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| af4b7015ee | |||
| d48d473942 | |||
| e80e5dd49f | |||
| 3c339474a7 | |||
| c79afdfab0 | |||
| 8172793c68 | |||
| 8e46440944 | |||
| dfe1dc1c99 | |||
| 4007da3302 | |||
| 9836ea3c4f | |||
| 803f84b3af | |||
| d05ca63545 | |||
| 9847baf327 | |||
| 8ec5bc55d9 | |||
| 9c844074e0 | |||
| c126b71852 | |||
| e89b88a513 | |||
| 8bb250220b | |||
| 2f64aee109 | |||
| ed55b88ec1 | |||
| ce747f687f | |||
| cf11b4d75b | |||
| 796901ec6b | |||
| de7b714b4d | |||
| 8045e22873 | |||
| 52f8c2adcb | |||
| 0ddae675a8 | |||
| 7ce8e948cd | |||
| 2a87220862 | |||
| f2d4cad566 | |||
| e7370e4820 | |||
| dc26eb313c | |||
| dc7cbfa6cd | |||
| cf9ca59eb1 | |||
| d2a9c3b8cb | |||
| 0177350e64 | |||
| 2f3a029098 | |||
| a4c79d9d81 | |||
| 18a90fbb4b | |||
| 30f076c85a | |||
| 6e65f81503 | |||
| 6123584a02 | |||
| c33e29016b | |||
| 2628a0c795 | |||
| c7eed6bdad | |||
| 6c61ad3860 | |||
| 2d1b541847 | |||
| c3491eb382 | |||
| 023ee63687 | |||
| 3a263a4846 | |||
| 68d3ace598 | |||
| 0ef98a23e1 | |||
| 6353da47c5 | |||
| 207f49f001 | |||
| a687d9b73e | |||
| e3459c76d0 | |||
| 254eb81496 | |||
| 9a6d7123ce | |||
| 151d253aff | |||
| dda6021116 | |||
| 2f3d184a3b | |||
| bc3ecad45a | |||
| 88a42f3f78 | |||
| af2c6ee533 | |||
| f382c25696 | |||
| d710a506e8 | |||
| 2ea65e906d | |||
| 2d438cf02b | |||
| 7ba10c893b | |||
| fb948ac951 |
@@ -28,3 +28,5 @@ Thumbs.db
|
||||
*.tmp
|
||||
*.log
|
||||
.serena/
|
||||
.claude/settings.local.json
|
||||
memory/
|
||||
|
||||
@@ -90,7 +90,7 @@ Wenn Drift vermutet wird, nicht raten. Erst die Pflichtmatrix in `docs/GITOPS_DR
|
||||
- `traefik`: Host-Ports 80/443
|
||||
- `gitea`: SSH-Port 222
|
||||
- `AdGuard Home`: DNS-Port 53 und LAN-Admin-Port 8082
|
||||
- `tailscale`: `network_mode: host`
|
||||
- `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.
|
||||
- `Plex-Media-Server`: historischer Host-Netz-Sonderfall, nicht als Repo-Stack enthalten
|
||||
- `scrutiny`: `privileged: true` fuer SMART/Laufwerkszugriff
|
||||
- `Komodo`: Docker-Socket und native Auth ohne pauschale ForwardAuth
|
||||
|
||||
@@ -145,6 +145,7 @@ Diese Dienste sind über echte `*.kaleschke.info`-Domains erreichbar:
|
||||
- `gitea` (Web) — git.kaleschke.info
|
||||
- `immich_server` — immich.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
|
||||
Diese Dienste sind **keine Public Apps**:
|
||||
@@ -240,7 +241,7 @@ Legende Status:
|
||||
| `AdGuard Home` | ✅ | `dns_net` (172.23.0.3), `frontend_net` | Port 53 DNS direkt, Port 8082 Admin nur auf Tailscale-IP `100.80.98.33` | DNS-Server + Upstream zu unbound; kein Traefik fuer Admin-UI | Admin-Port bleibt bewusst ohne Traefik/2FA, aber nicht mehr auf allen LAN-Interfaces |
|
||||
| `unbound` | ✅ | `dns_net` | intern | Upstream-Resolver für AdGuard, isoliert | — |
|
||||
| `ddns-updater` | ✅ | `frontend_net` | intern | Cloudflare DNS API; bleibt in `frontend_net` | Dokumentierte Ausnahme |
|
||||
| `tailscale` | ✅ | `host` | VPN-Zugang | Git-Stack (`host-services/tailscale/`) | nutzt `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` als dokumentierte VPN-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 |
|
||||
|
||||
### 7.2 Sicherheit / Identity
|
||||
|
||||
@@ -273,7 +274,7 @@ Legende Status:
|
||||
| `immich_server` | ✅ | `immich_default`, `frontend_net` | Traefik | aktiv via `immich.kaleschke.info` | — |
|
||||
| `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 |
|
||||
| `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 | — |
|
||||
| `plex` | ✅ | `host` | Traefik via `plex.kaleschke.info` + Plex native Auth; LAN direkt `:32400` | Compose-Stack unter `host-services/plex/`; Host-Netz bleibt fuer Discovery / Plex GDM dokumentierte Ausnahme; Traefik routet per File-Provider-Ausnahme auf `http://192.168.178.58:32400`, weil Docker-Labels Host-Netz-Container aus Traefik heraus auf `127.0.0.1` routen wuerden; kein direkter WAN-Port 32400 und Plex Remote Access bleibt aus; Server geclaimt von `Xeridos`; Smart-TVs (Schlafzimmer, Wohnzimmer) ueber WLAN-LAN per mDNS | — |
|
||||
| `super-productivity` | ✅ vorbereitet | `frontend_net` | Traefik + Middleware | Persoenliche Task-PWA des Operators; Issues kommen aus Gitea `Micha/mails` via n8n-Mail-Workflow | Deploy + Webhook + DNS-Eintrag offen |
|
||||
| `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 |
|
||||
|
||||
@@ -308,7 +309,7 @@ Legende Status:
|
||||
|
||||
| Container | Status | Ziel |
|
||||
|---|---|---|
|
||||
| — | — | Plex ist nicht mehr offen: der Dienst ist als Repo-Compose-Stack unter `host-services/plex/` dokumentiert; `host`-Netz bleibt als Discovery-Ausnahme. |
|
||||
| — | — | 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. |
|
||||
|
||||
### 7.8 Entfernte Container
|
||||
|
||||
@@ -407,6 +408,7 @@ 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-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) |
|
||||
| `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. |
|
||||
|
||||
---
|
||||
|
||||
@@ -495,7 +497,8 @@ Endstand:
|
||||
|
||||
- `PlexOnlineUsername="Xeridos"`, `PlexOnlineMail="michideheld@gmx.de"`, `PlexOnlineHome="1"`.
|
||||
- Bibliotheken neu angelegt via Plex-Web → Verwalte Mediatheken → `/data/movies`, `/data/Heimatfilme` etc.
|
||||
- `PublishServerOnPlexOnlineKey="0"` (Remote Access deaktiviert), Plex-Relay aus → Plex bleibt strikt LAN/Tailscale-only, konsistent zum Tailscale-First-Operator-Modell.
|
||||
- `PublishServerOnPlexOnlineKey="0"` (Remote Access deaktiviert), Plex-Relay aus.
|
||||
- 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:
|
||||
|
||||
|
||||
@@ -34,6 +34,17 @@ services:
|
||||
container_name: immich_machine_learning
|
||||
image: ghcr.io/immich-app/immich-machine-learning:release@sha256:a2501141440f10516d329fdfba2c68082e19eb9ba6016c061ac80d23beadf7f3
|
||||
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:
|
||||
- model-cache:/cache
|
||||
networks:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
mail-archiver:
|
||||
image: s1t5/mailarchiver@sha256:ea7fd8c2e3e0ef0941e8dd9e726e35a8de33296f5c7b9ed811df5168ae6a9714
|
||||
image: s1t5/mailarchiver@sha256:4ea7ecc47ad1dd2c523b85c3967574b61e39def1b6fd26edf874e21733c4018c
|
||||
container_name: mail-archiver
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
|
||||
@@ -4,6 +4,12 @@ services:
|
||||
container_name: mealie
|
||||
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:
|
||||
TZ: Europe/Berlin
|
||||
ALLOW_SIGNUP: "false"
|
||||
@@ -18,6 +24,16 @@ services:
|
||||
|
||||
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:
|
||||
- /mnt/user/appdata/mealie/data:/app/data
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
n8n:
|
||||
image: docker.n8n.io/n8nio/n8n:2.25.5@sha256:08862289f9e9b387d91eab66a74d40d307c0c9b74d2504866f8fe61e9063c838
|
||||
image: docker.n8n.io/n8nio/n8n:2.26.2@sha256:61ba01bc5e39304bbc928c9dbecd938c3a5cc1331b68affba6a34d0f654c43d9
|
||||
container_name: n8n
|
||||
restart: unless-stopped
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
nextcloud:
|
||||
image: nextcloud:33.0.5-apache@sha256:4c8bf9140e07ad0293e32500de47a97647677cd4cace358db3ecdc225dff9856
|
||||
image: nextcloud:33.0.5-apache@sha256:56bdc45109067500fd0832fa64832b7c77a167d9394cbf5f0f4b59740b94194d
|
||||
container_name: nextcloud
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
|
||||
@@ -3,6 +3,9 @@ services:
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:2.20.15@sha256:6c86cad803970ea782683a8e80e7403444c5bf3cf70de63b4d3c8e87500db92f
|
||||
container_name: paperless-ngx
|
||||
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:
|
||||
- no-new-privileges:true
|
||||
environment:
|
||||
@@ -17,6 +20,11 @@ services:
|
||||
- PAPERLESS_OCR_LANGUAGE=deu+eng
|
||||
- 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
|
||||
- PAPERLESS_CONSUMER_ENABLE_BARCODES=1
|
||||
- PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE=1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
super-productivity:
|
||||
image: johannesjo/super-productivity:v18.8.0@sha256:c739caca8e0c5e83ea4a6289884079ac49e0c3c87c7f95598b5a9fb10cc2d9c4
|
||||
image: johannesjo/super-productivity:v18.9.1@sha256:773760107344e739f4c29409f7842db66a1b167d50eb2c40248cb5b5b328652e
|
||||
container_name: super-productivity
|
||||
restart: unless-stopped
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
unbound:
|
||||
image: shaanmajid/unbound:1.25.1@sha256:96809ff052e8bd79bba30e067d8b27ed9a2f069b6b2a3484fe1d0eb45aba07c5
|
||||
image: shaanmajid/unbound:1.25.1@sha256:f140db02a005904802bf5840093e95e675321aa060a00426fdffc2a3ac2eeb6b
|
||||
container_name: unbound
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
### 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]
|
||||
@@ -0,0 +1,52 @@
|
||||
### 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
|
||||
@@ -0,0 +1,63 @@
|
||||
### 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.
|
||||
@@ -0,0 +1,58 @@
|
||||
### 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.
|
||||
@@ -0,0 +1,66 @@
|
||||
### 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]
|
||||
@@ -0,0 +1,45 @@
|
||||
### 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)
|
||||
+20
-2
@@ -1,6 +1,6 @@
|
||||
# AI Context
|
||||
|
||||
Stand: 2026-06-02
|
||||
Stand: 2026-06-05
|
||||
|
||||
Kurzer Kontext fuer KI-Agenten. Nicht als Ersatz fuer die echten Runbooks lesen.
|
||||
|
||||
@@ -43,14 +43,32 @@ Kurzer Kontext fuer KI-Agenten. Nicht als Ersatz fuer die echten Runbooks lesen.
|
||||
|
||||
## Aktuelle Restpunkte
|
||||
|
||||
Authoritativ: `docs/AUDIT_2026-05-25_TODO.md`.
|
||||
Authoritativ: `docs/MASTER_TODO.md`.
|
||||
|
||||
Kurzfassung:
|
||||
|
||||
- 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:
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
# Alert Rules
|
||||
|
||||
Stand: 2026-05-31
|
||||
Stand: 2026-06-05
|
||||
|
||||
Diese Datei beschreibt die produktiven Alarmwege und wichtigsten Regeln. Die
|
||||
Konfiguration selbst liegt in `monitoring/prometheus/alerts.yml` und in den
|
||||
@@ -49,4 +49,6 @@ Die Liste der ueberwachten Critical-Container steht in
|
||||
- Kein Inode-Alarm. Bei Paperless/Immich spaeter sinnvoll, aber aktuell kein
|
||||
dokumentierter Vorfall.
|
||||
- Container-Memory-Limits werden erst nach realen Peak-Daten gesetzt; OOM/kill
|
||||
wird bereits ueber `docker-critical-events.sh` gemeldet.
|
||||
wird ueber `docker-critical-events.sh` gemeldet, sobald der Host-Watcher per
|
||||
Unraid User Script aktiviert ist. Start/Stop/Status/Smoke laufen ueber
|
||||
`services/posture-check/docker-critical-events-supervisor.sh`.
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
Status: **kompakte Restliste**. Die erledigten Sprint-Tabellen und langen
|
||||
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
|
||||
|
||||
| Prioritaet | Punkt | Naechster Schritt |
|
||||
|---|---|---|
|
||||
| P1 | DR-Workstation Bare-Metal-Kit: WSL2 + Borg-Client installieren | Hetzner-DR-SSH-Key ist 2026-06-03 erledigt und offline gesichert. Verbleibend: WSL2 auf dem Gaming-PC einrichten (`wsl --install -d Ubuntu`), `sudo apt install borgbackup` und ein erster Smoke `borg list ssh://u565255@u565255.your-storagebox.de/./hetzner_borg_appdata_critical` mit dem offline gesicherten Key + Passphrase. Bestandteile dokumentiert in `docs/EXTERNAL_DEPENDENCIES.md` Abschnitt "DR-Workstation Bare-Metal-Kit" |
|
||||
| P2 | Family-Onboarding praktisch starten | Fokus: Vaultwarden als Passwortbasis, Immich-Mobile-Backup auf jedem Handy, Mealie mit erstem Rezept/Einkaufsliste; Ablauf steht in `docs/FAMILY_ONBOARDING.md` |
|
||||
|
||||
## Restore-Audit Backlog (Stand 2026-06-03)
|
||||
@@ -40,6 +43,7 @@ Ergebnis des Restore-Skills-Audits (Session 2026-06-02/03). Die kritischen Bugfi
|
||||
|
||||
## Zuletzt geschlossen
|
||||
|
||||
- DR-Workstation Bare-Metal-Kit abgeschlossen (2026-06-06): WSL2 Ubuntu 24.04, SSH/Git, Borg 1.2.8, DR-Key-Arbeitskopien `~/.ssh/dr-readonly` und `~/.ssh/dr-hetzner`, `~/dr-smoke.sh`. Finaler Operator-Smoke erfolgreich: GitHub HEAD `3a263a4...`, Hetzner Storage Box Repos sichtbar (`backup`, `backup2`, `hetzner_borg_appdata`, `hetzner_borg_appdata_critical`), Borg-Repo `hetzner_borg_appdata_critical` gelesen, Repository ID `5dd9b949...`, encrypted `Yes (repokey)`, `DR-Smoke OK (2026-06-06 10:05:30)`. Borg-Passphrase wurde nur interaktiv eingegeben und nicht gespeichert.
|
||||
- Nextcloud-Restore-Test 2026-06-03 erfolgreich (Tier-2 damit komplett belegt). Drei Laeufe noetig: Lauf 1 schlug an `chmod()` der data-Dir auf shfs fehl (`OC_Util.php:486`), Lauf 2 an fehlender `.ncdata`-Marker-Datei, Lauf 3 sauber durch. Beide Bug-Fixes ins Skript `ops/restore-tests/nextcloud-restore-test.sh` integriert. Endresultat: HTTP 200 auf `/status.php`, `occ status` ok, 126 Tabellen in der DB. Source: `hetzner_borg_appdata_critical`, Archiv `Taegliche-Sicherung-2026-06-03T04:30:41.432`. Report unter `/mnt/user/backups/restore-reports/nextcloud-2026-06-03.md`.
|
||||
- Hetzner Storage Box DR-SSH-Key `dr-hetzner-2026-06-03` (ed25519, Passphrase-frei) angelegt: Pubkey via `install-ssh-key` auf der Storage Box autorisiert, passwortloser Login erfolgreich (Borg-Repos `backup`, `backup2`, `hetzner_borg_appdata`, `hetzner_borg_appdata_critical` sichtbar), Private-Key offline neben KOMODO_*-Notiz und GitHub-Deploy-Key abgelegt, Arbeitsplatz-Kopie geloescht. Damit ist Bare-Metal-Borg-Zugang von der DR-Workstation moeglich, sobald WSL2+Borg installiert sind.
|
||||
- Fix Common Problems Plugin (FCP) 2026-06-03 deinstalliert. Befund: drei `grep -R ... /usr/local/emhttp`-Prozesse aus einem FCP-Daily-Scan hingen seit ~7 Tagen in einem Symlink-Loop (`/usr/local/emhttp/mnt -> /mnt`, gesamte Array). 3 Cores dauerhaft 100 %, IOWAIT bis 55 %, USB-Flash unter Dauer-IO. Plugin via `plugin remove` entfernt, Cron + /tmp-Reste sauber, Load von 14.6 auf 1.08 gefallen. FCP wird bewusst nicht wieder installiert (Begruendung siehe `HOMELAB_ARCHITECTURE_MASTER_V2.md` Sektion 13). Bekannte Risiken decken Scrutiny, Monitoring, Posture-Check und Critical-Events-Watcher bereits ab.
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
# 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.
|
||||
@@ -290,7 +290,14 @@ Erfolgskriterium: `docker network ls` zeigt `frontend_net`, `backend_net`, `moni
|
||||
|
||||
1. `traefik/`
|
||||
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.
|
||||
|
||||
@@ -522,6 +529,40 @@ 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.
|
||||
|
||||
### 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
|
||||
@@ -531,6 +572,8 @@ Smoke-Test: `hermes-gateway` healthcheck ist gruen, `hermes.kaleschke.info` leit
|
||||
- Komodo Stack-ENV-Werte zentral ausserhalb von Komodo dokumentieren
|
||||
- regelmaessige automatisierte Restore-Smoke-Tests fuer Vaultwarden, Gitea und Paperless etablieren
|
||||
- `komodo-mongo`-Dump nach Major-Upgrades gezielt kontrollieren
|
||||
- `baerchen` Recovery-USB-Boot-/SMB-Test nach erfolgreichem erstem Full-Lauf
|
||||
verifizieren
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -115,6 +115,15 @@ Erwartet: HEAD und mindestens ein `refs/heads/master`-Eintrag.
|
||||
|
||||
Damit der "ich pruefe das vierteljaehrlich"-Schritt zur Routine wird, ein kleines Skript ins WSL-Home:
|
||||
|
||||
Stand 2026-06-06: Das Skript liegt zusaetzlich versioniert unter
|
||||
`ops/maintenance/dr-workstation-smoke.sh` und wurde auf `baerchen` bereits nach
|
||||
`~/dr-smoke.sh` in die Ubuntu-WSL kopiert. Borg 1.2.8 ist installiert, die
|
||||
DR-Key-Arbeitskopien liegen unter `~/.ssh/dr-readonly` und
|
||||
`~/.ssh/dr-hetzner`, GitHub-Read-Smoke und Hetzner-SSH-Smoke sind erfolgreich.
|
||||
Der finale Borg-Smoke via `bash ~/dr-smoke.sh` wurde am 2026-06-06 ebenfalls
|
||||
erfolgreich gefahren (`DR-Smoke OK (2026-06-06 10:05:30)`). Die Borg-Passphrase
|
||||
wurde nur interaktiv eingegeben und nicht gespeichert.
|
||||
|
||||
```bash
|
||||
cat > ~/dr-smoke.sh <<'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
@@ -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 |
|
||||
| Let's Encrypt | TLS-Zertifikate | hoch | Cert-Erneuerung faellt aus | automatisch via Traefik und Cloudflare DNS-Challenge | Cert-Expiry Alert einrichten; Cloudflare-Token und Traefik-Storage pruefen |
|
||||
| Container Registries | Image Pulls von Docker Hub, GHCR, LSCR, Gitea Registry u. a. | mittel | Redeploy/Update blockiert | ueberwiegend oeffentlich; keine produktiven Registry-Tokens im Repo | Gepinnte Digests und lokale Runtime helfen kurzfristig; Updates geplant und einzeln deployen |
|
||||
| Plex Konto/Remote Access | Plex native Auth, ggf. Remote Access und Claim | mittel | Plex-Clients/Remote-Funktionen koennen ausfallen | Plex-Konto ausserhalb Repo; `PLEX_CLAIM` nur fuer Setup | LAN-Medienpfade bleiben lokal; Konto-Recovery separat sichern |
|
||||
| Plex Konto | Plex native Auth, Claim und Client-Zugriff ueber `plex.kaleschke.info` | mittel | Plex-Web/App-Login und Clients koennen ausfallen; LAN-Medienpfade bleiben lokal | Plex-Konto ausserhalb Repo; `PLEX_CLAIM` nur fuer Setup | Plex Remote Access bleibt aus; externer Zugriff laeuft ueber Traefik/443. Konto-Recovery separat sichern |
|
||||
| Mobile Push | ntfy und ggf. mobile Plattform-Pushes | niedrig/mittel | Alerts erreichen Mobilgeraete ggf. nicht | App-/Device-seitig | Kritische Alerts zusaetzlich in Grafana/Glance sichtbar halten |
|
||||
| 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,3 +107,4 @@ Operative Regel: Die DR-Workstation wird nicht als Test-/Spiel-PC betrachtet. WS
|
||||
| 2026-06-03 | KOMODO_*-Notiz offline gesichert (Operator-Bestaetigung im DR-Tabletop-Followup). Quelle bleibt host-seitige `.env` (`/mnt/user/services/stacks/komodo/.env`) bzw. Drift-Recovery-Kopie vom 2026-05-04. Bare-Metal-Komodo-Bootstrap ist damit ohne Vaultwarden moeglich. | Restliche P1-Operator-Aufgaben: GitHub-Read-PAT, DR-Workstation-Setup, Nextcloud-Restore-Test |
|
||||
| 2026-06-03 | GitHub-Mirror Read-Only Deploy-Key `DR Read-Only 2026-06-03` (ed25519, Passphrase-frei) erzeugt, in GitHub Repo Settings ohne Write-Access hinterlegt, Smoke `git ls-remote` erfolgreich (`d947c7f` matched master HEAD), Private-Key offline neben KOMODO_*-Notiz abgelegt, Arbeitsplatz-Kopie geloescht. | Restliche P1-Operator-Aufgaben: DR-Workstation-Setup, Nextcloud-Restore-Test |
|
||||
| 2026-06-03 | Hetzner Storage Box DR-SSH-Key `dr-hetzner-2026-06-03` (ed25519, Passphrase-frei) erzeugt, via `install-ssh-key` auf Storage Box `u565255.your-storagebox.de:23` autorisiert, passwortloser Login erfolgreich (Borg-Repos sichtbar), Private-Key offline neben KOMODO_*-Notiz und GitHub-Deploy-Key abgelegt, Arbeitsplatz-Kopie geloescht. Bare-Metal-Borg-Restore von der DR-Workstation ist damit moeglich, sobald WSL2 + Borg-Client installiert sind. | Restliche P1-Operator-Aufgaben: WSL2 + Borg-Client auf DR-Workstation installieren, Nextcloud-Restore-Test |
|
||||
| 2026-06-06 | DR-Workstation produktiv: WSL2 Ubuntu 24.04 vorhanden, SSH/Git und Borg 1.2.8 in WSL vorhanden, DR-Key-Arbeitskopien unter `~/.ssh/dr-readonly` und `~/.ssh/dr-hetzner`, GitHub-Read-Smoke und Hetzner-SSH-Smoke erfolgreich, `ops/maintenance/dr-workstation-smoke.sh` nach `~/dr-smoke.sh` kopiert. Finaler Operator-Smoke erfolgreich: GitHub HEAD `3a263a4...`, Hetzner Storage Box Repos sichtbar, Borg-Repo `hetzner_borg_appdata_critical` gelesen, Repository ID `5dd9b949...`, encrypted `Yes (repokey)`, `DR-Smoke OK (2026-06-06 10:05:30)`. | Quartalsweise `bash ~/dr-smoke.sh`; Borg-Passphrase weiterhin nur interaktiv eingeben und nicht speichern |
|
||||
|
||||
+59
-13
@@ -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 |
|
||||
| **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 |
|
||||
| **Plex** | Filme und Musik auf Fernseher, Handy und Tablet | Plex-App auf dem Geraet, mit Konto anmelden |
|
||||
| **Plex** | Filme und Musik auf Fernseher, Handy und Tablet | Web `https://plex.kaleschke.info` oder 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.
|
||||
|
||||
@@ -193,20 +193,66 @@ Michi laesst es dich wissen, wenn ein Wartungsfenster geplant ist.
|
||||
|
||||
---
|
||||
|
||||
## Onboarding-Checkliste fuer Michi
|
||||
## Erster Onboarding-Termin - Ablauf fuer Michi
|
||||
|
||||
Diese Punkte gehoeren in das erste echte Familien-Onboarding. Keine Secret-Werte
|
||||
in diese Datei schreiben.
|
||||
Diese Sektion ist die konkrete Checkliste fuer den **ersten echten
|
||||
Familien-Onboarding-Termin**. Sie ist als ein zusammenhaengender Termin von
|
||||
ca. 30-45 Minuten pro Person gedacht. Keine Secret-Werte in diese Datei
|
||||
schreiben.
|
||||
|
||||
| Status | Aufgabe |
|
||||
|---|---|
|
||||
| offen | Pro Familienmitglied Konto/Start-Passwort persoenlich uebergeben |
|
||||
| offen | Vaultwarden/Bitwarden-App auf Handy einrichten |
|
||||
| offen | Testeintrag in Vaultwarden anlegen |
|
||||
| offen | Immich-App auf jedem Familien-Handy einrichten |
|
||||
| offen | Immich-Backup mit ersten Fotos sichtbar pruefen |
|
||||
| offen | Mealie mit erstem Rezept und Einkaufsliste praktisch ausprobieren |
|
||||
| offen | Danach entscheiden, ob Nextcloud/Paperless/Plex direkt mitkommen oder spaeter |
|
||||
> Operator-Eingabe vor dem Termin: festlegen, **wer** beim ersten Termin dabei
|
||||
> ist und **welche Geraete** real vorliegen. Die Checkliste funktioniert pro
|
||||
> Person identisch.
|
||||
|
||||
### Vorher bereitlegen (Operator-Vorbereitung)
|
||||
|
||||
Diese Dinge muessen **vor** dem Termin fertig sein, sonst stockt der Ablauf:
|
||||
|
||||
- [ ] Pro Teilnehmer ist in **Vaultwarden** ein Benutzerkonto angelegt (Benutzername = Vorname klein).
|
||||
- [ ] 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
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
# 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.
|
||||
@@ -3,8 +3,20 @@
|
||||
Status: Hardware-Baseline erfasst; USV/Power-Loss ist als bewusst akzeptiertes Betreiber-Risiko dokumentiert.
|
||||
Host: `Kallilabcore`
|
||||
Letzte Pruefung: 2026-05-26
|
||||
Doku-Stand Betreiberentscheidungen: 2026-06-05
|
||||
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
|
||||
|
||||
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.
|
||||
@@ -96,7 +108,7 @@ tailscale ip -4
|
||||
| Disk1 | `md1p1` / physisch `sdc` | WDC WD60EFAX-68JH4N1 | `WD-WX32D90PC0V0` | 5.5T | XFS auf md1p1 | Array-Daten | SMART passed |
|
||||
| Parity | physisch `sdb` | TOSHIBA HDWG480 | `2460A03VFA3H` | 7.3T | n/a | Parity | SMART passed |
|
||||
| Boot | `sda1` | Samsung Flash Drive | `0375125090000587` | 59.8G | FAT32 | Unraid Boot | aktiv |
|
||||
| Cold Backup | TBD | TBD | TBD | TBD | TBD | Externe Rotation | offen |
|
||||
| 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 |
|
||||
|
||||
Pruefkommando:
|
||||
|
||||
@@ -138,18 +150,27 @@ Bewertung:
|
||||
|
||||
- Aktueller Befund 2026-05-26: keine funktionierende USV-Absicherung nachgewiesen.
|
||||
- `apcupsd` ist zwar auf dem System vorhanden, aber nicht aktiv.
|
||||
- Operator-Entscheidung 2026-05-26: aktuell keine USV-Anschaffung.
|
||||
- **Operator-Entscheidung 2026-06-05: USV-Anschaffung bewusst auf Q3/2026 geparkt.** Keine Beschaffung in diesem Quartal.
|
||||
- Power-Loss bleibt damit ein bewusst akzeptiertes Risiko fuer Docker-/DB-State und laufende Writes.
|
||||
- Review-Ausloeser: Hardware-Erweiterung, wiederholte Stromausfaelle, Datenkorruption oder Veraenderung der Betreiber-Prioritaet.
|
||||
- Review-Trigger (einer reicht): naechstes Hardware-Upgrade, ein erneuter realer Stromausfall mit Datenfolge, oder der Q3-Review ab 2026-07-01.
|
||||
- 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
|
||||
|
||||
**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 |
|
||||
|---|---:|---|---|
|
||||
| Idle | TBD | externes Messgeraet erforderlich | TBD |
|
||||
| Normalbetrieb | TBD | externes Messgeraet erforderlich | TBD |
|
||||
| Backup-Lauf | TBD | externes Messgeraet erforderlich | TBD |
|
||||
| Last | TBD | externes Messgeraet erforderlich | TBD |
|
||||
| Idle | offen | schaltbare Mess-Steckdose, 10 min Mittelwert ohne aktive Jobs | nach Beschaffung |
|
||||
| Normalbetrieb | offen | Mess-Steckdose, typischer Tagbetrieb mit laufenden Apps | nach Beschaffung |
|
||||
| Backup-Lauf | offen | Mess-Steckdose, waehrend naechtlichem Borg-Lauf | nach Beschaffung |
|
||||
| Last | offen | Mess-Steckdose, unter CPU-Last (z. B. Immich-ML/Parity-Check) | nach Beschaffung |
|
||||
|
||||
Beschaffungs-Trigger: einfache schaltbare Energiemess-Steckdose; danach ein
|
||||
einziger Messdurchlauf reicht, um diese Tabelle dauerhaft zu fuellen.
|
||||
|
||||
## Ersatzteil- und Lifecycle-Plan
|
||||
|
||||
@@ -160,7 +181,7 @@ Bewertung:
|
||||
| Parity | Kleiner als neue groesste Datenplatte | Parity-Upgrade vor Datenplatten-Upgrade |
|
||||
| Boot-USB | Lesefehler oder Alter TBD | Flash-Backup verifizieren, Ersatzstick vorbereiten |
|
||||
| RAM | Swap/OOM oder Immich/Nextcloud-Druck | Ausbau planen |
|
||||
| USV | keine funktionierende USV-Abschaltung | Risiko am 2026-05-26 bewusst akzeptiert; bei Review erneut bewerten |
|
||||
| USV | keine funktionierende USV-Abschaltung | Anschaffung 2026-06-05 bewusst auf Q3/2026 geparkt; Trigger: Hardware-Upgrade, realer Stromausfall mit Datenfolge, oder Q3-Review |
|
||||
|
||||
## Audit-Kommandos
|
||||
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
# 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.
|
||||
|
||||
## Live-Stand 2026-05-04
|
||||
## Historischer Live-Stand 2026-05-04
|
||||
|
||||
- Home Assistant ist per SSH unter `192.168.178.50:22222` erreichbar.
|
||||
- `ha core check` ist erfolgreich.
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# 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.
|
||||
+275
-12
@@ -1,7 +1,7 @@
|
||||
# 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.
|
||||
Letzte Pruefung: 2026-06-01
|
||||
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.
|
||||
Letzte Pruefung: 2026-06-05 (Tailscale-Inventar), 2026-06-01 (Router/Ports)
|
||||
|
||||
## Zweck
|
||||
|
||||
@@ -44,23 +44,163 @@ Dieses Dokument beschreibt Router, DNS, Tailscale, Portfreigaben und Netztrennun
|
||||
|
||||
## Tailscale
|
||||
|
||||
| Feld | Wert |
|
||||
Gemessen am 2026-06-05 per read-only SSH auf den Host (`tailscale status`,
|
||||
`tailscale status --json`, `tailscale ip -4/-6`).
|
||||
|
||||
| Feld | Wert / Status |
|
||||
|---|---|
|
||||
| Node-Name | Kallilabcore |
|
||||
| Tailscale IPv4 | 100.80.98.33 |
|
||||
| Tailscale IPv6 | TBD |
|
||||
| Exit Node | TBD |
|
||||
| Subnet Router | TBD |
|
||||
| ACL-Policy extern dokumentiert | TBD |
|
||||
| Tailnet / MagicDNS | `taild9fcf2.ts.net`; DNSName `kallilabcore.taild9fcf2.ts.net` |
|
||||
| Tailscale IPv4 | `100.80.98.33` |
|
||||
| Tailscale IPv6 | `fd7a:115c:a1e0::2c01:62b2` (gemessen 2026-06-05) |
|
||||
| Exit Node | **Nein.** `Self.ExitNodeOption: false` und `Self.ExitNode: false` — Host bietet keinen Exit Node an und nutzt keinen. Entspricht dem Ziel (Operator-Zugang ist eingehend, nicht als Internet-Ausgang). |
|
||||
| Subnet Router | **Ja, aktiv.** Host advertised und ist Primary fuer `192.168.178.0/24` (`Self.PrimaryRoutes: ["192.168.178.0/24"]`, ebenfalls in `AllowedIPs`). Das LAN ist also fuer das gesamte Tailnet ueber diesen Subnet-Router erreichbar — bewusst gemessener Ist-Zustand, **kein** "keine Route" wie zuvor vermutet. |
|
||||
| ACL-Policy extern dokumentiert | **Angewendet 2026-06-06** — restriktive Tag-basierte `grants`-Policy live (`tag:server`/`tag:operator`, `tag:family` schlafend). Default-Allow entfernt, verifiziert. Details im Block unten. |
|
||||
|
||||
Pruefkommando:
|
||||
### Tailnet-Geraete (Snapshot 2026-06-05)
|
||||
|
||||
| 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
|
||||
tailscale status
|
||||
tailscale status --json | jq '{exitNode: .Self.ExitNodeOption, primaryRoutes: .Self.PrimaryRoutes, allowedIPs: .Self.AllowedIPs}'
|
||||
tailscale ip -4
|
||||
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
|
||||
|
||||
### FRITZ!Box (WAN -> Host)
|
||||
@@ -77,6 +217,7 @@ 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. |
|
||||
| `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
|
||||
|
||||
@@ -105,6 +246,7 @@ Historischer UI-Befund vor Bereinigung vom 2026-05-27 (`Internet -> Freigaben ->
|
||||
| 443/tcp | Traefik | HTTPS | WAN-Freigabe in FRITZ!Box erwartet |
|
||||
| 222/tcp | Gitea SSH | Git SSH | nur LAN/Tailscale; keine WAN-Freigabe |
|
||||
| 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 |
|
||||
| 8181/tcp | InfluxDB 3 Core | Home Assistant / Ecowitt Writer | 2026-05-31 effektiv nur `127.0.0.1:8181`, nicht LAN-exponiert |
|
||||
|
||||
@@ -121,7 +263,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 |
|
||||
| WLAN 2,4 / 5 GHz | aktiv, SSID `Fritzi` | Standard-WLAN, im LAN-Adressbereich, kein eigener Adressraum |
|
||||
| Gast-WLAN | **inaktiv** (FRITZ!Box-UI) | Solange inaktiv: kein Gast-Pfad zu LAN-Diensten; AdGuard-Admin-Trennung primaer ueber Tailscale-Bind statt Netzsegmentierung |
|
||||
| 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 |
|
||||
| IoT-Netz | nicht existent | Keine VLAN-Trennung dokumentiert |
|
||||
| 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 |
|
||||
@@ -146,6 +288,126 @@ docker network inspect frontend_net | jq '.[0].Containers | keys'
|
||||
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
|
||||
|
||||
| Thema | Status | Naechster Schritt |
|
||||
@@ -154,7 +416,8 @@ docker network inspect backend_net | jq '.[0].Internal'
|
||||
| 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!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 | 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 |
|
||||
| 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. |
|
||||
| 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 | bewusst nicht eingerichtet | Mobilfunk-Stick-Failover an FRITZ!Box ist nicht aktiv; Internet-Ausfall = ACME/DDNS pausieren, lokale Apps laufen weiter |
|
||||
| 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. |
|
||||
| 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". |
|
||||
|
||||
+7
-2
@@ -1,6 +1,6 @@
|
||||
# Documentation Index
|
||||
|
||||
Stand: 2026-06-01
|
||||
Stand: 2026-06-05
|
||||
|
||||
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,8 +31,10 @@ Diese Datei trennt aktive Betriebsdokumentation von historischer Arbeitsdoku. Ne
|
||||
|---|---|
|
||||
| `STORAGE_LAYOUT.md` | verbindliche Storage-/Share-/Pfad-Regeln |
|
||||
| `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 |
|
||||
| `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_OPERATOR_RUNBOOK.md` | Hetzner-/Borg-/FRITZ!Box-Betreibercheck |
|
||||
| `CAPACITY_AND_LIFECYCLE.md` | Kapazitaet, Wachstum und Upgrade-Trigger |
|
||||
@@ -43,7 +45,7 @@ Diese Datei trennt aktive Betriebsdokumentation von historischer Arbeitsdoku. Ne
|
||||
|---|---|
|
||||
| `ALERT_RULES.md` | Prometheus-/ntfy-Regeln und Handlungslogik |
|
||||
| `RENOVATE.md` | Self-hosted Renovate gegen Gitea |
|
||||
| `HOME_ASSISTANT_INFLUXDB_ECOWITT.md` | Home Assistant -> InfluxDB 3 -> Grafana |
|
||||
| `HOME_ASSISTANT_INFLUXDB_ECOWITT.md` | Archivierter Entwurf: Home Assistant -> InfluxDB 3 -> Grafana; nicht aktiv seit Crash |
|
||||
| `H_DRIVE_NEARLINE_PULL.md` | Windows-H:/ Nearline-Pull fuer kritische Restore-Artefakte |
|
||||
|
||||
## Nutzer- und Planungsdoku
|
||||
@@ -52,6 +54,9 @@ Diese Datei trennt aktive Betriebsdokumentation von historischer Arbeitsdoku. Ne
|
||||
|---|---|
|
||||
| `FAMILY_ONBOARDING.md` | familienverstaendliche Nutzungsdoku |
|
||||
| `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 |
|
||||
|
||||
Windows-Neuaufsetzen-Dokumente liegen nicht mehr in `docs/`, sondern im fachlich passenden Ordner `../ops/windows-reinstall/docs/`.
|
||||
|
||||
+9
-1
@@ -93,7 +93,15 @@ 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 |
|
||||
| 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 |
|
||||
| Ignore-Pfade | `_archive`, `ops/grafana-influxdb`, `ops/loki` | Renovate scant alte/abgeloeste Stacks nicht |
|
||||
| Ignore-Pfade | `_archive`, `ops/grafana-influxdb`, `ops/loki`, `ops/komodo` | Renovate scant alte/abgeloeste Stacks nicht; `ops/komodo` ist bewusst raus (siehe unten) |
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ Details gilt immer die betroffene Compose-Datei oder das jeweilige Runbook.
|
||||
| `docs/GITOPS_DRIFT_RUNBOOK.md` | Git/Gitea/Komodo/Docker/Host-Drift |
|
||||
| `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/runbooks/komodo-bulk-deploy-dns.md` | Bulk-Deploy-Pulls scheitern an DNS, wenn AdGuard im selben Batch recreated wird |
|
||||
|
||||
## Wichtige Skripte
|
||||
|
||||
|
||||
+191
-12
@@ -28,10 +28,10 @@ Sie ist die fachliche Ergaenzung zu `docs/DISASTER_RECOVERY.md`.
|
||||
|---|---|---|---|---|---|---|
|
||||
| Unraid OS Flash | Borg-Artefakt + optional Unraid Connect | `/boot/config` aus `unraid-flash-config.tar.gz` | `unraid-flash-config.tar.gz`, `.sha256`, Manifest | enthaelt sensible Host-Konfiguration, wie Secret-Material behandeln | Unraid USB Flash Creator / neuer Boot-Stick | Unraid bootet, Array-Zuordnung und Shares sind sichtbar |
|
||||
| Traefik | Share / Borg | `/mnt/user/appdata/traefik`, besonders `dynamic/`, `letsencrypt`, `secrets` | keine eigene DB | `cloudflare_dns_api_token` | `frontend_net`, `backend_net` | `https://traefik.kaleschke.info` erreichbar, Dashboard ueber Authelia |
|
||||
| AdGuard Home | Share / Borg | `/mnt/user/appdata/adguard/conf` | keine | keine zusaetzlichen Repo-Secrets dokumentiert | `dns_net`, `frontend_net` | DNS-Aufloesung funktioniert |
|
||||
| Tailscale | Share / Borg | `/mnt/user/appdata/tailscale` | keine | Tailscale-State im Pfad | Host-Netz | Tailscale verbunden |
|
||||
| AdGuard Home | Share / Borg | `/mnt/user/appdata/adguard/conf` | keine | keine zusaetzlichen Repo-Secrets dokumentiert | `dns_net`, `frontend_net` | DNS-Aufloesung funktioniert; Restore-Smoke am 2026-06-06 erfolgreich |
|
||||
| Tailscale | Flash-Backup (funktional) / Share | **Funktional: `/boot/config/plugins/tailscale/state`** (native Unraid-Plugin-Instanz `kallilabcore`, Subnet-Router, im Flash-Backup gesichert). Der frueher hier genannte Pfad `/mnt/user/appdata/tailscale` gehoert zum **userspace-only Docker-Stack** `kallilab-core` (redundant, Abbau geplant — siehe `docs/NETWORK_INVENTORY.md`) | keine | Tailscale-State im jeweiligen State-Pfad | Host-Netz | Tailscale verbunden, Subnet-Route `192.168.178.0/24` aktiv |
|
||||
| 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 |
|
||||
| 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 |
|
||||
| Authelia | Borg | `/mnt/user/appdata/authelia/config`, `/mnt/user/appdata/secrets/*authelia*` | Shared PostgreSQL 18, optional Dump `postgresql17-authelia.dump` | JWT/Session/Storage/Postgres-/SMTP-Secret-Dateien | PostgreSQL 18, Traefik, GMX SMTP | Login-Seite und ForwardAuth funktionieren; SMTP-Notifier startet; aktive Sessions werden nach Restart neu aufgebaut; Restore-Smoke am 2026-06-03 erfolgreich: Config aus Borg, minimale Test-Config, frisches Test-Postgres, HTTP `/api/health` 200, Report `/mnt/user/backups/restore-reports/authelia-2026-06-03.md` |
|
||||
| Gitea | GitHub-Mirror + Gitea-Bundles fuer Repo-Bootstrap, Borg + Dump fuer Gitea-Appstate | `/mnt/user/services/gitea/data`, `/mnt/user/backups/git-bundles/gitea` | `gitea.sqlite.dump`, Bundle-Report `latest-report.md` | `borg_repo_passphrase.txt` fuer Restore-Tests; GitHub-Push-Mirror-PAT liegt nur in Gitea-Mirror-Settings | Traefik | Web-UI erreichbar, Repo sichtbar, SSH-Port reagiert; Bundle laesst sich klonen und `git fsck` ist sauber; GitHub-Push-Mirror synchronisiert ohne `last_error`; Mini-Restore nach `/mnt/user/backups/restore-lab/gitea` am 2026-05-07 erfolgreich validiert |
|
||||
| Komodo | Borg / Share | `/mnt/user/appdata/komodo/core`, `/mnt/user/appdata/komodo/periphery`, `/mnt/user/services/stacks` | `komodo-mongo.archive.gz` falls verifiziert | `komodo_mongo_password.txt`, `KOMODO_*` Stack ENV | Traefik, Mongo, Gitea | UI erreichbar, Periphery verbunden |
|
||||
@@ -40,6 +40,14 @@ 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
|
||||
|
||||
| Dienst | Fuehrende Quelle | Datei-Restore | Dump / DB | Secrets / ENV | Abhaengigkeiten | Smoke-Test |
|
||||
@@ -127,7 +135,7 @@ Die Dump-Erzeugung ist host-seitig ueber `ops/borg-ui/scripts/pre-backup-dumps.s
|
||||
|
||||
## Restore-Test-Reifegrad
|
||||
|
||||
Stand 2026-06-03. Pro Dienst auf einen Blick: Wurde der Restore schon einmal real getestet?
|
||||
Stand 2026-06-06. Pro Dienst auf einen Blick: Wurde der Restore schon einmal real getestet?
|
||||
|
||||
| Dienst | Tier | Letzter Restore-Test | Typ | Naechster Lauf |
|
||||
|---|---|---|---|---|
|
||||
@@ -137,12 +145,12 @@ Stand 2026-06-03. Pro Dienst auf einen Blick: Wurde der Restore schon einmal rea
|
||||
| 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.) |
|
||||
| Immich | 2 | 2026-05-27 | Dump + Container + HTTP + Asset-Count | quartalsweise (2. So Feb/Mai/Aug/Nov) |
|
||||
| Unraid OS Flash | 1 | - | noch kein Test | - |
|
||||
| Unraid OS Flash | 1 | 2026-06-05 (Artefakt-Validierung) | sha256 OK + 390 Eintraege + 8 Kern-Configs vorhanden (`ops/maintenance/check-unraid-flash-backup.sh`); **physischer Ersatzstick-Boot-Test weiter offen** | Stick-Boot-Test nach Bedarf |
|
||||
| Traefik | 1 | 2026-06-03 | Config + LE-State + File-Provider + Ping 200 | quartalsweise |
|
||||
| AdGuard Home | 1 | - | noch kein Test | - |
|
||||
| AdGuard Home | 1 | 2026-06-06 | Config + Container + HTTP 401 + DNS + Filter-Count | quartalsweise oder nach DNS-Aenderungen |
|
||||
| Tailscale | 1 | - | noch kein Test | - |
|
||||
| PostgreSQL 18 Cluster | 1 | 2026-06-03 | globals + 5 per-DB dumps, 290 Tabellen gesamt | quartalsweise |
|
||||
| Redis 8 | 1 | - | noch kein Test | - |
|
||||
| Redis 8 | 1 | 2026-06-06 | Pre-Cutover-Artefakt + Container + PING + INFO + DBSIZE | quartalsweise oder vor/nach Redis-Major-Aenderungen |
|
||||
| Komodo Mongo Daten | 1 | 2026-06-03 | mongorestore --archive --gzip, 86904 docs | quartalsweise |
|
||||
| Nextcloud | 2 | 2026-06-03 | File + Dump + Container + HTTP 200 + occ status + Table-Count (126) | quartalsweise |
|
||||
| Mealie | 2 | 2026-06-03 | File + Dump + Container + HTTP + Recipe-Count (3) | quartalsweise |
|
||||
@@ -151,13 +159,184 @@ Stand 2026-06-03. Pro Dienst auf einen Blick: Wurde der Restore schon einmal rea
|
||||
| ntfy | 2 | - | rebuildbar, kein Test noetig | - |
|
||||
| Borg UI | 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)
|
||||
|
||||
1. **Shared PostgreSQL 18 Cluster** - globals + per-DB-Dumps, Bootstrap-Konflikt `mailarchiver`
|
||||
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
|
||||
5. **Mealie** - Tier 2, eigene Postgres
|
||||
6. **Traefik** - Tier 1, aber komplex (dynamic/, LE-State, CF-Token-Mount)
|
||||
Stand 2026-06-06. Die frueheren Kandidaten (Shared PG18, Komodo Mongo, Mailarchiver, Mealie, Traefik)
|
||||
wurden alle am 2026-06-03 abgeschlossen und sind in der Reifegrad-Tabelle belegt.
|
||||
|
||||
Verbleibende offene Restore-Pfade ohne vollstaendigen Test:
|
||||
|
||||
1. **Unraid OS Flash** - Artefakt-Validierung am 2026-06-05 erfolgreich (siehe Reifegrad-Tabelle und Runbook unten); offen bleibt nur der **physische Ersatzstick-Boot-Test**.
|
||||
2. **Tailscale** - State-/Reconnect-Pfad dokumentiert testen
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
+11
-1
@@ -53,12 +53,16 @@ Dieses Dokument listet sensible Daten, deren Ablageorte und die vorgesehene Einb
|
||||
| InfluxDB 3 Core | Admin Token JSON | `/mnt/user/appdata/secrets/influxdb3_admin_token.json` -> Docker Secret `/run/secrets/influxdb3_admin_token` | aktiv |
|
||||
| Monitoring Grafana | Admin Password | `/mnt/user/appdata/secrets/monitoring_grafana_admin_password.txt` -> Docker Secret `/run/secrets/monitoring_grafana_admin_password` -> `GF_SECURITY_ADMIN_PASSWORD__FILE` | aktiv |
|
||||
| Monitoring Grafana -> InfluxDB | Datasource Token | `/mnt/user/appdata/secrets/monitoring_grafana_influxdb_token.txt` -> Docker Secret `/run/secrets/monitoring_grafana_influxdb_token` | aktiv |
|
||||
| Home Assistant -> InfluxDB | HA InfluxDB Token | `/homeassistant/secrets.yaml` -> `influxdb3_homeassistant_token` | geplant |
|
||||
| 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) |
|
||||
| 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`) |
|
||||
| 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 | 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 |
|
||||
| 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) |
|
||||
|
||||
---
|
||||
|
||||
@@ -108,6 +112,12 @@ 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.
|
||||
- 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.
|
||||
- `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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ Secret-Werte sind nicht enthalten. Es werden nur Secret-Namen, Env-Key-Namen und
|
||||
| `traefik` | zentraler Reverse Proxy, TLS, Docker-Label-Routing | `traefik/docker-compose.yml`, `traefik/dynamic/*` | `https://traefik.kaleschke.info` | Docker socket, Cloudflare DNS API, `frontend_net`, `backend_net` | `/mnt/user/appdata/traefik/dynamic`, `/mnt/user/appdata/traefik/letsencrypt` | Tier 1, Share/Borg | ja, eigene Dashboard-Route mit Authelia | Host-Ports 80/443 sind zentrale Ausnahme; dynamic configs werden nicht automatisch von Komodo deployed |
|
||||
| `adguard` | DNS-Server / LAN DNS | `host-services/Adguard/docker-compose.yml` | LAN-Port `53`, Admin `100.80.98.33:8082` | `dns_net`, `frontend_net`, Unbound | `/mnt/user/appdata/adguard/conf`, `/mnt/user/appdata/adguard/work` | Tier 1, config relevant | nein | Direkter DNS-Port 53 bleibt; Admin-Port ist bewusst ohne Traefik/2FA, aber auf Tailscale-IP begrenzt (Operator-Entscheidung 2026-05-26) |
|
||||
| `unbound` | Upstream DNS Resolver fuer AdGuard | `apps/unbound/docker-compose.yml` | intern | `dns_net` | `/mnt/user/appdata/unbound/config` | rebuildbar / config relevant | nein | intern isoliert |
|
||||
| `tailscale` | VPN/Remote-Zugang | `host-services/tailscale/docker-compose.yml` | Tailscale | Host-Netz | `/mnt/user/appdata/tailscale` | Tier 1, State relevant | nein | `network_mode: host`, `NET_ADMIN`, `NET_RAW` und `/dev/net/tun` sind dokumentierte VPN-Ausnahmen |
|
||||
| `tailscale` | VPN/Remote-Zugang, 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 |
|
||||
| `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
|
||||
@@ -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-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 |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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). |
|
||||
| `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 |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| `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` | Unraid User-Script / Hintergrundprozess | Docker CLI, ntfy | `/mnt/user/services/posture-check/docker-critical-events-last.log` | Repo-Skript + letzter Event-Log | nein | Optional als Unraid User-Script `at array start` starten; sendet nach `homelab-alerts` |
|
||||
| `docker-critical-events` | Live-Alarmierung fuer Docker `die`/`oom`/`kill` Events | `services/posture-check/docker-critical-events.sh`, Supervisor `services/posture-check/docker-critical-events-supervisor.sh` | Unraid User-Script / Hintergrundprozess | Docker CLI, ntfy | `/mnt/user/services/posture-check/docker-critical-events-last.log`, PID/Outfile unter `/mnt/user/services/posture-check/` | Repo-Skript + letzter Event-Log | nein | Optional als Unraid User-Script `at array start` starten; Supervisor kann `start`, `stop`, `status`, `smoke`; sendet nach `homelab-alerts` |
|
||||
|
||||
## Backup- und Restore-Hinweise
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,41 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,137 @@
|
||||
# DR-Workstation Readiness - 2026-06-06
|
||||
|
||||
Automatisch erzeugter lokaler Readiness-Check fuer den Operator-PC. Es wurden keine Secret-Werte, Passphrases oder Private-Key-Inhalte ausgegeben.
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
| Check | Ergebnis |
|
||||
|---|---|
|
||||
| WSL2 Ubuntu | vorhanden (`Ubuntu 24.04`, WSL Version 2) |
|
||||
| SSH/Git in WSL | vorhanden |
|
||||
| GitHub-Read-Smoke mit DR-Key | ok |
|
||||
| Borg Client | installiert |
|
||||
| Hetzner Storage Box mit DR-Key | ok |
|
||||
| `~/dr-smoke.sh` | vorhanden |
|
||||
| Finaler Borg-Smoke | ok, Operator-Bestaetigung 2026-06-06 10:05:30 |
|
||||
| WSL sudo ohne Passwortprompt | nein, Operator muss Passwort eingeben |
|
||||
|
||||
## Bewertung
|
||||
|
||||
- Der lokale WSL2-/Ubuntu-Unterbau ist vorhanden.
|
||||
- Die DR-Key-Arbeitskopien liegen in WSL unter `~/.ssh/dr-readonly` und `~/.ssh/dr-hetzner`.
|
||||
- GitHub-Read-Smoke und Hetzner-SSH-Smoke sind erfolgreich.
|
||||
- `borgbackup` ist installiert.
|
||||
- Der vollstaendige Bare-Metal-DR-Smoke ist erfolgreich abgeschlossen.
|
||||
|
||||
## Finaler Borg-Smoke
|
||||
|
||||
Operator-Bestaetigung vom 2026-06-06:
|
||||
|
||||
- Befehl: `bash ~/dr-smoke.sh`
|
||||
- GitHub Deploy-Key: HEAD `3a263a4...`
|
||||
- Hetzner SSH-Login: Repos `backup`, `backup2`, `hetzner_borg_appdata`, `hetzner_borg_appdata_critical` sichtbar
|
||||
- Borg-Repo: `ssh://u565255@u565255.your-storagebox.de/./hetzner_borg_appdata_critical`
|
||||
- Repository ID: `5dd9b949...`
|
||||
- Encryption: `Yes (repokey)`
|
||||
- Borg-Statistik: `Original size 1.16 TB`, `Compressed size 1.13 TB`, `Deduplicated size 35.92 GB`
|
||||
- Ergebnis: `DR-Smoke OK (2026-06-06 10:05:30)`
|
||||
|
||||
Die Borg-Passphrase wurde nur interaktiv eingegeben und nicht dauerhaft auf `baerchen` gespeichert.
|
||||
|
||||
## Rohchecks
|
||||
|
||||
### wsl_status
|
||||
|
||||
- ExitCode: `0`
|
||||
|
||||
```text
|
||||
Standarddistribution: Ubuntu
|
||||
|
||||
Standardversion: 2
|
||||
|
||||
|
||||
```
|
||||
|
||||
### wsl_list
|
||||
|
||||
- ExitCode: `0`
|
||||
|
||||
```text
|
||||
NAME STATE VERSION
|
||||
|
||||
* Ubuntu Stopped 2
|
||||
|
||||
docker-desktop Stopped 2
|
||||
|
||||
|
||||
```
|
||||
|
||||
### ubuntu_os
|
||||
|
||||
- ExitCode: `0`
|
||||
|
||||
```text
|
||||
Distributor ID: Ubuntu
|
||||
Description: Ubuntu 24.04.4 LTS
|
||||
Release: 24.04
|
||||
Codename: noble
|
||||
6.6.114.1-microsoft-standard-WSL2
|
||||
```
|
||||
|
||||
### tools
|
||||
|
||||
- ExitCode: `0`
|
||||
|
||||
```text
|
||||
/usr/bin/borg
|
||||
borg 1.2.8
|
||||
/usr/bin/ssh
|
||||
OpenSSH_9.6p1 Ubuntu-3ubuntu13.16, OpenSSL 3.0.13 30 Jan 2024
|
||||
/usr/bin/git
|
||||
git version 2.43.0
|
||||
```
|
||||
|
||||
### sudo
|
||||
|
||||
- ExitCode: `0`
|
||||
|
||||
```text
|
||||
sudo-password-needed
|
||||
```
|
||||
|
||||
### wsl_ssh_files
|
||||
|
||||
- ExitCode: `0`
|
||||
|
||||
```text
|
||||
total 40
|
||||
drwx------ 2 michi michi 4096 Jun 6 09:14 .
|
||||
drwxr-x--- 5 michi michi 4096 Jun 6 08:37 ..
|
||||
-rw------- 1 michi michi 411 Jun 6 09:14 dr-hetzner
|
||||
-rw------- 1 michi michi 419 Jun 6 09:14 dr-readonly
|
||||
-rw------- 1 michi michi 411 Apr 4 19:29 id_ed25519
|
||||
-rw-r--r-- 1 michi michi 97 Apr 4 19:29 id_ed25519.pub
|
||||
-rw------- 1 michi michi 6358 Jun 6 09:14 known_hosts
|
||||
-rw------- 1 michi michi 3013 Apr 20 20:13 known_hosts.old
|
||||
-rw------- 1 michi michi 3858 Apr 24 08:27 known_hosts.pre-port222-20260604-122031.bak
|
||||
-rwxr-xr-x 1 michi michi 1311 Jun 6 08:37 /home/michi/dr-smoke.sh
|
||||
```
|
||||
|
||||
### github_dr_key_smoke
|
||||
|
||||
- ExitCode: `0`
|
||||
|
||||
```text
|
||||
68d3ace598ee4d1cdad3ed94b63ae5046ac187fb HEAD
|
||||
```
|
||||
|
||||
### hetzner_dr_key_smoke
|
||||
|
||||
- ExitCode: `0`
|
||||
|
||||
```text
|
||||
backup
|
||||
backup2
|
||||
hetzner_borg_appdata
|
||||
hetzner_borg_appdata_critical
|
||||
```
|
||||
@@ -0,0 +1,303 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,228 @@
|
||||
# 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, 7–14 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: niedrig–mittel | 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?
|
||||
@@ -0,0 +1,58 @@
|
||||
# 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>
|
||||
@@ -1,25 +0,0 @@
|
||||
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
|
||||
@@ -66,15 +66,18 @@ services:
|
||||
image: prom/blackbox-exporter:v0.28.0@sha256:e753ff9f3fc458d02cca5eddab5a77e1c175eee484a8925ac7d524f04366c2fc
|
||||
container_name: monitoring-blackbox-exporter
|
||||
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:
|
||||
- 1.1.1.1
|
||||
- 8.8.8.8
|
||||
- 172.23.0.3
|
||||
command:
|
||||
- --config.file=/etc/blackbox_exporter/blackbox.yml
|
||||
volumes:
|
||||
- ./blackbox/blackbox.yml:/etc/blackbox_exporter/blackbox.yml:ro
|
||||
networks:
|
||||
- monitoring_net
|
||||
- dns_net
|
||||
expose:
|
||||
- "9115"
|
||||
security_opt:
|
||||
@@ -129,6 +132,20 @@ services:
|
||||
GF_USERS_ALLOW_SIGN_UP: "false"
|
||||
GF_AUTH_ANONYMOUS_ENABLED: "false"
|
||||
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:
|
||||
- /bin/sh
|
||||
- -c
|
||||
@@ -145,6 +162,7 @@ services:
|
||||
secrets:
|
||||
- monitoring_grafana_admin_password
|
||||
- monitoring_grafana_influxdb_token
|
||||
- grafana_oidc_client_secret
|
||||
expose:
|
||||
- "3000"
|
||||
security_opt:
|
||||
@@ -160,7 +178,8 @@ services:
|
||||
- traefik.http.routers.monitoring-grafana.entrypoints=websecure
|
||||
- traefik.http.routers.monitoring-grafana.tls=true
|
||||
- traefik.http.routers.monitoring-grafana.tls.certresolver=le
|
||||
- traefik.http.routers.monitoring-grafana.middlewares=authelia@file,secure-headers@file
|
||||
# ForwardAuth bewusst entfernt 2026-06-06: Grafana macht jetzt eigenes OIDC-SSO gegen Authelia
|
||||
- traefik.http.routers.monitoring-grafana.middlewares=secure-headers@file
|
||||
- traefik.http.services.monitoring-grafana.loadbalancer.server.port=3000
|
||||
|
||||
grafana-dashboard-importer:
|
||||
@@ -351,6 +370,8 @@ networks:
|
||||
driver: bridge
|
||||
frontend_net:
|
||||
external: true
|
||||
dns_net:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
prometheus_data:
|
||||
@@ -364,5 +385,7 @@ secrets:
|
||||
file: /mnt/user/appdata/secrets/monitoring_grafana_admin_password.txt
|
||||
monitoring_grafana_influxdb_token:
|
||||
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:
|
||||
file: /mnt/user/appdata/secrets/influxdb3_admin_token.json
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
borg-ui:
|
||||
image: ainullcode/borg-ui@sha256:acb0fbe83dc4a3843abc06f814c5f1061a0701b2cfc574da2e851d17a34ab745
|
||||
image: ainullcode/borg-ui@sha256:0922157e8f77a1b2bd23cd09366a458ea6de07fd9306aa1485f9cfe623eca17f
|
||||
container_name: borg-ui
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
code-server:
|
||||
image: lscr.io/linuxserver/code-server:4.123.0@sha256:9dd4555720db04eb92d92cc84e7a34f0862bada5679889446a3004c45b5fa59b
|
||||
image: lscr.io/linuxserver/code-server:4.123.0@sha256:cb261a7f87674b445e0fd66d87d55900c1b823d276c727ab0d168a75e69e9992
|
||||
container_name: code-server
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
filebrowser:
|
||||
image: filebrowser/filebrowser:v2.63.12@sha256:fc8c3a46c16bbdf97362b20c50164e97de9c1dd5f63230d28a4cb15248b53ec3
|
||||
image: filebrowser/filebrowser:v2.63.14@sha256:1ec9b0c68297550c92f4a93feed432850c2993b261706cc3cc2e808f94a95e76
|
||||
container_name: filebrowser
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
|
||||
@@ -497,12 +497,6 @@ pages:
|
||||
description: Upstream Resolver
|
||||
category: network
|
||||
hide: false
|
||||
Tailscale-Docker:
|
||||
name: Tailscale
|
||||
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/tailscale.svg
|
||||
description: VPN
|
||||
category: network
|
||||
hide: false
|
||||
ddns-updater:
|
||||
name: DDNS Updater
|
||||
icon: mdi:cloud-sync
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM nousresearch/hermes-agent:v2026.5.29
|
||||
FROM nousresearch/hermes-agent:v2026.6.5
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@ services:
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# MongoDB – Datenbank fuer Komodo Core
|
||||
# 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:
|
||||
image: mongo:8.0.23@sha256:44aa79ae28ff80b56fe58681b66cda9336706df408a5175a6c04988aa54610d3
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
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"
|
||||
@@ -0,0 +1,127 @@
|
||||
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
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/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"
|
||||
Executable
+125
@@ -0,0 +1,125 @@
|
||||
#!/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
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/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'))"
|
||||
@@ -42,12 +42,17 @@ Ziel:
|
||||
- `authelia-compose.test.yml`: isolierte Testinstanz fuer Authelia inkl. Test-Postgres, Filesystem-Notifier (kein echter SMTP-Versand)
|
||||
- `authelia-plan.md`: konkreter Authelia-Testplan
|
||||
- `authelia-runbook.md`: Operator-Runbook fuer den ersten Authelia-Lauf
|
||||
- `adguard-restore-test.sh`: AdGuard-Home-Restore-Job (Config + isolierter Container + HTTP/DNS-Smoke; Erstlauf 2026-06-06 erfolgreich)
|
||||
- `adguard-compose.test.yml`: isolierte AdGuard-Testinstanz auf localhost-Ports `13001` und `15353`
|
||||
- `redis-restore-test.sh`: Redis-8-Restore-Job (Pre-Cutover-Artefakt + isolierter Container + PING/INFO/DBSIZE; Erstlauf 2026-06-06 erfolgreich)
|
||||
- `redis-compose.test.yml`: isolierte Redis-8-Testinstanz auf localhost-Port `16379`
|
||||
- `nextcloud-restore-test.sh`: Nextcloud-Restore-Job (Scaffold; **blockiert** durch Unraid shfs-chmod-Inkompatibilitaet - siehe unten)
|
||||
- `nextcloud-compose.test.yml`: isolierte Testinstanz fuer Nextcloud inkl. Test-Postgres und Test-Redis
|
||||
|
||||
- `check-restore-freshness.ps1`: woechentlicher Frische-Check fuer Dumps und Reports
|
||||
- `run-restore-checks.ps1`: einfacher Dispatcher fuer Restore-Jobs
|
||||
- `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
|
||||
- `common.sh`: gemeinsame Host-Helferfunktionen
|
||||
- `automation-plan.md`: Host-Job- und Automatisierungsmodell
|
||||
@@ -91,9 +96,12 @@ Aktuell ist das erste validierte Muster vorhanden.
|
||||
- echter Paperless-Restore am 2026-05-07 erfolgreich verifiziert
|
||||
- Immich-Restore-Test am 2026-05-27 erfolgreich verifiziert; Test-Postgres wurde nach der VectorChord-Migration am 2026-05-31 auf das produktive Immich-Postgres-Image umgestellt
|
||||
- Authelia-Restore-Smoke am 2026-06-03 erfolgreich verifiziert; bewusst ohne produktiven Dump-Restore wegen Storage-Encryption-Key-Kopplung
|
||||
- AdGuard-Home-Restore-Smoke am 2026-06-06 erfolgreich verifiziert; Borg-Config-Restore, HTTP `/control/status` 401, DNS-Smoke ok, 7 Filterlisten-Eintraege, Report `/mnt/user/backups/restore-reports/adguard-2026-06-06.md`
|
||||
- Redis-8-Restore-Smoke am 2026-06-06 erfolgreich verifiziert; Pre-Cutover-Artefakt, Redis 8.8, PING ok, AOF aktiv, DBSIZE 1, Report `/mnt/user/backups/restore-reports/redis-2026-06-06.md`
|
||||
- Bash-Dispatcher und Bash-Restore-Jobs am 2026-05-07 erfolgreich hostseitig verifiziert
|
||||
- Restore-Lab und Report-Pfade auf dem Host angelegt
|
||||
- `ntfy`-Wrapper ist fuer Host-Jobs verfuegbar
|
||||
- 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.
|
||||
- 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)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
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
|
||||
Executable
+181
@@ -0,0 +1,181 @@
|
||||
#!/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"
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
#!/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
|
||||
@@ -0,0 +1,16 @@
|
||||
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
|
||||
Executable
+152
@@ -0,0 +1,152 @@
|
||||
#!/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"
|
||||
@@ -10,6 +10,9 @@ case "$MODE" in
|
||||
freshness)
|
||||
exec "$SCRIPT_DIR/check-restore-freshness.sh"
|
||||
;;
|
||||
freshness-negative)
|
||||
exec "$SCRIPT_DIR/negative-freshness-alert-test.sh"
|
||||
;;
|
||||
vaultwarden)
|
||||
if [ "$WHATIF" = "--what-if" ]; then
|
||||
exec "$SCRIPT_DIR/vaultwarden-restore-test.sh" --what-if
|
||||
@@ -40,6 +43,18 @@ case "$MODE" in
|
||||
fi
|
||||
exec "$SCRIPT_DIR/authelia-restore-test.sh"
|
||||
;;
|
||||
adguard)
|
||||
if [ "$WHATIF" = "--what-if" ]; then
|
||||
exec "$SCRIPT_DIR/adguard-restore-test.sh" --what-if
|
||||
fi
|
||||
exec "$SCRIPT_DIR/adguard-restore-test.sh"
|
||||
;;
|
||||
redis)
|
||||
if [ "$WHATIF" = "--what-if" ]; then
|
||||
exec "$SCRIPT_DIR/redis-restore-test.sh" --what-if
|
||||
fi
|
||||
exec "$SCRIPT_DIR/redis-restore-test.sh"
|
||||
;;
|
||||
nextcloud)
|
||||
if [ "$WHATIF" = "--what-if" ]; then
|
||||
exec "$SCRIPT_DIR/nextcloud-restore-test.sh" --what-if
|
||||
@@ -83,7 +98,7 @@ case "$MODE" in
|
||||
exec "$SCRIPT_DIR/shared-pg-cluster-restore-test.sh"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {freshness|vaultwarden|gitea|paperless|immich|authelia|nextcloud|komodo-bootstrap|komodo-mongo-restore|shared-pg-cluster} [--what-if]" >&2
|
||||
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
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -28,6 +28,8 @@ Quartalsweise:
|
||||
|
||||
- Restore-/DR-Sanity-Check
|
||||
- `immich` Restore-Smoke-Test (DB + UI, ohne produktive Foto-Mounts; Erstlauf 2026-05-27 erfolgreich)
|
||||
- `adguard` Restore-Smoke-Test (Config + HTTP/DNS, nach DNS-Aenderungen auch ausserhalb des Quartals)
|
||||
- `redis` Restore-Smoke-Test (Pre-Cutover-Artefakt + Redis 8, vor/nach Major-Aenderungen auch ausserhalb des Quartals)
|
||||
- pruefen:
|
||||
- Restore-Lab-Struktur
|
||||
- Reports
|
||||
@@ -45,7 +47,9 @@ Quartals-Belegung:
|
||||
|
||||
Bestaetigte Mini-Restores: Vaultwarden, Gitea und Paperless am 2026-05-07;
|
||||
Immich am 2026-05-27; Paperless erneut am 2026-05-31; Authelia am
|
||||
2026-06-03 (Config-Smoke ohne produktiven Dump-Restore).
|
||||
2026-06-03 (Config-Smoke ohne produktiven Dump-Restore); AdGuard Home am
|
||||
2026-06-06 (Config + HTTP/DNS-Smoke); Redis 8 am 2026-06-06
|
||||
(Pre-Cutover-Artefakt + PING/INFO/DBSIZE-Smoke).
|
||||
|
||||
## Konkreter Kalender
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
scrutiny:
|
||||
image: ghcr.io/starosdev/scrutiny:latest-omnibus@sha256:bf5a07f583998b1b0a690d4b06be0378baa6f6f0140b4c815bd4fa55268df223
|
||||
image: ghcr.io/starosdev/scrutiny:latest-omnibus@sha256:228483f16a6236d2fa9b2fbfca2e76dc861e648fbc6ae6e680d23e5d00211a5d
|
||||
container_name: scrutiny
|
||||
restart: unless-stopped
|
||||
privileged: true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
speedtest-tracker:
|
||||
image: lscr.io/linuxserver/speedtest-tracker:1.14.3@sha256:79c00631575dec6d91c10ed904c211224f00813013a305c2284324e195a538bb
|
||||
image: lscr.io/linuxserver/speedtest-tracker:1.14.3@sha256:c3750c40948a9360000ce62d694da92e85584b4ab6d3d9a9d1432d76fa5e0726
|
||||
container_name: speedtest-tracker
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
|
||||
@@ -6,5 +6,6 @@ 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.
|
||||
- `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/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.
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
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"
|
||||
@@ -0,0 +1,81 @@
|
||||
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
|
||||
@@ -0,0 +1,86 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,132 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,325 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,148 @@
|
||||
# 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.
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,184 @@
|
||||
# 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,12 +43,19 @@ Erledigt:
|
||||
Noch offen:
|
||||
|
||||
- Manuelle Screenshots in `H:\Windows-Neuaufsetzen-Backup\14_Screenshots` ablegen.
|
||||
- BitLocker-Status mit Adminrechten pruefen.
|
||||
- Passwortmanager, 2FA-Recovery-Codes und Browser-Sync manuell pruefen.
|
||||
- Banking4-Speicherort explizit pruefen.
|
||||
- Banking4 im Programm selbst oeffnen und aktuellen Datentresor/Backup-Export bestaetigen. Der Key und der Datentresor sind bereits lokal auf H: gesichert.
|
||||
- WISO Steuer 2026 oeffnen und Lizenz/Buhl-Konto sowie Speicherorte der Steuerdateien bestaetigen.
|
||||
- Microsoft-Konto fuer M365 pruefen: Office-Webkonto/Abonnement, Installationsrecht, OneDrive-Sync.
|
||||
- BitLocker-Status mit Adminrechten pruefen. **Nachlauf 2026-06-05:** Status
|
||||
wurde geprueft; C:/D:/E:/G:/H: sind `FullyDecrypted`, Protection `Off`.
|
||||
Offen bleibt nur die bewusste BitLocker-Entscheidung.
|
||||
- Passwortmanager, 2FA-Recovery-Codes und Browser-Sync manuell pruefen. **Erledigt 2026-06-06 laut Operator-Bestaetigung.**
|
||||
- Banking4-Speicherort explizit pruefen. **Erledigt 2026-06-06 laut Operator-Bestaetigung.**
|
||||
- Banking4 im Programm selbst oeffnen und aktuellen Datentresor/Backup-Export bestaetigen. Der Key und der Datentresor sind bereits lokal auf H: gesichert. **Erledigt 2026-06-06 laut Operator-Bestaetigung.**
|
||||
- WISO Steuer 2026 oeffnen und Lizenz/Buhl-Konto sowie Speicherorte der Steuerdateien bestaetigen. **Erledigt 2026-06-06 laut Operator-Bestaetigung.**
|
||||
- Microsoft-Konto fuer M365 pruefen: Office-Webkonto/Abonnement, Installationsrecht, OneDrive-Sync. **Erledigt 2026-06-06 laut Operator-Bestaetigung.**
|
||||
- Technisches Nachinstallations-Inventar 2026-06-06 erledigt:
|
||||
`baerchen-app-license-readiness-2026-06-06.md`. Banking4, WISO Steuer
|
||||
2026, Microsoft 365/OneDrive und relevante Datenpfade sind lokal sichtbar;
|
||||
Konto-/App-Oeffnen-Checks wurden am 2026-06-06 durch den Operator
|
||||
bestaetigt ("alle Dienste laufen").
|
||||
- Optional Keyfinder-Lauf durchfuehren und Ergebnisse lokal auf H: speichern.
|
||||
- `G:\Ollama` bewusst entscheiden: nicht gesichert, ca. 40,9 GB lokale Modell-/Cache-Daten.
|
||||
- D:, F: und G: vor dem spaeteren Loeschen noch einmal in Ruhe final bestaetigen.
|
||||
@@ -528,16 +535,22 @@ Zielstruktur:
|
||||
|
||||
## 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
|
||||
- [ ] Programmliste exportiert
|
||||
- [ ] Laufwerksliste exportiert
|
||||
- [ ] Windows-Aktivierung dokumentiert
|
||||
- [ ] Benutzerordner gesichert
|
||||
- [ ] Browser-Lesezeichen exportiert oder Sync geprüft
|
||||
- [ ] Passwortmanager geprüft
|
||||
- [ ] 2FA-Recovery-Codes gesichert
|
||||
- [x] Passwortmanager geprüft
|
||||
- [x] 2FA-Recovery-Codes gesichert
|
||||
- [ ] SSH/API/GPG/Zertifikate gesichert
|
||||
- [ ] Banking4-Speicherort geprüft und gesichert
|
||||
- [x] Banking4-Speicherort geprüft und gesichert
|
||||
- [ ] Homelab-/NAS-Doku gesichert
|
||||
- [ ] D:, F: und G: analysiert
|
||||
- [ ] Unklare Daten nach `99_Unsortiert_von_D_F_G` kopiert
|
||||
|
||||
+15
-1
@@ -38,6 +38,19 @@
|
||||
"automerge": false,
|
||||
"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",
|
||||
"matchPackageNames": [
|
||||
@@ -99,6 +112,7 @@
|
||||
"ignorePaths": [
|
||||
"**/_archive/**",
|
||||
"ops/grafana-influxdb/**",
|
||||
"ops/loki/**"
|
||||
"ops/loki/**",
|
||||
"ops/komodo/**"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -41,18 +41,21 @@ access_control:
|
||||
- git.kaleschke.info
|
||||
policy: bypass
|
||||
|
||||
# Admin-Dienste - 2FA erforderlich (Operator-UIs mit Host-/Backup-Zugriff)
|
||||
# Expliziter 2FA-Block (Tier-1-Operator-UIs). Redundant, seit die Catch-all
|
||||
# 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:
|
||||
- files.kaleschke.info
|
||||
- scrutiny.kaleschke.info
|
||||
- borg.kaleschke.info
|
||||
- code.kaleschke.info
|
||||
policy: two_factor
|
||||
|
||||
# Alles andere mit Authelia-Middleware - 1FA.
|
||||
# Alles andere mit Authelia-Middleware - 2FA (Haertung 2026-06-06).
|
||||
# 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.
|
||||
- domain: "*.kaleschke.info"
|
||||
policy: one_factor
|
||||
policy: two_factor
|
||||
|
||||
session:
|
||||
name: authelia_session
|
||||
|
||||
@@ -11,6 +11,7 @@ SINCE="${SINCE:-24h}"
|
||||
MAX_LOG_LINES="${MAX_LOG_LINES:-80}"
|
||||
CERT_MAX_ROWS="${CERT_MAX_ROWS:-12}"
|
||||
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}"
|
||||
DISK_USAGE_WARN_PCT="${DISK_USAGE_WARN_PCT:-85}"
|
||||
CERT_WARN_DAYS="${CERT_WARN_DAYS:-21}"
|
||||
@@ -28,6 +29,7 @@ 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}"
|
||||
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_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}"
|
||||
POSTURE_CHECK_FILE="${POSTURE_CHECK_FILE:-/mnt/user/services/posture-check/last.json}"
|
||||
LOCK_FILE="${LOCK_FILE:-/tmp/homelab-daily-report.lock}"
|
||||
@@ -459,6 +461,10 @@ with open("/acme.json", "r", encoding="utf-8") as handle:
|
||||
data = json.load(handle)
|
||||
|
||||
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 cert in resolver.get("Certificates", []):
|
||||
domain = cert.get("domain", {}).get("main") or "-"
|
||||
@@ -474,7 +480,11 @@ for resolver in data.values():
|
||||
not_after = datetime.strptime(decoded["notAfter"], "%b %d %H:%M:%S %Y %Z").replace(tzinfo=timezone.utc)
|
||||
days = (not_after - now).days
|
||||
names = ", ".join([domain, *sans])
|
||||
print(f"{days}\t{not_after.date().isoformat()}\t{names}")
|
||||
key = frozenset([domain, *sans])
|
||||
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
|
||||
then
|
||||
if [ ! -s "$cert_file" ]; then
|
||||
@@ -573,13 +583,36 @@ collect_image_freshness() {
|
||||
|
||||
local image_file="$TMP_DIR/images.tsv"
|
||||
local image_warnings=0
|
||||
local image_allowed=0
|
||||
local now_epoch
|
||||
: > "$image_file"
|
||||
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
|
||||
[ -n "$name" ] || continue
|
||||
local image_id created_iso created_epoch age_days image_tag
|
||||
local image_id created_iso created_epoch age_days image_tag note recheck
|
||||
image_id="$(docker inspect --format '{{.Image}}' "$name" 2>/dev/null || true)"
|
||||
[ -n "$image_id" ] || continue
|
||||
created_iso="$(docker image inspect --format '{{.Created}}' "$image_id" 2>/dev/null || true)"
|
||||
@@ -588,33 +621,46 @@ collect_image_freshness() {
|
||||
created_epoch="$(date -d "$created_iso" +%s 2>/dev/null || echo 0)"
|
||||
[ "$created_epoch" -gt 0 ] || continue
|
||||
age_days=$(( (now_epoch - created_epoch) / 86400 ))
|
||||
printf '%d\t%s\t%s\n' "$age_days" "$name" "$image_tag" >> "$image_file"
|
||||
note=""
|
||||
if [ "$age_days" -ge "$IMAGE_AGE_WARN_DAYS" ]; then
|
||||
image_warnings=$((image_warnings + 1))
|
||||
recheck="$(awk -F '\t' -v n="$name" '$1 == n { print $2; found = 1 } END { exit !found }' "$allow_file" || true)"
|
||||
if [ -n "$recheck" ]; then
|
||||
note="bewusst gepinnt (recheck $recheck)"
|
||||
image_allowed=$((image_allowed + 1))
|
||||
else
|
||||
note="ueberaltert"
|
||||
image_warnings=$((image_warnings + 1))
|
||||
fi
|
||||
fi
|
||||
printf '%d\t%s\t%s\t%s\n' "$age_days" "$name" "$image_tag" "$note" >> "$image_file"
|
||||
done < <(docker ps --format '{{.Names}}')
|
||||
|
||||
set_summary "image_warnings" "$image_warnings"
|
||||
set_summary "image_allowed" "$image_allowed"
|
||||
|
||||
if [ ! -s "$image_file" ]; then
|
||||
append "- Keine Image-Daten verfuegbar."
|
||||
record_section_error "images" "Keine Image-Daten ermittelt"
|
||||
else
|
||||
append "- Schwelle Warnung: Image aelter als $IMAGE_AGE_WARN_DAYS Tage"
|
||||
append "- Container mit Image >= $IMAGE_AGE_WARN_DAYS Tage: $image_warnings"
|
||||
append "- Container mit ueberaltertem Image (gewarnt): $image_warnings"
|
||||
append "- Davon bewusst gepinnt (von Warnung ausgenommen): $image_allowed"
|
||||
append "- Allowlist-Quelle: \`$IMAGE_AGE_ALLOW_FILE\`"
|
||||
append ""
|
||||
append "### Aelteste Images (Top 10)"
|
||||
append ""
|
||||
append "| Alter Tage | Container | Image |"
|
||||
append "|---:|---|---|"
|
||||
sort -nr "$image_file" | head -n 10 | while IFS="$(printf '\t')" read -r age name img; do
|
||||
append "| $age | $name | $img |"
|
||||
append "| Alter Tage | Container | Image | Hinweis |"
|
||||
append "|---:|---|---|---|"
|
||||
sort -nr "$image_file" | head -n 10 | while IFS="$(printf '\t')" read -r age name img note; do
|
||||
append "| $age | $name | $img | ${note:-} |"
|
||||
done
|
||||
append ""
|
||||
if [ "$image_warnings" -eq 0 ]; then
|
||||
if [ "$image_warnings" -eq 0 ] && [ "$image_allowed" -eq 0 ]; then
|
||||
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
|
||||
append "Bewertung: $image_warnings Container nutzen Images aelter als $IMAGE_AGE_WARN_DAYS Tage. Update-Pipeline und CVE-Status pruefen."
|
||||
append "Bewertung: $image_warnings Container nutzen ueberalterte Images (nicht in der Allowlist). Update-Pipeline und CVE-Status pruefen."
|
||||
fi
|
||||
fi
|
||||
append ""
|
||||
@@ -655,6 +701,31 @@ collect_container_events() {
|
||||
collect_container_state() {
|
||||
append "## Container-Zustand"
|
||||
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"
|
||||
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"
|
||||
@@ -810,12 +881,35 @@ collect_log_highlights() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# Threshold escalation: how many patterns produced more than the threshold?
|
||||
local noise_threshold_exceeded=0
|
||||
# Escalation-exempt patterns: known noise that is also permanently very loud
|
||||
# (e.g. Unraid mdadm parse spam). Without this, such a pattern would keep the
|
||||
# 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
|
||||
noise_threshold_exceeded="$(awk -v t="$NOISE_ESCALATION_THRESHOLD" '$1 > t { n++ } END { print n + 0 }' "$noise_by_pattern")"
|
||||
noise_threshold_exceeded="$(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")"
|
||||
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
|
||||
set_summary "noise_threshold_exceeded" "$noise_threshold_exceeded"
|
||||
set_summary "noise_threshold_exempt" "$noise_threshold_exempt"
|
||||
|
||||
local hit_count attention_count known_noise_count
|
||||
hit_count="$(count_lines < "$hits")"
|
||||
@@ -836,6 +930,9 @@ collect_log_highlights() {
|
||||
if [ "$noise_threshold_exceeded" -gt 0 ]; then
|
||||
append "- WARNUNG: $noise_threshold_exceeded Pattern ueberschreit(en) die Schwelle - bitte pruefen ob noch wirklich Noise."
|
||||
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 ""
|
||||
|
||||
if [ "$attention_count" -eq 0 ]; then
|
||||
@@ -885,22 +982,32 @@ collect_log_highlights() {
|
||||
if [ -s "$noise_by_pattern" ]; then
|
||||
append "#### Pattern mit den meisten Treffern"
|
||||
append ""
|
||||
append "| Pattern | Anzahl |"
|
||||
append "|---|---:|"
|
||||
append "| Pattern | Anzahl | Hinweis |"
|
||||
append "|---|---:|---|"
|
||||
head -n "$NOISE_BREAKDOWN_TOP_N" "$noise_by_pattern" \
|
||||
| while IFS="$(printf '\t')" read -r cnt pat; do
|
||||
local short="$pat"
|
||||
local short="$pat" note=""
|
||||
# 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
|
||||
short="${short:0:77}..."
|
||||
fi
|
||||
# Escape pipe characters that would break the markdown table.
|
||||
short="${short//|/\\|}"
|
||||
append "| \`$short\` | $cnt |"
|
||||
append "| \`$short\` | $cnt | $note |"
|
||||
done
|
||||
append ""
|
||||
fi
|
||||
if [ "$noise_threshold_exceeded" -gt 0 ]; then
|
||||
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."
|
||||
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."
|
||||
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
|
||||
append "Bewertung: Kein Pattern ueberschreitet die Eskalations-Schwelle ($NOISE_ESCALATION_THRESHOLD)."
|
||||
fi
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
#!/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,7 +4,9 @@ set -euo pipefail
|
||||
TEXTFILE_DIR="${TEXTFILE_DIR:-/mnt/user/services/posture-check/textfile}"
|
||||
OUTPUT_FILE="${OUTPUT_FILE:-$TEXTFILE_DIR/homelab.prom}"
|
||||
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 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}"
|
||||
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}"
|
||||
# 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"
|
||||
tmp="$(mktemp "$TEXTFILE_DIR/homelab.prom.XXXXXX")"
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# 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
|
||||
@@ -18,7 +18,7 @@
|
||||
# Removing a pattern: replace with a fresh attention example in the next
|
||||
# daily report and consult before reintroducing.
|
||||
#
|
||||
# Last reviewed: 2026-05-21
|
||||
# Last reviewed: 2026-06-10
|
||||
|
||||
# Loki internal query cancellations / scheduler chatter.
|
||||
# Why: Loki cancels internal queries continuously when downstream Promtails
|
||||
@@ -72,3 +72,18 @@ authelia.*Request timeout occurred.*status_code=408
|
||||
# noise becomes overwhelming, add a *narrow* pattern restricted to
|
||||
# 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)
|
||||
|
||||
# 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
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# 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
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/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"
|
||||
@@ -93,12 +93,29 @@ bash /mnt/user/services/homelab-infra/services/posture-check/daily-status-report
|
||||
|
||||
## `docker-critical-events-at-start`
|
||||
|
||||
Zeit: Array Start. Dieser Job startet einen Hintergrund-Watcher und beendet sich sofort.
|
||||
Zeit: Array Start. Dieser Job startet einen Hintergrund-Watcher und beendet sich
|
||||
sofort. Der Supervisor schreibt PID, stdout/stderr und Event-Log nach
|
||||
`/mnt/user/services/posture-check/`.
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
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 &
|
||||
exit 0
|
||||
exec /mnt/user/services/homelab-infra/services/posture-check/docker-critical-events-supervisor.sh start
|
||||
```
|
||||
|
||||
Status pruefen:
|
||||
|
||||
```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,6 +1,6 @@
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.7@sha256:25cd7b175a493ea66a40329e23a649b59eda38b7e2a570493bf63fc4d74fd1c1
|
||||
image: traefik:v3.7@sha256:d6858791f9e74df44ca4014166647c41cdc2abd3bf2a71b832ca4e1c6a91b257
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user