stig: ship fapolicyd_tmpfs SELinux module — close overlay exec-guard gap
fapolicyd is the app-allowlisting exec guard for the writable RAM overlay
(dm-verity covers only the read-only erofs lower). It statfs's mounts to decide
which to watch, and tmpfs is in its default watch_fs. This image's root is
systemd.volatile=overlay, so statfs("/") returns the overlay upper — a tmpfs —
and the shipped policy has no 'fapolicyd_t tmpfs_t:filesystem getattr' (no
ordinary host roots on tmpfs). Under enforcing the getattr is DENIED (audit:
dev=tmpfs ino=1 name="/", permissive=0, seen 2026-07-03 on comet-beacon-54),
so fapolicyd can't fully mark the tmpfs mounts (/, /tmp, /dev/shm) and its guard
over the writable overlay is incomplete — a real exec-guard gap, not just SIEM
noise.
Ship a minimal local allow module (allow, not dontaudit — we want fapolicyd to
see the tmpfs), compiled + semodule -i'd in 2c-STIG right after the chrony one.
Registered as deviation #15.
BENCH-VERIFY: the AVC is gone on an enforcing boot AND fapolicyd blocks a non-RPM
executable dropped in /tmp (proves the guard now covers the overlay), trusted
kiosk/appliance binaries still run, and the verity hash is reproducible across
two builds (semodule rebuilds the policy store).
This commit is contained in:
@@ -730,6 +730,21 @@ build {
|
|||||||
"semodule -i /tmp/chrony_dhcp_dispatcher.pp",
|
"semodule -i /tmp/chrony_dhcp_dispatcher.pp",
|
||||||
"rm -f /tmp/chrony_dhcp_dispatcher.mod /tmp/chrony_dhcp_dispatcher.pp",
|
"rm -f /tmp/chrony_dhcp_dispatcher.mod /tmp/chrony_dhcp_dispatcher.pp",
|
||||||
"semodule -l | grep -q '^chrony_dhcp_dispatcher' && echo '==> SELinux module chrony_dhcp_dispatcher installed' || { echo 'ERROR: chrony_dhcp_dispatcher module not installed' >&2; exit 1; }",
|
"semodule -l | grep -q '^chrony_dhcp_dispatcher' && echo '==> SELinux module chrony_dhcp_dispatcher installed' || { echo 'ERROR: chrony_dhcp_dispatcher module not installed' >&2; exit 1; }",
|
||||||
|
# SELinux: allow fapolicyd to statfs (getattr) tmpfs. fapolicyd is the exec
|
||||||
|
# guard for the WRITABLE overlay (dm-verity covers only the RO erofs lower);
|
||||||
|
# it statfs's mounts to decide which to watch (tmpfs is in its default
|
||||||
|
# watch_fs). This host's root is systemd.volatile=overlay, so statfs("/")
|
||||||
|
# lands on the tmpfs upper (audit: dev=tmpfs ino=1 name="/") and the shipped
|
||||||
|
# policy denies it -> fapolicyd can't fully guard the tmpfs mounts (/, /tmp,
|
||||||
|
# /dev/shm) — a real exec-guard gap. Ship a minimal allow (docs/stig-deviations.md).
|
||||||
|
# checkpolicy already ensured above. Same policy-store-rebuild reproducibility
|
||||||
|
# caveat as chrony.
|
||||||
|
"echo '==> supplemental: SELinux module fapolicyd_tmpfs (fapolicyd statfs the volatile-overlay tmpfs)'",
|
||||||
|
"checkmodule -M -m -o /tmp/fapolicyd_tmpfs.mod /tmp/stig/fapolicyd-tmpfs.te",
|
||||||
|
"semodule_package -o /tmp/fapolicyd_tmpfs.pp -m /tmp/fapolicyd_tmpfs.mod",
|
||||||
|
"semodule -i /tmp/fapolicyd_tmpfs.pp",
|
||||||
|
"rm -f /tmp/fapolicyd_tmpfs.mod /tmp/fapolicyd_tmpfs.pp",
|
||||||
|
"semodule -l | grep -q '^fapolicyd_tmpfs' && echo '==> SELinux module fapolicyd_tmpfs installed' || { echo 'ERROR: fapolicyd_tmpfs module not installed' >&2; exit 1; }",
|
||||||
"rm -rf /tmp/stig",
|
"rm -rf /tmp/stig",
|
||||||
"echo '==> STIG remediation complete (report at /root/stig-report.html, ARF at /root/stig-arf.xml)'",
|
"echo '==> STIG remediation complete (report at /root/stig-report.html, ARF at /root/stig-arf.xml)'",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -167,6 +167,36 @@ re-check the verity hash is stable across two builds (same concern as the
|
|||||||
(the module install lives in the STIG-gated provisioner); a non-STIG *enforcing*
|
(the module install lives in the STIG-gated provisioner); a non-STIG *enforcing*
|
||||||
build would still see the AVC.
|
build would still see the AVC.
|
||||||
|
|
||||||
|
## 15. Local SELinux policy module `fapolicyd_tmpfs` (added 2026-07-03)
|
||||||
|
|
||||||
|
Not a STIG-rule waiver — a **minimal local `allow` module** shipped to close an
|
||||||
|
**exec-guard gap** under enforcing SELinux. Source: `files/stig/fapolicyd-tmpfs.te`;
|
||||||
|
built + `semodule -i`'d in the 2c-STIG provisioner (right after #14).
|
||||||
|
|
||||||
|
**The one rule:** `allow fapolicyd_t tmpfs_t:filesystem getattr;`
|
||||||
|
|
||||||
|
**Why:** fapolicyd is the app-allowlisting **exec guard for the writable RAM
|
||||||
|
overlay** — dm-verity covers only the read-only erofs lower, so a binary dropped
|
||||||
|
into the tmpfs overlay upper is exactly what fapolicyd must catch (this is why we
|
||||||
|
KEEP fapolicyd; see "Things that are NOT deviations"). fapolicyd `statfs`'s mounts
|
||||||
|
to decide which to watch, and **tmpfs is in its default `watch_fs`**. This image's
|
||||||
|
root is `systemd.volatile=overlay`, so `statfs("/")` returns the overlay **upper**
|
||||||
|
— a tmpfs (audit: `dev=tmpfs ino=1 name="/"`, `fapolicyd_t → tmpfs_t:filesystem
|
||||||
|
getattr`, `permissive=0`, seen 2026-07-03). The shipped policy has no such rule
|
||||||
|
(no ordinary host has its root on tmpfs), so under enforcing the `getattr` is
|
||||||
|
**denied**, fapolicyd cannot fully mark the tmpfs mounts (`/`, `/tmp`, `/dev/shm`),
|
||||||
|
and its guard over the writable overlay is **incomplete** — a real gap, not just
|
||||||
|
SIEM noise. An **`allow`** (not `dontaudit`): we *want* fapolicyd to see the tmpfs.
|
||||||
|
|
||||||
|
**Scope:** one perm (`getattr`) on one class (`filesystem`), two well-known types.
|
||||||
|
Grants no file read/write, only the `statfs` fapolicyd needs to classify + watch
|
||||||
|
the mount. **BENCH-VERIFY:** (a) the AVC is gone on an enforcing boot; (b) fapolicyd
|
||||||
|
**blocks a non-RPM executable dropped in `/tmp`** (proves the guard now covers the
|
||||||
|
overlay tmpfs) while the trusted kiosk/appliance binaries still run; (c)
|
||||||
|
**reproducibility** — `semodule -i` rebuilds the policy store, so re-check verity
|
||||||
|
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.
|
||||||
|
|
||||||
## 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).
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Raw module-policy syntax (compiled with `checkmodule -M -m`), NOT the refpolicy
|
||||||
|
# `policy_module()` m4 macro — that macro is only expanded by the refpolicy
|
||||||
|
# Makefile and checkmodule rejects it. See chrony-dhcp-dispatcher.te for the why.
|
||||||
|
module fapolicyd_tmpfs 1.0.0;
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Let fapolicyd statfs (getattr) tmpfs filesystems.
|
||||||
|
#
|
||||||
|
# fapolicyd is the app-allowlisting EXEC GUARD for the writable RAM overlay:
|
||||||
|
# dm-verity covers only the read-only erofs lower, so an executable dropped into
|
||||||
|
# the tmpfs overlay upper is exactly what fapolicyd must catch. fapolicyd statfs's
|
||||||
|
# mounts to decide which to watch, and tmpfs is in its default watch_fs. This
|
||||||
|
# host's root is `systemd.volatile=overlay`, so statfs("/") returns the overlay
|
||||||
|
# UPPER — a tmpfs (the audit shows dev=tmpfs ino=1, name="/") — and the shipped
|
||||||
|
# policy has no `fapolicyd_t tmpfs_t:filesystem getattr` rule (no normal host has
|
||||||
|
# its root on tmpfs). Result under enforcing: the getattr is DENIED, fapolicyd
|
||||||
|
# cannot fully mark the tmpfs mounts (/, /tmp, /dev/shm), and its guard over the
|
||||||
|
# writable overlay is incomplete — a real exec-guard gap, not just SIEM noise.
|
||||||
|
# This single allow closes it. Minimal + auditable; justified in
|
||||||
|
# docs/stig-deviations.md. BENCH-VERIFY: after this, fapolicyd blocks a non-RPM
|
||||||
|
# executable dropped in /tmp (the overlay). If a follow-on fapolicyd AVC on the
|
||||||
|
# overlay appears on the bench, extend here.
|
||||||
|
########################################
|
||||||
|
|
||||||
|
require {
|
||||||
|
type fapolicyd_t;
|
||||||
|
type tmpfs_t;
|
||||||
|
class filesystem getattr;
|
||||||
|
}
|
||||||
|
|
||||||
|
allow fapolicyd_t tmpfs_t:filesystem getattr;
|
||||||
Reference in New Issue
Block a user