docs/stig-deviations #15: fapolicyd overlay watch VERIFIED enforcing (2026-07-04)
Bench pass on fapolicyd-tmpfs-12043d3 / comet-beacon-54: permissive=0, watch_fs has overlay, / and /tmp both overlayfs; novel-hash /tmp binary DENIED (FANOTIFY resp=2, obj_trust=0, exit=126); empty deny sweep = zero false denials (kiosk/weston/chromium/ NM-dispatcher all trusted, admin SSH+sudo work under staff_t). The exec guard went from effectively OFF to enforcing on the whole overlay root, nothing legit broken. Also records the scope caveat: integrity=none path-trust closes the UNTRUSTED-PATH half (proven) but not the MODIFIED-TRUSTED-PATH half — an overlayfs copy-up of a trusted path (e.g. /usr/bin/bash) lands a same-path tmpfs copy outside dm-verity that path-trust still trusts. Closing it needs integrity=sha256 or an un-shadowable /usr (TODO #15, preferred).
This commit is contained in:
@@ -216,6 +216,38 @@ still work (nothing legit is untrusted — the 4 custom binaries + rpmdb are tru
|
|||||||
a novel `/tmp` binary is now **blocked** (EPERM), and no new AVCs (extend the `.te`
|
a novel `/tmp` binary is now **blocked** (EPERM), and no new AVCs (extend the `.te`
|
||||||
if any). `enable_stig=false` is the escape hatch if it regresses the boot/secure path.
|
if any). `enable_stig=false` is the escape hatch if it regresses the boot/secure path.
|
||||||
|
|
||||||
|
**VERIFIED 2026-07-04 (build `fapolicyd-tmpfs-12043d3`, host `comet-beacon-54`) —
|
||||||
|
enforcing, no false positives.** `fapolicyd active (running)`, `permissive = 0`,
|
||||||
|
`watch_fs = ...,overlay` (the fix took), `stat -f / /tmp` → both `overlayfs` (so the
|
||||||
|
test hits the overlay rule, not the pre-existing tmpfs one). A novel-hash binary
|
||||||
|
(`cp /bin/true /tmp/n; printf '\0' >>/tmp/n`) was **denied**: `bash: /tmp/n: Operation
|
||||||
|
not permitted`, `exit=126`, and the audit record confirms fapolicyd did it —
|
||||||
|
`type=FANOTIFY ... resp=2` (FAN_DENY) `obj_trust=0` (target not in trust db). The
|
||||||
|
**deny sweep was empty** (`journalctl -t fapolicyd -b | grep dec=deny` → nothing),
|
||||||
|
i.e. **zero false denials**: kiosk-session/weston/chromium + the NM dispatcher scripts
|
||||||
|
all ran trusted; admin SSH + `sudo` worked under `staff_u:staff_r:staff_t`. So the
|
||||||
|
exec guard went from **effectively OFF** (pre-fix, overlay unwatched) to **enforcing
|
||||||
|
on the whole overlay root** with nothing legit broken. My NM-dispatcher false-denial
|
||||||
|
worry did not materialise.
|
||||||
|
|
||||||
|
**Scope caveat — this closes the UNTRUSTED-PATH half, not the MODIFIED-TRUSTED-PATH
|
||||||
|
half.** With `integrity = none`, trust is by **path**, so watching the overlay blocks
|
||||||
|
a *new* file at an *untrusted* path (`/tmp/n`) — proven above. It does **not** catch a
|
||||||
|
*trusted* path whose **content** was swapped: writing to e.g. `/usr/bin/bash` triggers
|
||||||
|
an overlayfs **copy-up** into the tmpfs upper — same path, new inode, **outside
|
||||||
|
dm-verity's coverage** (verity only protects the erofs lower, which is now shadowed) —
|
||||||
|
and the path-based rule still trusts it. Closing that residual gap needs either
|
||||||
|
`integrity = sha256` (fapolicyd re-hashes at exec, content ≠ trust-db hash → deny; the
|
||||||
|
rpmdb backend already carries SHA-256 digests, so it's a one-line `fapolicyd.conf`
|
||||||
|
change — cost is per-exec userspace hashing, partly redundant with verity on the
|
||||||
|
un-tampered lower) **or** an **un-shadowable `/usr`** so copy-up can't happen at all
|
||||||
|
(read-only mount → write fails EROFS, no tmpfs copy is ever created; path-trust becomes
|
||||||
|
sound again). The latter is tracked as **TODO #15** ("scope root overlay to per-subtree
|
||||||
|
tmpfs — un-shadowable `/usr`") and is the preferred fix since it removes the attack
|
||||||
|
surface rather than detecting abuse of it. Requires a write primitive to a root-owned
|
||||||
|
path either way (already-privileged context), which is fapolicyd's defense-in-depth
|
||||||
|
threat model — not an unprivileged bypass.
|
||||||
|
|
||||||
## Things that are NOT deviations (kept / enforced)
|
## Things that are NOT deviations (kept / enforced)
|
||||||
|
|
||||||
- **SELinux enforcing** (`selinux --enforcing`; labels preserved into the erofs).
|
- **SELinux enforcing** (`selinux --enforcing`; labels preserved into the erofs).
|
||||||
|
|||||||
Reference in New Issue
Block a user