# Infrastrukturübersicht — K3s Umgebung

## Netzwerk / Perimeter

- Cluster-Zone: DMZ
- Edge/Router: OPNsense
  - IP: 192.168.20.1
- Reverse Proxy: nginx auf Raspberry Pi
  - IP: 192.168.20.20
  - Position: vor dem K3s-Cluster, hinter OPNsense
- K3s-Cluster Nodes:
  - 192.168.20.40 — mbrkanov-Mini-IT13 / mbrkanov-mini-it13 — control-plane/master
  - 192.168.20.45 — Mini-IT13-02 / mini-it13-02 — worker

## Betriebsprinzip

- Diagnosen müssen den Pfad OPNsense → nginx Proxy → K3s/Traefik/Services berücksichtigen.
- Bei Erreichbarkeitsproblemen zuerst unterscheiden:
  1. DNS / Public IP / NAT / Firewall auf OPNsense
  2. nginx Reverse Proxy auf 192.168.20.20
  3. K3s ServiceLB / Traefik / Ingress
  4. Kubernetes Service / Pod / Storage / App

## Reverse Proxy Details

- Hostname: raspi
- OS: Debian GNU/Linux 12 (bookworm), Raspberry Pi aarch64
- nginx/WebUI läuft containerisiert via Docker.
- Offene Proxy-Ports auf 192.168.20.20:
  - 80/tcp
  - 81/tcp — vermutlich WebUI / Management UI
  - 443/tcp
- Zusätzlich sichtbar: Docker-Ports 8000/tcp und 9000/tcp für einen weiteren Container/Service.
- Direkte Änderungen an nginx Config-Dateien vermeiden; Einstellungen wurden bisher über die WebUI gepflegt.

## Nginx Proxy Manager Inventur (read-only)

Container:
- `ngm2-app-1` — `jc21/nginx-proxy-manager:latest`
- Daten: `/data/compose/2/data:/data`
- Let's Encrypt: `/data/compose/2/letsencrypt:/etc/letsencrypt`
- DB: `/data/database.sqlite` im Container

Aktive Proxy Hosts:
- nextcloud.cattaro.eu → http://192.168.20.30:80
- service.heckenfels.com → http://192.168.20.40:8080
- office.cattaro.eu → http://192.168.188.40:80
- meshcentral.cattaro.eu → http://192.168.188.42:2087
- dashboard.heckenfels.com → http://192.168.20.40:30718
- bitwarden.cattaro.eu → http://192.168.20.20:8080
- matomo.heckenfels.com → http://192.168.20.40:80
- argocd.cattaro.eu → http://192.168.20.40:31797
- mautic.heckenfels.com → http://192.168.20.40:31797
- crm.heckenfels.com → http://192.168.20.40:31797
- suitecrm.heckenfels.com → http://192.168.20.40:31797
- n8n.heckenfels.com → https://192.168.20.40:443

Betriebsregel:
- Proxy-Einstellungen können ausgelesen werden.
- Änderungen nur nach explizitem OK von Mislav pro konkreter Änderung.
- Bevorzugt über Nginx Proxy Manager Mechanismus/API/DB/WebUI-nah, nicht durch freies Editieren generierter nginx-Dateien.

## Mandanten / Firmenkontext

Der Cluster betreibt Services für zwei Firmenkontexte:

### Cattaro GmbH

- Primäre Domain: `*.cattaro.eu`
- Beispiele aus Nginx Proxy Manager:
  - `nextcloud.cattaro.eu`
  - `office.cattaro.eu`
  - `meshcentral.cattaro.eu`
  - `bitwarden.cattaro.eu`
  - `argocd.cattaro.eu`

### Pidoka GmbH

- Primäre Domain: `*.heckenfels.com`
- Beispiele aus Nginx Proxy Manager:
  - `service.heckenfels.com`
  - `dashboard.heckenfels.com`
  - `matomo.heckenfels.com`
  - `mautic.heckenfels.com`
  - `crm.heckenfels.com`
  - `suitecrm.heckenfels.com`
  - `n8n.heckenfels.com`

## Deployment / GitOps Kontext

- ArgoCD wurde eingeführt, um Deployments zu professionalisieren.
- Der ArgoCD-Aufbau ist noch nicht fertiggestellt und vermutlich noch nicht vollständig professionell strukturiert.
- Ziel für Unterstützung:
  - Ist-Zustand read-only erfassen
  - App-/Namespace-/Repo-Struktur verstehen
  - Risiken und Inkonsistenzen benennen
  - Vorschlag für saubere GitOps-Struktur ausarbeiten
  - Änderungen nur nach explizitem OK von Mislav

## SSH-Zugriff / Betriebszugriff

Zielvorgabe von Mislav (2026-06-15): kube-bot soll grundsätzlich SSH-Zugriff auf alle bekannten Infrastruktur-Systeme haben, damit Diagnose/Troubleshooting vollständig möglich ist.

Bekannte Zielsysteme für SSH-Zugriff:
- OPNsense / Edge-Router — `192.168.20.1` — Zone: DMZ/Perimeter
- Raspberry Pi / Nginx Proxy Manager — `192.168.20.20` — Zone: DMZ
- K3s Control-Plane `mbrkanov-Mini-IT13` / `mbrkanov-mini-it13` — `192.168.20.40` — Zone: DMZ
- K3s Worker `Mini-IT13-02` / `mini-it13-02` — `192.168.20.45` — Zone: DMZ
- IT15 Management-Node — `192.168.1.121` — Zone: Intranet/Management
- Synology DiskStation — Backup-System — `192.168.1.40` — Zone: Intranet/Backup

Betriebsregel:
- SSH wird primär für read-only Diagnose, Statusprüfung und Troubleshooting verwendet.
- Änderungen über SSH nur nach explizitem OK von Mislav und mit konkretem Scope.
- SSH-Private-Keys oder Credentials niemals in Slack/Logs ausgeben.
- Offene Aufgabe: kube-bot/OpenClaw SSH-Private-Keys sicher in Infisical ablegen, damit sie nach OpenClaw-Updates nicht verloren gehen.

## Backup-Systeme

### DMZ Backup Staging — dmz-backup

- Hostname/Rolle: `dmz-backup`, Hilfs-Backup-Server in der DMZ.
- IP: `192.168.20.90`.
- Zielbild:
  - K3s/DMZ-Cluster pusht Backup-Dateien nach `dmz-backup`.
  - Synology/DiskStation pullt von `dmz-backup` und bleibt Backup-Master.
  - Direkte Schreibrechte aus der DMZ auf Synology werden vermieden.
- Vorgeschlagener Staging-Root: `/srv/dmz-backup/k3s-dmz/`.
- Restore-Staging-Root: `/srv/dmz-backup/k3s-dmz/_restore-staging/`.
- Restore-Richtung: DiskStation -> `dmz-backup` -> K3s/kube-bot. K3s greift nicht direkt auf DiskStation zu.
- Ordnerstruktur-Entwurf: `/home/node/.openclaw/workspace/inventory/backup-folder-structure-dmz-backup-20260619.md`.
- Homarr Phase-1 Backup-Push verifiziert am 2026-06-20:
  - Zielpfad: `/srv/dmz-backup/k3s-dmz/tenants/pidoka/prod/homarr/`.
  - Artefakt: `20260620T104142Z__dmz-k3s__pidoka__homarr__metadata__pilot.tar.gz.age`.
  - Verschluesselte SHA256: `b90154a732f756a10de218f30d28fa9afd8bd15644781aa207011188ca4a5f19`.
  - Verschluesselung: `age` v1.3.1, Recipient/Private-Key nur als Infisical-Secretreferenzen in `Backup_Restore/prod`; kein Private Key auf `dmz-backup`.
- Homarr Restore-Transport-Pilot verifiziert am 2026-06-21:
  - Restore-Staging-Pfad: `/srv/dmz-backup/k3s-dmz/_restore-staging/pidoka/prod/homarr/20260621T1733Z-homarr-pilot-restore/`.
  - `diskstation-bot` legte `.age` und `.sha256` aus der DiskStation-Kopie nach `dmz-backup`; kube-bot verifizierte read-only ab `dmz-backup`.
  - Decrypt, Archiv-Checksum und Kubernetes Server-Dry-Run fuer normale K8s-Objekte waren erfolgreich.
  - Grenze: Artefakt ist nur `metadata-manifest-pilot`, kein vollstaendiger App-Restore, keine PVC-Dateiinhalte, keine Longhorn-Backupdaten.
- Direkter DiskStation -> `dmz-backup` SSH-Zugriff wurde durch `diskstation-bot` am 2026-06-21 eingerichtet und getestet:
  - Quelle: `diskstation-bot@DiskStation`.
  - Ziel: `openclaw@192.168.20.90`.
  - Status: OK.
- kube-bot (OpenClaw Runtime) SSH-Zugriff auf `dmz-backup` eingerichtet und getestet — 2026-06-22:
  - Quelle: kube-bot OpenClaw Runtime.
  - Ziel: `openclaw@192.168.20.90`.
  - Key-Fingerprint: `SHA256:9ODQkpBJatqVklqycystgn/562ama40+w3bIt31ofsw`.
  - Key-Kommentar: `kube-bot@openclaw-dmz-backup-20260622`.
  - Infisical-Ablage: `Backup_Restore/prod` — Secret-Namen:
    - `KUBE_BOT_DMZ_BACKUP_SSH_PRIVATE_KEY`
    - `KUBE_BOT_DMZ_BACKUP_SSH_PUBLIC_KEY`
    - `KUBE_BOT_DMZ_BACKUP_SSH_FINGERPRINT`
  - Recovery: Private Key aus Infisical lesen -> chmod 600 -> Fingerprint prüfen -> SSH-Test.
  - Status: OK (verifiziert 2026-06-22).
- Änderungen an Backup-Konfigurationen nur nach explizitem OK von Mislav.

### Synology DiskStation

- Rolle: Backup-Ziel / Backup-System.
- IP: `192.168.1.40`.
- Operativer Bot/User: `diskstation-bot`.
- Gemeinsamer Share fuer Bot-Artefakte: `KI_Agenten`.
- Zielrolle: Backup-Master, der Backups von `dmz-backup` (`192.168.20.90`) pullt.
- Backup-Pfad fuer K3s-DMZ: `/volume1/KI_Agenten/backups/k3s-dmz/`.
- Homarr-Pilot-Ablage: `/volume1/KI_Agenten/backups/k3s-dmz/tenants/pidoka/prod/homarr/`.
- Restore-Staging-Pfad: `/volume1/KI_Agenten/backups/k3s-dmz/_restore-staging/pidoka/prod/homarr/`.
- Status/Logs: `/volume1/KI_Agenten/backups/k3s-dmz/status/` und `/volume1/KI_Agenten/backups/k3s-dmz/logs/`.
- DiskStation-Dokumentation von `diskstation-bot`:
  - `/volume1/KI_Agenten/backups/k3s-dmz/docs/BACKUP_RESTORE_PROCESS_K3S_DMZ.md`
  - `/volume1/KI_Agenten/backups/k3s-dmz/docs/ARCHITECTURE_AND_SECRETS_K3S_DMZ.md`
- Konkrete Synology Pull-Jobs, Retention, Snapshots/Immutability und Restore-Tests sind mit `diskstation-bot` abzugleichen.
- Änderungen an Backup-Konfigurationen nur nach explizitem OK von Mislav.

### Infisical Backup-/Restore-Secret-Merker

Stand: 2026-06-21. Keine Secretwerte dokumentieren.

- Cattaro Infisical/Intranet:
  - URL: `http://192.168.1.121:8088`.
  - Projekt: `Backup_Restore`.
  - Project-ID: `ba0a79d9-32fe-474c-be2f-88384a3d9749`.
  - Environment: `prod`.
  - Verifizierte Secret-Namensgruppen: `DMZ_BACKUP_*`, `DISKSTATION_*`, `BACKUP_HOMARR_AGE_*`.
- HOB Cattaro:
  - Lokale Credential-Datei fuer Bootstrap: `/home/node/.openclaw/workspace/.hob-cattaro-creds`.
  - kube-bot Pfad: `/kube-bot`.
  - Verifizierte Bridge-Secrets: `CATTARO_INFISICAL_URL`, `CATTARO_INFISICAL_CLIENT_ID`, `CATTARO_INFISICAL_CLIENT_SECRET`, `CATTARO_INFISICAL_PROJECT_ID`.
- Ziel nach naechstem Deployment: HOB-Cattaro injiziert die `CATTARO_INFISICAL_*` Runtime-Variablen; darueber kann kube-bot wieder auf die tenant-spezifische Cattaro-Infisical-Instanz zugreifen.


### SSH-Zugangsmerker — kube-bot/OpenClaw Runtime

Stand: 2026-06-15 13:00 UTC. Diese Merker dienen dazu, eingerichtete Diagnosezugänge nicht zu vergessen. Keine Private-Key-Inhalte dokumentieren.

- OpenClaw Runtime läuft nicht direkt auf den Zielsystemen, sondern aktuell als User `node` im Container/Host `0e9d9d0b8619`.
- Lokaler SSH-Key-Pfad in der Runtime: `/home/node/.ssh/kube-bot-it15-ed25519`.
- DMZ K3s Control-Plane:
  - Ziel: `kube-bot@192.168.20.40`
  - Hostnamen: `mbrkanov-Mini-IT13` / `mbrkanov-mini-it13`
  - Status: SSH funktioniert, sudo NOPASSWD funktioniert.
  - Kubernetes read-only Beispiel: `ssh -i ~/.ssh/kube-bot-it15-ed25519 kube-bot@192.168.20.40 'sudo kubectl --kubeconfig=/etc/rancher/k3s/k3s.yaml get nodes -o wide'`
  - Hinweis: `sudo KUBECONFIG=... kubectl` funktioniert nicht, weil sudo das Setzen der Env-Variable blockiert. Stattdessen `sudo kubectl --kubeconfig=/etc/rancher/k3s/k3s.yaml ...` verwenden.
- DMZ K3s Worker:
  - Ziel: `kube-bot@192.168.20.45`
  - Hostnamen: `Mini-IT13-02` / `mini-it13-02`
  - Status: als bekanntes Ziel dokumentiert; Zugriff bei Bedarf erneut testen.
- IT15 Management-Node:
  - Ziel laut älteren Notizen: `Misla@192.168.1.121`
  - Status am 2026-06-15 12:56 UTC: SSH von aktueller Runtime blockiert (`Permission denied`).
  - Früherer Zugriff war dokumentiert, aber aktueller nutzbarer Key/User muss neu bestätigt oder eingerichtet werden.
- Betriebsregel bleibt: SSH primär read-only Diagnose; Änderungen nur nach explizitem OK von Mislav.

### IT15 SSH-Korrektur — 2026-06-15 14:15 UTC

- Korrektes SSH-Ziel: `Misla@192.168.1.121` mit Key `/home/node/.ssh/kube-bot-it15-ed25519`.
- IT15 ist Windows; K3s/ArgoCD laufen in WSL `Ubuntu`.
- Für Linux/K3s-Kommandos über SSH verwenden: `wsl.exe -d Ubuntu -e bash -lc '<command>'`.
- Beispiel: `ssh -i /home/node/.ssh/kube-bot-it15-ed25519 Misla@192.168.1.121 "wsl.exe -d Ubuntu -e bash -lc 'kubectl get nodes'"`.

### Infisical Recovery-Ablage — kube-bot SSH Keys — 2026-06-15 14:29 UTC

- Infisical Instanz: `http://192.168.1.121:8088`.
- Projekt: `platform-kubernetes` (`c5e8661f-4b7a-43e1-80b0-8674f30950dc`).
- Environment: `prod`.
- Secret-Pfad: `/bots/kube-bot/ssh`.
- Abgelegte Secret-Namen:
  - `SSH_KEY_IT15_PRIVATE`
  - `SSH_KEY_IT15_PUBLIC`
  - `SSH_KEY_IT15_FINGERPRINT`
  - `SSH_HOST_IT15`
  - `SSH_KEY_IT15_LOCAL_PATH`
  - `SSH_HOST_DMZ_CONTROL_PLANE`
  - `SSH_HOST_DMZ_WORKER`
- Key-Fingerprint: `SHA256:v1vAeRvbpSAkVQe2kTdjZjwNexaFJ59XWt8+QlOXgmE`.
- Recovery-Prinzip: Private Key aus Infisical lesen, nach `/home/node/.ssh/kube-bot-it15-ed25519` schreiben, `chmod 600`, Fingerprint prüfen, SSH-Test ausführen.
- Verifiziert: temporär aus Infisical gelesener Key konnte mit `chmod 600` für SSH auf `Misla@192.168.1.121` genutzt werden. Temporäre Datei wurde gelöscht.
- Keine Secretwerte/Private-Key-Inhalte in Markdown, Slack oder Logs ausgeben.

### Ergänzung Infisical — DMZ SSH Key-Namenssatz — 2026-06-15 14:35 UTC

- Ergänzt im bestehenden Infisical-Pfad `platform-kubernetes/prod:/bots/kube-bot/ssh`:
  - `SSH_KEY_DMZ_PRIVATE`
  - `SSH_KEY_DMZ_PUBLIC`
  - `SSH_KEY_DMZ_FINGERPRINT`
  - `SSH_KEY_DMZ_LOCAL_PATH`
  - `SSH_HOST_DMZ_CONTROL_PLANE`
  - `SSH_HOST_DMZ_WORKER`
- Aktuell ist das DMZ-Keymaterial identisch mit dem lokal vorhandenen OpenClaw/kube-bot Key `/home/node/.ssh/kube-bot-it15-ed25519`; es wird bewusst zusätzlich unter DMZ-spezifischen Namen geführt.
- Verifiziert: Readback von `SSH_KEY_DMZ_PRIVATE` in temporäre Datei, `chmod 600`, Fingerprint geprüft, SSH-Probe auf `kube-bot@192.168.20.40` erfolgreich. Temporäre Datei gelöscht.
- Fingerprint: `SHA256:v1vAeRvbpSAkVQe2kTdjZjwNexaFJ59XWt8+QlOXgmE`.

### SSH Verifikation — DMZ Worker `192.168.20.45` — 2026-06-15 15:03 UTC

- Ziel: `kube-bot@192.168.20.45` (`mini-it13-02`).
- Key: `/home/node/.ssh/kube-bot-it15-ed25519` bzw. Infisical `platform-kubernetes/prod:/bots/kube-bot/ssh/SSH_KEY_DMZ_PRIVATE`.
- Status: SSH funktioniert, sudo NOPASSWD funktioniert.
- Read-only Probe: `hostname`, `whoami`, `sudo -n true`, `uname -a`, `df -h /` erfolgreich.
- Keine Änderungen durch kube-bot ausgeführt.

### SSH Verifikation — Nginx Proxy Host `192.168.20.20` — 2026-06-15 15:14 UTC

- Ziel: `kube-bot@192.168.20.20` (`raspi`, Nginx Proxy Manager / Reverse Proxy Host).
- Key: `/home/node/.ssh/kube-bot-it15-ed25519`; passender Public-Key-Kommentar `kube-bot@openclaw-it15-20260614`.
- Status: SSH funktioniert, sudo NOPASSWD funktioniert.
- Host: Raspberry Pi / Debian Bookworm, Kernel `6.12.62+rpt-rpi-v8`, aarch64.
- Authorized Keys bereinigt: alter Eintrag `kube-bot@openclaw-k3s-diagnostics` entfernt; aktueller Key bleibt erhalten.
- Vor Änderung wurde eine Backup-Datei von `/home/kube-bot/.ssh/authorized_keys` auf dem Zielhost angelegt.

### Ergänzung Infisical — Nginx/Raspi SSH Key-Namenssatz — 2026-06-15 15:16 UTC

- Ergänzt im bestehenden Infisical-Pfad `platform-kubernetes/prod:/bots/kube-bot/ssh`:
  - `SSH_KEY_NGINX_PRIVATE`
  - `SSH_KEY_NGINX_PUBLIC`
  - `SSH_KEY_NGINX_FINGERPRINT`
  - `SSH_KEY_NGINX_LOCAL_PATH`
  - `SSH_HOST_NGINX_PROXY`
- Ziel: `kube-bot@192.168.20.20` (`raspi`, Nginx Proxy Manager / Reverse Proxy Host).
- Aktuell ist das Nginx/Raspi-Keymaterial identisch mit dem lokal vorhandenen OpenClaw/kube-bot Key `/home/node/.ssh/kube-bot-it15-ed25519`; es wird bewusst zusätzlich unter Nginx-spezifischen Namen geführt.
- Verifiziert: Readback von `SSH_KEY_NGINX_PRIVATE` in temporäre Datei, `chmod 600`, Fingerprint geprüft, SSH-Probe auf `kube-bot@192.168.20.20` erfolgreich. Temporäre Datei gelöscht.
- Fingerprint: `SHA256:v1vAeRvbpSAkVQe2kTdjZjwNexaFJ59XWt8+QlOXgmE`.

### DMZ Infisical — deployed via IT15 ArgoCD — 2026-06-15 20:56 UTC

- Zweck: DMZ-lokaler Secret Store für K3s Workloads, ohne Firewall-Öffnung zur IT15-Infisical-Instanz.
- Namespace: `platform-infisical`.
- ArgoCD App: `platform-infisical-dmz` im IT15-ArgoCD Project `platform`.
- GitOps Repo: `GitMasterOne/platform-gitops`, Pfad `platform/infisical-dmz`, App Manifest `clusters/it15-mgmt/argocd/apps/platform-infisical-dmz.yaml`.
- Helm Chart: Infisical `infisical-standalone` `1.9.0`, Image `infisical/infisical:v0.158.0`.
- Exposure: nur ClusterIP, kein Ingress.
- Storage: Longhorn PVCs für PostgreSQL 8Gi und Redis 2Gi.
- Health: cluster-intern `/api/status` liefert `Ok`.
- Initial-Secrets liegen als Kubernetes Secrets im Namespace `platform-infisical`; keine Werte dokumentiert.
- Offene Betriebsaufgabe: Recovery/Backup für Initial-Secrets + PostgreSQL/PVC sichern; Erstzugang nur über Port-Forward/SSH, nicht öffentlich exposen.

## Infisical — Drei-Instanzen-Architektur (Stand 2026-06-21)

### Überblick

```
BotOps-Platform Infisical (ki-node 192.168.1.50)
  └── bot-platform/         ← injizierte Runtime-Secrets (read-only für kube-bot)
  └── hob_cattaro/kube-bot  ← kube-bot Credentials für IT15-Infisical (rw)

IT15-Infisical (192.168.1.121:8088) — Cattaro GmbH / Intranet
  └── platform-kubernetes/prod/
      ├── /platform/infisical/   ← DMZ-Infisical Credentials (bereinigt 2026-06-21)
      ├── /platform/kube-bot/    ← alt/Dopplung, Cleanup ausstehend
      ├── /platform/argocd/
      ├── /platform/gitops/
      ├── /bots/kube-bot/ssh/    ← SSH-Keys
      └── /tenants/pidoka/       ← App-Secrets (via ESO)

DMZ-Infisical (cluster-intern, platform-infisical Namespace)
  └── platform-kubernetes/prod/
      └── /tenants/pidoka/*      ← Workload-Secrets für n8n, matomo, mautic, suitecrm, zammad, homarr
```

### Architektur-Regel

- IT15-Infisical-Credentials NICHT in IT15-Infisical (Safe-im-Safe Problem)
- Credentials-Hierarchie: BotOps → IT15-Infisical → DMZ-Infisical

### Backup/Restore Skripte — kube-bot (2026-06-22)

- `backup-app.sh`: strukturiertes Backup mit Logging, Statusfile, Preflight, Age-Encrypt, dmz-backup Push + Remote-SHA-Verify
- `restore-staging-fetch.sh`: Artefakte vom Restore-Staging holen, SHA256-Check, Decrypt — mit Preflight für Staging-Pfad
- Ablage: `/home/node/.openclaw/workspace/scripts/`
- `_restore-staging` auf `dmz-backup` dauerhaft `openclaw:openclaw 770` — kein sudo mehr nötig

## Zammad Restore — 2026-06-22

- Backup-ID: `20260622T155023Z`
- Restore durchgeführt: 2026-06-22 ~19:08–19:24 UTC
- Methode: PostgreSQL-Dump + Storage-Restore (zammad-storage.tar)
- Ziel-PVCs: `zammad-postgresql-data-longhorn` + `zammad-var-longhorn`
- Longhorn-Snapshots vor Restore angelegt: `pre-restore-20260622-1908z` (alle 4 Zammad-PVCs)
- SHA256-Verifikation: 3-stufig (DiskStation → dmz-backup → lokal vor Decrypt) ✅
- Ergebnis: 394 Tickets, 371 User importiert ✅
- Elasticsearch-Reindex: automatisch als Init-Container durchgelaufen ✅
- Alle 5 Pods Running nach Restore ✅
- local-path-PVC-Cleanup: erledigt 2026-06-22 ~19:58 UTC — alle 4 Altlasten gelöscht ✅

## Platform Backup — Zwischenstand 2026-06-22 ~20:39 UTC

### Erledigt heute:

#### Schritt 1 — DMZ-Infisical Backup ✅
- Backup-ID: `20260622T201351Z`
- Age-Key neu generiert, in `Backup_Restore/prod` abgelegt: `BACKUP_INFISICAL_AGE_RECIPIENT`, `BACKUP_INFISICAL_AGE_PRIVATE_KEY`
- PostgreSQL-Dump `infisicalDB` (163K komprimiert)
- Pfad DiskStation: `/volume1/KI_Agenten/backups/k3s-dmz/tenants/platform/prod/infisical/20260622T201351Z/`
- SHA256 verifiziert: data OK, metadata OK ✅

#### Schritt 2 — K3s Control-Plane Backup ✅
- Backup-ID: `20260622T202035Z`
- SQLite Online-Backup via `python3 sqlite3.backup()` — konsistent, kein K3s-Stop nötig
- K3s Version: `v1.28.4+k3s2`, DB 3.3M
- Age-Key in `Backup_Restore/prod`: `BACKUP_K3S_CONTROLPLANE_AGE_RECIPIENT`, `BACKUP_K3S_CONTROLPLANE_AGE_PRIVATE_KEY`
- Auf dmz-backup: `platform/prod/k3s-controlplane/20260622T202035Z/`
- SHA256 verifiziert auf DiskStation: data OK, metadata OK ✅ (2026-06-23)

### Offen / Nächste Schritte:

#### Schritt 3 — ArgoCD Secrets Backup (IT15)
- Transport-Frage offen: direkt IT15 → DiskStation, oder via dmz-backup
- diskstation-bot hat nicht geantwortet (20:38 UTC) — Mislav startet neue Session
- kube-bot hat folgende Optionen vorgeschlagen:
  1. Direkt IT15 → DiskStation
  2. kube-bot holt von IT15, pusht via dmz-backup → DiskStation
  3. Nur lokal auf IT15 sichern
- Empfehlung kube-bot: Option 1 (direkt) wenn diskstation-bot SSH zu IT15 hat

#### Schritt 4 — IT15 Backup (ArgoCD + IT15-Infisical)
- Noch nicht begonnen

#### Fix ausstehend:
- `.sha256`-Dateien in backup-infisical enthalten absolute Pfade — diskstation-bot Hinweis, soll in nächstem Script korrigiert werden (k3s-backup.sh hat Fix bereits drin)

## DiskStation Share-Konsolidierung — abgeschlossen 2026-06-23

- `Backup_K3s_Longhorn` Share vollständig gelöscht ✅
- Alle Daten konsolidiert unter `KI_Agenten/backups/k3s-dmz/` ✅
- Neue Struktur:
  - `tenants/platform/prod/argocd/` ← ArgoCD-Backup
  - `tenants/platform/prod/infisical/` ← DMZ-Infisical
  - `tenants/platform/prod/k3s-controlplane/` ← K3s Controlplane
  - `tenants/platform/prod/nginx-proxy-manager/` ← nginx (neu)
  - `tenants/pidoka/prod/` ← App-Backups
  - `longhorn-staging/` ← Longhorn NFS Backupstore
  - `docs/` + `logs/` ← zusammengeführt
- DSM-Task 7 auf neuen Script-Pfad aktualisiert ✅
- Longhorn NFS Backup-Target bleibt unverändert auf `nfs://192.168.20.40:/srv/longhorn-backup-staging` ✅


## nginx Proxy Manager Backup (Stand 2026-06-23)

- Backup-Methode: kube-bot Runtime holt Archive via SSH vom Raspi (192.168.20.20), verschlüsselt clientseitig mit age, pusht nach dmz-backup.
- Age-Recipient/Private-Key in Infisical `Backup_Restore/prod`: `BACKUP_NGINX_AGE_RECIPIENT`, `BACKUP_NGINX_AGE_PRIVATE_KEY`
- Backup-Scope: `/data/compose/2/data/` (SQLite-DB + Config) + `/data/compose/2/letsencrypt/` (TLS-Zertifikate)
- Zielpfad dmz-backup: `k3s-dmz/tenants/platform/prod/nginx-proxy-manager/<backup-id>/`
- Artefakte: `nginx-data.tar.gz.age`, `nginx-letsencrypt.tar.gz.age` + je `.sha256`
- Erstes Backup verifiziert: `20260623T074018Z` — SHA256 OK (data + letsencrypt) ✅
- Automatisierung: ausstehend (Cronjob auf DMZ Control-Plane oder kube-bot)


## Backup-Automatisierung — eingerichtet 2026-06-23

### Zeitplan (täglich, UTC)

| Zeit  | Job                    | Läuft auf            | Ziel dmz-backup                              |
|-------|------------------------|----------------------|----------------------------------------------|
| 02:00 | K3s Controlplane DMZ   | DMZ Crontab          | platform/prod/k3s-controlplane/              |
| 02:10 | DMZ-Infisical          | DMZ Crontab          | platform/prod/infisical/                     |
| 02:20 | ArgoCD IT15 + K3s IT15 | kube-bot HEARTBEAT   | platform/prod/argocd-it15/                   |
| 02:30 | nginx Proxy Manager    | kube-bot HEARTBEAT   | platform/prod/nginx-proxy-manager/           |
| 03:00 | Zammad                 | DMZ Crontab          | tenants/pidoka/prod/zammad/                  |
| 03:20 | Matomo                 | DMZ Crontab          | tenants/pidoka/prod/matomo/                  |
| 03:40 | n8n                    | DMZ Crontab          | tenants/pidoka/prod/n8n/                     |
| 03:50 | Mautic                 | DMZ Crontab          | tenants/pidoka/prod/mautic/                  |
| 04:00 | SuiteCRM               | DMZ Crontab          | tenants/pidoka/prod/suitecrm/                |
| 04:10 | Homarr                 | DMZ Crontab          | tenants/pidoka/prod/homarr/                  |
| 05:30 | Longhorn NFS Pull      | DiskStation Task 7   | Backup_K3s_Longhorn → KI_Agenten (intern)    |
| 06:00 | dmz-backup Pull        | DiskStation Task 9   | alle .age Artefakte → KI_Agenten/backups/    |

### Scripts

- `backup-app.sh` — generisches Backup für K3s Apps (DMZ)
- `backup-argocd-it15.sh` — ArgoCD IT15 + K3s State DB
- `backup-nginx.sh` — nginx Proxy Manager (Raspi)
- `backup-all.sh` — Wrapper (manuell / Referenz)

### Age-Keys in Infisical (Backup_Restore/prod)

- `BACKUP_HOMARR_AGE_*` — Homarr
- `BACKUP_INFISICAL_AGE_*` — DMZ-Infisical
- `BACKUP_K3S_CONTROLPLANE_AGE_*` — K3s Controlplane
- `BACKUP_NGINX_AGE_*` — nginx PM
- `BACKUP_ARGOCD_IT15_AGE_*` — ArgoCD IT15

### DMZ Crontab (kube-bot@192.168.20.40)

Eingerichtet 2026-06-23. Logs unter `/home/kube-bot/logs/`.


## Nextcloud Backup-Kette (eingerichtet 2026-06-25)

### Architektur

```
nextcloudpi → dmz-backup → DiskStation
täglich automatisch, Ende-zu-Ende getestet ✅
```

### Jobs (täglich, UTC)

| Zeit       | Job                              | Scope                          |
|------------|----------------------------------|--------------------------------|
| 03:00 UTC  | Nextcloud Datenverzeichnis       | ~31 GB, rsync inkrementell     |
| 03:15 UTC  | Nextcloud DR-Paket               | NCP-Tarball ~15 GB + config.php|

### DiskStation-Ziele

- `/volume1/.../tenants/cattaro/prod/nextcloud/`
- `/volume1/.../tenants/cattaro/prod/nextcloud-config/`

### Retention-Policy

- Datenverzeichnis: 1 aktueller Stand, kein `--delete` vorerst
- NCP-Tarball/config: 7 Versionen behalten, Cleanup im Push-Job
- **Cluster-weiter Retention-Standard: 7 Tage** (abgestimmt zwischen kube-bot, diskstation-bot und nextcloud-bot — 2026-06-25)

### Verifikation (2026-06-25)

- STATUS=ok / RSYNC_EXIT=0
- nextcloud-config: 15G auf DiskStation
- MANIFEST.txt ✅ / config/ ✅ / ncp-backups/ ✅ (NCP-Tarball)
- Datenverzeichnis: 31 GB ✅ / DB + Config: 15 GB ✅


## Offene Aufgabe — Nextcloud Backup-Bereinigung (2026-06-26)

### Kontext

Mislav hat am 2026-06-26 bestätigt: der alte direkte Nextcloud→DiskStation-Backupweg soll entfernt werden.
Der neue Weg (NextcloudPi → dmz-backup → DiskStation) ist aktiv, getestet und bleibt der Standard.

### Freigabe

- Explizites OK von Mislav erteilt am 2026-06-26 im Chat.
- Aktion erst nach dem Wochenende (frühestens Mo 2026-06-30), wenn Mislav wieder eingreifen kann.
- Nichts wird übers Wochenende angefasst.

### Scope (read-only identifiziert durch nextcloud-bot, noch zu bestätigen)

- **NextcloudPi-Seite:** `fstab`-Eintrag für Synology-CIFS-Mount (bereits als "disabled legacy" markiert); ggf. zugehörige rsync-Jobs/Scripts
- **DiskStation-Seite:** alter Pull-Task/Share für direkten NC-Weg (diskstation-bot benennt konkret)

### Vorgehensweise (abgestimmt kube-bot + diskstation-bot)

1. diskstation-bot identifiziert alten Task/Share read-only und benennt konkret
2. kube-bot prüft NextcloudPi-Seite read-only (SSH, fstab + Scripts)
3. Gemeinsamer Vorschlag mit konkreten Schritten → OK von Mislav
4. Dann koordiniert entfernen: erst NextcloudPi, dann DiskStation

### Beteiligte Bots

- kube-bot: NextcloudPi-Seite + Koordination
- diskstation-bot: DiskStation-Seite
- nextcloud-bot: Bestätigung Nextcloud-Funktion nach Cleanup

## Nextcloud Backup-Bereinigung — abgeschlossen 2026-06-29

Alle 5 Schritte erledigt. Alter direkter DiskStation-Backup-Weg vollständig entfernt.

### Ergebnis

- **DiskStation Task 5** (`NextcloudPi Backup Pull`, täglich 04:30 UTC) — war bereits deaktiviert (`enable=False`) ✅
- **Shared Folder `Backup_Nextcloud`** (123 GB inkl. `/proc`-Reste) — per DSM API als root gelöscht ✅
- **SSH-Key `dsm_nextcloudpi_pull_ed25519`** auf DiskStation — entfernt ✅
- **Legacy-`fstab`-Zeile** (`//diskstationmb/Backup_Nextcloud`, inkl. Klartext-Passwort) auf NextcloudPi — entfernt ✅
- **Alter Pull-Key** (`dsm-nextcloudpi-pull`) aus `authorized_keys` auf NextcloudPi — entfernt ✅

### Aktiver Weg (bleibt Standard)

```
NextcloudPi → nc-rsync-auto (05:00 UTC) → dmz-backup (192.168.20.90)
NextcloudPi → nimbus-nextcloud-dr-backup (05:15 UTC) → dmz-backup
DiskStation Task 9 → pullt von dmz-backup → KI_Agenten/backups/k3s-dmz/tenants/cattaro/prod/nextcloud/
```

### Hinweis rsync exit code 23

- Ursache: NCP-lokaler Snapshot hatte `/proc` mit eingesogen → Permission Denied beim Übertragen.
- Kein Datenverlust, nur Teilfehler bei pseudo-files.
- nextcloud-bot sollte `/proc` aus dem NCP-Backup-Scope ausschließen (noch offen).

## DMZ Security Audit — dmz-backup (2026-06-29)

### Befund (nextcloud-bot + diskstation-bot, lesend)

- `/home/openclaw/diskstation-bot-infisical-client-secret` — root-owned, 65 Bytes, seit 20.06., wird von keinem aktiven Script referenziert
- `/home/openclaw/incoming-secrets/diskstation-bot/` — leer, nie produktiv genutzt
- `~/.ssh/` auf dmz-backup: kein privater SSH-Key, nur `authorized_keys` ✅

### Offene Aufgaben (braucht root/sudo auf dmz-backup)

1. `/home/openclaw/diskstation-bot-infisical-client-secret` löschen
2. `/home/openclaw/incoming-secrets/diskstation-bot/` löschen
3. Zugehöriges Infisical Client Secret rotieren (falls noch gültig)

Aktion: Mislav direkt per SSH auf dmz-backup (`192.168.20.90`) oder Raspberry-Pi-Konsole.

### DMZ Credential-Regel (abgestimmt 2026-06-29)

- Erlaubt: Public Keys / Age-Recipients / eingeschränkte `authorized_keys`
- Vermeiden: Infisical Client Secrets, private SSH-Keys, private Age-Keys, API-Tokens
- Prinzip: DMZ schreibt/empfängt nur in eng begrenzte Pfade; Entschlüsselung und Secret-Zugriff außerhalb der DMZ

## Backup-Script-Umbau — abgeschlossen 2026-06-29

### Problem (2026-06-29)
`backup-app.sh` auf dem K3s-Node hatte `WORKDIR="/home/node/.openclaw/workspace/..."` — OpenClaw-Container-Pfad, nicht auf dem Node vorhanden. Alle 8 Cron-Jobs seit 22.06. fehlgeschlagen.

### Sofortfix (genehmigt Mislav 2026-06-29)
- Script v2 auf dem K3s-Node installiert — läuft vollständig lokal, kein SSH-Hop auf sich selbst
- `WORKDIR` → `/home/kube-bot/backups/`
- `age` auf dem Node installiert (`/usr/bin/age`)
- Age-Recipients (öffentlich) unter `/home/kube-bot/.age-recipients/*.recipient`
- SSH-Push-Key für dmz-backup: `/home/kube-bot/.ssh/kube-bot-dmz-backup-ed25519`
  - Fingerprint: `SHA256:03GVm4i9Z0cFhkM362Guo45xNqVoASlhwevSVHrAWh0`
  - In Cattaro Infisical dokumentiert: `platform-kubernetes/prod//bots/kube-bot/ssh/KUBE_BOT_DMZ_NODE_SSH_PUBLIC_KEY`
  - In `authorized_keys` auf dmz-backup mit Restrictions: `restrict,from="192.168.20.40",no-pty,no-agent-forwarding,no-X11-forwarding`
- Testlauf Homarr: ✅ erfolgreich (10:07 UTC)

### DMZ-Regel (bestätigt)
- Keine Infisical-Credentials auf DMZ/K3s-Node
- Age-Recipients (public) lokal OK
- SSH-Push-Key (dediziert, eingeschränkt) lokal OK
- Infisical nur zur Dokumentation, kein Laufzeit-Zugriff aus DMZ

### Nächster Schritt (Roadmap)
Langfristig: DiskStation als Orchestrator oder Velero — sauberere Architektur ohne lokale Keys auf DMZ-Node.
