From 8b21797502d5eacbbb21756ad6ec750811451f5f Mon Sep 17 00:00:00 2001 From: spektr Date: Wed, 28 Oct 2026 16:30:33 +0100 Subject: [PATCH] CT-101 Samba: dark2devil-Korrektur, ACL-Abgleich und AGENTS.md-Grundwissen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dark2devil (UID 1002) aus der gesamten deklarierten State entfernt: derselbe Benutzer wie debugofen (Betreiberklärung 2026-10-28); GNAI-019 Entscheidung 11 revidiert, gn-media-Mitgliedschaft nur ghost - Share-ACLs im Inventar an die live smb.conf angeglichen (GNAI-018 R4): admin users auf elf Shares ergänzt, write list Dokumente/Extern ergänzt, Serien2 ohne force user/group; Template [printers] um browseable=no ergänzt - passdb-Asserts und Audit-Loops auf die drei deklarierten Medien-User umgestellt; Platzhalterzahl 11; provision-Script-Hash aktualisiert - AGENTS.md (Repo-Root) um Grundwissen für Agenten ergänzt: ghost-agent-Convention, Account-Landscape/Inventory-Pfade, Operator-Run-Steps, LXC-Konventionen, Repo-Konventionen Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe --- AGENTS.md | 92 +++++++++++++++++++ .../GNAI-017-PLAN_Samba-NFS-Export.md | 91 +++++++++++++++--- .../GNAI-019-DEC_Ansible-Migration.md | 76 +++++++++++---- proxmox/lxc/ct-101-samba/ansible/README.md | 24 +++-- .../agent/group_vars/samba_hosts.yml | 86 ++++++++++++----- .../agent/host_vars/samba-ct101.yml | 18 ++-- .../bootstrap/group_vars/samba_hosts.yml | 86 ++++++++++++----- .../bootstrap/host_vars/samba-ct101.yml | 18 ++-- .../production/group_vars/samba_hosts.yml | 86 ++++++++++++----- .../production/host_vars/samba-ct101.yml | 18 ++-- .../ansible/playbooks/audit-production.yml | 15 +-- .../ansible/playbooks/preflight.yml | 81 ++++++++++++++-- .../ansible/playbooks/verify-production.yml | 20 ++-- .../roles/samba_access/defaults/main.yml | 13 +-- .../roles/samba_lxc_baseline/README.md | 5 +- .../samba_lxc_baseline/defaults/main.yml | 2 +- .../roles/samba_lxc_baseline/tasks/main.yml | 28 ++++++ .../ansible/roles/samba_media/README.md | 22 +++-- .../roles/samba_media/defaults/main.yml | 8 +- .../ansible/roles/samba_smb/README.md | 3 +- .../ansible/roles/samba_smb/tasks/main.yml | 3 +- .../roles/samba_smb/templates/smb.conf.j2 | 1 + proxmox/lxc/ct-101-samba/scripts/README.md | 39 ++++---- .../scripts/provision-ct101-samba.sh | 37 +++++--- .../scripts/upgrade-ct101-to-debian13.sh | 7 +- 25 files changed, 671 insertions(+), 208 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3eb6fe4..46aa027 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,3 +5,95 @@ Before infrastructure work, read `.codex/LOCAL_MEMORY.md` if it exists. It conta For read-only access to the KI server, use the tested SSH profile `ghostnet-ai-agent`, which logs in as the unprivileged account `ghost-agent` on `10.0.0.90`. On this workstation, invoke it as `ssh -F ~/.ssh/config ghostnet-ai-agent` because the system-wide SSH client configuration currently has an ownership/permissions error. Do not assume sudo access or make server changes without an explicit user request. The KI server at `10.0.0.90` is deliberately a fresh base system. The operator plans to install the LLM application stack in a later Ansible sprint. Do not install applications or change server configuration as part of documentation review or access checks unless the operator explicitly requests that work. + +## Agent access convention: `ghost-agent` + +`ghost-agent` is the standard unprivileged account for the AI agent on the +Proxmox LXCs and the KI server (operator-confirmed 2026-10-28: "on the KI +server and many other Proxmox LXCs"). It is a convention, not a per-host +exception. + +- Login is key-based with a **passphrase-less** Ed25519 key kept in the + operator's `~/.ssh` (`id_ed25519_ghostnet__agent`); `~/.ssh/config` + carries the matching `ghostnet--agent` aliases, which also work with + `ssh -o BatchMode=yes`. Private keys never enter the repository. +- `ghost-agent` has **no sudo** (verified on CT 100, CT 101, CT 103 and + ghostnet-ki): treat every agent session as read-only unless the operator + explicitly authorizes a change. +- Known aliases (verify live state before use; the WireGuard LXC has no + `ghost-agent` alias and uses the legacy `admin` account via alias `lxc`): + + | Alias | System | IP | + | --- | --- | --- | + | `ghostnet-ct100-agent` | CT 100, Pi-hole/Split-DNS `dns` | 10.0.0.14 | + | `ghostnet-ct101-agent` | CT 101, Samba file server | 10.0.0.9 | + | `ghostnet-ct102-agent` | CT 102, Keycloak | 10.0.0.4 | + | `ghostnet-ct103-agent` | CT 103, reverse proxy (NPM) | 10.0.0.10 | + | `ghostnet-ct104-agent` | CT 104, Keycloak | 10.0.0.16 | + | `ghostnet-ai-agent` | ghostnet-ki, LLM/AI server | 10.0.0.90 | + +- Invoke ssh as `ssh -F ~/.ssh/config `: a bare `ssh` currently rejects + `/etc/ssh/ssh_config.d/20-systemd-ssh-proxy.conf` because of its + ownership/permissions; `-F` deliberately uses the local profile without + loading that broken system configuration. +- Dated per-host observations (host-key fingerprints, verified identities, + account details) live in `.codex/LOCAL_MEMORY.md`, not here. + +## Account landscape and Ansible inventory paths + +| Account | Role | Key (operator `~/.ssh`) | sudo | +| --- | --- | --- | --- | +| `ghost-agent` | AI agent, read-only | `id_ed25519_ghostnet__agent` (no passphrase) | no | +| `ghost` | human operator/admin | `id_ed25519_ghostnet__ghost` (passphrase-protected) | yes, password-gated | +| `ansible-deploy` | Ansible deployment; created by the project's access role during adoption | `id_ed25519_ghostnet__ansible_deploy` (no passphrase) | `NOPASSWD: ALL` (treat the key as root-equivalent) | + +Each Ansible LXC project (`proxmox/lxc//ansible/`) maps these accounts to +three inventories: `agent` (`ghost-agent`, read-only preflight), `bootstrap` +(`ghost`, `--ask-become-pass`), `production` (`ansible-deploy`, NOPASSWD). +Legacy exceptions exist (CT 103 is root-only and bootstrapped via the Proxmox +console; the WireGuard LXC uses `admin`) — see `.codex/LOCAL_MEMORY.md`. + +## Operator-run steps + +- The workstation has **no Proxmox host access** (no `pct`): PVE-side commands + (`pct exec`, `pct reboot`, `vzdump`, editing `/etc/pve/lxc/*.conf`) are run by + the operator as root@pve. The agent prepares exact commands and reviews the + pasted output. +- Interactive secret steps are operator-run: `passwd`, `smbpasswd -a`, playbook + runs with `--ask-become-pass`, and key passphrase prompts. The agent hands + over copy-paste commands; the operator executes them. +- Secrets never enter the repository, inventories, playbooks or logs: SMB + passwords, the Samba passdb, the `ghost` password, private SSH keys, Podman + secrets. Only public keys belong in the repo (`ansible/files/ssh/`). + +## LXC / Proxmox conventions + +- 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. +- In-place dist-upgrades: take a `vzdump` safety net first and use + `--force-confold` so productive configuration files survive. + +## Repository conventions + +- Documentation: `docs/platform/proxmox-server/lxc//GNAI-*.md` (German; + decisions carry operator/date stamps, findings are lettered a), b), …). + Ansible project READMEs are English. The central index is + `docs/GNAI-000-DOC_Index.md`. +- Ansible projects are self-contained under `proxmox/lxc//ansible/` + (`ansible.cfg`, `inventories/{agent,bootstrap,production}`, `playbooks/`, + `roles/`, `files/ssh/`), use the repository-wide control venv (`.venv`, built + from `shared/ansible/requirements-control.txt`) and the shared roles in + `shared/ansible/roles/`. +- Change discipline: productive changes only with explicit operator go; preview + with `--check --diff` first; convergence must be idempotent (a second run + reports `changed=0`); commit only when the operator asks. +- Communicate with the operator in German. +- Verify account/identity assumptions with the operator before declaring them + (CT 101 example: `dark2devil` turned out to be the same person as + `debugofen`; GNAI-019 decision 11 was revised accordingly). 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 04ff632..f4f4e93 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 @@ -10,8 +10,9 @@ CT 101 (`10.0.0.9`) ist der Samba-Dateiserver des Proxmox-Kontexts und trägt den Medienspeicher (Filme, Serien, Anime, Musik, Software, Downloads) auf -mehreren Platten (`/media/daten1`, `/media/daten2`, `/media/daten3`, -`/media/datenhdd2`, `/media/datenhdd3`). Bislang ist die LXC nicht im +mehreren Platten (Mounts `/media/daten1`, `/media/daten2`, +`/media/daten3`, `/media/datenhdd1`, `/media/datenhdd2/extern`, +`/media/datenhdd3`; erfasst am 27.10.2026). Bislang ist die LXC nicht im Infrastructure-Repository verwaltet; am 19.10.2026 wurde beschlossen, sie aufzunehmen (Betreiberentscheidung). @@ -26,9 +27,10 @@ offenen Punkte fest. Bestehende Shares bleiben unberührt. | Container-ID | CT 101 | | IPv4 | 10.0.0.9 | | Samba-Rolle | standalone server, Workgroup `Ghost.net` (aus `smb.conf`) | -| SMB-Benutzer | `ghost`, `daidalaw`, `debugofen` | +| SMB-Benutzer | `ghost`, `daidalaw`, `debugofen` (die passdb enthält zusätzlich den Alt-Eintrag `dark2devil`, UID 1002 — dieselbe Person wie `debugofen`, nicht deklariert; siehe Abschnitt 6.8 (e) und GNAI-019 Entscheidung 11) | | Betriebssystem | Debian 13 (trixie) seit In-Place-Upgrade durch den Betreiber am 21.10.2026 (manuell; nicht via `scripts/upgrade-ct101-to-debian13.sh`) | -| Hostname | nicht erfasst — `preflight.yml` meldet ihn vor der Adoption (Inventory-Platzhalter `samba_expected_hostname`) | +| Hostname | `Samba` (großgeschrieben; FQDN `Samba.fritz.box`), erfasst am 27.10.2026; Inventory-Platzhalter `samba_expected_hostname` | +| Ressourcen-Profil | 1 CPU (cpuset), 2048 MiB RAM, 2048 MiB Swap, 4 GiB Rootfs (lxcfs-/cgroup-Werte im Gast, 27.10.2026) | ## 3. Bestand: Shares (aus `smb.conf`, Stand 19.10.2026) @@ -37,7 +39,7 @@ offenen Punkte fest. Bestehende Shares bleiben unberührt. | `[homes]` | Home-Verzeichnisse | read-only, `valid users = %S` | Debian-Standard | | `[printers]` | `/var/tmp` | printable, read-only | **Abschnitt doppelt vorhanden** (siehe Abschnitt 6) | | `[print$]` | `/var/lib/samba/printers` | read-only | Druckertreiber | -| `[Dokumente]` | `/media/daten1/Dokumente` | RO, nur `ghost` | — | +| `[Dokumente]` | `/media/daten1/Dokumente` | RO, nur `ghost`, write `ghost` | — | | `[Filme]` | `/media/daten1/Filme` | RO, `ghost`/`daidalaw`/`debugofen`, write `ghost` | — | | `[Filme2]` | `/media/datenhdd3/Filme` | wie `[Filme]` | — | | `[Serien]` | `/media/daten2/Serien` | RO, alle drei, write `ghost` | `force user/group = root` | @@ -85,9 +87,12 @@ PVE-Skripte `scripts/upgrade-ct101-to-debian13.sh` (Adoption) und - Gruppe `gn-media` mit **GID 970** auf CT-101 anlegen (identisch zu `ghostnet-ki`). -- Benutzer `ghost` in `gn-media` aufnehmen: Schreib-/Löschrecht auf den - Pipeline-Verzeichnissen, damit die Abholung per `mv` aus `[Encode]` - funktioniert. `daidalaw`/`debugofen` optional für reinen Lesezugriff. +- Benutzer `ghost` in `gn-media` aufnehmen (GNAI-018 R3): + Schreib-/Löschrecht auf den Pipeline-Verzeichnissen, damit die Abholung per + `mv` aus `[Encode]` und der `.dlc`-Drop in `[Encode-In]` funktionieren. + `daidalaw`/`debugofen` benötigen keine Mitgliedschaft — reiner Lesezugriff + genügt. **Korrektur 28.10.2026:** Die zunächst geplante Aufnahme von + `dark2devil` entfällt (GNAI-019 Entscheidung 11 revidiert). - Die Platte `datenhdd3` erscheint im Monitoring über den NFS-Mount auf `ghostnet-ki` (Disk-Metrik des NFS-Exports, GNMON-001). @@ -147,9 +152,12 @@ GNENC-002 Abschnitt 4): `input/`, `jd/`, `queue/`, `work/`, `output/`, `proxmox/lxc/ct-101-samba/` ist gebaut (Design in [GNAI-019](GNAI-019-DEC_Ansible-Migration.md), Requirements in [GNAI-018](GNAI-018-REQ_Encoding-Pipeline-Integration.md)) und lokal - verifiziert (Syntax-Checks, Check-Mode-Smoke). **Ausstehend:** Adoption des - produktiven LXC gemäß Runbook in `ansible/README.md` (Dist-Upgrade auf - Debian 13, elf Inventory-Platzhalter, Schlüssel, Convergence, Verifikation). + verifiziert (Syntax-Checks, Check-Mode-Smoke). **In Arbeit (Stand + 27.10.2026):** Adoption gemäß Runbook in `ansible/README.md` — Schlüssel + installiert, SSH-Zugang für `ghost-agent` (UID 1004) und `ghost` (UID 1000) + hergestellt; die elf Inventory-Platzhalter wurden am 27.10.2026 aus + preflight/live gefüllt (alle drei Inventare byte-identisch); ausstehend: + audit, bootstrap, Konvergenz, Verifikation. 2. **`[printers]` doppelt** in der `smb.conf` — erledigt: Das Duplikat (inkl. Kommentarblöcke) ist im templated `smb.conf` bereinigt; `testparm` ist als `validate:`-Gate im Rollen-Flow verankert (GNAI-019 Entscheidung 7). @@ -158,7 +166,7 @@ GNENC-002 Abschnitt 4): `input/`, `jd/`, `queue/`, `work/`, `output/`, GNAI-019 Entscheidung 7). 4. **Backup** — entschieden: `backup.yml` (one-shot) sichert `/etc/samba` und `/var/lib/samba` nach `/var/backups/ghostnet/samba/` vor der ersten - Convergence; alle fünf Medien-Mounts tragen `backup=no`; das Upgrade-Skript + Convergence; alle sechs Medien-Mounts tragen `backup=no`; das Upgrade-Skript fährt vor dem Dist-Upgrade einen `vzdump --mode stop` (GNAI-019 Entscheidung 6). **Offen:** hostunabhängiger Restore-Test (GNAI-000 Abschnitt 4 Punkt 4). 5. **System-Baseline** — erfasst: OS-Version (Debian 13 nach Upgrade, GNAI-019 @@ -184,3 +192,62 @@ GNENC-002 Abschnitt 4): `input/`, `jd/`, `queue/`, `work/`, `output/`, den Verlust verhindert. Bei der kommenden Ansible-Adoption wird die `smb.conf` planmäßig durch das Template ersetzt (zusätzlich `[Encode]`/`[Encode-In]`, `[printers]` bereinigt). +8. **Adoption (27.10.2026): Befunde und Korrekturen vor der Konvergenz** — + erledigt: (a) Hostname erfasst: `Samba` (großgeschrieben, FQDN + `Samba.fritz.box`); die Defaults in `samba_lxc_baseline` und + `provision-ct101-samba.sh` standen fälschlich auf klein `samba` und wurden + korrigiert (Rolle-Identity-Assert, Fresh-State-Rebuild). (b) + `sys-kernel-config.mount` war auf CT 101 `failed` (configfs ist in + unprivileged LXC nicht verfügbar); CT 100/102/103 maskieren die Unit seit + ihrer Erstellung. Die Maske ist jetzt in `samba_lxc_baseline` und + `provision-ct101-samba.sh` deklariert und wurde auf CT 101 manuell gesetzt + (`preflight.yml` verlangt „no failed units"). (c) Das manuelle + Dist-Upgrade vom 21.10. hatte den leeren `lxc.seccomp.profile:` nicht + gesetzt (Seccomp-Filter aktiv, `Seccomp: 2`); am 27.10.2026 nachgetragen + und per CT-Neustart wirksam (GNAI-019 Entscheidung 2). (d) Das Konto + `ghost` hatte das Home-Feld `/home/ghost/` (Trailing Slash) und die Shell + `/bin/false`; vor der Baseline auf `/home/ghost` und `/bin/bash` + normalisiert (27.10.2026). (e) Konto `dark2devil` (UID 1002, Lars Peuser, + Home `/home/dark2devil`, seit 11.03.2024) — **Korrektur 28.10.2026:** Der + Betreiber klärte, dass `dark2devil` und `debugofen` (UID 1003) dieselbe + Person sind und `debugofen` der valide Account ist; `dark2devil` ist ein + Alt-Account. Die am 27.10.2026 beschlossene Gleichstellung mit `ghost` + (GNAI-019 Entscheidung 11) wurde zurückgenommen: `dark2devil` ist nicht in + `samba_media_users` deklariert, nicht Mitglied von `gn-media` und in keiner + Share-ACL eingetragen (die live `smb.conf` nennt ihn nirgends). Konto und + passdb-Eintrag bleiben auf der Adoption unangetastet (keine produktive + Account-Löschung ohne ausdrückliche Betreiberentscheidung); optionaler + Cleanup, Betreiberentscheidung: `smbpasswd -x dark2devil` auf CT 101. + (f) Der private + `ghost`-SSH-Key erhielt am 27.10.2026 eine Passphrase (GNAI-011 Abschnitt + 3.3); Entsperrung über den lokalen ssh-agent. (g) Mount-Topologie und + Ressourcen-Profil erfasst (27.10.2026, nach dem Reboot): Sechs Medien-Mounts + sind produktiv eingehängt — `/media/daten1`, `/media/daten2`, + `/media/daten3` (btrfs), `/media/datenhdd1` (ext4, leer, Verzeichnis auf der + Host-Root-Platte), `/media/datenhdd2/extern` (btrfs; die Extern-Platte ist + am Unterverzeichnis eingehängt, nicht an `/media/datenhdd2` selbst) und + `/media/datenhdd3` (btrfs). Die geplante Mount-Liste wich davon ab (fünf + Einträge mit `/media/datenhdd2`) und wurde korrigiert: Inventory + `samba_media_mount_points`, `samba_media`-Defaults, + `provision-ct101-samba.sh` (mp0–mp5) und `upgrade-ct101-to-debian13.sh` + (GNAI-019 entsprechend). Profil des produktiven CT: 1 CPU, 2048 MiB RAM, + 2048 MiB Swap, 4 GiB Rootfs — die Defaults in `provision-ct101-samba.sh` + wurden darauf korrigiert (zuvor 2 Cores / 32 GiB / 1024 MiB Swap). + `/media/datenhdd3/Encoding` existiert produktiv noch nicht; die Konvergenz + legt es als `root:gn-media 2775` an (GNAI-019 Entscheidung 2). (h) Die + Gruppe `gn-media` existiert produktiv noch nicht (GID 970 frei; `ghost` + nicht Mitglied) — `samba_media` legt sie bei der Konvergenz an und nimmt + `ghost` auf; `preflight.yml` toleriert das Fehlen (Report statt Abbruch). + (i) **Share-ACLs gegen live abgeglichen (28.10.2026):** Das Inventory + deklarierte die `admin users`-Zeilen der live `smb.conf` nicht (betroffen: + Dokumente, Filme, Filme2, Serien2, Animation, Anime, Extern, Musik, + Audiobooks, Software, Downloads), das `write list` von `[Dokumente]` und + `[Extern]` fehlte, und `[Serien2]` trug ein `force user/group = root`, das + live nicht existiert. Nach GNAI-018 R4 (funktionsgleicher Erhalt) deklariert + das Inventory die ACLs nun exakt wie die live `smb.conf` (Erfassung + 28.10.2026); die Konvergenz ändert damit keine produktiven Share-Rechte + außer der planmäßigen Ergänzung von `[Encode]`/`[Encode-In]`. Die + Normalisierung `public = yes` → `guest ok = no` auf den Medien-Shares ist + funktionsneutral (mit `valid users` ist Gastzugriff ohnehin verweigert). + Ebenfalls angeglichen: `[printers]` trägt im Template nun `browseable = no` + wie live (Sichtbarkeit im anonymen Listing). 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 6ff1c66..5a07400 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 @@ -20,9 +20,10 @@ und GNAI-018 R1. Es ist das Referenzdokument für das ausführende Projekt ## 2. Ausgangslage und Randbedingungen - Produktiver, unprivileged LXC (Proxmox VE), Debian 12 (bookworm), manuell - verwaltet; Samba-Dateiserver mit 14 Shares auf fünf Host-Mounts - (`/media/daten1`, `/media/daten2`, `/media/daten3`, `/media/datenhdd2`, - `/media/datenhdd3`). + verwaltet; Samba-Dateiserver mit 12 produktiven Shares auf sechs + Host-Mounts (`/media/daten1`, `/media/daten2`, `/media/daten3`, + `/media/datenhdd1`, `/media/datenhdd2/extern`, `/media/datenhdd3`) plus zwei + geplanten Pipeline-Shares (`[Encode]`, `[Encode-In]`). - Produktiver Samba-State: `smb.conf` plus `/var/lib/samba` inkl. `private/passdb.tdb`; SMB-Benutzer `ghost`, `daidalaw`, `debugofen` mit produktiven Passwörtern. @@ -47,7 +48,7 @@ ist als Jinja2-Template im Repository gepflegt (Entscheidung 8). **Begründung:** -- Der produktive Bestand umfasst gewachsene POSIX-Rechte auf fünf Host-Mounts +- Der produktive Bestand umfasst gewachsene POSIX-Rechte auf sechs Host-Mounts und einen Samba-State (tdb/passdb). Ein Container-Wechsel würde die Datei- und ID-Mapping-Semantik verändern, ohne einen Sicherheitsgewinn zu bringen: Der LXC bleibt unprivileged, und Samba ist der Dienst, der die @@ -145,6 +146,9 @@ Reboot: `ghostnet-samba-nfs.service` statt `nfs-kernel-server`). `/usr/sbin/nologin` und ohne Home-Anlage). - Mitgliedschaft in `gn-media`: nur `ghost` (GNAI-018 R3). `daidalaw` und `debugofen` benötigen keine Mitgliedschaft — reiner Lesezugriff genügt. + **Update 28.10.2026:** Die Gleichstellung von `dark2devil` wurde + zurückgenommen (Entscheidung 11 revidiert) — `dark2devil` und `debugofen` + sind dieselbe Person; nur `ghost` ist Mitglied von `gn-media`. **Begründung:** Die UID-Stabilität ist Voraussetzung der Pipeline (AUTH_SYS-Credentials, `mv`-Abholung aus `[Encode]`, `.dlc`-Drop in @@ -183,7 +187,7 @@ Ziel generiert bzw. operator-seitig gesetzt). assertet das Vorhandensein dieses pre-migration Backups (`samba_verify_require_premigration_backup: true`; Fresh-State-Rebuild: `-e samba_verify_require_premigration_backup=false`). -- **Medien-Mounts:** Alle fünf Mounts tragen `backup=no` — `vzdump` archiviert +- **Medien-Mounts:** Alle sechs Mounts tragen `backup=no` — `vzdump` archiviert die Medienbibliothek nicht. - **Upgrade:** `scripts/upgrade-ct101-to-debian13.sh` fährt vor dem Dist-Upgrade einen `vzdump --mode stop --storage local --compress zstd` als @@ -236,7 +240,7 @@ genau so wieder auf. Der dokumentierte Ablauf (Runbook in `ansible/README.md`): 1. `scripts/provision-ct101-samba.sh` auf dem Proxmox-Host: frischer CT 101 aus - einer Debian-13-Vorlage mit dem aufgezeichneten Profil, fünf Medien-Mounts + einer Debian-13-Vorlage mit dem aufgezeichneten Profil, sechs Medien-Mounts (`backup=no`), leerer Seccomp-Eintrag, Baseline-Hook mit den aufgezeichneten Konto-UIDs. 2. Operator auf der PVE-Konsole: `passwd ghost`, öffentliche Schlüssel aus @@ -245,8 +249,8 @@ genau so wieder auf. Der dokumentierte Ablauf (Runbook in 3. Erster `site.yml`-Lauf mit `-e samba_smb_preserve_passdb=false` (`samba_media` legt fehlende Konten mit den aufgezeichneten UIDs an, `samba_smb` installiert Samba und die templated `smb.conf`). -4. Operator: `smbpasswd -a` für `ghost`, `daidalaw`, `debugofen` (Secrets nie - im Repo). +4. Operator: `smbpasswd -a` für `ghost`, `daidalaw` und + `debugofen` (Secrets nie im Repo). 5. Zweiter `site.yml`-Lauf mit Default: volle passdb-Guardrails, erwartet `changed=0` (Idempotenz). 6. `verify-production.yml` mit @@ -268,15 +272,35 @@ Debian 13); frische Paketbasis für die Migration; die Baseline-Rolle erwartet Debian 13. **Ablauf (Skript):** Guards (CT läuft, unprivileged, Gast Debian 12, Dienste -aktiv, IPv4, uid_map, `backup=no` auf allen fünf Mounts) → `vzdump --mode stop` +aktiv, IPv4, uid_map, `backup=no` auf allen sechs Mounts) → `vzdump --mode stop` → laufender Zustand gesichert (leerer Seccomp-Eintrag) → In-Guest-Upgrade (`bookworm` → `trixie` in deb822- und One-Line-Quellen, `dist-upgrade` mit `--force-confold` — hält die produktive `smb.conf`, `autoremove`, wtmpdb-Cleanup analog Baseline) → Reboot → Post-Verify (Debian 13, Dienste, keine failed units, Listener 22/139/445, anonyme `smbclient -L`-Abfrage, -uid_map intakt, Seccomp deaktiviert, fünf Mounts eingehängt). +uid_map intakt, Seccomp deaktiviert, sechs Mounts eingehängt). -## 13. Verworfene Alternativen +## 13. Entscheidung 11: `dark2devil` — Alt-Account, keine Gleichstellung (revidiert 28.10.2026) + +**Entscheidung (revidiert):** Die am 27.10.2026 beschlossene Gleichstellung +von `dark2devil` mit `ghost` wird zurückgenommen. Der Betreiber klärte am +28.10.2026, dass `dark2devil` (UID 1002) und `debugofen` (UID 1003) dieselbe +Person sind (Lars Peuser) und `debugofen` der valide Account ist; +`dark2devil` ist ein Alt-Account ohne produktive Share-Berechtigung (die live +`smb.conf` nennt ihn nirgends). Konkret: `dark2devil` ist nicht in +`samba_media_users` deklariert, nicht Mitglied von `gn-media` und in keiner +Share-ACL eingetragen; die passdb-Asserts verlangen nur `ghost`, `daidalaw` +und `debugofen`. Konto und passdb-Eintrag bleiben auf der Adoption +unangetastet (keine produktive Account-Löschung ohne ausdrückliche +Betreiberentscheidung). Optionaler Cleanup (Betreiberentscheidung, nicht Teil +der Migration): `smbpasswd -x dark2devil` auf CT 101. + +**Begründung:** Die Gleichstellung beruhte auf der Annahme, `dark2devil` sei +ein aktiver privater Benutzer. Nach der Betreiberklärung ist die Person über +den validen Account `debugofen` angebunden; eine zweite Identität für +dieselbe Person auf Medien-/SMB-Ebene ist nicht erforderlich. + +## 14. Verworfene Alternativen | Alternative | Grund der Ablehnung | | --- | --- | @@ -287,7 +311,7 @@ uid_map intakt, Seccomp deaktiviert, fünf Mounts eingehängt). | SMB auch aus dem Admin-VPN (`10.8.0.0/24`) öffnen | Medien-Clients leben im LAN; Erweiterung ist ausdrückliche Betreiberentscheidung (GNAI-018 R7) | | Medien-Mounts mit `backup=yes` | `vzdump` würde die Medienbibliothek archivieren (Ressourcen, Datenschutz); `backup=no` plus gezieltes State-Backup (Entscheidung 6) | -## 14. Auswirkungen auf GNAI-017 und GNAI-018 +## 15. Auswirkungen auf GNAI-017 und GNAI-018 - **GNAI-017 Abschnitt 4.1** (`nfs-kernel-server`, `/etc/exports`) ist durch Entscheidung 2 und 3 überholt und wird entsprechend aktualisiert. @@ -304,16 +328,36 @@ uid_map intakt, Seccomp deaktiviert, fünf Mounts eingehängt). (Entscheidung 5), Backup-Ziel `/var/backups/ghostnet/samba/` plus vzdump im Upgrade-Skript (Entscheidung 6), `usershare allow guests` beibehalten (Entscheidung 7), System-Baseline via preflight-Platzhalter (Entscheidung 9). +- **GNAI-017 Abschnitt 1/2** (Mount-Topologie): korrigiert auf die sechs + produktiv eingehängten Mounts (inkl. `/media/datenhdd1` und + `/media/datenhdd2/extern`); `provision-ct101-samba.sh` attachiert `mp0`–`mp5` + mit dem aufgezeichneten Profil (1 CPU, 2048 MiB RAM, 2048 MiB Swap, 4 GiB + Rootfs). +- **Korrektur 28.10.2026 (GNAI-018 R4):** GNAI-017 Abschnitt 2 + (SMB-Benutzer-Tabelle) und Abschnitt 4.2 (`gn-media`-Mitgliedschaft) werden + korrigiert — `dark2devil` ist nicht deklariert (Entscheidung 11 + revidiert). Das Inventory deklariert die Share-ACLs exakt wie die live + `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. -## 15. Offene Punkte +## 16. Offene Punkte 1. Adoption des produktiven CT 101 gemäß Runbook in `proxmox/lxc/ct-101-samba/ansible/README.md` (erfordert Betreiber-Autorisierung). 2. Elf Inventory-Platzhalter mit den Ist-Werten füllen (`preflight.yml` / - `audit-production.yml` liefern sie). + `audit-production.yml` liefern sie) — **erledigt 27.10.2026**: preflight und + Live-Abfragen lieferten die Werte; alle drei Inventare sind byte-identisch + gefüllt (Hostname `Samba`, 1 vCPU, 2048 MiB RAM, Rootfs-Mindestwerte + 4 GB/1 GB, Medien-UIDs 1000/1001/1003 — der Alt-Account `dark2devil`, + UID 1002, ist nicht deklariert, siehe Entscheidung 11 —, admin 1000, + agent 1004, deploy 1005). 3. Drei Ed25519-Schlüsselpaare generieren (Operator); nur die öffentlichen - Schlüssel liegen im Repo unter `ansible/files/ssh/`. -4. Dist-Upgrade auf Debian 13 ausführen (`scripts/upgrade-ct101-to-debian13.sh`). + Schlüssel liegen im Repo unter `ansible/files/ssh/` — **erledigt**: agent und + deploy ohne Passphrase, ghost mit Passphrase (GNAI-011 Abschnitt 3.3). +4. Dist-Upgrade auf Debian 13 ausführen (`scripts/upgrade-ct101-to-debian13.sh`) — + **erledigt 21.10.2026**, manuell durch den Betreiber (GNAI-017 Abschnitt 6 + Punkt 7). 5. Reboot-Test nach der Convergence (GNAI-016 Abschnitt 7, LXC-boot_id-Caveat). 6. Hostunabhängiger Backup-/Restore-Test (GNAI-000 Abschnitt 4 Punkt 4). 7. Kapazitäts-Monitoring der daten-Platten auf CT 101 (GNAI-017 Abschnitt 6 diff --git a/proxmox/lxc/ct-101-samba/ansible/README.md b/proxmox/lxc/ct-101-samba/ansible/README.md index 07ad61d..5791281 100644 --- a/proxmox/lxc/ct-101-samba/ansible/README.md +++ b/proxmox/lxc/ct-101-samba/ansible/README.md @@ -59,8 +59,8 @@ role argument specs reject them before any task runs). `preflight.yml` on the `samba_minimum_root_available_bytes` (group_vars) - `samba_access_admin_uid`, `samba_access_agent_uid`, `samba_access_deploy_uid` (host_vars) -- `samba_media_users[ghost|daidalaw|debugofen].uid` (group_vars; the recorded - productive UIDs — the pipeline depends on them, GNAI-018 R1) +- `samba_media_users[ghost|daidalaw|debugofen].uid` (group_vars; the + recorded productive UIDs — the pipeline depends on them, GNAI-018 R1) ## Playbooks @@ -87,13 +87,21 @@ keeps the healthy file server and its Samba passdb intact: 2. On the Proxmox host, run `scripts/upgrade-ct101-to-debian13.sh` (see `scripts/README.md`): in-place Debian 12 → 13 dist-upgrade of the existing CT 101 with a pre-upgrade `vzdump` and the empty `lxc.seccomp.profile`. + When the dist-upgrade was performed manually instead of via the script, + ensure the empty `lxc.seccomp.profile:` line is present in + `/etc/pve/lxc/101.conf` and reboot the CT — the Ganesha container needs it + (GNAI-019 decision 2). 3. On the PVE console of CT 101: capture the productive `ghost` UID, pick a free UID for `ghost-agent`, then run the shared baseline `ghostnet-lxc-baseline.sh 101` with `GHOSTNET_AGENT_UID`/`GHOSTNET_ADMIN_UID` (validates the unprivileged mapping, creates the locked `ghost-agent`, - prepares `.ssh`, adds `ghost` to `sudo`, disables `ssh.socket`). Then set - `passwd ghost` and paste the `ghost-agent` and `ghost` public keys into the - respective `authorized_keys`. + prepares `.ssh`, adds `ghost` to `sudo`, disables `ssh.socket`). Also mask + `sys-kernel-config.mount` (configfs is unavailable in unprivileged LXC; CT + 100/102/103 convention) and clear its failed state — `preflight.yml` + requires no failed units: + `pct exec 101 -- systemctl mask sys-kernel-config.mount && pct exec 101 -- systemctl reset-failed sys-kernel-config.mount`. + Then set `passwd ghost` and paste the `ghost-agent` and `ghost` public + keys into the respective `authorized_keys`. 4. On the workstation: add the `ghostnet-ct101-agent` / `ghostnet-ct101-ghost` ssh aliases and seed `~/.ssh/known_hosts_ghostnet_ct101` (verify the fingerprint on the PVE console first, GNAI-011 section 3.3). @@ -122,11 +130,11 @@ and installs; the only state that cannot be declared is secret material. 1. Prerequisites: the inventory is fully filled (all 11 placeholders carry the recorded productive values), the three `.pub` files are committed, and the - five media host paths are known. + six media host paths are known. 2. On the Proxmox host, run `scripts/provision-ct101-samba.sh` with `GHOSTNET_AGENT_UID`, `GHOSTNET_ADMIN_UID` and `GHOSTNET_MP0_HOST` … - `GHOSTNET_MP4_HOST` (see `scripts/README.md`). This creates CT 101 from a - Debian 13 template with the recorded profile, attaches the five media + `GHOSTNET_MP5_HOST` (see `scripts/README.md`). This creates CT 101 from a + Debian 13 template with the recorded profile, attaches the six media mounts (`backup=no`), disables the LXC seccomp filter and runs the baseline hook, which creates `ghost-agent` and `ghost` with the recorded UIDs. 3. Operator: `pct exec 101 -- passwd ghost`, then install the `ghost-agent` and diff --git a/proxmox/lxc/ct-101-samba/ansible/inventories/agent/group_vars/samba_hosts.yml b/proxmox/lxc/ct-101-samba/ansible/inventories/agent/group_vars/samba_hosts.yml index 0a377d2..38128d6 100644 --- a/proxmox/lxc/ct-101-samba/ansible/inventories/agent/group_vars/samba_hosts.yml +++ b/proxmox/lxc/ct-101-samba/ansible/inventories/agent/group_vars/samba_hosts.yml @@ -8,15 +8,16 @@ ansible_ssh_common_args: >- samba_expected_distribution: Debian samba_expected_distribution_major_version: "13" -# Live hostname of CT 101; copy from preflight.yml (SMB1 is disabled, so the -# live server does not answer NetBIOS names): -samba_expected_hostname: REPLACE-WITH-PREFLIGHT-HOSTNAME +# Live hostname of CT 101 (recorded 2026-10-27 from preflight.yml; SMB1 is +# disabled, so the live server does not answer NetBIOS names): +samba_expected_hostname: Samba samba_expected_virtualization_type: lxc # Expected Podman version after the dist-upgrade (trixie), analog to CT 100/102: samba_expected_podman_version: "5.4.2" -# Resource minimums; copy from preflight.yml: -samba_minimum_root_size_bytes: REPLACE-WITH-PREFLIGHT-ROOT-SIZE -samba_minimum_root_available_bytes: REPLACE-WITH-PREFLIGHT-ROOT-AVAIL +# Resource minimums (recorded 2026-10-27; round values at/below the live +# capacity, CT 100/102 convention): +samba_minimum_root_size_bytes: 4000000000 +samba_minimum_root_available_bytes: 1000000000 samba_firewall_management_ipv4: - 10.0.0.0/24 @@ -35,48 +36,64 @@ samba_access_source_networks: 10.0.0.0/24,10.8.0.0/24 samba_media_group_name: gn-media samba_media_group_gid: 970 -# Media accounts with their productive UIDs; copy from preflight.yml / -# audit-production.yml (fresh-state rebuild: missing accounts are created with -# exactly these UIDs; existing accounts are only validated on the UID): +# Media accounts with their productive UIDs (recorded 2026-10-27 from +# preflight.yml / audit-production.yml; fresh-state rebuild: missing accounts +# are created with exactly these UIDs; existing accounts are only validated on +# the UID). The legacy account dark2devil (UID 1002) exists on the productive +# CT 101 but is deliberately NOT declared: it belongs to the same person as +# debugofen (operator clarification 2026-10-28, GNAI-019 decision 11) and is +# not referenced by any share in the live smb.conf. samba_media_users: - name: ghost - uid: REPLACE-WITH-AUDIT-UID-GHOST + uid: 1000 shell: /bin/bash home: /home/ghost create_home: true - name: daidalaw - uid: REPLACE-WITH-AUDIT-UID-DAIDALAW + uid: 1001 shell: /usr/sbin/nologin home: /home/daidalaw create_home: false - name: debugofen - uid: REPLACE-WITH-AUDIT-UID-DEBUGOFEN + uid: 1003 shell: /usr/sbin/nologin home: /home/debugofen create_home: false samba_media_group_members: - ghost +# Live media mount topology of the productive CT 101 (recorded 2026-10-27): +# six Proxmox mount points. /media/datenhdd1 is an empty ext4 directory from +# the host root filesystem; the Extern disk is mounted at the subdirectory +# /media/datenhdd2/extern (not at /media/datenhdd2 itself). samba_media_mount_points: - /media/daten1 - /media/daten2 - /media/daten3 - - /media/datenhdd2 + - /media/datenhdd1 + - /media/datenhdd2/extern - /media/datenhdd3 samba_smb_workgroup: Ghost.net samba_smb_service_units: - smbd.service - nmbd.service -# Complete share target state from GNAI-017 section 3 plus [Encode] and -# [Encode-In] from GNAI-018 R2. Comments only where the live system shows them -# (Debian defaults and the new pipeline shares); the media shares had empty -# comments in the anonymous listing and stay without comments. +# Complete share target state: the twelve productive shares mirror the live +# smb.conf ACLs exactly (valid users / write list / admin users / force +# user+group; recorded 2026-10-28, GNAI-018 R4 funktionsgleicher Erhalt), plus +# [Encode] and [Encode-In] per GNAI-017 section 4.3 / GNAI-018 R2. Comments +# only where the live system shows them (Debian defaults and the new pipeline +# shares); the media shares had empty comments in the anonymous listing and +# stay without comments. samba_smb_media_shares: - name: Dokumente path: /media/daten1/Dokumente read_only: "yes" valid_users: - ghost + write_list: + - ghost + admin_users: + - ghost - name: Filme path: /media/daten1/Filme read_only: "yes" @@ -86,6 +103,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Filme2 path: /media/datenhdd3/Filme read_only: "yes" @@ -95,6 +114,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Serien path: /media/daten2/Serien read_only: "yes" @@ -115,8 +136,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost - force_user: root - force_group: root + admin_users: + - ghost - name: Animation path: /media/daten1/Animation read_only: "yes" @@ -126,6 +147,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Anime path: /media/daten3/Anime read_only: "yes" @@ -135,11 +158,17 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Extern path: /media/datenhdd2/extern read_only: "no" valid_users: - ghost + write_list: + - ghost + admin_users: + - ghost - name: Musik path: /media/daten1/Musik read_only: "yes" @@ -149,6 +178,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Audiobooks path: /media/daten1/Audiobooks read_only: "yes" @@ -158,6 +189,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Software path: /media/datenhdd3/Software read_only: "yes" @@ -168,6 +201,9 @@ samba_smb_media_shares: write_list: - ghost - daidalaw + admin_users: + - ghost + - daidalaw - name: Downloads path: /media/datenhdd3/Downloads read_only: "no" @@ -175,6 +211,10 @@ samba_smb_media_shares: - ghost - daidalaw - debugofen + admin_users: + - ghost + - daidalaw + - debugofen - name: Encode comment: "Encoding-Pipeline: fertige Encodes (Abholung per mv)" path: /media/datenhdd3/Encoding/output @@ -183,10 +223,10 @@ samba_smb_media_shares: - ghost - daidalaw - debugofen - admin_users: - - ghost write_list: - ghost + admin_users: + - ghost force_group: gn-media create_mask: "0664" directory_mask: "0775" @@ -196,10 +236,10 @@ samba_smb_media_shares: read_only: "no" valid_users: - ghost - admin_users: - - ghost write_list: - ghost + admin_users: + - ghost force_group: gn-media create_mask: "0664" directory_mask: "0775" diff --git a/proxmox/lxc/ct-101-samba/ansible/inventories/agent/host_vars/samba-ct101.yml b/proxmox/lxc/ct-101-samba/ansible/inventories/agent/host_vars/samba-ct101.yml index ff98250..a1693ff 100644 --- a/proxmox/lxc/ct-101-samba/ansible/inventories/agent/host_vars/samba-ct101.yml +++ b/proxmox/lxc/ct-101-samba/ansible/inventories/agent/host_vars/samba-ct101.yml @@ -6,11 +6,13 @@ samba_expected_ipv4: 10.0.0.9 # (GNAI-011 section 3.3): samba_expected_ssh_host_key_fingerprint: >- SHA256:+MUO4J4x3TCYLc1iei95UcPTzkPwDPOzovfaIieuOKs -# Live values; copy from preflight.yml: -samba_expected_processor_vcpus: REPLACE-WITH-PREFLIGHT-VCPUS -samba_minimum_memory_mb: REPLACE-WITH-PREFLIGHT-MEMORY-MB -# UID of the existing ghost account; copy from audit-production.yml: -samba_access_admin_uid: REPLACE-WITH-AUDIT-GHOST-UID -# Free UIDs to pick from audit-production.yml (ghost-agent / ansible-deploy): -samba_access_agent_uid: REPLACE-WITH-FREE-AGENT-UID -samba_access_deploy_uid: REPLACE-WITH-FREE-DEPLOY-UID +# Live values (recorded 2026-10-27 from preflight.yml; lxcfs presents the +# configured profile inside the guest): +samba_expected_processor_vcpus: 1 +samba_minimum_memory_mb: 2048 +# UID of the existing ghost account (audit-production.yml): +samba_access_admin_uid: 1000 +# Recorded UIDs (audit-production.yml): ghost-agent exists with 1004; +# ansible-deploy takes the lowest free UID >= 1000: +samba_access_agent_uid: 1004 +samba_access_deploy_uid: 1005 diff --git a/proxmox/lxc/ct-101-samba/ansible/inventories/bootstrap/group_vars/samba_hosts.yml b/proxmox/lxc/ct-101-samba/ansible/inventories/bootstrap/group_vars/samba_hosts.yml index 0a377d2..38128d6 100644 --- a/proxmox/lxc/ct-101-samba/ansible/inventories/bootstrap/group_vars/samba_hosts.yml +++ b/proxmox/lxc/ct-101-samba/ansible/inventories/bootstrap/group_vars/samba_hosts.yml @@ -8,15 +8,16 @@ ansible_ssh_common_args: >- samba_expected_distribution: Debian samba_expected_distribution_major_version: "13" -# Live hostname of CT 101; copy from preflight.yml (SMB1 is disabled, so the -# live server does not answer NetBIOS names): -samba_expected_hostname: REPLACE-WITH-PREFLIGHT-HOSTNAME +# Live hostname of CT 101 (recorded 2026-10-27 from preflight.yml; SMB1 is +# disabled, so the live server does not answer NetBIOS names): +samba_expected_hostname: Samba samba_expected_virtualization_type: lxc # Expected Podman version after the dist-upgrade (trixie), analog to CT 100/102: samba_expected_podman_version: "5.4.2" -# Resource minimums; copy from preflight.yml: -samba_minimum_root_size_bytes: REPLACE-WITH-PREFLIGHT-ROOT-SIZE -samba_minimum_root_available_bytes: REPLACE-WITH-PREFLIGHT-ROOT-AVAIL +# Resource minimums (recorded 2026-10-27; round values at/below the live +# capacity, CT 100/102 convention): +samba_minimum_root_size_bytes: 4000000000 +samba_minimum_root_available_bytes: 1000000000 samba_firewall_management_ipv4: - 10.0.0.0/24 @@ -35,48 +36,64 @@ samba_access_source_networks: 10.0.0.0/24,10.8.0.0/24 samba_media_group_name: gn-media samba_media_group_gid: 970 -# Media accounts with their productive UIDs; copy from preflight.yml / -# audit-production.yml (fresh-state rebuild: missing accounts are created with -# exactly these UIDs; existing accounts are only validated on the UID): +# Media accounts with their productive UIDs (recorded 2026-10-27 from +# preflight.yml / audit-production.yml; fresh-state rebuild: missing accounts +# are created with exactly these UIDs; existing accounts are only validated on +# the UID). The legacy account dark2devil (UID 1002) exists on the productive +# CT 101 but is deliberately NOT declared: it belongs to the same person as +# debugofen (operator clarification 2026-10-28, GNAI-019 decision 11) and is +# not referenced by any share in the live smb.conf. samba_media_users: - name: ghost - uid: REPLACE-WITH-AUDIT-UID-GHOST + uid: 1000 shell: /bin/bash home: /home/ghost create_home: true - name: daidalaw - uid: REPLACE-WITH-AUDIT-UID-DAIDALAW + uid: 1001 shell: /usr/sbin/nologin home: /home/daidalaw create_home: false - name: debugofen - uid: REPLACE-WITH-AUDIT-UID-DEBUGOFEN + uid: 1003 shell: /usr/sbin/nologin home: /home/debugofen create_home: false samba_media_group_members: - ghost +# Live media mount topology of the productive CT 101 (recorded 2026-10-27): +# six Proxmox mount points. /media/datenhdd1 is an empty ext4 directory from +# the host root filesystem; the Extern disk is mounted at the subdirectory +# /media/datenhdd2/extern (not at /media/datenhdd2 itself). samba_media_mount_points: - /media/daten1 - /media/daten2 - /media/daten3 - - /media/datenhdd2 + - /media/datenhdd1 + - /media/datenhdd2/extern - /media/datenhdd3 samba_smb_workgroup: Ghost.net samba_smb_service_units: - smbd.service - nmbd.service -# Complete share target state from GNAI-017 section 3 plus [Encode] and -# [Encode-In] from GNAI-018 R2. Comments only where the live system shows them -# (Debian defaults and the new pipeline shares); the media shares had empty -# comments in the anonymous listing and stay without comments. +# Complete share target state: the twelve productive shares mirror the live +# smb.conf ACLs exactly (valid users / write list / admin users / force +# user+group; recorded 2026-10-28, GNAI-018 R4 funktionsgleicher Erhalt), plus +# [Encode] and [Encode-In] per GNAI-017 section 4.3 / GNAI-018 R2. Comments +# only where the live system shows them (Debian defaults and the new pipeline +# shares); the media shares had empty comments in the anonymous listing and +# stay without comments. samba_smb_media_shares: - name: Dokumente path: /media/daten1/Dokumente read_only: "yes" valid_users: - ghost + write_list: + - ghost + admin_users: + - ghost - name: Filme path: /media/daten1/Filme read_only: "yes" @@ -86,6 +103,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Filme2 path: /media/datenhdd3/Filme read_only: "yes" @@ -95,6 +114,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Serien path: /media/daten2/Serien read_only: "yes" @@ -115,8 +136,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost - force_user: root - force_group: root + admin_users: + - ghost - name: Animation path: /media/daten1/Animation read_only: "yes" @@ -126,6 +147,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Anime path: /media/daten3/Anime read_only: "yes" @@ -135,11 +158,17 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Extern path: /media/datenhdd2/extern read_only: "no" valid_users: - ghost + write_list: + - ghost + admin_users: + - ghost - name: Musik path: /media/daten1/Musik read_only: "yes" @@ -149,6 +178,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Audiobooks path: /media/daten1/Audiobooks read_only: "yes" @@ -158,6 +189,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Software path: /media/datenhdd3/Software read_only: "yes" @@ -168,6 +201,9 @@ samba_smb_media_shares: write_list: - ghost - daidalaw + admin_users: + - ghost + - daidalaw - name: Downloads path: /media/datenhdd3/Downloads read_only: "no" @@ -175,6 +211,10 @@ samba_smb_media_shares: - ghost - daidalaw - debugofen + admin_users: + - ghost + - daidalaw + - debugofen - name: Encode comment: "Encoding-Pipeline: fertige Encodes (Abholung per mv)" path: /media/datenhdd3/Encoding/output @@ -183,10 +223,10 @@ samba_smb_media_shares: - ghost - daidalaw - debugofen - admin_users: - - ghost write_list: - ghost + admin_users: + - ghost force_group: gn-media create_mask: "0664" directory_mask: "0775" @@ -196,10 +236,10 @@ samba_smb_media_shares: read_only: "no" valid_users: - ghost - admin_users: - - ghost write_list: - ghost + admin_users: + - ghost force_group: gn-media create_mask: "0664" directory_mask: "0775" diff --git a/proxmox/lxc/ct-101-samba/ansible/inventories/bootstrap/host_vars/samba-ct101.yml b/proxmox/lxc/ct-101-samba/ansible/inventories/bootstrap/host_vars/samba-ct101.yml index ff98250..a1693ff 100644 --- a/proxmox/lxc/ct-101-samba/ansible/inventories/bootstrap/host_vars/samba-ct101.yml +++ b/proxmox/lxc/ct-101-samba/ansible/inventories/bootstrap/host_vars/samba-ct101.yml @@ -6,11 +6,13 @@ samba_expected_ipv4: 10.0.0.9 # (GNAI-011 section 3.3): samba_expected_ssh_host_key_fingerprint: >- SHA256:+MUO4J4x3TCYLc1iei95UcPTzkPwDPOzovfaIieuOKs -# Live values; copy from preflight.yml: -samba_expected_processor_vcpus: REPLACE-WITH-PREFLIGHT-VCPUS -samba_minimum_memory_mb: REPLACE-WITH-PREFLIGHT-MEMORY-MB -# UID of the existing ghost account; copy from audit-production.yml: -samba_access_admin_uid: REPLACE-WITH-AUDIT-GHOST-UID -# Free UIDs to pick from audit-production.yml (ghost-agent / ansible-deploy): -samba_access_agent_uid: REPLACE-WITH-FREE-AGENT-UID -samba_access_deploy_uid: REPLACE-WITH-FREE-DEPLOY-UID +# Live values (recorded 2026-10-27 from preflight.yml; lxcfs presents the +# configured profile inside the guest): +samba_expected_processor_vcpus: 1 +samba_minimum_memory_mb: 2048 +# UID of the existing ghost account (audit-production.yml): +samba_access_admin_uid: 1000 +# Recorded UIDs (audit-production.yml): ghost-agent exists with 1004; +# ansible-deploy takes the lowest free UID >= 1000: +samba_access_agent_uid: 1004 +samba_access_deploy_uid: 1005 diff --git a/proxmox/lxc/ct-101-samba/ansible/inventories/production/group_vars/samba_hosts.yml b/proxmox/lxc/ct-101-samba/ansible/inventories/production/group_vars/samba_hosts.yml index 0a377d2..38128d6 100644 --- a/proxmox/lxc/ct-101-samba/ansible/inventories/production/group_vars/samba_hosts.yml +++ b/proxmox/lxc/ct-101-samba/ansible/inventories/production/group_vars/samba_hosts.yml @@ -8,15 +8,16 @@ ansible_ssh_common_args: >- samba_expected_distribution: Debian samba_expected_distribution_major_version: "13" -# Live hostname of CT 101; copy from preflight.yml (SMB1 is disabled, so the -# live server does not answer NetBIOS names): -samba_expected_hostname: REPLACE-WITH-PREFLIGHT-HOSTNAME +# Live hostname of CT 101 (recorded 2026-10-27 from preflight.yml; SMB1 is +# disabled, so the live server does not answer NetBIOS names): +samba_expected_hostname: Samba samba_expected_virtualization_type: lxc # Expected Podman version after the dist-upgrade (trixie), analog to CT 100/102: samba_expected_podman_version: "5.4.2" -# Resource minimums; copy from preflight.yml: -samba_minimum_root_size_bytes: REPLACE-WITH-PREFLIGHT-ROOT-SIZE -samba_minimum_root_available_bytes: REPLACE-WITH-PREFLIGHT-ROOT-AVAIL +# Resource minimums (recorded 2026-10-27; round values at/below the live +# capacity, CT 100/102 convention): +samba_minimum_root_size_bytes: 4000000000 +samba_minimum_root_available_bytes: 1000000000 samba_firewall_management_ipv4: - 10.0.0.0/24 @@ -35,48 +36,64 @@ samba_access_source_networks: 10.0.0.0/24,10.8.0.0/24 samba_media_group_name: gn-media samba_media_group_gid: 970 -# Media accounts with their productive UIDs; copy from preflight.yml / -# audit-production.yml (fresh-state rebuild: missing accounts are created with -# exactly these UIDs; existing accounts are only validated on the UID): +# Media accounts with their productive UIDs (recorded 2026-10-27 from +# preflight.yml / audit-production.yml; fresh-state rebuild: missing accounts +# are created with exactly these UIDs; existing accounts are only validated on +# the UID). The legacy account dark2devil (UID 1002) exists on the productive +# CT 101 but is deliberately NOT declared: it belongs to the same person as +# debugofen (operator clarification 2026-10-28, GNAI-019 decision 11) and is +# not referenced by any share in the live smb.conf. samba_media_users: - name: ghost - uid: REPLACE-WITH-AUDIT-UID-GHOST + uid: 1000 shell: /bin/bash home: /home/ghost create_home: true - name: daidalaw - uid: REPLACE-WITH-AUDIT-UID-DAIDALAW + uid: 1001 shell: /usr/sbin/nologin home: /home/daidalaw create_home: false - name: debugofen - uid: REPLACE-WITH-AUDIT-UID-DEBUGOFEN + uid: 1003 shell: /usr/sbin/nologin home: /home/debugofen create_home: false samba_media_group_members: - ghost +# Live media mount topology of the productive CT 101 (recorded 2026-10-27): +# six Proxmox mount points. /media/datenhdd1 is an empty ext4 directory from +# the host root filesystem; the Extern disk is mounted at the subdirectory +# /media/datenhdd2/extern (not at /media/datenhdd2 itself). samba_media_mount_points: - /media/daten1 - /media/daten2 - /media/daten3 - - /media/datenhdd2 + - /media/datenhdd1 + - /media/datenhdd2/extern - /media/datenhdd3 samba_smb_workgroup: Ghost.net samba_smb_service_units: - smbd.service - nmbd.service -# Complete share target state from GNAI-017 section 3 plus [Encode] and -# [Encode-In] from GNAI-018 R2. Comments only where the live system shows them -# (Debian defaults and the new pipeline shares); the media shares had empty -# comments in the anonymous listing and stay without comments. +# Complete share target state: the twelve productive shares mirror the live +# smb.conf ACLs exactly (valid users / write list / admin users / force +# user+group; recorded 2026-10-28, GNAI-018 R4 funktionsgleicher Erhalt), plus +# [Encode] and [Encode-In] per GNAI-017 section 4.3 / GNAI-018 R2. Comments +# only where the live system shows them (Debian defaults and the new pipeline +# shares); the media shares had empty comments in the anonymous listing and +# stay without comments. samba_smb_media_shares: - name: Dokumente path: /media/daten1/Dokumente read_only: "yes" valid_users: - ghost + write_list: + - ghost + admin_users: + - ghost - name: Filme path: /media/daten1/Filme read_only: "yes" @@ -86,6 +103,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Filme2 path: /media/datenhdd3/Filme read_only: "yes" @@ -95,6 +114,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Serien path: /media/daten2/Serien read_only: "yes" @@ -115,8 +136,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost - force_user: root - force_group: root + admin_users: + - ghost - name: Animation path: /media/daten1/Animation read_only: "yes" @@ -126,6 +147,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Anime path: /media/daten3/Anime read_only: "yes" @@ -135,11 +158,17 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Extern path: /media/datenhdd2/extern read_only: "no" valid_users: - ghost + write_list: + - ghost + admin_users: + - ghost - name: Musik path: /media/daten1/Musik read_only: "yes" @@ -149,6 +178,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Audiobooks path: /media/daten1/Audiobooks read_only: "yes" @@ -158,6 +189,8 @@ samba_smb_media_shares: - debugofen write_list: - ghost + admin_users: + - ghost - name: Software path: /media/datenhdd3/Software read_only: "yes" @@ -168,6 +201,9 @@ samba_smb_media_shares: write_list: - ghost - daidalaw + admin_users: + - ghost + - daidalaw - name: Downloads path: /media/datenhdd3/Downloads read_only: "no" @@ -175,6 +211,10 @@ samba_smb_media_shares: - ghost - daidalaw - debugofen + admin_users: + - ghost + - daidalaw + - debugofen - name: Encode comment: "Encoding-Pipeline: fertige Encodes (Abholung per mv)" path: /media/datenhdd3/Encoding/output @@ -183,10 +223,10 @@ samba_smb_media_shares: - ghost - daidalaw - debugofen - admin_users: - - ghost write_list: - ghost + admin_users: + - ghost force_group: gn-media create_mask: "0664" directory_mask: "0775" @@ -196,10 +236,10 @@ samba_smb_media_shares: read_only: "no" valid_users: - ghost - admin_users: - - ghost write_list: - ghost + admin_users: + - ghost force_group: gn-media create_mask: "0664" directory_mask: "0775" diff --git a/proxmox/lxc/ct-101-samba/ansible/inventories/production/host_vars/samba-ct101.yml b/proxmox/lxc/ct-101-samba/ansible/inventories/production/host_vars/samba-ct101.yml index ff98250..a1693ff 100644 --- a/proxmox/lxc/ct-101-samba/ansible/inventories/production/host_vars/samba-ct101.yml +++ b/proxmox/lxc/ct-101-samba/ansible/inventories/production/host_vars/samba-ct101.yml @@ -6,11 +6,13 @@ samba_expected_ipv4: 10.0.0.9 # (GNAI-011 section 3.3): samba_expected_ssh_host_key_fingerprint: >- SHA256:+MUO4J4x3TCYLc1iei95UcPTzkPwDPOzovfaIieuOKs -# Live values; copy from preflight.yml: -samba_expected_processor_vcpus: REPLACE-WITH-PREFLIGHT-VCPUS -samba_minimum_memory_mb: REPLACE-WITH-PREFLIGHT-MEMORY-MB -# UID of the existing ghost account; copy from audit-production.yml: -samba_access_admin_uid: REPLACE-WITH-AUDIT-GHOST-UID -# Free UIDs to pick from audit-production.yml (ghost-agent / ansible-deploy): -samba_access_agent_uid: REPLACE-WITH-FREE-AGENT-UID -samba_access_deploy_uid: REPLACE-WITH-FREE-DEPLOY-UID +# Live values (recorded 2026-10-27 from preflight.yml; lxcfs presents the +# configured profile inside the guest): +samba_expected_processor_vcpus: 1 +samba_minimum_memory_mb: 2048 +# UID of the existing ghost account (audit-production.yml): +samba_access_admin_uid: 1000 +# Recorded UIDs (audit-production.yml): ghost-agent exists with 1004; +# ansible-deploy takes the lowest free UID >= 1000: +samba_access_agent_uid: 1004 +samba_access_deploy_uid: 1005 diff --git a/proxmox/lxc/ct-101-samba/ansible/playbooks/audit-production.yml b/proxmox/lxc/ct-101-samba/ansible/playbooks/audit-production.yml index f5e46d3..cc591f2 100644 --- a/proxmox/lxc/ct-101-samba/ansible/playbooks/audit-production.yml +++ b/proxmox/lxc/ct-101-samba/ansible/playbooks/audit-production.yml @@ -107,8 +107,9 @@ samba_media_users | map(attribute='name') | list is subset(samba_audit_pdbedit.stdout_lines) fail_msg: >- - The productive SMB users ghost, daidalaw and debugofen are missing - from the passdb. Stop the adoption; the passdb must not be at risk. + The productive SMB users ghost, daidalaw and debugofen + are missing from the passdb. Stop the adoption; the passdb must not + be at risk. - name: Validate the effective SSH policy ansible.builtin.command: @@ -197,10 +198,7 @@ - "{{ item }}" check_mode: false changed_when: false - loop: - - ghost - - daidalaw - - debugofen + loop: "{{ samba_media_users | map(attribute='name') | list }}" register: samba_audit_password_states - name: Read the complete account database for UID slot picking @@ -224,10 +222,7 @@ - name: Stat the media account authorized keys ansible.builtin.stat: path: "/home/{{ item }}/.ssh/authorized_keys" - loop: - - ghost - - daidalaw - - debugofen + loop: "{{ samba_media_users | map(attribute='name') | list }}" register: samba_audit_keys_stats - name: Read the existing authorized keys diff --git a/proxmox/lxc/ct-101-samba/ansible/playbooks/preflight.yml b/proxmox/lxc/ct-101-samba/ansible/playbooks/preflight.yml index 8ecdf9f..22b8ee7 100644 --- a/proxmox/lxc/ct-101-samba/ansible/playbooks/preflight.yml +++ b/proxmox/lxc/ct-101-samba/ansible/playbooks/preflight.yml @@ -8,6 +8,7 @@ - hardware - network - virtual + - mounts tasks: - name: Verify required inventory safeguards are defined @@ -57,6 +58,32 @@ - ansible_facts["hostname"] == samba_expected_hostname when: samba_expected_hostname is not match('^REPLACE-') + - name: Read the effective CPU allocation (nproc reflects the configured cores) + ansible.builtin.command: + argv: + - /usr/bin/nproc + check_mode: false + changed_when: false + register: samba_preflight_nproc + + - name: Verify the declared CPU allocation when the placeholder is already replaced + ansible.builtin.assert: + that: + - samba_preflight_nproc.stdout | int == samba_expected_processor_vcpus | int + fail_msg: >- + The guest reports {{ samba_preflight_nproc.stdout }} CPUs, but the + inventory declares {{ samba_expected_processor_vcpus }} vCPUs. + when: samba_expected_processor_vcpus is not match('^REPLACE-') + + - name: Verify the declared memory minimum when the placeholder is already replaced + ansible.builtin.assert: + that: + - ansible_facts["memtotal_mb"] | int >= samba_minimum_memory_mb | int + fail_msg: >- + The guest reports {{ ansible_facts["memtotal_mb"] }} MiB RAM, below + the declared minimum {{ samba_minimum_memory_mb }} MiB. + when: samba_minimum_memory_mb is not match('^REPLACE-') + - name: Read the guest user-namespace ID mapping ansible.builtin.command: argv: @@ -182,6 +209,18 @@ The pre-migration listener set is unexpected: SSH and SMB must be served and NFS (2049) must not be active yet. + - name: Verify the declared media mount points are mounted + ansible.builtin.assert: + that: + - >- + ansible_facts["mounts"] | map(attribute="mount") | list is + superset(samba_media_mount_points) + fail_msg: >- + Not all declared media mount points are mounted on CT 101. The Samba + shares and the NFS export depend on the complete media library; stop + the adoption until the storage topology is complete. + success_msg: All declared media mount points are mounted. + - name: Read the Samba server version ansible.builtin.command: argv: @@ -200,7 +239,7 @@ changed_when: false register: samba_preflight_smb_conf - - name: Read the live anonymous Samba share listing + - name: Read the live anonymous Samba share listing (smbclient is installed by samba_smb during convergence) ansible.builtin.command: argv: - /usr/bin/smbclient @@ -209,6 +248,7 @@ - -N check_mode: false changed_when: false + failed_when: false register: samba_preflight_shares - name: Read the media account entries @@ -237,7 +277,7 @@ label: "{{ item.name }}" register: samba_preflight_media_uids - - name: Read the media group entry for the inventory placeholder + - name: Read the media group entry (created by samba_media during convergence) ansible.builtin.command: argv: - /usr/bin/getent @@ -245,6 +285,7 @@ - "{{ samba_media_group_name }}" check_mode: false changed_when: false + failed_when: false register: samba_preflight_media_group - name: Read the complete account database for UID slot picking @@ -279,6 +320,23 @@ changed_when: false register: samba_preflight_root_capacity + - name: Verify the declared root filesystem minimums when the placeholders are replaced + ansible.builtin.assert: + that: + - >- + samba_preflight_root_capacity.stdout.split()[0] | int >= + samba_minimum_root_size_bytes | int + - >- + samba_preflight_root_capacity.stdout.split()[1] | int >= + samba_minimum_root_available_bytes | int + fail_msg: >- + The root filesystem does not meet the declared minimums + ({{ samba_minimum_root_size_bytes }} bytes size, + {{ samba_minimum_root_available_bytes }} bytes available). + when: + - samba_minimum_root_size_bytes is not match('^REPLACE-') + - samba_minimum_root_available_bytes is not match('^REPLACE-') + - name: Read the media mount capacities ansible.builtin.command: argv: @@ -354,6 +412,7 @@ {{ ansible_facts['virtualization_type'] }} {{ ansible_facts['virtualization_role'] }} host_visible_processor_vcpus: "{{ ansible_facts['processor_vcpus'] }}" + configured_vcpus_nproc: "{{ samba_preflight_nproc.stdout }}" memory_mib: "{{ ansible_facts['memtotal_mb'] }}" cgroup_cpu_max: "{{ samba_preflight_cpu_max.stdout }}" cgroup_memory_max: "{{ samba_preflight_memory_max.stdout }}" @@ -367,7 +426,10 @@ map(attribute='stdout') | list }} samba_version: "{{ samba_preflight_samba_version.stdout }}" smb_conf_sha256: "{{ samba_preflight_smb_conf.stdout.split()[0] }}" - smb_share_listing: "{{ samba_preflight_shares.stdout }}" + smb_share_listing: >- + {{ samba_preflight_shares.stdout + if samba_preflight_shares.rc == 0 else + 'smbclient not installed yet (samba_smb installs it during convergence)' }} media_accounts: >- {{ samba_preflight_media_accounts.results | map(attribute='stdout') | list }} @@ -376,7 +438,10 @@ map(attribute='item.name') | zip(samba_preflight_media_uids.results | map(attribute='stdout')) | list }} - media_group: "{{ samba_preflight_media_group.stdout }}" + media_group: >- + {{ samba_preflight_media_group.stdout + if samba_preflight_media_group.rc == 0 else + 'missing (samba_media creates it during convergence)' }} agent_uid: "{{ samba_preflight_agent_uid.stdout }}" all_accounts: "{{ samba_preflight_all_accounts.stdout }}" all_groups: "{{ samba_preflight_all_groups.stdout }}" @@ -387,14 +452,14 @@ {{ samba_expected_ssh_host_key_fingerprint }} placeholder_sources: >- samba_expected_hostname=hostname, - samba_expected_processor_vcpus=host_visible_processor_vcpus, + samba_expected_processor_vcpus=configured_vcpus_nproc (nproc; configured cores), samba_minimum_memory_mb=memory_mib, - samba_minimum_root_size_bytes=root_size_bytes, - samba_minimum_root_available_bytes=root_available_bytes, + samba_minimum_root_size_bytes=round minimum at/below root_size_bytes, + samba_minimum_root_available_bytes=round minimum at/below root_available_bytes, samba_access_admin_uid=UID of ghost in media_accounts, samba_access_agent_uid=agent_uid, samba_access_deploy_uid=free UID from all_accounts, samba_media_users[ghost].uid=uid of ghost in media_user_uids, samba_media_users[daidalaw].uid=uid of daidalaw in media_user_uids, samba_media_users[debugofen].uid=uid of debugofen in media_user_uids, - samba_media_group_gid=gid of gn-media in media_group + samba_media_group_gid=recorded 970 (GID slot free; samba_media creates the group) diff --git a/proxmox/lxc/ct-101-samba/ansible/playbooks/verify-production.yml b/proxmox/lxc/ct-101-samba/ansible/playbooks/verify-production.yml index ad9f9ba..7205df8 100644 --- a/proxmox/lxc/ct-101-samba/ansible/playbooks/verify-production.yml +++ b/proxmox/lxc/ct-101-samba/ansible/playbooks/verify-production.yml @@ -235,7 +235,7 @@ - /usr/bin/findmnt - --noheadings - --output - - SOURCE,FSTYPE,OPTIONS + - TARGET,SOURCE,FSTYPE,OPTIONS - --target - "{{ item }}" check_mode: false @@ -245,11 +245,17 @@ label: "{{ item }}" register: samba_production_mounts - - name: Require every declared media mount to be mounted + - name: Require every declared media mount to be mounted at its declared path ansible.builtin.assert: that: - - samba_production_mounts.results | map(attribute='rc') | unique | list == [0] - fail_msg: Not all declared media mount points are mounted. + - item.rc == 0 + - item.stdout.split()[0] == item.item + fail_msg: >- + {{ item.item }} is not a mount point; findmnt resolved it to + {{ item.stdout.split()[0] }}. + loop: "{{ samba_production_mounts.results }}" + loop_control: + label: "{{ item.item }}" - name: Read the declared media accounts ansible.builtin.command: @@ -381,9 +387,9 @@ samba_media_users | map(attribute='name') | list is subset(samba_production_pdbedit.stdout_lines) fail_msg: >- - The productive SMB users ghost, daidalaw and debugofen are missing - from the passdb (GNAI-018 R5). On a fresh-state rebuild, set them - with smbpasswd -a after the first convergence. + The productive SMB users ghost, daidalaw and debugofen + are missing from the passdb (GNAI-018 R5). On a fresh-state rebuild, + set them with smbpasswd -a after the first convergence. - name: Read the anonymous Samba share listing ansible.builtin.command: diff --git a/proxmox/lxc/ct-101-samba/ansible/roles/samba_access/defaults/main.yml b/proxmox/lxc/ct-101-samba/ansible/roles/samba_access/defaults/main.yml index 4eb6c8f..7eda7ab 100644 --- a/proxmox/lxc/ct-101-samba/ansible/roles/samba_access/defaults/main.yml +++ b/proxmox/lxc/ct-101-samba/ansible/roles/samba_access/defaults/main.yml @@ -3,17 +3,17 @@ samba_access_source_networks: 10.0.0.0/24,10.8.0.0/24 samba_access_public_key_root: "{{ playbook_dir }}/../files/ssh" samba_access_agent_user: ghost-agent -samba_access_agent_uid: 1000 +samba_access_agent_uid: 1004 samba_access_agent_public_key_file: >- {{ samba_access_public_key_root }}/samba-ghost-agent.pub samba_access_admin_user: ghost -samba_access_admin_uid: 1001 +samba_access_admin_uid: 1000 samba_access_admin_public_key_file: >- {{ samba_access_public_key_root }}/samba-ghost.pub samba_access_deploy_user: ansible-deploy -samba_access_deploy_uid: 1002 +samba_access_deploy_uid: 1005 samba_access_deploy_public_key_file: >- {{ samba_access_public_key_root }}/samba-ansible-deploy.pub @@ -22,6 +22,7 @@ samba_access_sudoers_file: /etc/sudoers.d/90-ghostnet-ansible-deploy # CT 101 is an adopted legacy LXC: the personal administrator account ghost # already exists with its productive UID and is only validated here, never -# recreated. ghost-agent and ansible-deploy are created with fixed UIDs that -# the operator picks from the audit output because the media accounts -# ghost/daidalaw/debugofen already occupy the low UID range. +# recreated. ghost-agent (1004) and ansible-deploy (1005) are created with +# fixed UIDs because the existing accounts ghost/daidalaw/dark2devil/ +# debugofen already occupy the low UID range (recorded 2026-10-27; the +# inventory host_vars carry the same values). diff --git a/proxmox/lxc/ct-101-samba/ansible/roles/samba_lxc_baseline/README.md b/proxmox/lxc/ct-101-samba/ansible/roles/samba_lxc_baseline/README.md index e838c3e..7011565 100644 --- a/proxmox/lxc/ct-101-samba/ansible/roles/samba_lxc_baseline/README.md +++ b/proxmox/lxc/ct-101-samba/ansible/roles/samba_lxc_baseline/README.md @@ -1,9 +1,12 @@ # `samba_lxc_baseline` Declaratively manages the CT 101 Samba LXC baseline: guest identity -(hostname, Debian 13, `10.0.0.9`, LXC guest, systemd), the unprivileged +(hostname `Samba`, Debian 13, `10.0.0.9`, LXC guest, systemd), the unprivileged ID-mapping premise, baseline administration packages, the `Europe/Berlin` timezone and the protected Ghostnet configuration/data/backup directories. +It also masks `sys-kernel-config.mount` (configfs is unavailable in +unprivileged LXC; CT 100/102/103 convention) and clears a stale failed state, +so the no-failed-units gate of `playbooks/preflight.yml` holds. The role refuses to run while the inventory still contains `REPLACE-WITH-*` placeholders; `playbooks/preflight.yml` on the `agent` diff --git a/proxmox/lxc/ct-101-samba/ansible/roles/samba_lxc_baseline/defaults/main.yml b/proxmox/lxc/ct-101-samba/ansible/roles/samba_lxc_baseline/defaults/main.yml index ee226b8..250b13a 100644 --- a/proxmox/lxc/ct-101-samba/ansible/roles/samba_lxc_baseline/defaults/main.yml +++ b/proxmox/lxc/ct-101-samba/ansible/roles/samba_lxc_baseline/defaults/main.yml @@ -1,5 +1,5 @@ --- -samba_lxc_baseline_expected_hostname: samba +samba_lxc_baseline_expected_hostname: Samba samba_lxc_baseline_expected_distribution: Debian samba_lxc_baseline_expected_distribution_major_version: "13" samba_lxc_baseline_expected_ipv4: 10.0.0.9 diff --git a/proxmox/lxc/ct-101-samba/ansible/roles/samba_lxc_baseline/tasks/main.yml b/proxmox/lxc/ct-101-samba/ansible/roles/samba_lxc_baseline/tasks/main.yml index 6a37d7c..cd0fc4e 100644 --- a/proxmox/lxc/ct-101-samba/ansible/roles/samba_lxc_baseline/tasks/main.yml +++ b/proxmox/lxc/ct-101-samba/ansible/roles/samba_lxc_baseline/tasks/main.yml @@ -56,6 +56,34 @@ (GNAI-019) depends on the standard Proxmox ID mapping; a privileged conversion changes the UID/GID semantics of the media library. +- name: Mask the configfs mount unit (configfs is unavailable in unprivileged LXC) + ansible.builtin.systemd_service: + name: sys-kernel-config.mount + masked: true + register: samba_lxc_baseline_configfs_mask + +- name: Probe the configfs mount unit failure state + ansible.builtin.command: + argv: + - /usr/bin/systemctl + - is-failed + - sys-kernel-config.mount + check_mode: false + changed_when: false + failed_when: false + register: samba_lxc_baseline_configfs_failed + +- name: Clear a stale failed state of the configfs mount unit + ansible.builtin.command: + argv: + - /usr/bin/systemctl + - reset-failed + - sys-kernel-config.mount + when: >- + not ansible_check_mode and + (samba_lxc_baseline_configfs_mask.changed or + samba_lxc_baseline_configfs_failed.rc == 0) + - name: Install baseline administration packages ansible.builtin.apt: name: "{{ samba_lxc_baseline_packages }}" diff --git a/proxmox/lxc/ct-101-samba/ansible/roles/samba_media/README.md b/proxmox/lxc/ct-101-samba/ansible/roles/samba_media/README.md index 52ce282..cd8c8cd 100644 --- a/proxmox/lxc/ct-101-samba/ansible/roles/samba_media/README.md +++ b/proxmox/lxc/ct-101-samba/ansible/roles/samba_media/README.md @@ -4,12 +4,13 @@ Declaratively manages the CT 101 media prerequisites for the Samba shares and the NFS export (GNAI-018 R1/R3): - Verifies that all declared media mount points (`/media/daten1`, - `/media/daten2`, `/media/daten3`, `/media/datenhdd2`, `/media/datenhdd3`) - are actually mounted. Mount facts are gathered on demand when the playbook - fact subset omits them. -- Validates the productive media accounts `ghost`, `daidalaw` and `debugofen` - against their recorded UIDs (`samba_media_users`; the `REPLACE-WITH-*` UID - placeholders fail closed in the argument spec before any task runs). + `/media/daten2`, `/media/daten3`, `/media/datenhdd1`, + `/media/datenhdd2/extern`, `/media/datenhdd3`) are actually mounted. Mount + facts are gathered on demand when the playbook fact subset omits them. +- Validates the productive media accounts `ghost`, `daidalaw` + and `debugofen` against their recorded UIDs (`samba_media_users`; the + `REPLACE-WITH-*` UID placeholders fail closed in the argument spec before + any task runs). Existing accounts are only validated, never modified. On a fresh-state rebuild, missing accounts are created with exactly the declared UIDs (primary group `gid=uid`, `password_lock: true`; shell/home/create_home come @@ -18,10 +19,11 @@ the NFS export (GNAI-018 R1/R3): - Creates the group `gn-media` with the pipeline GID **970** — the numeric identity must match `ghostnet-ki` (GNENC-002). The role fails closed when the GID slot or the group name is occupied by a different identity. -- Adds `ghost` to `gn-media` (required for the `mv` pickup from `[Encode]` and - the `.dlc` drop into `[Encode-In]`, because the pipeline directories carry - group `gn-media` with mode 2770). `daidalaw`/`debugofen` deliberately get no - membership (read-only world access is sufficient, GNAI-018 R3). +- Adds `ghost` to `gn-media` (required for the `mv` pickup + from `[Encode]` and the `.dlc` drop into `[Encode-In]`, because the pipeline + directories carry group `gn-media` with mode 2770; GNAI-018 R3). + `daidalaw`/`debugofen` deliberately get no membership (read-only world + access is sufficient). Post-state verification (converged group entry, effective memberships) runs on real executions and on check-mode reruns of an already converged host; diff --git a/proxmox/lxc/ct-101-samba/ansible/roles/samba_media/defaults/main.yml b/proxmox/lxc/ct-101-samba/ansible/roles/samba_media/defaults/main.yml index 2badf25..32f7a24 100644 --- a/proxmox/lxc/ct-101-samba/ansible/roles/samba_media/defaults/main.yml +++ b/proxmox/lxc/ct-101-samba/ansible/roles/samba_media/defaults/main.yml @@ -5,7 +5,8 @@ samba_media_group_gid: 970 # audit-production.yml). shell/home/create_home are only used when creating # missing accounts (fresh-state rebuild); existing accounts are only validated # on the UID and never modified. The UID placeholders stop the role -# validation fail-closed until the live values are entered. +# validation fail-closed until the live values are entered. The legacy account +# dark2devil (UID 1002) is deliberately not declared (GNAI-019 decision 11). samba_media_users: - name: ghost uid: REPLACE-WITH-AUDIT-UID-GHOST @@ -24,9 +25,12 @@ samba_media_users: create_home: false samba_media_group_members: - ghost +# Live media mount topology of the productive CT 101 (recorded 2026-10-27): +# six Proxmox mount points; the Extern disk is mounted at /media/datenhdd2/extern. samba_media_mount_points: - /media/daten1 - /media/daten2 - /media/daten3 - - /media/datenhdd2 + - /media/datenhdd1 + - /media/datenhdd2/extern - /media/datenhdd3 diff --git a/proxmox/lxc/ct-101-samba/ansible/roles/samba_smb/README.md b/proxmox/lxc/ct-101-samba/ansible/roles/samba_smb/README.md index c0672f5..1132414 100644 --- a/proxmox/lxc/ct-101-samba/ansible/roles/samba_smb/README.md +++ b/proxmox/lxc/ct-101-samba/ansible/roles/samba_smb/README.md @@ -7,7 +7,8 @@ decision 1: Debian package, no container — see there for the rationale). `samba_smb_preserve_passdb: true`, `/var/lib/samba/private/passdb.tdb` must exist before anything converges, the `pdbedit -L` user list is read before and after the convergence and must be identical, and the productive users - `ghost`, `daidalaw`, `debugofen` must be present. On a fresh-state rebuild + `ghost`, `daidalaw`, `debugofen` must be present. On a + fresh-state rebuild the first `site.yml` run passes `-e samba_smb_preserve_passdb=false` (no passdb exists yet); the operator then sets the SMB passwords interactively (`smbpasswd -a`), and the second run validates the populated passdb with the diff --git a/proxmox/lxc/ct-101-samba/ansible/roles/samba_smb/tasks/main.yml b/proxmox/lxc/ct-101-samba/ansible/roles/samba_smb/tasks/main.yml index 5587662..5f2d073 100644 --- a/proxmox/lxc/ct-101-samba/ansible/roles/samba_smb/tasks/main.yml +++ b/proxmox/lxc/ct-101-samba/ansible/roles/samba_smb/tasks/main.yml @@ -114,7 +114,8 @@ subset(samba_smb_passdb_after.stdout_lines) fail_msg: >- The Samba user database changed during convergence. The productive - users ghost, daidalaw and debugofen must be preserved (GNAI-018 R5). + users ghost, daidalaw and debugofen must be preserved + (GNAI-018 R5). when: samba_smb_preserve_passdb | bool - name: Read the anonymous Samba share listing diff --git a/proxmox/lxc/ct-101-samba/ansible/roles/samba_smb/templates/smb.conf.j2 b/proxmox/lxc/ct-101-samba/ansible/roles/samba_smb/templates/smb.conf.j2 index ff6b175..563d6e6 100644 --- a/proxmox/lxc/ct-101-samba/ansible/roles/samba_smb/templates/smb.conf.j2 +++ b/proxmox/lxc/ct-101-samba/ansible/roles/samba_smb/templates/smb.conf.j2 @@ -33,6 +33,7 @@ [printers] comment = All Printers + browseable = no path = /var/tmp printable = yes guest ok = no diff --git a/proxmox/lxc/ct-101-samba/scripts/README.md b/proxmox/lxc/ct-101-samba/scripts/README.md index adf1bb6..228a92c 100644 --- a/proxmox/lxc/ct-101-samba/scripts/README.md +++ b/proxmox/lxc/ct-101-samba/scripts/README.md @@ -14,7 +14,7 @@ Upgrades the productive CT 101 from Debian 12 (bookworm) to Debian 13 productive `smb.conf` survive the upgrade (`--force-confold` keeps the productive `smb.conf`). Sequence: -1. Preflight: CT 101 is running, is unprivileged (GNAI-015), all five media +1. Preflight: CT 101 is running, is unprivileged (GNAI-015), all six media mounts carry `backup=no` (otherwise `vzdump` would archive the media library), and in the guest Debian 12, `ssh`/`smbd`/`nmbd` are active and the IPv4 matches. @@ -29,7 +29,7 @@ productive `smb.conf`). Sequence: `wtmpdb-rotate.timer` cleanup rule from `ghostnet-lxc-baseline.sh`. 5. Reboot and post-verify: Debian 13, `ssh`/`smbd`/`nmbd` active, no failed units, listeners 22/139/445, anonymous `smbclient -L` query successful, - unprivileged uid_map intact, seccomp filter disabled, all five media + unprivileged uid_map intact, seccomp filter disabled, all six media mounts mounted. Before the run, all media mounts in the productive `pct config 101` must carry @@ -51,32 +51,35 @@ file-server service; the host key is unchanged, so the workstation does not have to replace a known host key. The reviewed repository copy has SHA-256 -`f7adfbde7378708620975a64c30bb3de8727b96915ad8c58100893a52c2c2d6e`. +`99f34cd991143d7d11dc1f64a49d29e966dee9db23737d081b06b67c1e409aac`. ## `provision-ct101-samba.sh` (fresh-state rebuild) Creates a fresh CT 101 from a Debian 13 template with the recorded profile, -attaches the five media mounts, disables the LXC seccomp filter and then runs -the reviewed baseline hook with the recorded account UIDs. The script refuses -to run when CT 101 already exists, and it names every material setting -explicitly. +attaches the six media mounts, disables the LXC seccomp filter and then runs +the reviewed baseline hook with the recorded account UIDs. After the hook it +masks `sys-kernel-config.mount` (configfs is unavailable in unprivileged LXC; +CT 100/102/103 convention) and clears a stale failed state, so the fresh CT +passes the no-failed-units gate of `playbooks/preflight.yml`. The script +refuses to run when CT 101 already exists, and it names every material +setting explicitly. | Setting | Value | | --- | --- | -| CT ID / hostname | `101` / `samba` (overridable via `GHOSTNET_CT101_HOSTNAME`) | +| CT ID / hostname | `101` / `Samba` (overridable via `GHOSTNET_CT101_HOSTNAME`) | | OS | Debian 13, amd64 (newest `debian-13-*amd64*` template in the local cache, otherwise `GHOSTNET_OS_TEMPLATE`) | | Container type | unprivileged LXC with `nesting=1,keyctl=1` | -| CPU / RAM / swap | 2 cores / 2048 MiB / 1024 MiB (adjustable via env) | -| Rootfs | 32 GiB on `local-lvm` (adjustable via env) | +| CPU / RAM / swap | 1 core / 2048 MiB / 2048 MiB (recorded profile, adjustable via env) | +| Rootfs | 4 GiB on `local-lvm` (recorded profile, adjustable via env) | | Network | `vmbr0`, `10.0.0.9/24`, gateway `10.0.0.1`, DNS `10.0.0.14`, no IPv6 | | Boot / timezone | `onboot: 1`, `Europe/Berlin` | | Seccomp | empty `lxc.seccomp.profile:` (Ganesha `open_by_handle_at`) | -| Media mounts | `mp0`–`mp4` → `/media/daten1`, `/media/daten2`, `/media/daten3`, `/media/datenhdd2`, `/media/datenhdd3`, each with `backup=no` | +| Media mounts | `mp0`–`mp5` → `/media/daten1`, `/media/daten2`, `/media/daten3`, `/media/datenhdd1`, `/media/datenhdd2/extern`, `/media/datenhdd3`, each with `backup=no` | | Baseline hook | `/usr/local/sbin/ghostnet-lxc-baseline.sh` (SHA-256 pinned) | -The resource defaults are conservative starting values; for an exact rebuild of -the productive profile, set the values from `pct config 101` of the productive -CT 101 via the env variables. +The resource defaults are the recorded productive profile (captured 2026-10-27 +inside the guest via lxcfs/cgroup and `findmnt`); override them via the env +variables only deliberately. Mandatory environment variables (the recorded values from the Ansible inventory or the productive `pct config 101`): @@ -85,7 +88,7 @@ or the productive `pct config 101`): | --- | --- | | `GHOSTNET_AGENT_UID` | UID of `ghost-agent` (must match `samba_access_agent_uid`) | | `GHOSTNET_ADMIN_UID` | UID of `ghost` (must match `samba_access_admin_uid`) | -| `GHOSTNET_MP0_HOST` … `GHOSTNET_MP4_HOST` | Host paths of the five media mounts (must exist) | +| `GHOSTNET_MP0_HOST` … `GHOSTNET_MP5_HOST` | Host paths of the six media mounts (must exist) | Optional variables: `GHOSTNET_CT101_HOSTNAME`, `GHOSTNET_CT101_CORES`, `GHOSTNET_CT101_MEMORY_MB`, `GHOSTNET_CT101_ROOTFS_GB`, `GHOSTNET_CT101_SWAP_MB`, @@ -100,8 +103,8 @@ install -o root -g root -m 0700 \ GHOSTNET_AGENT_UID= GHOSTNET_ADMIN_UID= \ GHOSTNET_MP0_HOST=/path/daten1 GHOSTNET_MP1_HOST=/path/daten2 \ -GHOSTNET_MP2_HOST=/path/daten3 GHOSTNET_MP3_HOST=/path/datenhdd2 \ -GHOSTNET_MP4_HOST=/path/datenhdd3 \ +GHOSTNET_MP2_HOST=/path/daten3 GHOSTNET_MP3_HOST=/path/datenhdd1 \ +GHOSTNET_MP4_HOST=/path/datenhdd2-extern GHOSTNET_MP5_HOST=/path/datenhdd3 \ /usr/local/sbin/provision-ct101-samba.sh ``` @@ -112,7 +115,7 @@ steps (set the ghost password, install the public keys from placeholders, rebuild sequence from `ansible/README.md`). The reviewed repository copy has SHA-256 -`ac5d613fb3c3526135c96f02efff9fd7d8dc01952e3d4701b9b5a4a511fff1cd`. +`f40f3da836d3b0b31773c8293fa245eb970fae079dfe6189c91c699a48c21306`. ## `ghostnet-lxc-baseline.sh` diff --git a/proxmox/lxc/ct-101-samba/scripts/provision-ct101-samba.sh b/proxmox/lxc/ct-101-samba/scripts/provision-ct101-samba.sh index 03e0533..f9fd970 100644 --- a/proxmox/lxc/ct-101-samba/scripts/provision-ct101-samba.sh +++ b/proxmox/lxc/ct-101-samba/scripts/provision-ct101-samba.sh @@ -2,9 +2,11 @@ # Fresh-state rebuild of CT 101 (Samba file server) on the Proxmox VE host. # Creates the container from a Debian 13 template with the recorded profile, -# attaches the five media mounts, disables the LXC seccomp filter (required +# attaches the six media mounts, disables the LXC seccomp filter (required # by the rootful NFS-Ganesha container, GNAI-019) and runs the reviewed -# generic LXC baseline hook with the recorded account UIDs. +# generic LXC baseline hook with the recorded account UIDs. It then masks the +# configfs mount unit (configfs is unavailable in unprivileged LXC; CT +# 100/102/103 convention) and clears a stale failed state. # # The script never replaces or destroys an existing CT 101. It contains no # password, private key or SMB secret. The SMB passwords are set by the @@ -14,7 +16,7 @@ set -Eeuo pipefail umask 077 readonly CTID='101' -readonly HOSTNAME="${GHOSTNET_CT101_HOSTNAME:-samba}" +readonly HOSTNAME="${GHOSTNET_CT101_HOSTNAME:-Samba}" readonly IPV4_CIDR='10.0.0.9/24' readonly GATEWAY='10.0.0.1' readonly NAMESERVER='10.0.0.14' @@ -24,10 +26,10 @@ readonly CONTAINER_STORAGE='local-lvm' readonly BASELINE_HOOK="${GHOSTNET_BASELINE_HOOK:-/usr/local/sbin/ghostnet-lxc-baseline.sh}" # Reviewed repository copy of proxmox/shared/scripts/ghostnet-lxc-baseline.sh: readonly BASELINE_HOOK_SHA256='111f1b70e47e3a2f6a1bcce22cb0ec50aacd8fbcff37d87fbcf853a318381b10' -readonly CORES="${GHOSTNET_CT101_CORES:-2}" +readonly CORES="${GHOSTNET_CT101_CORES:-1}" readonly MEMORY_MB="${GHOSTNET_CT101_MEMORY_MB:-2048}" -readonly ROOTFS_GB="${GHOSTNET_CT101_ROOTFS_GB:-32}" -readonly SWAP_MB="${GHOSTNET_CT101_SWAP_MB:-1024}" +readonly ROOTFS_GB="${GHOSTNET_CT101_ROOTFS_GB:-4}" +readonly SWAP_MB="${GHOSTNET_CT101_SWAP_MB:-2048}" # Empty: auto-detect the newest debian-13 amd64 template in the local cache. readonly OS_TEMPLATE="${GHOSTNET_OS_TEMPLATE:-}" # The recorded account UIDs must match samba_access_agent_uid and @@ -35,13 +37,15 @@ readonly OS_TEMPLATE="${GHOSTNET_OS_TEMPLATE:-}" readonly AGENT_UID="${GHOSTNET_AGENT_UID:-}" readonly ADMIN_UID="${GHOSTNET_ADMIN_UID:-}" -# Guest mount points of the media library (GNAI-017 section 2); the host -# paths are supplied per environment variable and must already exist. +# Guest mount points of the live media library (GNAI-017 section 1, recorded +# 2026-10-27); the host paths are supplied per environment variable and must +# already exist. The Extern disk is mounted at /media/datenhdd2/extern. readonly -a MOUNT_GUEST_PATHS=( '/media/daten1' '/media/daten2' '/media/daten3' - '/media/datenhdd2' + '/media/datenhdd1' + '/media/datenhdd2/extern' '/media/datenhdd3' ) readonly -a MOUNT_HOST_PATHS=( @@ -50,6 +54,7 @@ readonly -a MOUNT_HOST_PATHS=( "${GHOSTNET_MP2_HOST:-}" "${GHOSTNET_MP3_HOST:-}" "${GHOSTNET_MP4_HOST:-}" + "${GHOSTNET_MP5_HOST:-}" ) log() { @@ -101,7 +106,7 @@ if pct status "$CTID" >/dev/null 2>&1; then die "CT ${CTID} is still registered" fi -[[ "$HOSTNAME" =~ ^[a-z0-9][a-z0-9-]*$ ]] || +[[ "$HOSTNAME" =~ ^[A-Za-z0-9][A-Za-z0-9-]*$ ]] || die "invalid hostname: ${HOSTNAME}" require_numeric GHOSTNET_CT101_CORES "$CORES" require_numeric GHOSTNET_CT101_MEMORY_MB "$MEMORY_MB" @@ -114,7 +119,7 @@ require_numeric GHOSTNET_ADMIN_UID "$ADMIN_UID" for index in "${!MOUNT_HOST_PATHS[@]}"; do [[ -n "${MOUNT_HOST_PATHS[$index]}" ]] || - die "GHOSTNET_MP${index}_HOST is not set; all five media mount host paths are required" + die "GHOSTNET_MP${index}_HOST is not set; all six media mount host paths are required" [[ -d "${MOUNT_HOST_PATHS[$index]}" ]] || die "media mount host path does not exist: ${MOUNT_HOST_PATHS[$index]}" done @@ -236,6 +241,10 @@ GHOSTNET_AGENT_UID="$AGENT_UID" GHOSTNET_ADMIN_UID="$ADMIN_UID" \ "$BASELINE_HOOK" "$CTID" || die 'the baseline hook failed; inspect the output above before any rerun' +log 'masking the configfs mount unit (configfs is unavailable in unprivileged LXC)' +pct exec "$CTID" -- systemctl mask sys-kernel-config.mount +pct exec "$CTID" -- systemctl reset-failed sys-kernel-config.mount + pct exec "$CTID" -- /bin/bash -s -- \ "$HOSTNAME" "${IPV4_CIDR%/*}" "$AGENT_UID" "$ADMIN_UID" \ "${MOUNT_GUEST_PATHS[@]}" <<'GUEST_VERIFY' || @@ -268,8 +277,12 @@ ip -o -4 address show scope global | grep -Eq "[[:space:]]inet[[:space:]]+${ipv4 test "$(id -u ghost-agent)" = "$agent_uid" test "$(id -u ghost)" = "$admin_uid" systemctl is-active --quiet ssh.service +if systemctl --failed --no-legend --plain | grep -q .; then + die 'failed systemd units are present after the baseline' +fi for mount_point in "${mounts[@]}"; do - findmnt --noheadings --target "$mount_point" >/dev/null + awk -v target="$mount_point" '$2 == target { found = 1 } END { exit(found ? 0 : 1) }' /proc/mounts || + die "media mount point is not mounted: $mount_point" done # The empty lxc.seccomp.profile must disable the container seccomp filter. test "$(awk '$1 == "Seccomp:" { print $2 }' /proc/self/status)" = '0' diff --git a/proxmox/lxc/ct-101-samba/scripts/upgrade-ct101-to-debian13.sh b/proxmox/lxc/ct-101-samba/scripts/upgrade-ct101-to-debian13.sh index 896026a..47ccda4 100644 --- a/proxmox/lxc/ct-101-samba/scripts/upgrade-ct101-to-debian13.sh +++ b/proxmox/lxc/ct-101-samba/scripts/upgrade-ct101-to-debian13.sh @@ -21,11 +21,14 @@ readonly EXPECTED_IPV4='10.0.0.9' readonly CONFIRMATION_PHRASE='DIST-UPGRADE_CT101_TO_DEBIAN_13' readonly BACKUP_STORAGE="${GHOSTNET_BACKUP_STORAGE:-local}" readonly BACKUP_DUMP_DIR="${GHOSTNET_BACKUP_DUMP_DIR:-/var/lib/vz/dump}" +# Live media mount topology of the productive CT 101 (recorded 2026-10-27): +# six Proxmox mount points; the Extern disk is mounted at /media/datenhdd2/extern. readonly -a MEDIA_MOUNT_POINTS=( '/media/daten1' '/media/daten2' '/media/daten3' - '/media/datenhdd2' + '/media/datenhdd1' + '/media/datenhdd2/extern' '/media/datenhdd3' ) @@ -254,7 +257,7 @@ grep -q ':445 ' <<<"$listeners" || die 'SMB listener 445 is missing' test "$(awk '$1 == "Seccomp:" { print $2 }' /proc/self/status)" = '0' || die 'the container seccomp filter is still active' for mount_point in "${mounts[@]}"; do - findmnt --noheadings --target "$mount_point" >/dev/null || + awk -v target="$mount_point" '$2 == target { found = 1 } END { exit(found ? 0 : 1) }' /proc/mounts || die "media mount is not mounted: ${mount_point}" done