PermissionError crashes status/TUI; group members can never read WAL store #54

Closed
opened 2026-09-10 04:25:53 +00:00 by xavierk · 1 comment
Owner

Two related defects in the group-read story (found on openSUSE Tumbleweed, v0.3.2)

1. Unhandled PermissionError crashes views instead of degrading (contract violation)

CONTEXT.md defines Store fault as "the store ... cannot be read ... — degrading every view that depends on it rather than crashing". But open_store_readonly() (status.py:91) calls store_path.exists() before the fault handling — and stat() on a root:fenris 2750 directory by a non-group user raises PermissionError ([Errno 13]), which neither fenris status nor the TUI (tui.py:347) catches. Both crash with a full traceback instead of rendering the degraded view.

_open_store() catches only StoreFault/NewerSchema — PermissionError from stat() slips through before a StoreFault can even be raised.

Fix: catch OSError in open_store_readonly() and raise StoreFault (or make the caller degrade). Add a regression test: non-group user + 2750 dir → status/TUI render the fault view, exit 0.

2. Group members cannot actually read the WAL-mode store — StoreFault "attempt to write a readonly database"

After joining the fenris group, a member still cannot open the store:

  • tmpfiles.d creates only the directory (2750); the collector (root, umask 022) creates observations.db + WAL sidecars 644 — group has no write.
  • SQLite in WAL mode requires write access to -shm/-wal (and to create them when absent) even for read-only connections. mode=ro → attempt to write a readonly database → open_store_readonly wraps it into StoreFault.

So every group reader gets the fault view forever, and only root sees data. The 2750 group placement suggests group read was intended.

Fix (packaging + collector):

  • tmpfiles.d: 2770 for /var/lib/fenris (group create/traverse).
  • Collector: after init_store(), chmod g+rw the db and sidecars (or run the unit with UMask=002).
  • Keep open_store_readonly on plain mode=ro — immutable=1 is unsafe with a live collector.

Verified locally on the affected machine: chmod 2770 /var/lib/fenris + group membership → fenris status renders live data for a non-root group member.

Notes for the release-notes matrix

who 2750 (current) 2770 + g+rw sidecars (proposed)
root ok ok
group member crash / permanent fault ok
other crash / fault fault (no traversal)
## Two related defects in the group-read story (found on openSUSE Tumbleweed, v0.3.2) ### 1. Unhandled `PermissionError` crashes views instead of degrading (contract violation) `CONTEXT.md` defines **Store fault** as "the store ... cannot be read ... — degrading every view that depends on it rather than crashing". But `open_store_readonly()` (`status.py:91`) calls `store_path.exists()` before the fault handling — and `stat()` on a `root:fenris 2750` directory by a non-group user raises `PermissionError` (`[Errno 13]`), which neither `fenris status` nor the TUI (`tui.py:347`) catches. Both crash with a full traceback instead of rendering the degraded view. `_open_store()` catches only `StoreFault`/`NewerSchema` — `PermissionError` from `stat()` slips through before a `StoreFault` can even be raised. **Fix:** catch `OSError` in `open_store_readonly()` and raise `StoreFault` (or make the caller degrade). Add a regression test: non-group user + `2750` dir → status/TUI render the fault view, exit 0. ### 2. Group members cannot actually read the WAL-mode store — `StoreFault` "attempt to write a readonly database" After joining the `fenris` group, a member still cannot open the store: - `tmpfiles.d` creates only the directory (`2750`); the collector (root, umask 022) creates `observations.db` + WAL sidecars `644` — group has no write. - SQLite in WAL mode requires **write** access to `-shm`/`-wal` (and to create them when absent) even for read-only connections. `mode=ro` → `attempt to write a readonly database` → `open_store_readonly` wraps it into `StoreFault`. So every group reader gets the fault view forever, and only root sees data. The `2750` group placement suggests group read was intended. **Fix (packaging + collector):** - `tmpfiles.d`: `2770` for `/var/lib/fenris` (group create/traverse). - Collector: after `init_store()`, `chmod g+rw` the db and sidecars (or run the unit with `UMask=002`). - Keep `open_store_readonly` on plain `mode=ro` — `immutable=1` is unsafe with a live collector. Verified locally on the affected machine: `chmod 2770 /var/lib/fenris` + group membership → `fenris status` renders live data for a non-root group member. ### Notes for the release-notes matrix | who | 2750 (current) | 2770 + g+rw sidecars (proposed) | |---|---|---| | root | ok | ok | | group member | crash / permanent fault | ok | | other | crash / fault | fault (no traversal) |
Author
Owner

Resolved in v0.3.3 (commits fb683f5 + 4a7661d, release):

  • open_store_readonly() maps OSError from stat() to StoreFault — status/TUI degrade instead of crashing (src/fenris/status.py)
  • init_store() enforces g+rw on the db and -wal/-shm sidecars (src/fenris/store.py)
  • Store dir ships 2770 (tmpfiles.d + make install); collect unit sets UMask=002
  • rpm %post upgrade path re-runs systemd-tmpfiles --create to correct existing machines
  • tests/test_store_group_access.py: stat-denied → StoreFault, sidecar modes, packaging assertions

Verified end to end on the reporting machine (openSUSE Tumbleweed): upgraded 0.3.2 → 0.3.3, tmpfiles re-applied 2770, collector recreated group-writable sidecars, non-root fenris group member reads live data via status/TUI store path.

Resolved in **v0.3.3** (commits fb683f5 + 4a7661d, [release](https://git.bongbetic.com/xavierk/Fenris/releases/tag/v0.3.3)): - `open_store_readonly()` maps `OSError` from `stat()` to `StoreFault` — status/TUI degrade instead of crashing (src/fenris/status.py) - `init_store()` enforces `g+rw` on the db and `-wal`/`-shm` sidecars (src/fenris/store.py) - Store dir ships `2770` (tmpfiles.d + `make install`); collect unit sets `UMask=002` - rpm `%post` upgrade path re-runs `systemd-tmpfiles --create` to correct existing machines - `tests/test_store_group_access.py`: stat-denied → StoreFault, sidecar modes, packaging assertions Verified end to end on the reporting machine (openSUSE Tumbleweed): upgraded 0.3.2 → 0.3.3, tmpfiles re-applied `2770`, collector recreated group-writable sidecars, non-root `fenris` group member reads live data via status/TUI store path.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: xavierk/Fenris#54