Description
The kernel loads a filesystem module on demand, the first time something mounts a filesystem
of that type. This control blocks the modules listed in {{ modules }} for good: each one gets
an install <module> /bin/false rule, so any load attempt fails, and a blacklist rule, so it
is never picked by hardware aliases. A module that no installed kernel ships is compliant.
The core list holds filesystems that have no use on an Ubuntu server or workstation (cramfs, freevxfs, hfs, hfsplus, jffs2). The hardened profiles add overlay, squashfs and udf.
squashfs is always left alone when snapd is installed: every snap is a squashfs image, and
Ubuntu ships parts of the system (LXD on servers, Firefox and the App Center on desktops) as
snaps. Blocking it would stop every snap from mounting.
On Ubuntu 20.04 to 24.04, the linux-modules package of the generic kernel does not ship cramfs,
freevxfs, hfs, hfsplus or jffs2 (they come with linux-modules-extra), so the core list passes
there without any configuration. Ubuntu 26.04 ships them in linux-modules, and firewire too.
Ubuntu kernels build squashfs into the kernel image: modprobe rules cannot block a built-in
driver. The check reports such a module as built in and does not count it as a failure, since
only a rebuilt kernel could remove it. To really drop squashfs, remove snapd (if nothing uses
snaps) and use a kernel that builds it as a module.
Review the other filesystem modules the same way: ls /lib/modules/$(uname -r)/kernel/fs
lists what the kernel can load, and the ones the system never mounts can be added to
{{ modules }}.
Rationale
Filesystem drivers parse complex on-disk structures. Rarely used ones get little review, and crafted images have repeatedly led to kernel memory corruption and privilege escalation. Desktop automounters, udisks and loop devices let an unprivileged user reach this code with a USB stick or a downloaded image. A module that cannot be loaded removes that attack surface.
Impact
Media using a blocked filesystem can no longer be mounted. In the hardened profiles this
includes overlay (Docker, Podman, LXD and other container engines), squashfs (live images;
snaps are protected, see above) and udf (DVDs and some USB media). Remove a module from
{{ modules }} in the tailoring file when the system needs it.
Setting
| Location | /etc/modprobe.d/60-cirsec-filesystems.conf |
| Default | Every module shipped by the kernel can be loaded |
| Recommended | install /bin/false and blacklist for: {{ modules }} |
Parameters
| Parameter | Core | Server | Server hardened | Desktop | Desktop hardened | Bounds |
|---|---|---|---|---|---|---|
moduleslist. Kernel modules to block, as named by modprobe. | cramfs, freevxfs, hfs, hfsplus, jffs2 | cramfs, freevxfs, hfs, hfsplus, jffs2 | cramfs, freevxfs, hfs, hfsplus, jffs2, overlay, squashfs, udf | cramfs, freevxfs, hfs, hfsplus, jffs2 | cramfs, freevxfs, hfs, hfsplus, jffs2, overlay, squashfs, udf |
The value used is your override (tailoring file), else the profile's, else the one of the profile it extends, else the default. Bounds limit what an override may set.
Audit
For each module of {{ modules }} (except squashfs when dpkg -l snapd shows it installed):
modprobe --showconfig | grep -E '^(install|blacklist) <module>'must showinstall <module> /bin/falseandblacklist <module>(dashes appear as underscores), orfind /lib/modules -name '<module>.ko*'must find nothing. A module listed in/lib/modules/*/modules.builtinis built into the kernel and cannot be blocked: note it.grep '^<module> ' /proc/modulesmust find nothing.
Automated: every check must pass. Scanners report each check by its id.
modules
Each listed module is blocked in the modprobe configuration, or not shipped, and not loaded.
script: |
cfg=$(modprobe --showconfig 2>/dev/null) || exit 3
# Every snap is a squashfs image: never block it while snapd is installed.
skip=
dpkg-query -W -f='${Status}' snapd 2>/dev/null | grep -q 'ok installed' && skip=squashfs
rc=0
for m in $CB_MODULES; do
n=$(printf '%s' "$m" | tr - _)
if [ "$m" = "$skip" ]; then
echo "$m: kept, snapd is installed"; continue
fi
if grep -q "^$n " /proc/modules; then
echo "$m: loaded"; rc=1; continue
fi
if grep -qs "/$n.ko\$\|/$m.ko\$" /lib/modules/*/modules.builtin; then
echo "$m: built into the kernel, modprobe cannot block it"
elif printf '%s\n' "$cfg" | grep -Eq "^install $n (/usr)?/bin/(false|true)( |\$)" &&
printf '%s\n' "$cfg" | grep -qx "blacklist $n"; then
echo "$m: blocked"
elif [ -z "$(find /lib/modules/ \( -name "$m.ko*" -o -name "$n.ko*" \) 2>/dev/null | head -n 1)" ]; then
echo "$m: not shipped by any installed kernel"
else
echo "$m: can be loaded"; rc=1
fi
done
exit $rc
Remediation
- Create
/etc/modprobe.d/60-cirsec-filesystems.confwith, for each module of{{ modules }}(leave outsquashfswhen snapd is installed):install <module> /bin/false blacklist <module> - Unload the modules that are loaded with
modprobe -r <module>. A module that is in use (mounted filesystem) stays loaded until it is unmounted or the system reboots.
| Restart | none |
| Disruption | low |
Rollback
Delete /etc/modprobe.d/60-cirsec-filesystems.conf. The modules load again on demand.
modprobe-conf
f=/etc/modprobe.d/60-cirsec-filesystems.conf
skip=
dpkg-query -W -f='${Status}' snapd 2>/dev/null | grep -q 'ok installed' && skip=squashfs
cb_backup "$f"
{
echo '# Written by CIRSEC Baselines CB-UBU-FS-001'
for m in $CB_MODULES; do
[ "$m" = "$skip" ] || printf 'install %s /bin/false\nblacklist %s\n' "$m" "$m"
done
} >"$f"
chmod 644 "$f"
rc=0
for m in $CB_MODULES; do
[ "$m" = "$skip" ] && continue
n=$(printf '%s' "$m" | tr - _)
grep -q "^$n " /proc/modules || continue
modprobe -r "$n" || { echo "$m is in use, it stays loaded until reboot" >&2; rc=1; }
done
exit $rc
Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.
References
- modprobe.d(5), Ubuntu manual pages Canonical, vendor-doc, accessed 2026-10-19
- modprobe(8), Ubuntu manual pages Canonical, vendor-doc, accessed 2026-10-19
- The snap format Canonical, vendor-doc, accessed 2026-10-19
Mappings
| Framework | ID | Relation | Note |
|---|---|---|---|
| NIST SP 800-53 Rev. 55.1.1 | CM-7 | related |
How this control relates to the other framework's: equivalent, stricter, looser, partial or related.
Threats mitigated
Related controls
- related
CB-UBU-FS-002Block USB mass storage and FireWire kernel modules
People and lifecycle
| Authors |
|
| Verified | 2026-10-19 by Sn0wAlice on 20.04, Proxmox VM 2520, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic. Core list passes unconfigured (modules not shipped by linux-modules); hardened list fails on overlay and udf, squashfs kept because snapd is installed; fix blocks them, modprobe then refuses them; kept after reboot; snaps (LXD on 20.04/22.04, hello-world) still install and run. |
| Verified | 2026-10-19 by Sn0wAlice on 22.04, Proxmox VM 2522, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic. Core list passes unconfigured (modules not shipped by linux-modules); hardened list fails on overlay and udf, squashfs kept because snapd is installed; fix blocks them, modprobe then refuses them; kept after reboot; snaps (LXD on 20.04/22.04, hello-world) still install and run. |
| Verified | 2026-10-19 by Sn0wAlice on 24.04, Proxmox VM 2524, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic. Core list passes unconfigured (modules not shipped by linux-modules); hardened list fails on overlay and udf, squashfs kept because snapd is installed; fix blocks them, modprobe then refuses them; kept after reboot; snaps (LXD on 20.04/22.04, hello-world) still install and run. |
| Verified | 2026-10-19 by Sn0wAlice on 26.04, Proxmox VM 2526, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic. Core list fails on fresh install (cramfs, freevxfs, hfs, hfsplus, jffs2 shipped by linux-modules); fix blocks them, modprobe then refuses cramfs; hardened adds overlay and udf, squashfs kept because snapd is installed; kept after reboot; a snap still installs and runs. With snapd purged, squashfs is reported as built into the kernel. |
| Introduced in | v0.1.0 |
| Created, updated | 2026-10-19, 2026-10-19 |
| Translations | fr |