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:
@@ -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",
|
||||
" fapolicyd-cli --update 2>/dev/null || true",
|
||||
" 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",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
`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)
|
||||
|
||||
- **SELinux enforcing** (`selinux --enforcing`; labels preserved into the erofs).
|
||||
|
||||
Reference in New Issue
Block a user