stig: add overlay to fapolicyd watch_fs — actually guard the overlay root

Bench-verified the getattr allow (620a604) alone did NOT close the exec gap: with
the fapolicyd_tmpfs module installed (AVC gone, fapolicyd active, permissive=0), a
NOVEL non-RPM binary in /tmp still ran. Root cause: the diskless root is overlayfs
(stat -f / -> overlayfs), but the shipped watch_fs is
ext2,ext3,ext4,tmpfs,xfs,vfat,iso9660,btrfs — no 'overlay'. fapolicyd marks mounts
by fs type, so it never marked the overlay and intercepted no exec on / at all.
The exec guard we keep fapolicyd for was effectively OFF at runtime on every
diskless host.

Fix: the 2c fapolicyd step now appends 'overlay' to watch_fs (idempotent) so
fapolicyd watches the overlay root. Trust is path-based (integrity=none) so overlay
paths resolve against the rpmdb trust normally; the fapolicyd_tmpfs .te still
supports fapolicyd statfs'ing the overlay's tmpfs upper. Deviation #15 updated.

This turns app-allowlisting ON for the whole root at runtime for the first time.
BENCH-VERIFY HARD: boot + kiosk render + admin SSH + sudo + TPM enrolment all still
work (nothing legit untrusted), a novel /tmp binary is now BLOCKED (EPERM), no new
AVCs. enable_stig=false is the escape hatch if it regresses.
This commit is contained in:
Audiobuzz
2026-07-03 11:56:13 +00:00
parent 620a60401f
commit 12043d3444
2 changed files with 38 additions and 0 deletions
+19
View File
@@ -810,6 +810,25 @@ build {
" for f in /usr/local/bin/kiosk-session /usr/local/bin/fleet-sb-enroll /usr/local/bin/fleet-hostapi /usr/sbin/fleet-enrol; do { [ -f \"$f\" ] && fapolicyd-cli --file add \"$f\"; } || true; done", " for f in /usr/local/bin/kiosk-session /usr/local/bin/fleet-sb-enroll /usr/local/bin/fleet-hostapi /usr/sbin/fleet-enrol; do { [ -f \"$f\" ] && fapolicyd-cli --file add \"$f\"; } || true; done",
" fapolicyd-cli --update 2>/dev/null || true", " fapolicyd-cli --update 2>/dev/null || true",
" echo '==> fapolicyd: trusted custom binaries (kiosk-session, fleet-sb-enroll, fleet-hostapi) + rebuilt trust db'", " echo '==> fapolicyd: trusted custom binaries (kiosk-session, fleet-sb-enroll, fleet-hostapi) + rebuilt trust db'",
# watch_fs: the diskless root is OVERLAYFS (systemd.volatile=overlay), but the
# shipped watch_fs lacks 'overlay', so fapolicyd marks NO overlay mount and
# therefore intercepts NO exec on / — the exec guard for the writable overlay
# (the whole reason we keep fapolicyd; dm-verity can't cover the tmpfs upper)
# is effectively OFF at runtime. VERIFIED on the bench 2026-07-03: a novel
# non-RPM binary in /tmp (overlayfs) ran freely under `permissive=0`. Add
# 'overlay' so fapolicyd actually watches the root; trust is path-based
# (integrity=none) so overlay paths resolve against the rpmdb trust as normal.
# The #15 tmpfs getattr allow supports this (fapolicyd statfs's the overlay's
# tmpfs upper). Idempotent. BENCH-VERIFY HARD — this turns app-allowlisting ON
# for the WHOLE root at runtime for the first time: boot + kiosk render + admin
# SSH + sudo + TPM enrolment must ALL still work (nothing legit is untrusted),
# a novel /tmp binary must now be BLOCKED (EPERM), and watch for new AVCs.
# enable_stig=false is the escape hatch if it regresses the boot/secure path.
" if [ -f /etc/fapolicyd/fapolicyd.conf ]; then",
" grep -Eq '^watch_fs.*overlay' /etc/fapolicyd/fapolicyd.conf || sed -ri '/^watch_fs *=/ s/$/,overlay/' /etc/fapolicyd/fapolicyd.conf",
" grep -Eq '^watch_fs.*overlay' /etc/fapolicyd/fapolicyd.conf || { echo 'ERROR: failed to add overlay to fapolicyd watch_fs' >&2; exit 1; }",
" echo '==> fapolicyd: watch_fs now includes overlay (guards the volatile-overlay root)'",
" fi",
"fi", "fi",
] ]
} }
+19
View File
@@ -197,6 +197,25 @@ overlay tmpfs) while the trusted kiosk/appliance binaries still run; (c)
hash stability across two builds (same concern as #12/#14). NB: only shipped when hash stability across two builds (same concern as #12/#14). NB: only shipped when
`enable_stig=true`; a non-STIG enforcing build would still see the AVC and the gap. `enable_stig=true`; a non-STIG enforcing build would still see the AVC and the gap.
**UPDATE 2026-07-03 — the getattr allow alone did NOT close the exec gap; the real
enabler is `watch_fs`.** Bench test on `comet-beacon-54`: with the `.te` installed
(AVC gone, `fapolicyd active`, `permissive=0`), a **novel** non-RPM executable in
`/tmp` (hash NOT in the trust db) still **ran** (`exit=0`). Root cause: the diskless
root is **overlayfs** (`stat -f /` → `overlayfs`, `/tmp` too), but the shipped
`watch_fs = ext2,ext3,ext4,tmpfs,xfs,vfat,iso9660,btrfs` has **no `overlay`**.
fapolicyd marks mounts by fs type, so it never marks the overlay = it intercepts
**no exec on `/`** — the whole root, `/usr` and `/tmp` alike. So the exec guard was
effectively **OFF at runtime** on every diskless host. Fix: the 2c-STIG fapolicyd
step now appends **`overlay` to `watch_fs`** in `fapolicyd.conf` (idempotent) so
fapolicyd watches the overlay root; trust is path-based (`integrity=none`) so overlay
paths resolve against the rpmdb trust normally. The `fapolicyd_tmpfs` `.te` above is
still needed — it supports fapolicyd statfs'ing the overlay's tmpfs upper. **This
turns app-allowlisting ON for the whole root at runtime for the first time**, so
**BENCH-VERIFY HARD:** boot + kiosk render + admin SSH + sudo + TPM enrolment all
still work (nothing legit is untrusted — the 4 custom binaries + rpmdb are trusted),
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.
## 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).