Handbook sections

Hardening kernel modules

Last reviewed

Every loadable module is kernel-mode code waiting to run. Disable the unused filesystems and network protocols so a stray socket or a crafted disk image can't drag them in.

The threat: dormant kernel code that auto-loads

The Linux kernel ships hundreds of drivers as loadable modules, and most can be pulled in on demand, automatically, without any administrator action. Open a socket for an exotic protocol and modprobe loads the handler; insert a USB stick and the matching filesystem driver mounts it. Each module is code running with full kernel privileges, so a parsing bug in a driver you never use is still a path to privilege escalation or a kernel crash.

The usual suspects are obsolete or niche filesystems (cramfs, udf, squashfs, freevxfs, hfs, hfsplus, jffs2), auto-loaded when a crafted image is mounted, and rarely-used network protocols (dccp, sctp, rds, tipc) that are remotely reachable and have a track record of serious flaws (e.g. dccp's CVE-2017-6074 use-after-free giving local root). Left enabled, they sit as latent attack surface no firewall rule can see.

Why harden it

This is dormant code you don't need and can't watch. You won't notice sctp is loadable until an exploit needs it, and then it loads silently the instant an attacker opens the right socket. Disabling a module removes the code path entirely: there is nothing left to trigger. It is the cleanest form of attack-surface reduction (CIS least functionality, NIST CM-7, ANSSI BP-028 R10): you don't patch the bug, you delete the door.

The trap: blacklist is not enough

A blacklist <mod> line in /etc/modprobe.d/ only stops automatic loading by alias. The module can still be loaded explicitly (modprobe <mod>) or pulled in as a dependency. To truly disable it, redirect it to a no-op so any load request fails:

# /etc/modprobe.d/00-hardening.conf
install dccp /bin/false
blacklist dccp

install <mod> /bin/false runs /bin/false instead of inserting the module, and the blacklist line covers dependency auto-load. Both are needed.

What Pavois audits: the effective module state

Pavois audits the effective module state, not a single .conf file. Each rule uses the InSpec kernel_module('<name>') resource to assert the module is not loaded (read from the live kernel) and is disabled (read from the resolved modprobe policy via modprobe --showconfig). That dual check catches the realistic failures a file scan misses: a module blacklisted in one drop-in yet still resident in the running kernel, or one re-enabled by an override elsewhere in /etc/modprobe.d/. The rule set spans filesystems (cramfs, udf, squashfs, freevxfs, hfs, hfsplus, jffs2, overlayfs), network protocols (dccp, sctp, rds, tipc, atm, can), and hardware/driver stacks (usb-storage, firewire-core, bluetooth, the wireless stack, uvcvideo).

How do you verify and operate?

  • Is it loaded right now? lsmod | grep <mod>.
  • Will a load attempt be refused? modprobe -n -v <mod> does a dry run; on a disabled module it prints install /bin/false, proving the policy is effective.
  • What is the resolved policy? modprobe --showconfig | grep <mod> shows the merged result of every /etc/modprobe.d/ drop-in, which is what Pavois reads.
  • Persistence: modprobe.d covers on-demand loading, but a module needed early in boot lives in the initramfs (rebuild with update-initramfs -u / dracut -f), and module_blacklist=<mod> on the kernel command line blocks it even earlier.
  • Lock all loading: sysctl kernel.modules_disabled=1 freezes the module set, root included, until the next reboot. Pair it with signed-module enforcement and the lockdown LSM on a fully hardened host.

Pitfalls

  • Do not disable a module the workload uses (a storage/network/filesystem driver the server actually needs). Inventory with lsmod first.
  • blacklist alone leaves an explicit-load path open: always add install <mod> /bin/false.
  • An already-resident module survives the config change until it is unloaded or the host reboots; Pavois marks these remediations reboot_required.
  • Cloud and virtual machines need some modules (virtio, the hypervisor's storage/net): blacklist by role, not blindly.

FAQ

Why both install /bin/false and blacklist? blacklist blocks auto-load by alias; install /bin/false blocks explicit modprobe and dependency loads. Together they make the module truly unloadable.

Why does Pavois check both loaded and disabled? Because a module can be blacklisted on disk yet still resident in the running kernel; only the live kernel plus the resolved policy tell the truth.

How do I block all module loading? kernel.modules_disabled=1 (sysctl) until reboot, root included; combine with signed modules and lockdown.