diff --git a/AGENTS.md b/AGENTS.md index 46aa027..bab1365 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -71,11 +71,12 @@ console; the WireGuard LXC uses `admin`) — see `.codex/LOCAL_MEMORY.md`. - Reboot caveat (GNAI-016 section 7): in LXC, `/proc/sys/kernel/random/boot_id` belongs to the PVE host and does **not** change on a guest reboot — never use boot_id-based waiters; reboot via PVE and verify independently over SSH. -- Unprivileged LXCs: keep the empty `lxc.seccomp.profile:` line (rootful - Podman/Ganesha containers need `open_by_handle_at`), mask - `sys-kernel-config.mount` (configfs is unavailable), and expect - `uid_map 0 100000 65536` — host UIDs appear as 65534 (nobody) inside the - guest. +- Unprivileged LXCs: mask `sys-kernel-config.mount` (configfs is unavailable) + and expect `uid_map 0 100000 65536` — host UIDs outside 100000–165535 + appear as 65534 (nobody) inside the guest, so guest root cannot write to + files owned by such UIDs without world-write bits. (The former empty + `lxc.seccomp.profile:` line was a Ganesha-era exception and was removed on + 2026-10-30 when the NFS export moved to the PVE host; GNAI-019 decision 2.) - In-place dist-upgrades: take a `vzdump` safety net first and use `--force-confold` so productive configuration files survive. diff --git a/docs/platform/llm-server/encoding/GNENC-000-DOC_Index.md b/docs/platform/llm-server/encoding/GNENC-000-DOC_Index.md index 2ab25f0..6fcfe0a 100644 --- a/docs/platform/llm-server/encoding/GNENC-000-DOC_Index.md +++ b/docs/platform/llm-server/encoding/GNENC-000-DOC_Index.md @@ -1,8 +1,8 @@ # Video-Encoding – Dokumentationsindex und Projektstand **Dokumenten-ID:** GNENC-000-DOC -**Stand:** 2026-10-19 -**Status:** Planungsabschluss; Pipeline geplant, Implementierung noch nicht begonnen +**Stand:** 2026-10-30 +**Status:** Planungsabschluss; externe Abhängigkeiten (CT-101, PVE-Host-NFS) umgesetzt; Implementierung auf ghostnet-ki noch nicht begonnen ## Dokumente @@ -23,9 +23,16 @@ beschrieben. (ProtonVPN), jDownloader2 (Download), Encoding-Worker (HandBrakeCLI + ffmpeg); Handover ausschließlich über das Filesystem, In-Progress-Schutz über Work-Ordner und atomares `mv`. -- Speicherort: CT-101 (Samba/NAS, 10.0.0.9) — NFS-Export - `/media/datenhdd3/Encoding`, gemountet auf `/var/lib/ghostnet-encoding` - (ghostnet-ki hat nur eine 2-TB-NVMe; die Medienbibliothek liegt auf CT-101). +- Speicherort: PVE-Host-Medienverbund `/media/datenhdd3` (per LXC-Mount auch + in CT-101 sichtbar) — NFS-Export `10.0.0.2:/media/datenhdd3/Encoding`, + gemountet auf `/var/lib/ghostnet-encoding` (ghostnet-ki hat nur eine + 2-TB-NVMe; die Medienbibliothek liegt auf dem PVE-Host/CT-101). +- CT-101 per Ansible adoptiert und PVE-Host-NFS umgesetzt (GNAI-019); + Verifikation 30.10.2026: CT-101 `verify-production` `ok=47 changed=0`; + PVE-Host nach autorisierter Konvergenz: Export-Root `100000:970 2775`, + NFSv4-only wirksam (`-3`), `verify-production` `ok=17 changed=0`, + Idempotenz `changed=0`. Berechtigungsmodell entschieden: Mode 2777 auf den + SMB-berührten Verzeichnissen (GNENC-002 Abschnitt 4). - Verzeichnislayout: `input//*.dlc`, `jd/`, `queue///`, `work/`, `output///` + `output/_failed/`, `state/jobs/` + `state/system.json`. @@ -64,10 +71,12 @@ beschrieben. ## Noch offen -1. Feinjustierung der Ressourcenlimits nach einem repräsentativen Encoding-Test +1. NFSv4.2-Mount-Abnahme von `ghostnet-ki` gegen `10.0.0.2` (inkl. + Schreib-/Lesetest als UID 982/983 und mv-Simulation als `ghost` über SMB) + — mit der Encoding-Implementierung. +2. Feinjustierung der Ressourcenlimits nach einem repräsentativen Encoding-Test unter gleichzeitiger Inferenzlast. -2. Rootless-TUN-Verifizierung für den VPN-Sidecar auf dem Host. -3. Implementierung: CT-101 (NFS-Export, Shares `[Encode]`/`[Encode-In]`, - Gruppe `gn-media`/GID-Abgleich — GNAI-017), Konten `gn-download`/ - `gn-dashboard`, NFS-Mount, Image-Bauten, Quadlet-Units (inkl. VPN-Sidecar), - Skripte, Monitoring-Bereich (Collector, Dashboard). +3. Rootless-TUN-Verifizierung für den VPN-Sidecar auf dem Host. +4. Implementierung auf ghostnet-ki: Konten `gn-download`/`gn-dashboard`, + NFS-Mount, Image-Bauten, Quadlet-Units (inkl. VPN-Sidecar), Skripte, + Monitoring-Bereich (Collector, Dashboard). diff --git a/docs/platform/llm-server/encoding/GNENC-001-PLAN_Architecture.md b/docs/platform/llm-server/encoding/GNENC-001-PLAN_Architecture.md index 7afd4b9..77ff6b8 100644 --- a/docs/platform/llm-server/encoding/GNENC-001-PLAN_Architecture.md +++ b/docs/platform/llm-server/encoding/GNENC-001-PLAN_Architecture.md @@ -2,8 +2,8 @@ **Dokumenten-ID:** GNENC-001-PLAN **Titel:** CPU-basiertes Video-Encoding auf dem KI-Server -**Stand:** 2026-10-19 -**Status:** Finaler Plan nach Planungsabschluss am 19.10.2026; die operative Pipeline ist in GNENC-002 spezifiziert, das host-weite Monitoring in GNMON-001 +**Stand:** 2026-10-30 +**Status:** Finaler Plan nach Planungsabschluss am 19.10.2026; externe Abhängigkeiten (CT-101, PVE-Host-NFS) umgesetzt (GNAI-019); die operative Pipeline ist in GNENC-002 spezifiziert, das host-weite Monitoring in GNMON-001 ## 1. Zweck und Abgrenzung @@ -40,10 +40,10 @@ beschrieben. | Download-Stufe | jDownloader2-Container (`jlesage/jdownloader-2`, gepinnter Digest) liest `.dlc` aus `input//`; Event-Scripter verschiebt den Release-Ordner atomar in die Queue | Betreiberentscheidung 19.10.2026 | | VPN | der gesamte jDownloader2-Verkehr läuft über einen ProtonVPN-WireGuard-Tunnel: gluetun-Sidecar (`qmcgaw/gluetun`, gepinnt) unter `gn-download`, JD2 teilt dessen Netzwerk-Namespace, Kill Switch aktiv (kein Traffic ohne Tunnel); WireGuard-Credentials server-lokal in `/etc/ghostnet-download/vpn.env`, nicht in git | Betreiberentscheidung 19.10.2026 | | Anwendung | Worker-Image als Eigenbau auf Basis des gepinnten HandBrake-Images `linuxserver/handbrake:1.11.2-ls48` (`sha256:fda92bd1…48a3c7`) mit python3 und ffmpeg; die Digests von Worker- und VPN-Image werden nach dem Bau gepinnt | Betreiberentscheidung 19.10.2026 | -| Speicherort | Pipeline-Daten liegen auf CT-101 (Samba/NAS, 10.0.0.9): dort liegen die Medienbibliothek und der Plattenverbund; ghostnet-ki selbst hat nur eine 2-TB-NVMe (OS + LLM) | Betreiberentscheidung 19.10.2026 | -| Ein-/Ausgabe | NFS-Export `/media/datenhdd3/Encoding` von CT-101 (10.0.0.9), gemountet auf `/var/lib/ghostnet-encoding`; Verzeichnislayout `input/`, `jd/`, `queue/`, `work/`, `output/`, `state/` | Betreiberentscheidung 19.10.2026; Server, IP und Export-Pfad festgelegt | +| Speicherort | Pipeline-Daten liegen auf dem PVE-Host-Medienverbund (`/media/datenhdd3`; per LXC-Mount auch in CT-101 sichtbar): dort liegt die Medienbibliothek; ghostnet-ki selbst hat nur eine 2-TB-NVMe (OS + LLM) | Betreiberentscheidung 19.10.2026; NFS seit 30.10.2026 auf dem PVE-Host (GNAI-019 Entscheidung 2) | +| Ein-/Ausgabe | NFS-Export `10.0.0.2:/media/datenhdd3/Encoding` (PVE-Host, Rolle `pve_nfs_export`, NFSv4-only, nur für 10.0.0.90, `root_squash`), gemountet auf `/var/lib/ghostnet-encoding`; Verzeichnislayout `input/`, `jd/`, `queue/`, `work/`, `output/`, `state/` | Betreiberentscheidung 19.10.2026; Umsetzung GNAI-019 (Revision 30.10.2026); Mount-Abnahme durch das Encoding-Ansible ausstehend | | Abholung | fertige Outputs per SMB über CT-101: Share `[Encode]` (`output/`, RW für `ghost`, lesend für `daidalaw`/`debugofen`) — Entnahme per `mv` in die Bibliotheks-Shares räumt `output/` auf; optionaler Clean-Job löscht Dateien nach 30 Tagen aus `output/`; kein smbd auf `ghostnet-ki` | Betreiberentscheidung 19.10.2026 | -| Samba-Integration (CT-101) | CT-101 wird ins Infrastructure-Repository aufgenommen (GNAI-017): NFS-Export nur für 10.0.0.90, Shares `[Encode]`/`[Encode-In]`, Gruppe `gn-media` (GID 970) auf CT-101 mit `ghost` als Mitglied (UID/GID-Abgleich); `.dlc`-Drop über `[Encode-In]` | Betreiberentscheidung 19.10.2026; GNAI-017 | +| Samba-Integration (CT-101) | CT-101 ist per Ansible adoptiert (GNAI-019; verifiziert 30.10.2026, `ok=47 changed=0`): Shares `[Encode]`/`[Encode-In]`/`[Encoding]`, Gruppe `gn-media` (GID 970) mit `ghost` als Mitglied; `.dlc`-Drop über `[Encode-In]`. NFS liegt nicht auf CT-101 (PVE-Host); die guest-seitige GID 970 gleicht die Host-GID 970 wegen des LXC-idmap nicht ab — Berechtigungsmodell: Mode 2777 auf den SMB-berührten Verzeichnissen (Betreiberentscheidung 30.10.2026, GNENC-002 Abschnitt 4) | Betreiberentscheidung 19.10.2026; GNAI-017/GNAI-019 | | Profile | drei versionierte HandBrake-Presets (SVT-AV1 10-bit, MKV, Audio-Passthrough) unter `llm-server/encoding/profiles/`; Auswahl über gleichnamige Ordner unter `input/` bzw. `queue/` | Betreiberentscheidung 19.10.2026; Presets versioniert | | Warteschlange | dateibasiert: `.dlc`-Drop in `input//`, jDownloader2 befüllt `queue///`, der Worker arbeitet die Queue mit genau einem gleichzeitigen Encode ab | Betreiberentscheidung 19.10.2026 | | Filter | Nicht-Video-Dateien und Dateien < 100 MB werden ignoriert; alle übrigen Videodateien eines Releases werden encodet (keine Hauptdatei-Auswahl) | Betreiberentscheidung 19.10.2026 | @@ -65,7 +65,7 @@ flowchart LR ENC[gn-encoding\nrootless Podman] DL[gn-download\nrootless Podman] DASH[gn-dashboard\nrootless Podman] - NAS[CT-101 Samba/NFS\n10.0.0.9] + NAS[PVE-Host NFS 10.0.0.2\nCT-101 SMB 10.0.0.9] GPU[beide GPUs] CPU[CPU / I-O Budget] TIMER[nächtliche Fensterlogik\nim Worker] @@ -233,18 +233,28 @@ anschließend in den rootless Store von `gn-encoding` gezogen; der Pull bestätigte den Manifest-Digest. Am 19.10.2026 wurde die Planung abgeschlossen (Abschnitte 2 und 7). Die -Implementierungsphase baut als Nächstes auf: der NFS-Export und die Shares -`[Encode]`/`[Encode-In]` auf CT-101 (GNAI-017), die Konten `gn-download` und -`gn-dashboard` samt Gruppe `gn-media` (GID 970, auch auf CT-101), der NFS-Mount -auf ghostnet-ki, die Image-Bauten mit Digest-Pinning (Worker, VPN-Sidecar, -Dashboard), die Quadlet-Units für jDownloader2, VPN-Sidecar, Worker und -Dashboard, der host-seitige Metrics-Collector sowie die Skripte aus `scripts/`. -Die Rolle `video_encoding` stellt bis dahin sicher, dass weder Quadlet-Dateien -noch Container für `gn-encoding` existieren. +externen Abhängigkeiten sind umgesetzt: CT-101 ist per Ansible adoptiert +(GNAI-019, verifiziert 30.10.2026), der NFS-Export läuft auf dem PVE-Host +(Rolle `pve_nfs_export`); der PVE-Host ist konvergiert (Export-Root +`100000:970 2775`, NFSv4-only wirksam, `verify-production` `ok=17 changed=0`, +Idempotenz `changed=0`; autorisierter Lauf 30.10.2026). Das +Berechtigungsmodell für die SMB-Schreibpfade ist entschieden (Mode 2777, +GNENC-002 Abschnitt 4); offener Restpunkt ist die NFSv4.2-Mount-Abnahme von +ghostnet-ki. Die Implementierungsphase auf +ghostnet-ki baut als Nächstes auf: die Konten `gn-download` und `gn-dashboard` +samt Gruppe `gn-media` (GID 970), den NFS-Mount von `10.0.0.2`, die +Image-Bauten mit Digest-Pinning (Worker, VPN-Sidecar, Dashboard), die +Quadlet-Units für jDownloader2, VPN-Sidecar, Worker und Dashboard, den +host-seitigen Metrics-Collector sowie die Skripte aus `scripts/`. Die Rolle +`video_encoding` stellt bis dahin sicher, dass weder Quadlet-Dateien noch +Container für `gn-encoding` existieren. ## 7. Offene Entscheidungen 1. Feinjustierung der Ressourcenlimits nach einem repräsentativen Encoding-Test unter gleichzeitiger Inferenzlast (Abschnitt 4). -Diese Werte werden nicht vorab in Playbooks angenommen. +Diese Werte werden nicht vorab in Playbooks angenommen. Die am 30.10.2026 +offenen Punkte Berechtigungsmodell (Mode 2777, GNENC-002 Abschnitt 4) und +PVE-Host-Konvergenz (GNAI-017 Abschnitt 6.9) sind entschieden beziehungsweise +erledigt. diff --git a/docs/platform/llm-server/encoding/GNENC-002-PLAN_Pipeline.md b/docs/platform/llm-server/encoding/GNENC-002-PLAN_Pipeline.md index d88f1b0..ea26fb7 100644 --- a/docs/platform/llm-server/encoding/GNENC-002-PLAN_Pipeline.md +++ b/docs/platform/llm-server/encoding/GNENC-002-PLAN_Pipeline.md @@ -2,11 +2,11 @@ **Dokumenten-ID:** GNENC-002-PLAN **Titel:** Encoding-Pipeline: Komponenten, Konten, Verzeichnislayout und Datenfluss -**Stand:** 2026-10-19 -**Status:** Finaler Plan nach Betreiberentscheidungen vom 19.10.2026 +**Stand:** 2026-10-30 +**Status:** Finaler Plan nach Betreiberentscheidungen vom 19.10.2026; externe Abhängigkeiten umgesetzt (GNAI-019) **Vorgänger:** [GNENC-001-PLAN_Architecture.md](GNENC-001-PLAN_Architecture.md) **Monitoring:** [GNMON-001-PLAN_Architecture.md](../monitoring/GNMON-001-PLAN_Architecture.md) -**Samba/NFS (CT-101):** [GNAI-017-PLAN_Samba-NFS-Export.md](../../proxmox-server/lxc/ct-101-samba/GNAI-017-PLAN_Samba-NFS-Export.md) +**Samba/NFS:** [GNAI-017](../../proxmox-server/lxc/ct-101-samba/GNAI-017-PLAN_Samba-NFS-Export.md), [GNAI-019](../../proxmox-server/lxc/ct-101-samba/GNAI-019-DEC_Ansible-Migration.md) — SMB auf CT-101, Kernel-NFS auf dem PVE-Host ## 1. Architekturprinzip @@ -38,28 +38,35 @@ Dashboard und Metrics-Collector werden im Monitoring-Bereich betrieben | `gn-dashboard` | 984 | 624288–689823 | `/var/lib/ghostnet-dashboard` | `/etc/ghostnet-dashboard` | `gn-media` (nur lesend) | Gruppe `gn-media`: gemeinsame Gruppe der drei Konten für die Übergabe- -Verzeichnisse und den lesenden Zugriff auf `state/`; GID 970, auf `ghostnet-ki` -und CT-101 identisch angelegt (GNAI-017). Alle Konten sind gesperrt -(Passwort `!`, Shell `/usr/sbin/nologin`, Linger aktiv). +Verzeichnisse und den lesenden Zugriff auf `state/`; GID 970. Alle Konten sind +gesperrt (Passwort `!`, Shell `/usr/sbin/nologin`, Linger aktiv). + +**Anmerkung 30.10.2026 (LXC-idmap):** Die guest-seitige Gruppe `gn-media` +(GID 970) in CT-101 erscheint auf dem Host als GID 100970 und gleicht die +reale Host-GID 970 der Pipeline nicht ab — die Gruppen-Alignment wirkt nur +innerhalb von ghostnet-ki. Die SMB-Schreibpfade aus CT-101 laufen über +guest-root (Host-UID 100000, da `admin users` Dateioperationen als root +ausführt); siehe Abschnitt 11. ## 4. Verzeichnislayout ```text -/var/lib/ghostnet-encoding/ (NFS-Mount: 10.0.0.9:/media/datenhdd3/Encoding, CT-101) -├── input// gn-media 2770 Drop-Ziel, nur .dlc -├── jd/ gn-download JD2-Arbeitsverzeichnis +/var/lib/ghostnet-encoding/ (NFS-Mount: 10.0.0.2:/media/datenhdd3/Encoding, PVE-Host) +├── input/ gn-media 2777 SMB-Drop ([Encode-In]) +│ └── / gn-media 2777 nur .dlc +├── jd/ gn-media 2770 JD2-Arbeitsverzeichnis (gn-download) │ ├── / Download + Entpacken │ ├── dlc_done/ erfolgreich abgearbeitete .dlc │ └── failed/ .dlc mit Download-Fehler ├── queue/// gn-media 2770 Roh-Queue, atomar befüllt ├── work// gn-encoding 0750 Encode-Ziele in Arbeit -├── output/ -│ ├── // gn-encoding Erfolge bis Abholung -│ └── _failed// gn-encoding Quarantäne -└── state/ - ├── jobs/.json gn-encoding Status pro Videodatei - ├── tmp/ gn-encoding atomare Schreib-Zwischenlage - └── system.json Collector Systemmetriken (Monitoring) +├── output/ gn-media 2777 Abholung ([Encode]) +│ ├── // gn-media 2777 Erfolge bis Abholung +│ └── _failed// gn-media 2777 Quarantäne +└── state/ gn-media 2750 Status (Dashboard liest) + ├── jobs/.json gn-encoding 0750 Status pro Videodatei + ├── tmp/ gn-encoding 0750 atomare Schreib-Zwischenlage + └── system.json Collector 0644 Systemmetriken (Monitoring) ``` `state/` ist `gn-encoding:gn-media` mit Mode `2750`, damit `gn-dashboard` lesen @@ -71,8 +78,12 @@ Handover-Regeln: - Übergabe immer per atomarem `mv` innerhalb des Mounts; Work-Dateien liegen nie in Übergabe-Ordnern. -- `input/`, `jd/`, `queue/` mit Mode `2770` (setgid), damit neue Dateien die - Gruppe `gn-media` erben. +- `jd/`, `queue/` mit Mode `2770` (setgid); `state/` mit `2750` (setgid). +- `input/` (inkl. Profil-Ordner) und der gesamte `output/`-Baum (inkl. + Release- und `_failed/`-Verzeichnisse, vom Worker angelegt) mit Mode + `2777` (setgid) — Welt-Schreibrecht für den SMB-Schreibpfad aus CT-101 + (Betreiberentscheidung 30.10.2026, siehe unten). Neue Dateien erben die + Gruppe `gn-media`. - jDownloader2 läuft mit `UMASK=022`, damit der Worker die Dateien lesen kann. - Der Worker löscht Quelldateien und geleerte Release-Ordner aus `queue/`; Löschrechte folgen aus dem Schreibrecht der Gruppe am Verzeichnis (kein @@ -80,6 +91,18 @@ Handover-Regeln: - `work/` wird pro Lauf geleert; `state/tmp/` dient nur atomaren Schreibzugriffen. +**Berechtigungsmodell (Betreiberentscheidung 30.10.2026):** Die SMB-Zugriffe +aus CT-101 laufen über guest-root (Host-UID 100000; `admin users` führt +Dateioperationen als root aus). Da die Pipeline-Verzeichnisse via NFS den +realen UIDs 982/983:970 gehören und diese im LXC-idmap nicht abgebildet sind +(im Gast: `nobody:nogroup`), tragen `input/` und der `output/`-Baum Mode +`2777`; `jd/`, `queue/`, `work/` und `state/` bleiben eng (2770/0750). + +**Layout-Anlage:** Wegen `root_squash` auf dem Export kann Ansible als root +keine chowned-Verzeichnisse anlegen; die Struktur wird als `gn-encoding` +angelegt (runuser; Muster der Rolle `video_encoding`) oder vom Worker beim +Start erzeugt und geprüft. + **Abholung:** Die fertigen Outputs werden über CT-101 per SMB abgeholt: Share `[Encode]` (Pfad `/media/datenhdd3/Encoding/output`, RW für `ghost`, lesend für `daidalaw`/`debugofen`). Die Entnahme per `mv` in die Bibliotheks-Shares @@ -231,9 +254,10 @@ die Container gemountet (Updates ohne Image-Rebuild, versioniert über git). ## 11. Offene Punkte -1. Feinjustierung der Ressourcenlimits nach Lasttest unter Inferenzlast +1. NFSv4.2-Mount-Abnahme von `ghostnet-ki` gegen `10.0.0.2` — inkl. + Schreib-/Lesetest als UID 982/983 über den Mount und mv-Simulation als + `ghost` über SMB; mit der Encoding-Implementierung. +2. Feinjustierung der Ressourcenlimits nach Lasttest unter Inferenzlast (GNENC-001 Abschnitt 4). -2. Rootless-TUN-Verifizierung für den VPN-Sidecar auf dem Host (Fallback: +3. Rootless-TUN-Verifizierung für den VPN-Sidecar auf dem Host (Fallback: host-seitiger WireGuard mit Policy-Routing). -3. UID/GID-Abgleich: Gruppe `gn-media` (GID 970) muss auf CT-101 identisch - existieren; `ghost` Mitglied für die Abholung per `mv` (GNAI-017). diff --git a/docs/platform/proxmox-server/lxc/ct-101-samba/GNAI-017-PLAN_Samba-NFS-Export.md b/docs/platform/proxmox-server/lxc/ct-101-samba/GNAI-017-PLAN_Samba-NFS-Export.md index bb661eb..d9e2d93 100644 --- a/docs/platform/proxmox-server/lxc/ct-101-samba/GNAI-017-PLAN_Samba-NFS-Export.md +++ b/docs/platform/proxmox-server/lxc/ct-101-samba/GNAI-017-PLAN_Samba-NFS-Export.md @@ -2,7 +2,7 @@ **Dokumenten-ID:** GNAI-017-PLAN **Titel:** CT 101 Samba: Aufnahme ins Repository, NFS-Export und Shares für die Encoding-Pipeline -**Stand:** 2026-10-21 +**Stand:** 2026-10-30 **Status:** Plan; bestehende Shares produktiv und bislang manuell verwaltet; Ansible-Projekt gebaut (GNAI-019), Adoption des produktiven LXC ausstehend **Referenzen:** [GNENC-001](../../../llm-server/encoding/GNENC-001-PLAN_Architecture.md), [GNENC-002](../../../llm-server/encoding/GNENC-002-PLAN_Pipeline.md), [GNMON-001](../../../llm-server/monitoring/GNMON-001-PLAN_Architecture.md), [GNAI-011](../../GNAI-011-SEC_LXC-SSH-Architecture.md), [GNAI-018](GNAI-018-REQ_Encoding-Pipeline-Integration.md), [GNAI-019](GNAI-019-DEC_Ansible-Migration.md) @@ -60,8 +60,9 @@ guests = yes`, `obey pam restrictions = yes`. ### 4.1 NFS-Export **Korrektur 30.10.2026:** Der Export läuft als **Kernel-NFS -(`nfs-kernel-server`) auf dem PVE-Host** — umgesetzt via -`Host-Projekt `proxmox/pve-host/ansible/` (Rolle `pve_nfs_export`)` (idempotent, versioniert im Repo). +(`nfs-kernel-server`) auf dem PVE-Host** — umgesetzt über das +Host-Projekt `proxmox/pve-host/ansible/` (Rolle `pve_nfs_export`; idempotent, +versioniert im Repo). Zuvor war Ganesha-in-CT-101 geplant; das ist in einem unprivileged LXC nicht möglich (`open_by_handle_at` braucht CAP_DAC_READ_SEARCH in der init-userns und liefert dort immer EPERM — verifiziert 30.10.2026 auf Kernel @@ -91,6 +92,21 @@ Reproduzierbarkeit bleibt durch das versionierte Script gewährleistet. Dafür SMB-Freigaben `[Encode]`, `[Encode-In]` und `[Encoding]` (Root) für die Pipeline und die Kontrolle von anderen Geräten. +**Verifikation 30.10.2026 (read-only, von der Admin-Workstation):** CT 101: +`verify-production` `ok=47 changed=0 failed=0`; keine Ganesha-Reste (kein +Quadlet-Verzeichnis, keine Ganesha-Units), passdb unverändert (`ghost`, +`daidalaw`, `debugofen`). PVE-Host: `exportfs -v` zeigt den Export live +(`10.0.0.90/32`, `rw`, `root_squash`), nft-Tabelle `ghostnet_host_nfs` mit +2049 tcp/udp nur für `10.0.0.90`. Das `verify-production` des Host-Projekts +**schlug fehl (`failed=1`)**: Der Export-Root ist live `root:root 2777` statt +deklariert `100000:970 2775`; zudem läuft der nfsd noch mit NFSv3 +(`/proc/fs/nfsd/versions`: `+3 +4 +4.1 +4.2`) — die `RPCNFSDARGS`-Änderung +(NFSv4-only) wurde nach dem Edit nicht wirksam (fehlender Dienst-Restart; die +Rolle hat dafür keinen Handler). pve-firewall ist disabled/running — die +Netzrestriktion von 2049 beruht praktisch nur auf `/etc/exports`. **Korrektur +erledigt (30.10.2026, autorisiert):** Rollen-Fix und Konvergenz — Details in +Abschnitt 6.9; Berechtigungsmodell entschieden (Abschnitt 6.10). + ### 4.2 Gruppen- und UID-Abgleich - Gruppe `gn-media` mit **GID 970** auf CT-101 anlegen (identisch zu @@ -130,6 +146,18 @@ write list = ghost force group = gn-media create mask = 0664 directory mask = 0775 + +[Encoding] +comment = Encoding-Pipeline: Root (Verwaltung/Kontrolle per SMB) +path = /media/datenhdd3/Encoding +public = no +writeable = yes +valid users = ghost +admin users = ghost +write list = ghost +force group = gn-media +create mask = 0664 +directory mask = 0775 ``` - `[Encode]`: RW für `ghost` — die Entnahme per `mv` in die Bibliotheks-Shares @@ -270,3 +298,34 @@ GNENC-002 Abschnitt 4): `input/`, `jd/`, `queue/`, `work/`, `output/`, funktionsneutral (mit `valid users` ist Gastzugriff ohnehin verweigert). Ebenfalls angeglichen: `[printers]` trägt im Template nun `browseable = no` wie live (Sichtbarkeit im anonymen Listing). +9. **PVE-Host-Konvergenz — erledigt (30.10.2026, autorisierter Lauf):** + Rollen-Fix: Restart-Handler plus selbstheilende Versionsprüfung; NFSv4-only + wird auf Debian 13 über `/etc/nfs.conf` (`[nfsd] vers2=n`, `vers3=n`) + erzwungen, weil die systemd-Unit nfsd über `nfsdctl autostart` ohne + EnvironmentFile startet und `RPCNFSDARGS` in + `/etc/default/nfs-kernel-server` ignoriert (die wirkungslose Zeile wurde + entfernt). Konvergenz: Export-Root auf `100000:970 2775` gesetzt, nfsd neu + gestartet; danach `/proc/fs/nfsd/versions`: `-3 +4 +4.1 +4.2` (NFSv4-only + wirksam); `verify-production` (mit neuen Assertions zu Export-Root und + nfsd-Versionen) `ok=17 changed=0 failed=0`; Idempotenz-Lauf `changed=0`. + Präzisierung zu GNAI-019 Entscheidung 3: Die Stock-Unit hält + `nfs-mountd.service` als Required-Dienst und wanted `rpcbind.socket` — + mountd läuft also weiterhin, wird von NFSv4-Clients aber nicht genutzt. + Offen bleibt der reale NFSv4.2-Mount-Test von `ghostnet-ki` (mit der + Encoding-Implementierung). +10. **Berechtigungsmodell Pipeline-vs-SMB (30.10.2026)** — Die SMB-Schreibpfade + (`[Encode-In]` → `input//`, `[Encode]` → `output/...`) laufen über + guest-root (Host-UID 100000, da `admin users` Dateioperationen als root + ausführt); die Pipeline-Verzeichnisse gehören via NFS aber den realen + UIDs 982/983:970, die im idmap des unprivileged LXC nicht abgebildet sind + (im Gast: `nobody:nogroup`). Verzeichnisse mit Mode 2770/2750 sind für den + SMB-Pfad daher nicht schreibbar; die guest-seitige Gruppe `gn-media` + (GID 970 → Host-GID 100970) gleicht die Host-GID 970 nicht ab. Offene + Entscheidung: world-write (2777) auf den ghost-berührten Verzeichnissen + (aktuelles Ist-Verhalten des Export-Roots) oder Host-seitige ACLs; + Festlegung in GNENC-002 Abschnitt 11 vor der Encoding-Implementierung. + **Entschieden (Betreiber, 30.10.2026):** world-write — Mode `2777` auf dem + `input/`-Baum und dem gesamten `output/`-Baum (inkl. Release- und + `_failed/`-Verzeichnisse, vom Worker angelegt); `jd/`, `queue/`, `work/` + und `state/` bleiben eng (2770/2750/0750). Festgehalten in GNENC-002 + Abschnitt 4. diff --git a/docs/platform/proxmox-server/lxc/ct-101-samba/GNAI-018-REQ_Encoding-Pipeline-Integration.md b/docs/platform/proxmox-server/lxc/ct-101-samba/GNAI-018-REQ_Encoding-Pipeline-Integration.md index e2d379d..6ae935f 100644 --- a/docs/platform/proxmox-server/lxc/ct-101-samba/GNAI-018-REQ_Encoding-Pipeline-Integration.md +++ b/docs/platform/proxmox-server/lxc/ct-101-samba/GNAI-018-REQ_Encoding-Pipeline-Integration.md @@ -3,7 +3,7 @@ **Dokumenten-ID:** GNAI-018-REQ **Titel:** Requirements der LLM-Server-Encoding-Pipeline an CT 101 — Spezifikation für den Ansible-Rebuild **Stand:** 2026-10-19 -**Status:** Übergabedokument; verbindliche Anforderungsliste für den ausführenden Agenten +**Status:** Erfüllt am 30.10.2026 — Umsetzung und Revisionen in [GNAI-019](GNAI-019-DEC_Ansible-Migration.md) dokumentiert (R1 durch Kernel-NFS auf dem PVE-Host ersetzt; Abnahme-Nachweise in GNAI-017 Abschnitt 4.1 und 6.9/6.10) **Adressat:** Ansible-Rebuild CT 101 (ausführendes Projekt: `proxmox/lxc/ct-101-samba/ansible/`) **Quellen:** [GNAI-017](GNAI-017-PLAN_Samba-NFS-Export.md), [GNENC-001](../../../llm-server/encoding/GNENC-001-PLAN_Architecture.md), [GNENC-002](../../../llm-server/encoding/GNENC-002-PLAN_Pipeline.md), [GNMON-001](../../../llm-server/monitoring/GNMON-001-PLAN_Architecture.md) diff --git a/docs/platform/proxmox-server/lxc/ct-101-samba/GNAI-019-DEC_Ansible-Migration.md b/docs/platform/proxmox-server/lxc/ct-101-samba/GNAI-019-DEC_Ansible-Migration.md index ab6dc5a..675cbc3 100644 --- a/docs/platform/proxmox-server/lxc/ct-101-samba/GNAI-019-DEC_Ansible-Migration.md +++ b/docs/platform/proxmox-server/lxc/ct-101-samba/GNAI-019-DEC_Ansible-Migration.md @@ -1,7 +1,7 @@ # CT 101 – Samba-Dateiserver: Entwurfsentscheidungen der Ansible-Migration **Dokumenten-ID:** GNAI-019-DEC -**Titel:** CT 101 Samba: Design-Entscheidungen für die Ansible-Migration (natives Samba, NFS-Ganesha als Quadlet, Fresh-State-Rebuild) +**Titel:** CT 101 Samba: Design-Entscheidungen für die Ansible-Migration (natives Samba, Kernel-NFS auf dem PVE-Host, Fresh-State-Rebuild) **Stand:** 2026-10-21 **Status:** Entwurfsentscheidungen verbindlich; im Projekt `proxmox/lxc/ct-101-samba/` umgesetzt und lokal verifiziert (Syntax-Checks, Check-Mode-Smoke); die Adoption des produktiven LXC steht aus und erfordert Betreiber-Autorisierung **Referenzen:** [GNAI-017](GNAI-017-PLAN_Samba-NFS-Export.md), [GNAI-018](GNAI-018-REQ_Encoding-Pipeline-Integration.md), [GNAI-011](../../GNAI-011-SEC_LXC-SSH-Architecture.md), [GNAI-016](../ct-102-keycloak/GNAI-016-PLAN_Keycloak-Ansible-Rebuild.md), [GNENC-001](../../../llm-server/encoding/GNENC-001-PLAN_Architecture.md), [GNENC-002](../../../llm-server/encoding/GNENC-002-PLAN_Pipeline.md), [GNMON-001](../../../llm-server/monitoring/GNMON-001-PLAN_Architecture.md); Projektkonventionen: `proxmox/lxc/ct-100-dns/` und `proxmox/lxc/ct-102-keycloak/` @@ -72,8 +72,8 @@ und anonymer `smbclient -L`-Abfrage. (GNAI-018 R1) läuft als **Kernel-NFS (`nfs-kernel-server`) auf dem Proxmox-Host**, exportiert `/media/datenhdd3/Encoding` exklusiv für `ghostnet-ki` (`10.0.0.90/32`), NFSv4-only — umgesetzt durch das idempotente, -versionierte Script `Host-Projekt `proxmox/pve-host/ansible/` (Rolle `pve_nfs_export`)` (siehe -`scripts/README.md`). CT 101 kennt NFS nicht mehr: keine `samba_nfs`-Rolle, +versionierte Host-Projekt `proxmox/pve-host/ansible/` (Rolle +`pve_nfs_export`; siehe dessen README). CT 101 kennt NFS nicht mehr: keine `samba_nfs`-Rolle, kein Podman, keine 2049-Firewall-Regel, kein `lxc.seccomp.profile:`-Eintrag. Dafür stellt CT 101 die SMB-Freigaben `[Encode]`, `[Encode-In]` und `[Encoding]` (Root) für die Pipeline bzw. die Kontrolle von anderen Geräten. @@ -217,8 +217,10 @@ hand-editierten Zustand auf dem Ziel: - `nftables.conf` plus systemd-Drop-in (Ghostnet-eigene Tabelle `inet ghostnet_filter`, Netavark-Tabellen bleiben unberührt; `nft --check` vor Installation). -- `ganesha.conf` (NFSv4-only, siehe Entscheidung 3). -- Quadlet `ghostnet-samba-nfs.container` (siehe Entscheidung 2). + + (Korrektur 30.10.2026: Die Ganesha-Ära-Einträge `ganesha.conf` und Quadlet + `ghostnet-samba-nfs.container` sind mit der NFS-Verlagerung auf den PVE-Host + entfallen — siehe Entscheidung 2.) `verify-production.yml` rendert die Templates per `lookup` erneut und vergleicht sie mit den Live-Dateien (Drift-Erkennung). @@ -310,9 +312,10 @@ dieselbe Person auf Medien-/SMB-Ebene ist nicht erforderlich. - **GNAI-017 Abschnitt 2** (System-Baseline) wird um OS-Version (Debian 13 nach Upgrade) und Hostname (Erfassung per `preflight.yml`) ergänzt; Abschnitt 6 (offene Punkte) wird um die Entscheidungen dieses Dokuments aktualisiert. -- **GNAI-018 R1** wird revidiert: NFS-Ganesha-Quadlet statt - `nfs-kernel-server`; Export-Root `root:gn-media 2775` statt `root:root 0755` - (begründete Abweichung, siehe Entscheidung 2). +- **GNAI-018 R1** wird revidiert (Revision 30.10.2026): Kernel-NFS auf dem + PVE-Host statt `nfs-kernel-server` in CT 101; Export-Root + `100000:970 2775` statt `root:root 0755` (begründete Abweichung, siehe + Entscheidung 2). - **GNAI-018 Abschnitt 4** (Abnahme): Punkt 2 (`showmount`) wird als realer NFSv4.2-Mount-Test von `ghostnet-ki` interpretiert (Entscheidung 3); Punkt 8 nennt `ghostnet-samba-nfs.service` statt `nfs-kernel-server`. @@ -370,3 +373,19 @@ dieselbe Person auf Medien-/SMB-Ebene ist nicht erforderlich. GNMON-001; eigener Collector später). 8. Erweiterung der SMB-Allowlist auf das Admin-VPN (`10.8.0.0/24`) — Betreiberentscheidung, aktuell nicht geöffnet. +9. **PVE-Host-Konvergenz-Nachweis — erledigt (30.10.2026, autorisierter + Lauf):** Rollen-Fix (Restart-Handler, selbstheilende Versionsprüfung; + NFSv4-only via `/etc/nfs.conf` `[nfsd] vers2=n`/`vers3=n`, da die + Debian-13-Unit `nfsdctl autostart` ohne EnvironmentFile startet und + `RPCNFSDARGS` ignoriert) und Konvergenz: Export-Root + `100000:970 2775`, `/proc/fs/nfsd/versions` danach + `-3 +4 +4.1 +4.2`, `verify-production` `ok=17 changed=0 failed=0` + (jetzt mit Assertions zu Export-Root und nfsd-Versionen), Idempotenz + `changed=0`. Siehe GNAI-017 Abschnitt 6.9. Präzisierung zu Entscheidung 3: + Die Stock-Unit hält `nfs-mountd.service` als Required-Dienst — mountd + läuft weiterhin, wird von NFSv4-Clients aber nicht genutzt. +10. **Berechtigungsmodell für SMB-Schreibpfade — entschieden + (Betreiber, 30.10.2026):** world-write (Mode 2777) auf dem `input/`-Baum + und dem gesamten `output/`-Baum der Pipeline; `jd/`, `queue/`, `work/` + und `state/` bleiben eng. Begründung und Umsetzung: GNAI-017 + Abschnitt 6.10, GNENC-002 Abschnitt 4. diff --git a/proxmox/pve-host/ansible/playbooks/verify-production.yml b/proxmox/pve-host/ansible/playbooks/verify-production.yml index 28d04f9..12d0b17 100644 --- a/proxmox/pve-host/ansible/playbooks/verify-production.yml +++ b/proxmox/pve-host/ansible/playbooks/verify-production.yml @@ -106,6 +106,24 @@ - pve_verify_listeners.stdout is search(':2049 ') fail_msg: The PVE host does not listen on 2049 (NFSv4) yet. + - name: Read the NFS versions of the running nfsd + ansible.builtin.command: + argv: + - /bin/cat + - /proc/fs/nfsd/versions + check_mode: false + changed_when: false + register: pve_verify_nfs_versions + + - name: Require NFSv4-only on the running nfsd + ansible.builtin.assert: + that: + - "'+2' not in pve_verify_nfs_versions.stdout.split()" + - "'+3' not in pve_verify_nfs_versions.stdout.split()" + fail_msg: >- + The running nfsd still supports NFSv2/v3; the RPCNFSDARGS change is + not effective (expected NFSv4-only). + - name: Read the CT 101 config seccomp lines ansible.builtin.command: argv: diff --git a/proxmox/pve-host/ansible/roles/pve_nfs_export/README.md b/proxmox/pve-host/ansible/roles/pve_nfs_export/README.md index 56a8976..be5e495 100644 --- a/proxmox/pve-host/ansible/roles/pve_nfs_export/README.md +++ b/proxmox/pve-host/ansible/roles/pve_nfs_export/README.md @@ -7,18 +7,24 @@ decision 2, revised 2026-10-30: NFS cannot run in an unprivileged LXC — `open_by_handle_at` needs CAP_DAC_READ_SEARCH in the init user namespace). - Installs `nfs-kernel-server`. -- Disables NFSv2/v3 in `/etc/default/nfs-kernel-server` (`RPCNFSDARGS`). +- Disables NFSv2/v3 via `/etc/nfs.conf` (`[nfsd] vers2=n`, `vers3=n`). On + Debian 13 the systemd unit starts nfsd through `nfsdctl autostart` without + an EnvironmentFile, so the legacy `RPCNFSDARGS` line in + `/etc/default/nfs-kernel-server` is ineffective and removed. - Manages a marked block in `/etc/exports` (idempotent `blockinfile`): ` /32(rw,sync,no_subtree_check,root_squash)`. - Sets the export root to `100000:970 2775`: UID 100000 is the CT 101 guest root (so the SMB `[Encoding]` share inside CT 101 can write), GID 970 is `gn-media` (group-write + setgid for the pipeline identities arriving via AUTH_SYS from ghostnet-ki; `root_squash` stays active). -- Applies `exportfs -ra` and enables/starts `nfs-kernel-server.service`. +- Applies `exportfs -ra` and enables/starts `nfs-kernel-server.service`; + restarts the service when config changed (handler) and self-heals while the + running nfsd still supports NFSv2/v3 (`/proc/fs/nfsd/versions`). - Firewall: when `pve-firewall` is **not** active on the host, allows 2049/tcp+udp from the client only, in a dedicated nftables table `inet ghostnet_host_nfs`; when pve-firewall **is** active, the role only prints the required PVE-firewall rule instead of touching nftables. Post-state verification lives in `playbooks/verify-production.yml` -(package, service, `exportfs -s`, export-root ownership/mode, listener 2049). +(package, service, `exportfs -s`, export-root ownership/mode, listener 2049, +NFSv4-only on the running nfsd). diff --git a/proxmox/pve-host/ansible/roles/pve_nfs_export/handlers/main.yml b/proxmox/pve-host/ansible/roles/pve_nfs_export/handlers/main.yml new file mode 100644 index 0000000..b3a057f --- /dev/null +++ b/proxmox/pve-host/ansible/roles/pve_nfs_export/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: Restart the kernel NFS server + ansible.builtin.systemd_service: + name: "{{ pve_nfs_service_unit }}" + state: restarted diff --git a/proxmox/pve-host/ansible/roles/pve_nfs_export/tasks/main.yml b/proxmox/pve-host/ansible/roles/pve_nfs_export/tasks/main.yml index 12d2e42..e42518d 100644 --- a/proxmox/pve-host/ansible/roles/pve_nfs_export/tasks/main.yml +++ b/proxmox/pve-host/ansible/roles/pve_nfs_export/tasks/main.yml @@ -29,15 +29,33 @@ cache_valid_time: 3600 install_recommends: false -- name: Disable NFSv2/v3 in the kernel server defaults (NFSv4-only) +- name: Disable NFSv2 in nfs.conf (NFSv4-only) ansible.builtin.lineinfile: - path: /etc/default/nfs-kernel-server - create: true + path: /etc/nfs.conf + insertafter: '^\[nfsd\]' + regexp: '^vers2=' + line: 'vers2=n' owner: root group: root mode: "0644" + notify: Restart the kernel NFS server + +- name: Disable NFSv3 in nfs.conf (NFSv4-only) + ansible.builtin.lineinfile: + path: /etc/nfs.conf + insertafter: '^\[nfsd\]' + regexp: '^vers3=' + line: 'vers3=n' + owner: root + group: root + mode: "0644" + notify: Restart the kernel NFS server + +- name: Remove the ineffective legacy RPCNFSDARGS line + ansible.builtin.lineinfile: + path: /etc/default/nfs-kernel-server regexp: '^RPCNFSDARGS=' - line: 'RPCNFSDARGS="--no-nfs-version 2 --no-nfs-version 3"' + state: absent - name: Manage the marked export block in /etc/exports ansible.builtin.blockinfile: @@ -49,6 +67,7 @@ marker: "# {mark} ghostnet CT-101 encoding export (managed by role pve_nfs_export)" block: | {{ pve_nfs_export_path }} {{ pve_nfs_client_ipv4 }}/32(rw,sync,no_subtree_check,root_squash) + notify: Restart the kernel NFS server - name: Set the export root ownership and mode ansible.builtin.file: @@ -71,6 +90,23 @@ enabled: true state: started +- name: Read the NFS versions of the running nfsd + ansible.builtin.command: + argv: + - /bin/cat + - /proc/fs/nfsd/versions + check_mode: false + changed_when: false + register: pve_nfs_running_versions + +- name: Restart the kernel NFS server while NFSv2/v3 is still active + ansible.builtin.systemd_service: + name: "{{ pve_nfs_service_unit }}" + state: restarted + when: >- + '+2' in pve_nfs_running_versions.stdout.split() or + '+3' in pve_nfs_running_versions.stdout.split() + - name: Read the PVE firewall status ansible.builtin.command: argv: