docs(samba): CHANGELOG v0.144.0 + REPORT + CONTEXT + README §16 + REUSE
Records the R-7 slice 1 ship, the four red-proof outcomes, live-validation evidence, the two bugs live validation caught, and the Part-4 Step-1 enumeration finding + the reported design fork (share data classified but not yet in a live backup run — needs a Viktor ruling, suggested R-7b).
This commit is contained in:
@@ -2305,6 +2305,82 @@ Toggle switches on the provider's deploy/settings page ("Integrációk" section,
|
||||
|
||||
---
|
||||
|
||||
### 16. Network File Sharing — SMB („Megosztás") (v0.144.0, R-7 slice 1)
|
||||
|
||||
LAN file sharing so the box behaves like a NAS: the customer enables sharing, sets ONE household SMB
|
||||
password, and exports folders that appear in Windows Explorer / Mac Finder as `\\FELHOM\<share>`.
|
||||
|
||||
**Architecturally this is an EMBEDDED CONTROLLER FEATURE, not a catalog app** — three reasons:
|
||||
it requires `network_mode: host` (the R-6 spike proved the default docker bridge is deaf to the LAN
|
||||
multicast that WSD/mDNS discovery needs), its configuration is a dynamic share list rendered into
|
||||
`smb.conf` (not env vars), and its share roots must ride the backup classification. It is therefore
|
||||
the **fourth protected infra stack**: traefik / cloudflared / filebrowser / **samba**.
|
||||
|
||||
#### The image (`controller/infra-images/samba/`, `felhom-samba:1.0.0`)
|
||||
|
||||
Our own pinned image (alpine 3.21 by digest) running **three** daemons — `smbd` (445), **`nmbd`**
|
||||
(NetBIOS flat-name resolution), `wsdd` (WS-Discovery), under `tini`. nmbd is not optional: the R-6
|
||||
spike proved wsdd alone makes the box *visible* in Explorer while the double-click still fails
|
||||
`0x80070035`, because WSD supplies an icon, not a name→IP mapping. The image is deliberately dumb —
|
||||
`/etc/samba/smb.conf` is bind-mounted READ-ONLY by the controller, nothing is templated inside, no
|
||||
name or password is baked, and the passdb lives on a named volume so the household password survives
|
||||
container recreation. Built by `controller/scripts/build-samba-image.sh` (never `:latest`).
|
||||
|
||||
#### Data model (`internal/settings/smb.go`)
|
||||
|
||||
- `SMBSettings{Enabled, ServerName, UserSet}` — `ServerName` is the NetBIOS name (≤15, NetBIOS-safe).
|
||||
- `SMBShare{Name, Path, ReadOnly, Offsite, CreatedAt}` — the share registry.
|
||||
- **The SMB password is NEVER persisted.** Only `UserSet` (a boolean) is stored; the secret lives in
|
||||
the container's passdb, applied via `smbpasswd` on STDIN.
|
||||
|
||||
#### Rendering + lifecycle (`internal/infra/samba.go`, `internal/stacks/samba.go`)
|
||||
|
||||
Pure renderers produce a hardened `smb.conf` (`server min protocol = SMB2`, `bind interfaces only`
|
||||
on `lo eth0`, `disable netbios = no`, `map to guest = never`, per-share `force user/group = felhom`
|
||||
so every SMB write lands as uid:gid 1000) and a compose file (`network_mode: host`, pinned image,
|
||||
config `:ro`, passdb volume, one bind per share — `:ro` for read-only shares as defence in depth).
|
||||
|
||||
`ensureSamba` joins `EnsureBaseStack` after filebrowser, gated on `SMB.Enabled` (the cloudflared
|
||||
conditional-deploy precedent); `ReconcileSamba` runs after every mutation. Both are idempotent —
|
||||
unchanged config plus a running container performs **zero** compose calls. Config writes are atomic
|
||||
(tmp+fsync+rename). A share whose drive is disconnected/decommissioned is rendered ABSENT from
|
||||
smb.conf (never export a dead mountpoint) while its configuration is retained.
|
||||
|
||||
**Nothing in this feature deletes or moves customer files.** Disabling sharing is `compose down`
|
||||
(passdb volume kept); deleting a share is a config-only edit. The only `os.Mkdir*` is the guarded
|
||||
new-share-folder create.
|
||||
|
||||
#### UI + the picker guard (`internal/web/sharing_handlers.go`, `templates/sharing.html`)
|
||||
|
||||
Top-nav category **„Megosztás"** → **„Hálózati megosztás"**: enable/server-name card, household
|
||||
password, shares table (Név · Mappa · Írásvédett · Felhőmentés · Törlés), and a create flow — either a
|
||||
NEW folder under `<storage>/shares/` or an EXISTING folder chosen in a browse modal.
|
||||
|
||||
`sharingResolvePath` is the security gate for every customer-supplied path: absolute →
|
||||
`EvalSymlinks` (before containment, so a planted symlink cannot escape) → must live inside a
|
||||
registered, live storage root → must not be in a deny-listed system subtree → must be a directory.
|
||||
**Refusals are uniform** (`Ez a mappa nem osztható meg.`) so the picker can never act as a filesystem
|
||||
oracle. The deny-list is DERIVED from `stacks.SharingDeniedRoots`, itself provably a subset of
|
||||
`ProtectedHDDPaths` — it can only shrink relative to the delete guard, never drift into a stale second
|
||||
list. The drive root is an exact-match denial (a whole drive is never shareable) while user-data
|
||||
folders under it stay shareable. `sharingResolveStorageRoot` is a separate, strictly tighter check
|
||||
used only as the new-folder parent. The picker endpoint is `/api/sharing/browse`, registered on the
|
||||
main mux behind `RequireAuth`+`CsrfProtect` (the `/api/` subtree is routed there, not in the web
|
||||
`ServeHTTP` switch).
|
||||
|
||||
#### Backup classification (`internal/stacks/samba_classify.go`)
|
||||
|
||||
`ClassifiedBinds("samba")` resolves from the shares registry rather than catalog metadata (samba has
|
||||
no `.felhom.yml` and its binds are absolute share paths). Per-share `Felhőmentés` ON → `mandatory`
|
||||
(offsite + tier-2); OFF → `optional` (tier-2 only). smb.conf/passdb are never classified.
|
||||
|
||||
> **KNOWN GAP (design fork, deliberately not improvised).** The seam is correct, but share data is not
|
||||
> yet in a live tier-2/offsite RUN: `backup.RunTier2` short-circuits on a missing recovery unit before
|
||||
> it reaches `GetStackClassifiedBinds`, and the offsite runner enumerates `settings.GetOffboxApps()`.
|
||||
> Both engines are recovery-unit shaped, which a share-only infra stack has not. See root `REPORT.md`.
|
||||
|
||||
---
|
||||
|
||||
## Repository Layout
|
||||
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user