v0.328.0: the catalog allow-list — the box refuses container rights a template asks for and the box does not give (R-971, D2 = A)
gates / gates (push) Successful in 45s
gates / gates (push) Successful in 45s
internal/privpolicy checks the RENDERED compose (privileged, cap_add, devices, host namespaces, security_opt,
sysctls, volumes_from, build/include/extends, env_file/config files outside the stack dir, aliasing named volumes,
binds outside the box's data roots, the Docker socket, propagation, ${VAR} in a privileged key) against an
allow-list compiled into the controller (one entry: homepage's read-only socket). Asked before every compose
write: the 15-minute sync, the guarded update/ladder step, the install, both settings changes, both restores
(before the stop). A refusal writes nothing, keeps the app on its last good compose, raises the operator-only
event app_definition_refused once per definition and puts one line on the app page. The sync never copies a
template named like a controller infra stack. Structural guard TestAllowList_IsWiredIntoEveryComposeWriter.
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -1182,6 +1182,26 @@ Multiple checks per app are supported (all must pass). The probe scheduler runs
|
||||
|
||||
---
|
||||
|
||||
### 1a. The catalog allow-list — container rights the box gives (v0.328.0, R-971)
|
||||
|
||||
A push to the catalog's `main` reaches every installed app within about 15 minutes, so the box no longer trusts a
|
||||
template's container rights. Every path that writes an app compose — the install, a settings change, the 15-minute
|
||||
correction, an update or ladder step, a unit or off-site restore — first renders the compose with the app's own values
|
||||
(as `compose up` will) and asks `internal/privpolicy`: `privileged`, `cap_add`, `devices`, host namespaces
|
||||
(`network_mode`/`pid`/`ipc`/`uts`/`userns_mode`/`cgroup`), `security_opt` other than `no-new-privileges`, `sysctls`,
|
||||
`volumes_from`, `build`/`include`/`extends` files, an `env_file` or config file outside the stack directory, a named
|
||||
volume with a driver/`driver_opts`/`external`/`name`, a `${VAR}` inside any of those, and any bind outside the data
|
||||
roots the box itself gives (`${HDD_PATH}`, `${USERDATA_PATH}`, `${IMPORT_PATH}`) or of the Docker socket. Such a setting
|
||||
is refused unless this app's service has EXACTLY that setting on the list compiled into the controller
|
||||
(`internal/privpolicy/allowlist.json` — today one entry: homepage's read-only Docker socket). A refusal writes nothing:
|
||||
a refused correction or update leaves the app running on its last good compose, raises the operator-only hub event
|
||||
`app_definition_refused` once per refused definition, and puts one line on the app page; a refused install, settings
|
||||
change or restore is refused with a plain message („Semmi nem változott"). A catalog template named like a controller
|
||||
infra stack (traefik, cloudflared, filebrowser, samba, felhom-controller) is never copied. The controller's own infra
|
||||
stacks and override files are rendered by the controller and are out of scope. The catalog's `check-privileges.py`
|
||||
mirrors the check in CI (a byte-identical copy of the list). The structural guard
|
||||
`TestAllowList_IsWiredIntoEveryComposeWriter` fails on any new compose writer that does not ask first.
|
||||
|
||||
### 2. App Export/Import (.fab bundles)
|
||||
|
||||
Per-app export creates a self-contained `.fab` file (tar.gz, optionally encrypted) that can be stored externally or used to restore the app on the same server. Distinct from the automatic backup system — user-initiated, per-app, produces a single portable file.
|
||||
|
||||
Reference in New Issue
Block a user