Bug 2541665 - Review Request: modulejail - Shrink Linux kernel-module attack surface
Summary: Review Request: modulejail - Shrink Linux kernel-module attack surface
Keywords:
Status: ASSIGNED
Alias: None
Product: Fedora
Classification: Fedora
Component: Package Review
Version: rawhide
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Jeremy Cline
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-09-25 21:37 UTC by Justin M. Forbes
Modified: 2026-11-03 23:49 UTC (History)
4 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Type: ---
Embargoed:


Attachments (Terms of Use)

Description Justin M. Forbes 2026-09-25 21:37:17 UTC
Spec URL: https://jforbes.fedorapeople.org/modulejail/modulejail.spec
SRPM URL: https://jforbes.fedorapeople.org/modulejail/modulejail-1.6.2-1.fc46.src.rpm

Description: 
ModuleJail snapshots the set of currently loaded modules and writes a
modprobe.d blacklist for every kernel module not currently in use,
minus a built-in baseline and an optional sysadmin-supplied whitelist.
  
Aimed at Linux fleet operators who need to harden many servers against
the wave of AI-assisted kernel privilege-escalation discoveries. Every
additional loaded module is additional latent attack surface for the
next disclosed CVE.

No daemon, no continuous monitoring, no AI inside the tool. One shell
script, run once on a steady-state host, that writes one modprobe.d
blacklist file.

Fedora Account System Username: jforbes

Comment 1 Justin M. Forbes 2026-09-25 22:00:16 UTC
https://koji.fedoraproject.org/koji/taskinfo?taskID=150604967 scratch build

Comment 2 Jasper Nuyens 2026-10-11 07:43:00 UTC
Hi, upstream author here. Thanks Justin for packaging this.

Some context for whoever picks up the review:

* Debian already ships it. 1.6.1-1 is in unstable and testing (ITP #1138266).

* I'm happy to change things upstream if it makes the Fedora package cleaner:

  - %post runs "modulejail --install-initramfs-hook", which writes /usr/lib/dracut/modules.d/99modulejail-strip/ at install time. The hook keeps the blacklist out of the initramfs. Otherwise dracut bakes the current blacklist into the initramfs on a kernel update, and if the new kernel renames a storage driver the machine could fail to boot (upstream issue #19). Dropping it from the initrd costs no security, since no unprivileged users exist before pivot_root. Right now no package owns those files. I can add a way to generate the hook at build time so the spec ships it in %files instead.

  - The script runs an update check against api.github.com after each run (MODULEJAIL_NO_UPDATE_CHECK turns it off). I can add a build-time switch so distro packages ship with the check disabled.

* Source0 can point at https://github.com/jnuyens/modulejail/archive/v%{version}/%{name}-%{version}.tar.gz

If anything else comes up, reply here or open an issue on GitHub and I'll respond quickly.

Jasper

Comment 3 Jasper Nuyens 2026-10-20 23:18:02 UTC
Hi Justin,

Both changes are in 1.6.3:

1. Package the dracut hook and drop %post/%preun:

  %install
  ./modulejail --install-initramfs-hook --initramfs-builder dracut --destdir %{buildroot}

  %files
  %dir %{_prefix}/lib/dracut/modules.d/99modulejail-strip
  %{_prefix}/lib/dracut/modules.d/99modulejail-strip/module-setup.sh

2. Disable the GitHub update check:

  sed -i 's/^UPDATE_CHECK_DEFAULT=1$/UPDATE_CHECK_DEFAULT=0/' %{buildroot}%{_bindir}/modulejail

Tested on Fedora 45: rpm -V is clean, an upgrade from 1.6.2 takes over the old hook file, removal deletes it, and the hook strips the blacklist from hostonly images.

Source0: https://github.com/jnuyens/modulejail/archive/v1.6.3/modulejail-1.6.3.tar.gz

Thanks,
Jasper

Comment 4 Justin M. Forbes 2026-10-27 14:48:12 UTC
Package was updated:
SRPM URL: https://jforbes.fedorapeople.org/modulejail/modulejail-1.6.3-1.fc46.src.rpm
Scratch build: https://koji.fedoraproject.org/koji/taskinfo?taskID=151723906
spec URL doesn't change.

Comment 5 Jeremy Cline 2026-11-03 15:37:02 UTC
For lack of a better review tool, I've posted my suggestions as a diff below with notes inline. For convenience, the full spec is at https://jcline.fedorapeople.org/new-packages/modulejail.spec and you can fetch all the sources with "spectool -g modulejail.spec". None of these are particularly deal-breaking, so if there's something you don't like that's fine.

 Name:           modulejail
 Version:        1.6.3
-Release:        1%{?dist}
+Release:        %{autorelease}

I'm lazy and don't care to manually bump the release, this should be paired with %{autochangelog}

 Summary:        Shrink Linux kernel-module attack surface
 
 License:        GPL-3.0-only
 URL:            https://modulejail.com
-Source0:        %{name}-%{version}.tar.gz
+Source0:        https://github.com/jnuyens/modulejail/archive/v%{version}/%{name}-v%{version}.tar.gz

This way you can bump the version and run spectool -g on the specfile to fetch the new sources

+Source1:        https://github.com/jnuyens/modulejail/releases/download/v%{version}/v%{version}.tar.gz.sig
+# The repository documents the signing key ID is 095F 5C8B 39AF 010E 7B61  5CD4 487B C00D 69C2 A955
+#
+# At the time I fetched this, the following signing key was present:
+#
+# sq inspect --cert=095F5C8B39AF010E7B615CD4487BC00D69C2A955 --keyring ./jnuyens.gpg
+#  OpenPGP Certificate.
+#
+#        Fingerprint: 095F5C8B39AF010E7B615CD4487BC00D69C2A955
+#    Public-key algo: RSA
+#    Public-key size: 4096 bits
+#      Creation time: 2026-05-24 19:26:07 UTC
+#          Key flags: certification, signing
+Source2:        https://github.com/jnuyens.gpg

Upstream does sign their releases, so pulling in the signature and signing key lets us check that.
You can check in the keyfile rather than uploading it as a source if you'd prefer, but either way
you'll see if it changes during the normal update workflow.
 
 BuildArch:      noarch
+BuildRequires:  openpgpverify
 Requires:       coreutils
 Requires:       findutils
 Requires:       gawk
 Recommends:     curl
 
-%description
+%global _description %{expand:
 ModuleJail snapshots the set of currently loaded modules and writes a
 modprobe.d blacklist for every kernel module not currently in use,
 minus a built-in baseline and an optional sysadmin-supplied whitelist.
@@ -25,90 +40,39 @@
 
 No daemon, no continuous monitoring, no AI inside the tool. One shell
 script, run once on a steady-state host, that writes one modprobe.d
-blacklist file.
+blacklist file.}
+
+%description %{_description}
+

This is a silly hack to handle the description macro needing to either be one line or escaping the line breaks
 
 %prep
-%setup -q
+%{openpgpverify} --keyring='%{SOURCE2}' --signature='%{SOURCE1}' --data='%{SOURCE0}'
+%autosetup -n %{name}-%{version} -p1
+

Verify the signature and use autosetup - if you drop in "Patch:   some.patch" at the top it'll apply them which is convenient.

+
+%build
+sed -i 's/__VERSION__/%{version}/' man/modulejail.8.in
+sed -i "s/__DATE__/$(date -u -d @${SOURCE_DATE_EPOCH} +%Y-%m-%d)/" man/modulejail.8.in
+# Updates are shipped via dnf
+sed -i 's/^UPDATE_CHECK_DEFAULT=1$/UPDATE_CHECK_DEFAULT=0/' modulejail
+

Disable updates and set the version and date in the manpage. This seems to be all the build script upstream does.
 
 %install
 install -Dm755 modulejail   %{buildroot}%{_bindir}/modulejail
-install -Dm644 README.md    %{buildroot}%{_docdir}/%{name}/README.md
-install -Dm644 LICENSE      %{buildroot}%{_docdir}/%{name}/LICENSE
-install -Dm644 modulejail.8 %{buildroot}%{_mandir}/man8/modulejail.8
+install -Dm644 man/modulejail.8.in %{buildroot}%{_mandir}/man8/modulejail.8

These days you don't need to manually install files referenced in %doc or %license, they're handled automagically.

 # Stage the dracut strip hook so the package owns it (see gh #19 and
 # docs/DEFENSE-IN-DEPTH.md). It keeps the blacklist out of rebuilt
 # initramfs images; dracut picks it up on the next kernel install.
 ./modulejail --install-initramfs-hook --initramfs-builder dracut --destdir %{buildroot}
-# Distro repositories ship updates through dnf, so a distro build would
-# also turn off the post-run GitHub update check here:
-#   sed -i 's/^UPDATE_CHECK_DEFAULT=1$/UPDATE_CHECK_DEFAULT=0/' %%{buildroot}%%{_bindir}/modulejail
-# This upstream package is installed from GitHub releases, so it keeps it.
+
 
 %files
+%license LICENSE
+%doc README.md
 %{_bindir}/modulejail
-%dir %{_docdir}/%{name}
-%doc %{_docdir}/%{name}/README.md
-%license %{_docdir}/%{name}/LICENSE
 %{_mandir}/man8/modulejail.8*
 %dir %{_prefix}/lib/dracut/modules.d/99modulejail-strip
 %{_prefix}/lib/dracut/modules.d/99modulejail-strip/module-setup.sh
 
 %changelog
-* Tue Oct 27 2026 Justin M. Forbes <jforbes> - 1.6.3-1
-- Initial Fedora packaging
-
-* Mon May 18 2026 Jasper Nuyens <jnuyens> - 1.6.3-1
-- Add --whitelist-file PATH flag (closes GitHub Issue #2). Reads a
-  site-local whitelist file (one module per line, # comments, blank
-  lines ignored), validates each line against [a-zA-Z0-9_-]+, refuses
-  group- or world-writable files, and appends valid names to the
-  in-script WHITELIST.  Operators no longer lose site-local additions
-  on .deb/.rpm/curl|sh reinstalls.
-- Add --no-syslog-logging flag.  Default behaviour change: when
-  /usr/bin/logger is executable on the host, generated install lines
-  now call logger with tag 'modulejail' so blocked module loads are
-  visible via 'journalctl -t modulejail' or 'grep modulejail
-  /var/log/syslog'.  --no-syslog-logging restores the v1.1.4
-  /bin/true install-line body byte-for-byte.
-- Add archived v1.1.4 regression fixture under
-  tests/fixtures/v1.1.4-regression/, exercising the body-identical
-  invariant as a permanent baseline.
-
-* Wed May 13 2026 Jasper Nuyens <jnuyens> - 1.1.4-1
-- Add project logo to README.
-- Test infra: update container fixture to be version-agnostic (was
-  hardcoded to 1.0.0 strings) and add four new assertions covering the
-  v1.1.x update-check surface, including a static regression guard
-  against the v1.1.2 busybox-wget bug.
-
-* Wed May 13 2026 Jasper Nuyens <jnuyens> - 1.1.3-1
-- Update check now works on Alpine / busybox wget. The wget invocation
-  used GNU long-form flags (--quiet, --max-redirect=5,
-  --output-document=-) that busybox wget rejects, causing the check to
-  silently exit non-zero on every Alpine host. Switched to the
-  universal short-flag subset (-q, -T 10, -O -) and dropped
-  --max-redirect (the GitHub tags API does not redirect). No other
-  changes.
-
-* Wed May 13 2026 Jasper Nuyens <jnuyens> - 1.1.2-1
-- Adds modulejail(8) manpage; ships at /usr/share/man/man8/modulejail.8.gz.
-
-* Tue May 12 2026 Jasper Nuyens <jnuyens> - 1.1.1-1
-- Documentation: swap the order of "Why?" and "What ModuleJail is" in
-  the README so the motivation leads.
-- Packaging: drop the per-distro %dist suffix from the RPM filename
-  (was modulejail-X.Y.Z-1.el9.noarch.rpm, now
-  modulejail-X.Y.Z-1.noarch.rpm). ModuleJail is a noarch shell script
-  with no per-major-RHEL semantics.
-
-* Tue May 12 2026 Jasper Nuyens <jnuyens> - 1.1.0-1
-- Adds .deb and .rpm packaging.
-- Adds optional post-run check for a newer release on GitHub: silent on
-  any failure, 10-second hard timeout, only complains when reachable
-  and a newer tag exists. Honours MODULEJAIL_NO_UPDATE_CHECK.
-
-* Tue May 12 2026 Jasper Nuyens <jnuyens> - 1.0.1-1
-- Documentation cleanup release.
-
-* Tue May 12 2026 Jasper Nuyens <jnuyens> - 1.0.0-1
-- Initial release.
+%{autochangelog}

If you use this, the changelog is pulled from the git commit. To save this existing log, write it to a "changelog" file in the repository (https://jcline.fedorapeople.org/new-packages/modulejail-1.6.3-1.fc46.src.rpm has it as an example).


Note You need to log in before you can comment on or make changes to this bug.