Debian GNU/Linux · Filesystem & permissions

Block kernel modules for filesystems the system does not use

When it fails: The kernel can load modules for rarely used filesystems. Any user who can attach a disk image or removable media can make the kernel parse it with old, rarely audited filesystem code.

CB-DEB-FS-001lowactiveautomatedautomated fixprofile core

As released in v0.1.0 beta · pre-release. Current state.

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
DefaultEvery module shipped by the kernel can be loaded
Recommendedinstall /bin/false and blacklist for: {{ modules }}

Parameters

ParameterCoreServerServer hardenedDesktopDesktop hardenedBounds
moduleslist. Kernel modules to block, as named by modprobe.cramfs, freevxfs, hfs, hfsplus, jffs2cramfs, freevxfs, hfs, hfsplus, jffs2cramfs, freevxfs, hfs, hfsplus, jffs2, overlay, squashfs, udfcramfs, freevxfs, hfs, hfsplus, jffs2cramfs, 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 }}:

  1. modprobe --showconfig | grep -E '^(install|blacklist) <module>' must show install <module> /bin/false and blacklist <module> (dashes appear as underscores), or find /lib/modules -name '<module>.ko*' must find nothing.
  2. grep '^<module> ' /proc/modules must 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

  1. Create /etc/modprobe.d/60-cirsec-filesystems.conf with, for each module of {{ modules }}:
    install <module> /bin/false
    blacklist <module>
    
  2. 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.
Restartnone
Disruptionlow

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

Mappings

FrameworkIDRelationNote
NIST SP 800-53 Rev. 55.1.1CM-7related

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

  • related CB-DEB-FS-002 Block USB mass storage and FireWire kernel modules

People and lifecycle

Authors
Verified2026-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.
Verified2026-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 inv0.1.0
Created, updated2026-10-18, 2026-10-18
Translationsfr