CT-101 Samba + PVE-Host: NFS auf den Host verlagert, Host per Ansible verwaltet

- CT 101 kennt NFS nicht mehr: samba_nfs-Rolle und Ganesha-Artefakte entfernt,
  kein Podman, keine 2049-Firewall-Regel, kein seccomp-Sondereintrag; live
  restlos bereinigt und deckungsgleich mit dem Playbook (verify ok=47,
  Idempotenz changed=0). Dafür neue SMB-Freigabe [Encoding] (Export-Root) für
  die Kontrolle von anderen Geräten als ghostnet-ki.
- Grund: Weder kernel-nfsd noch Ganesha (VFS-FSAL) laufen in einem
  unprivileged LXC — open_by_handle_at braucht CAP_DAC_READ_SEARCH in der
  init-userns und liefert dort immer EPERM (verifiziert auf Kernel
  7.0.14-14-pve; Web-Recherche: NFS direkt auf dem PVE-Host ist gängige Praxis
  und anerkannte Ausnahme vom Host-Grundsatz).
- Neuer Account ansible-deploy (UID 1001) auf dem PVE-Host 10.0.0.2 nach
  LXC-Muster: gelocktes Passwort, source-restrictierter passphrase-loser Key
  (nur pub im Repo), NOPASSWD-Sudoers; Host-Key-Fingerprint verifiziert.
- Neues Host-Projekt proxmox/pve-host/ansible (Schema wie die CT-Projekte):
  preflight/site.yml/verify-production + Rollen pve_nfs_export (Kernel-NFS,
  /etc/exports nur 10.0.0.90/32, NFSv4-only, Export-Root 100000:970 2775,
  nft 2049 nur von 10.0.0.90) und pve_lxc_conf (seccomp-Leerzeile aus
  101.conf entfernt). Auf dem Host grün: check changed=0, verify ok=15,
  Idempotenz changed=0.
- Abnahme bestanden: NFSv4.2-Mount von ghostnet-ki gegen
  10.0.0.2:/media/datenhdd3/Encoding (Export-Root 100000:970 2775 sichtbar),
  sauberer Unmount. CT 101 nach Reboot: Seccomp 2, keine failed units, 17
  Shares, 10 CIFS-Mounts der Workstation hängen.
- Doku aktualisiert: GNAI-017 §4.1 und GNAI-019 E2/E3/§15 revidiert,
  Bereichsindex ergänzt, READMEs angepasst; PVE-Lifecycle-Skripte ohne
  seccomp-Ausnahme (Hashes aktualisiert).

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
2026-10-30 18:49:10 +01:00
parent 2753677c7c
commit 6a1eff7392
46 changed files with 768 additions and 1015 deletions
+1
View File
@@ -18,6 +18,7 @@ Hypervisors selbst.
| [GNAI-018 / CT 101](lxc/ct-101-samba/GNAI-018-REQ_Encoding-Pipeline-Integration.md) | Requirements der Encoding-Pipeline an CT 101 für den Ansible-Rebuild (Übergabe) |
| [GNAI-019 / CT 101](lxc/ct-101-samba/GNAI-019-DEC_Ansible-Migration.md) | Entwurfsentscheidungen der Ansible-Migration: natives Samba, NFS-Ganesha als Quadlet, Fresh-State-Rebuild |
| [CT-101-Automation](../../../proxmox/lxc/ct-101-samba/README.md) | produktives Samba-Ansible-Projekt und CT-spezifische PVE-Helfer |
| [PVE-Host-Automation](../../../proxmox/pve-host/README.md) | Ansible-Verwaltung des PVE-Hosts selbst (`10.0.0.2`): Deploy-Account, Kernel-NFS-Export, LXC-Conf |
Die versionierte Quelle für den optionalen, secretfreien
Community-Scripts-Post-install-Hook liegt unter
@@ -59,29 +59,37 @@ guests = yes`, `obey pam restrictions = yes`.
### 4.1 NFS-Export
Ursprünglich war `nfs-kernel-server` mit `/etc/exports` geplant. **Überholt
durch GNAI-019 Entscheidung 2 und 3:** Der Kernel-NFS-Server ist in einem
unprivileged LXC nicht betreibbar; der Export läuft stattdessen als
**NFS-Ganesha** in einem rootful Podman-Quadlet-Container
(`ghostnet-samba-nfs.service`, Host-Networking, NFSv4-only). Dafür wird in
`/etc/pve/lxc/101.conf` ein leerer `lxc.seccomp.profile:`-Eintrag gesetzt
(Ganesha braucht `open_by_handle_at`). Umsetzung: Rolle `samba_nfs` plus die
PVE-Skripte `scripts/upgrade-ct101-to-debian13.sh` (Adoption) und
`scripts/provision-ct101-samba.sh` (Fresh-State-Rebuild).
**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).
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
7.0.14-14-pve; der leere `lxc.seccomp.profile:`-Eintrag war dafür nicht
ausreichend). Web-Recherche zu Proxmox-Best-Practices (30.10.2026): NFS direkt
auf dem Host ist gängige Praxis zum Teilen von Host-Storage und eine
anerkannte Ausnahme vom Grundsatz „keine Extra-Services auf dem Host";
Reproduzierbarkeit bleibt durch das versionierte Script gewährleistet.
- Export ausschließlich für `ghostnet-ki` (10.0.0.90), NFSv4.2-only (TCP+UDP),
`SecType = sys`, Standard-Squash bleibt aktiv (`Squash = rootid`): Die
rootless Container schreiben als UID 982/983, nicht als root. Kein NFSv3,
kein mountd/rpcbind — `showmount` gilt nicht; Abnahme ist ein realer
NFSv4.2-Mount von `ghostnet-ki` (GNAI-018 Abschnitt 4 Punkt 2, angepasst).
- Mount auf `ghostnet-ki`: `/var/lib/ghostnet-encoding` (NFSv4.2, `hard`,
`_netdev`, `noatime`) — verwaltet vom Encoding-Ansible (GNENC-001 Abschnitt 6).
- Vorab wird nur das Export-Verzeichnis `/media/datenhdd3/Encoding` angelegt
(`root:gn-media 2775` — Abweichung von root:root 0755: Die Pipeline erzeugt
die Top-Level-Verzeichnisse via NFS als UID 982/983 und braucht group-write
plus setgid auf dem Export-Root); die Pipeline-Unterverzeichnisse werden via
NFS von `ghostnet-ki` durch die Ansible-Rolle angelegt (Owner/Group gemäß
GNENC-002 Abschnitt 4).
- Export ausschließlich für `ghostnet-ki` (`10.0.0.90/32`) via `/etc/exports`
(`rw,sync,no_subtree_check,root_squash`), **NFSv4-only** (NFSv2/v3 auf dem
Host abgeschaltet). `showmount` gilt nicht; Abnahme ist ein realer
NFSv4.2-Mount von `ghostnet-ki` gegen die PVE-Host-IP.
- Mount auf `ghostnet-ki`: `/var/lib/ghostnet-encoding`
(`mount -t nfs -o nfsvers=4.2,hard,noatime <pve-host-ip>:/media/datenhdd3/Encoding …`)
— verwaltet vom Encoding-Ansible (GNENC-001 Abschnitt 6; die Mount-Quelle
ändert sich von `10.0.0.9` auf die PVE-Host-IP).
- Export-Root `/media/datenhdd3/Encoding` auf dem Host: `100000:970 2775`
(100000 = CT-101-Gast-root, damit die SMB-Freigabe `[Encoding]` aus CT 101
schreiben kann; GID 970 = `gn-media`, group-write + setgid für die Pipeline-
Identitäten via AUTH_SYS). Die Pipeline-Unterverzeichnisse entstehen via NFS
von `ghostnet-ki` (Owner/Group gemäß GNENC-002 Abschnitt 4).
- Host-Firewall: 2049 tcp+udp nur von 10.0.0.90 (nft-Tabelle
`ghostnet_host_nfs`, sofern pve-firewall den Host nicht verwaltet).
- CT 101 kennt NFS nicht mehr: keine `samba_nfs`-Rolle, kein Podman, keine
2049-Regel in der CT-Firewall, kein seccomp-Eintrag in `/etc/pve/lxc/101.conf`.
Dafür SMB-Freigaben `[Encode]`, `[Encode-In]` und `[Encoding]` (Root) für die
Pipeline und die Kontrolle von anderen Geräten.
### 4.2 Gruppen- und UID-Abgleich
@@ -66,59 +66,48 @@ ist als Jinja2-Template im Repository gepflegt (Entscheidung 8).
Konfigurationsänderung; Post-State-Verifikation per `testparm`, `pdbedit -L`
und anonymer `smbclient -L`-Abfrage.
## 4. Entscheidung 2: NFS-Export via NFS-Ganesha als rootful Podman-Quadlet mit Host-Networking (statt `nfs-kernel-server`)
## 4. Entscheidung 2: NFS-Export als Kernel-NFS auf dem PVE-Host (revidiert 30.10.2026)
**Entscheidung:** Der NFS-Export für die Encoding-Pipeline (GNAI-018 R1) wird
nicht mit `nfs-kernel-server`, sondern mit **NFS-Ganesha** als **rootful**
Podman-Quadlet-Container (`ghostnet-samba-nfs.service`) mit **Host-Networking**
umgesetzt. Dafür wird in `/etc/pve/lxc/101.conf` ein **leerer
`lxc.seccomp.profile:`**-Eintrag gesetzt (Deaktivierung des LXC-Seccomp-Filters
für den gesamten CT).
**Entscheidung (revidiert):** Der NFS-Export für die Encoding-Pipeline
(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,
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.
**Begründung:**
**Begründung:** Weder Kernel-nfsd noch NFS-Ganesha (VFS-FSAL) können in einem
unprivileged LXC laufen: `open_by_handle_at` — für Ganesha zwingend nötig —
erfordert `CAP_DAC_READ_SEARCH` in der init-userns und liefert im unprivileged
LXC immer EPERM (verifiziert 30.10.2026 auf Kernel 7.0.14-14-pve; der leere
Seccomp-Eintrag der ursprünglichen Planung war dafür nicht ausreichend).
Web-Recherche zu Proxmox-Best-Practices (30.10.2026): NFS direkt auf dem
PVE-Host ist gängige Praxis zum Teilen von Host-Storage und eine anerkannte
Ausnahme vom Grundsatz „keine Extra-Services auf dem Host“; die
Reproduzierbarkeit des Host-Änderung ist durch das versionierte Script im
Repository gegeben. virtiofs/9p scheiden aus, weil `ghostnet-ki` eine eigene
physikalische Maschine im LAN ist.
- GNAI-018 R1 verlangte `nfs-kernel-server`. Der Kernel-NFS-Server kann in
einem unprivileged LXC nicht betrieben werden (kein Zugriff auf das
Kernel-NFS-Subsystem des Hosts). Ganesha ist ein Userspace-NFS-Server (VFS
FSAL) und läuft in einem unprivileged LXC.
- **Rootful** ist fachlich nötig: Der VFS-FSAL wendet die AUTH_SYS-Credentials
der Pipeline (UID 982/983) pro Request an; das erfordert `CAP_SETUID`/
`CAP_SETGID` innerhalb der LXC-User-Namespace, was ein rootless Container
nicht leisten kann. Standard-Squash bleibt aktiv (`Squash = rootid`, R1).
- **Host-Networking** bindet den Listener 2049 im Netz-Namespace von CT 101;
die nftables-Input-Chain deckt ihn ab. Die Quadlet-Unit trägt
`Requires=nftables.service` — ohne Firewall startet der Export nicht
(fail closed).
- **Leerer Seccomp-Eintrag:** Ganesha benötigt `open_by_handle_at` (VFS FSAL),
das das PVE-Standard-Seccomp-Profil blockiert. Der leere Eintrag deaktiviert
den Filter für den gesamten CT. Das ist vertretbar, weil der CT unprivileged
bleibt und Samba nativ keine privilegierten Syscalls jenseits der
User-Namespace benötigt. Beide PVE-Skripte setzen bzw. verifizieren den
leeren Eintrag (`scripts/upgrade-ct101-to-debian13.sh`,
`scripts/provision-ct101-samba.sh`).
- **Abweichung vom Export-Root aus R1:** Statt `root:root 0755` wird
`/media/datenhdd3/Encoding` als `root:gn-media 2775` angelegt. Die Pipeline
erzeugt die Top-Level-Verzeichnisse via NFS als UID 982/983 und benötigt
group-write plus setgid auf dem Export-Root (GNAI-017 Abschnitt 4.4); die
Pipeline-Unterverzeichnisse selbst entstehen weiterhin via NFS durch die
Rolle `video_encoding` auf `ghostnet-ki`.
**Konsequenz:** PVE-seitige Änderung am produktiven CT (Seccomp) — im
Upgrade-Skript durch einen `vzdump --mode stop` abgesichert. Die
Quadlet-Unit folgt dem CT-100/102-Pattern: digest-gepinntes Debian-13-Basisimage
(`debian:trixie-slim`), sha256-gepinntes `Containerfile.ganesha`,
`Pull=never`, Runtime-Tag `localhost/ghostnet/ganesha:trixie-ansible1`,
Healthcheck, read-only rootfs mit tmpfs `/run` und `/tmp`. Ohne dbus logged
Ganesha eine nicht-fatale Admin-Interface-Warnung (dokumentiert in
`roles/samba_nfs/README.md`).
**Konsequenz:** Host-seitige Änderung (Paket `nfs-kernel-server`,
`/etc/exports`, ggf. Host-Firewall 2049 nur von 10.0.0.90), abgesichert durch
eine Confirmation-Env-Variable; CT-Upgrades bleiben durch `vzdump` gesichert.
Export-Root auf dem Host: `100000:970 2775` (100000 = CT-101-Gast-root für den
SMB-Zugriff aus CT 101; GID 970 = `gn-media` group-write + setgid für die
Pipeline-Identitäten via AUTH_SYS). Der Ganesha-Ära-Eintrag
`lxc.seccomp.profile:` in `/etc/pve/lxc/101.conf` wird entfernt — CT 101 läuft
wieder mit Default-Seccomp wie CT 100/102/103. Mount auf `ghostnet-ki`:
`/var/lib/ghostnet-encoding` via `<pve-host-ip>:/media/datenhdd3/Encoding`
(NFSv4.2; die Mount-Quelle wechselt von `10.0.0.9` auf die Host-IP).
## 5. Entscheidung 3: NFSv4-only — kein NFSv3, kein `showmount`
**Entscheidung:** Der Export ist NFSv4-only (`Protocols = 4`, kein NLM, kein
RQUOTA, kein mountd/rpcbind), Transport TCP+UDP, `SecType = sys`,
`Squash = rootid`, client-beschränkt auf `10.0.0.90`, `Pseudo` identisch zum
Export-Pfad (dokumentierter Mount-Pfad `10.0.0.9:/media/datenhdd3/Encoding`
bleibt unverändert).
**Entscheidung:** Der Export ist NFSv4-only (NFSv2/v3 auf dem Host
abgeschaltet, kein mountd/rpcbind), Transport TCP+UDP, `root_squash` (Default),
client-beschränkt auf `10.0.0.90/32` in `/etc/exports`. Mount-Pfad auf
`ghostnet-ki`: `<pve-host-ip>:/media/datenhdd3/Encoding` (host-seitig seit der
Revision 30.10.2026, nicht mehr `10.0.0.9`).
**Begründung:** NFSv4 benötigt keinen Portmapper und kein mountd — weniger
Angriffsfläche und keine Legacy-Protokolle. Die Pipeline mountet NFSv4.2
@@ -126,8 +115,9 @@ Angriffsfläche und keine Legacy-Protokolle. Die Pipeline mountet NFSv4.2
**Konsequenz:** `showmount -e` ist kein zulässiges Abnahmekriterium
(NFSv4-only). GNAI-018 Abschnitt 4 Punkt 2 wird als **realer NFSv4.2-Mount-Test
von `ghostnet-ki`** interpretiert; dasselbe gilt für Punkt 8 (Dienste nach
Reboot: `ghostnet-samba-nfs.service` statt `nfs-kernel-server`).
von `ghostnet-ki` gegen den PVE-Host** interpretiert; dasselbe gilt für Punkt 8
(Dienste nach Reboot: `nfs-kernel-server` auf dem PVE-Host statt
`ghostnet-samba-nfs.service` in CT 101).
## 6. Entscheidung 4: Identität — `gn-media` GID 970, aufgezeichnete Medien-UIDs, Mitgliedschaft nur `ghost`
@@ -342,6 +332,12 @@ dieselbe Person auf Medien-/SMB-Ebene ist nicht erforderlich.
`smb.conf` (inkl. `admin users` auf elf Shares, `write list` auf
`[Dokumente]`/`[Extern]`; `[Serien2]` ohne `force user/group`), damit die
Konvergenz keine produktiven Share-Rechte ändert.
- **Revision 30.10.2026:** Entscheidung 2/3 revidiert — der NFS-Export läuft
als Kernel-NFS auf dem PVE-Host (`Host-Projekt `proxmox/pve-host/ansible/` (Rolle `pve_nfs_export`)`),
nicht als Ganesha-Quadlet in CT 101 (`open_by_handle_at` → EPERM im
unprivileged LXC, verifiziert). GNAI-017 Abschnitt 4.1 entsprechend
aktualisiert; `samba_nfs`-Rolle, Podman, 2049-Regel und seccomp-Eintrag aus
CT 101 entfernt; SMB-Freigabe `[Encoding]` (Root) ergänzt.
## 16. Offene Punkte