Skip to content

fix(guest-image): stage /sbin/modprobe so in-guest egress arm autoloads netfilter (RIG-3028) - #759

Merged
mattwilkinsonn merged 2 commits into
mainfrom
compass-runner/rig-3028-guest-modprobe-autoload
Aug 31, 2026
Merged

fix(guest-image): stage /sbin/modprobe so in-guest egress arm autoloads netfilter (RIG-3028)#759
mattwilkinsonn merged 2 commits into
mainfrom
compass-runner/rig-3028-guest-modprobe-autoload

Conversation

@rigel-mintaka

Copy link
Copy Markdown
Contributor

The V3 in-guest egress arm (W1, #754) runs nft as guest root, whose first
NETLINK_NETFILTER socket open requires the kernel to autoload nfnetlink /
nf_tables on demand. The guest has no udev / systemd-modules-load (guestd is
PID 1, record §(d)), so on-demand loading rides entirely on the kernel's
request_module() usermode-helper path — which execs the binary named by
/proc/sys/kernel/modprobe (default /sbin/modprobe, CONFIG_MODPROBE_PATH unset in
the pinned kernel). Nothing staged that binary, so request_module was a silent
no-op: the arm failed mnl.c:66: Unable to initialize Netlink socket: Protocol not supported (EPROTONOSUPPORT), and under §(e) always-arm this reddened every
microVM Start. The shipped /lib/modules tree was necessary but not sufficient —
the false OQ-3 autoload assumption CI surfaced on the W2 microvm leg.

Fix: symlink /sbin/modprobe to kmod's modprobe in the rootfs (kmod, not busybox
— modules are .ko.xz and CONFIG_MODULE_DECOMPRESS is unset, so the helper must
decompress in userspace, the same reason the initrd already uses kmod). With the
helper staged, any module the guest asks for autoloads on demand from the shipped
tree via its depmod alias/dep metadata. This is the general request_module
mechanism, not a fixed preload, so it also covers egress rulesets beyond the base
one: a future user-defined rule pulling a new nft expression module autoloads
with no guest-image change.

Adds a KVM-gated red-green guard, TestInGuestEgressArmAutoloadsNetfilter: it
boots a real guest and Provisions a non-empty nft_script (table + chain + ct state rule under set -eu), so Provision succeeding proves the whole chain
(nfnetlink, nf_tables, nf_conntrack, nft_ct) autoloaded. Verified red on a
post-W1/pre-fix rootfs (reproduces mnl.c:66 EPROTONOSUPPORT) and green with the
fix.

Refs RIG-3028

Co-authored-by: Matt Wilkinson matt@rigel.build

@linear-code

linear-code Bot commented Aug 30, 2026

Copy link
Copy Markdown

RIG-3028

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown

Compass engineering docs preview: https://compass-runner-rig-3028-gues.compass-eng-docs.pages.dev

Deployed from compass-runner/rig-3028-guest-modprobe-autoload at 0e4b215.

rigel-mintaka added a commit that referenced this pull request Aug 30, 2026
…t -f (RIG-3028)

Review-round low findings on PR #759, all comment/precision (no behavior change):

- boot_microvm_test.go: soften the armRuleset doc-comment — it proves a
  representative slice of the netfilter autoload chain (nfnetlink, nf_tables,
  nf_conntrack, nft_ct), not "exactly" every module the base ruleset needs (the
  base ruleset's interval sets + DNS rules pull further modules the same way via
  request_module).
- boot_microvm_test.go: note the 30s Provision context is a deliberately generous
  CLIENT bound; the real ceiling is the server's 120s armTimeout.
- guest-image/default.nix: reword the modprobe symlink comment to attribute
  closure inclusion to the ${pkgs.kmod} reference, not the ln flag; drop the inert
  -f (no prior modprobe name exists to overwrite) to match the sibling `ln -s`
  for /sbin/init and /lib/modules.

Re-verified GREEN on real KVM after the change (rootfs still packs
/sbin/modprobe -> kmod; TestInGuestEgressArmAutoloadsNetfilter passes).

Refs RIG-3028

Co-authored-by: Matt Wilkinson <matt@rigel.build>
rigel-mintaka and others added 2 commits August 30, 2026 21:47
…ds netfilter (RIG-3028)

The V3 in-guest egress arm (W1, #754) runs `nft` as guest root, whose first
NETLINK_NETFILTER socket open requires the kernel to autoload nfnetlink /
nf_tables on demand. The guest has no udev / systemd-modules-load (guestd is
PID 1, record §(d)), so on-demand loading rides entirely on the kernel's
request_module() usermode-helper path — which execs the binary named by
/proc/sys/kernel/modprobe (default /sbin/modprobe, CONFIG_MODPROBE_PATH unset in
the pinned kernel). Nothing staged that binary, so request_module was a silent
no-op: the arm failed `mnl.c:66: Unable to initialize Netlink socket: Protocol
not supported` (EPROTONOSUPPORT), and under §(e) always-arm this reddened every
microVM Start. The shipped /lib/modules tree was necessary but not sufficient —
the false OQ-3 autoload assumption CI surfaced on the W2 microvm leg.

Fix: symlink /sbin/modprobe to kmod's modprobe in the rootfs (kmod, not busybox
— modules are .ko.xz and CONFIG_MODULE_DECOMPRESS is unset, so the helper must
decompress in userspace, the same reason the initrd already uses kmod). With the
helper staged, any module the guest asks for autoloads on demand from the shipped
tree via its depmod alias/dep metadata. This is the general request_module
mechanism, not a fixed preload, so it also covers egress rulesets beyond the base
one: a future user-defined rule pulling a new nft expression module autoloads
with no guest-image change.

Adds a KVM-gated red-green guard, TestInGuestEgressArmAutoloadsNetfilter: it
boots a real guest and Provisions a non-empty nft_script (table + chain + `ct
state` rule under `set -eu`), so Provision succeeding proves the whole chain
(nfnetlink, nf_tables, nf_conntrack, nft_ct) autoloaded. Verified red on a
post-W1/pre-fix rootfs (reproduces `mnl.c:66 EPROTONOSUPPORT`) and green with the
fix.

Refs RIG-3028

Co-authored-by: Matt Wilkinson <matt@rigel.build>
…t -f (RIG-3028)

Review-round low findings on PR #759, all comment/precision (no behavior change):

- boot_microvm_test.go: soften the armRuleset doc-comment — it proves a
  representative slice of the netfilter autoload chain (nfnetlink, nf_tables,
  nf_conntrack, nft_ct), not "exactly" every module the base ruleset needs (the
  base ruleset's interval sets + DNS rules pull further modules the same way via
  request_module).
- boot_microvm_test.go: note the 30s Provision context is a deliberately generous
  CLIENT bound; the real ceiling is the server's 120s armTimeout.
- guest-image/default.nix: reword the modprobe symlink comment to attribute
  closure inclusion to the ${pkgs.kmod} reference, not the ln flag; drop the inert
  -f (no prior modprobe name exists to overwrite) to match the sibling `ln -s`
  for /sbin/init and /lib/modules.

Re-verified GREEN on real KVM after the change (rootfs still packs
/sbin/modprobe -> kmod; TestInGuestEgressArmAutoloadsNetfilter passes).

Refs RIG-3028

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@rigel-mintaka
rigel-mintaka force-pushed the compass-runner/rig-3028-guest-modprobe-autoload branch from 1c05ffa to 0e4b215 Compare August 31, 2026 01:50
@mattwilkinsonn
mattwilkinsonn merged commit e01a20a into main Aug 31, 2026
13 checks passed
@mattwilkinsonn
mattwilkinsonn deleted the compass-runner/rig-3028-guest-modprobe-autoload branch August 31, 2026 03:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants