agent v0.50.0: NAS network storage Part A1 (NFS/SMB automount foundation)

Host-side NFS/SMB automount of a bulk-media NAS share under /mnt/felhom-drives/<name>
(propagates into the guest via the existing shared bind), the +100000 uid recipe,
per-share liveness, and add/list/remove local-API endpoints. A NAS is a distinct class
that bypasses the drive enroll/eject/decommission/SMART/watchdog machinery.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HxLA1mZurFq9kt8hneFeCs
This commit is contained in:
2026-06-30 11:12:27 +02:00
parent 7aeb7caefe
commit 63aa63d0d6
10 changed files with 1351 additions and 5 deletions
+43
View File
@@ -1,3 +1,46 @@
## v0.50.0 — NAS network storage Part A1: NFS/SMB automount foundation (2026-06-30)
Agent foundation of the validated `SPIKE-nas-storage-2026-06-29.md` (verdict READY): a customer NAS can
serve **bulk media** to a media app. The agent mounts a NAS share **host-side** under
`/mnt/felhom-drives/<name>` via a systemd `.automount` (+ `.mount`) pair; it propagates into guest 9201 for
free through the existing shared `mp8` bind (no new mountpoint, no restart). A NAS is a **distinct storage
class** — it carries **no durable-id** and never enters the drive enroll/eject/decommission/wipe/SMART/
watchdog machinery. **Bulk-media class only; STOP before A2 (controller registry/UI) + B (restic-SFTP).**
- **`internal/storage/netmount.go` (NEW).** `NetworkMountSpec` + the locked SPIKE recipe:
- **NFS (preferred):** `What=server:/export`, `Type=nfs4`, `Options=vers=4.1,soft,timeo=50,retrans=2,noatime,_netdev`.
`soft` is the failure-isolation knob (clean EIO, never a `df`/guest wedge); a default `hard` mount is
never emitted. The `+100000` uid mapping is the **export's** job (`anonuid=101000`), so the client mount
carries no uid.
- **SMB (fallback):** `What=//server/share`, `Type=cifs`,
`Options=vers=3.0,credentials=<0600 file>,uid=<+100000>,gid=<+100000>,forceuid,forcegid,file_mode=0664,dir_mode=0775,_netdev`
(plain octal modes, never setgid 2775). **The +100000 rule** (container uid/gid N = host N+100000): a
container uid 1000 renders `uid=101000` so the guest sees its native id and reads+writes; a naïve `+0`
lands as `nobody:nogroup` (not writable) — the documented trap, asserted by a companion test.
- **`.automount` with `TimeoutIdleSec`** (on-demand + idle-unmount): an idle NAS reboot is a non-event.
- **per-share liveness** (`ListNetworkMounts`): TCP-probes the NAS endpoint (2049/445) + reads
`/proc/mounts` — it never `stat`s the (possibly EIO/D-state) mountpoint, so a black-holed NAS cannot
wedge a list. Health `ok | idle | unreachable`, scoped to the affected share, never box-wide.
- **role gate** `NetworkMountRole`: network storage is **bulk-userdata only** — confined to the
`/mnt/felhom-drives` namespace; any other target is refused (most-protected).
- Full validation (`ValidateNetworkMountSpec`) before any unit is rendered: share name (safe segment),
server, NFS export (absolute, no traversal) / SMB share name, uid/gid range, creds path.
- **Drive-machinery bypass (Scenario D).** `parseFelhomMountUnit` (the host-reboot drive re-assert's
classifier) explicitly refuses any unit carrying the network marker, so a NAS mount is never given a
durable-id, SMART-probed, or re-asserted as a drive. Companion red-proof: the same by-uuid-shaped unit
with the drive marker DOES parse — the guard is the discriminator, not luck.
- **`internal/localapi/netstorage.go` (NEW).** Self-scoped endpoints `POST /netstorage/add`,
`GET /netstorage`, `POST /netstorage/remove`. SMB credentials are written **out-of-band** to a 0600 file
the agent owns (never in git, never in a plaintext registry, never logged). Role-gated to the user-data
namespace.
- **sudoers:** new narrow `FELHOM_NETMOUNT` alias (install/enable/disable/stop the `.automount` + remove the
felhom mount-unit files; the `.mount` half reuses `FELHOM_MOUNT`, the mountpoint mkdir reuses
`FELHOM_INTERMEDIARY`). `visudo -cf` clean.
- **config:** `privileged.smb_creds_dir` (default `/var/lib/felhom-agent/smb-creds`).
- **Runtime deps:** `mount.nfs` (nfs-common) + `mount.cifs` (cifs-utils) present on the host (confirmed live).
- Tests: exact NFS/SMB option-set string-asserts + the +100000 companion; validation matrix; role gate;
unit round-trip + health; the drive-machinery guard + companion; Ensure/Remove command sequences.
## v0.49.0 — reboot-during-backup stale-lock recovery (F2-b) + shared-parent script redeploy fix (F2-a) (2026-06-30)
Closes the two host-reboot findings from `TESTRUN-fullstack-2026-06-29.md`.