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 a Debian server or workstation (cramfs, freevxfs, hfs, hfsplus, jffs2). The hardened profiles add overlay, squashfs and udf.
Debian cloud kernels do not ship cramfs, freevxfs, hfs, hfsplus or jffs2, so the core list
passes there without any configuration. The generic kernels (linux-image-amd64) ship them.
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 and other container engines), squashfs (snap packages,
live images) 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 }}:
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.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
rc=0
for m in $CB_MODULES; do
n=$(printf '%s' "$m" | tr - _)
if grep -q "^$n " /proc/modules; then
echo "$m: loaded"; rc=1; continue
fi
if 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)" ] &&
! grep -qs "/$n.ko\$\|/$m.ko\$" /lib/modules/*/modules.builtin; 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 }}: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
cb_backup "$f"
{
echo '# Written by CIRSEC Baselines CB-DEB-FS-001'
for m in $CB_MODULES; do printf 'install %s /bin/false\nblacklist %s\n' "$m" "$m"; done
} >"$f"
chmod 644 "$f"
rc=0
for m in $CB_MODULES; do
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), Debian manual pages Debian, vendor-doc, accessed 2026-10-18
- modprobe(8), Debian manual pages Debian, vendor-doc, accessed 2026-10-18
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-DEB-FS-002Block USB mass storage and FireWire kernel modules
People and lifecycle
| Authors |
|
| Verified | 2026-10-18 by Sn0wAlice on 12, Proxmox VM 922, Debian 12 genericcloud image, kernel 6.1.0-54-cloud-amd64. Core list passes unconfigured (modules not shipped by the cloud kernel); hardened list fails on overlay, squashfs and udf, fix blocks them, unloads a loaded udf, modprobe then refuses them; kept after reboot. |
| Verified | 2026-10-18 by Sn0wAlice on 13, Proxmox VM 932, Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64. Core list passes unconfigured (modules not shipped by the cloud kernel); hardened list fails on overlay, squashfs and udf, fix blocks them, unloads a loaded udf, modprobe then refuses them; kept after reboot. |
| Introduced in | v0.1.0 |
| Created, updated | 2026-10-18, 2026-10-18 |
| Translations | fr |