From d0856819118e2f7d0a96d52bb8c84636c80fb388 Mon Sep 17 00:00:00 2001 From: "Brian C. Lane" Date: Mon, 21 Jul 2025 16:53:16 -0700 Subject: [PATCH] org.osbuild.grub2.iso.legacy: Add support for optional fips menu On RHEL 9.7+ and on RHEL 10.1+ we need to be able to include a menu that boots the installer environment with fips=1 on the cmdline. This adds an optional menu entry controlled by the "fips" boolean. This also includes a test for the new behavior. Related: RHEL-104075 --- stages/org.osbuild.grub2.iso.legacy | 28 +++++++++++--- stages/org.osbuild.grub2.iso.legacy.meta.json | 3 ++ stages/test/test_grub2_iso_legacy.py | 37 +++++++++++++++++-- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/stages/org.osbuild.grub2.iso.legacy b/stages/org.osbuild.grub2.iso.legacy index 8e9e464d..dec0c3a7 100755 --- a/stages/org.osbuild.grub2.iso.legacy +++ b/stages/org.osbuild.grub2.iso.legacy @@ -34,6 +34,7 @@ menuentry 'Test this media & install ${product} ${version}' --class fedora --cla linux ${kernelpath} ${root} rd.live.check quiet initrd ${initrdpath} } +$fipsentry submenu 'Troubleshooting -->' { menuentry 'Install ${product} ${version} in basic graphics mode' --class fedora --class gnu-linux --class gnu --class os { linux ${kernelpath} ${root} nomodeset quiet @@ -49,6 +50,14 @@ submenu 'Troubleshooting -->' { } """ +# Optional FIPS menu entry +FIPS_ENTRY_TEMPLATE = """ +menuentry 'Install ${product} ${version} in FIPS mode' --class fedora --class gnu-linux --class gnu --class os { + linux ${kernelpath} ${root} quiet fips=1 + initrd ${initrdpath} +} +""" + def main(root, options): name = options["product"]["name"] @@ -58,6 +67,7 @@ def main(root, options): kopts = options["kernel"].get("opts") cfg = options.get("config", {}) timeout = cfg.get("timeout", 60) + fips = options.get("fips", False) grub2dir = os.path.join(root, "boot", "grub2") os.makedirs(grub2dir, exist_ok=True) @@ -67,17 +77,25 @@ def main(root, options): shutil.copytree("/usr/lib/grub/i386-pc", moduledir, dirs_exist_ok=True) print(f"kernel dir at {kdir}") - - tplt = string.Template(GRUB2_CFG_TEMPLATE) - data = tplt.safe_substitute({ + tplt_variables = { "version": version, "product": name, "kernelpath": os.path.join(kdir, "vmlinuz"), "initrdpath": os.path.join(kdir, "initrd.img"), "isolabel": isolabel, "root": " ".join(kopts), - "timeout": timeout - }) + "timeout": timeout, + "fipsentry": "" + } + + # Insert optional fips menu entry + if fips: + fips_tmpl = string.Template(FIPS_ENTRY_TEMPLATE) + fipsentry = fips_tmpl.safe_substitute(tplt_variables) + tplt_variables["fipsentry"] = fipsentry + + tplt = string.Template(GRUB2_CFG_TEMPLATE) + data = tplt.safe_substitute(tplt_variables) config = os.path.join(grub2dir, "grub.cfg") with open(config, "w", encoding="utf8") as cfg: diff --git a/stages/org.osbuild.grub2.iso.legacy.meta.json b/stages/org.osbuild.grub2.iso.legacy.meta.json index 1fba1b55..7869d6ae 100644 --- a/stages/org.osbuild.grub2.iso.legacy.meta.json +++ b/stages/org.osbuild.grub2.iso.legacy.meta.json @@ -47,6 +47,9 @@ "isolabel": { "type": "string" }, + "fips": { + "type": "boolean" + }, "config": { "description": "Configuration options for grub itself", "type": "object", diff --git a/stages/test/test_grub2_iso_legacy.py b/stages/test/test_grub2_iso_legacy.py index 0817a852..32b6654b 100644 --- a/stages/test/test_grub2_iso_legacy.py +++ b/stages/test/test_grub2_iso_legacy.py @@ -7,7 +7,7 @@ import pytest STAGE_NAME = "org.osbuild.grub2.iso.legacy" -expected_grub_cfg = """ +CONFIG_PART_1 = """ function load_video { insmod all_video } @@ -33,6 +33,9 @@ menuentry 'Test this media & install Fedora-IoT 41' --class fedora --class gnu-l linux /images/pxeboot/vmlinuz inst.ks=hd:LABEL=Fedora-41-X86_64:/install.ks rd.live.check quiet initrd /images/pxeboot/initrd.img } +""" + +CONFIG_PART_2 = """ submenu 'Troubleshooting -->' { menuentry 'Install Fedora-IoT 41 in basic graphics mode' --class fedora --class gnu-linux --class gnu --class os { linux /images/pxeboot/vmlinuz inst.ks=hd:LABEL=Fedora-41-X86_64:/install.ks nomodeset quiet @@ -48,9 +51,22 @@ submenu 'Troubleshooting -->' { } """ +CONFIG_FIPS = """ +menuentry 'Install Fedora-IoT 41 in FIPS mode' --class fedora --class gnu-linux --class gnu --class os { + linux /images/pxeboot/vmlinuz inst.ks=hd:LABEL=Fedora-41-X86_64:/install.ks quiet fips=1 + initrd /images/pxeboot/initrd.img +} +""" + @patch("shutil.copytree") -def test_grub2_iso_legacy_smoke(mocked_copytree, tmp_path, stage_module): +@pytest.mark.parametrize("test_data,expected_conf", [ + # default + ({}, CONFIG_PART_1 + CONFIG_PART_2), + # fips menu enable + ({"fips": True}, CONFIG_PART_1 + CONFIG_FIPS + CONFIG_PART_2) +]) +def test_grub2_iso_legacy_smoke(mocked_copytree, tmp_path, stage_module, test_data, expected_conf): treedir = tmp_path / "tree" confpath = treedir / "boot/grub2/grub.cfg" confpath.parent.mkdir(parents=True, exist_ok=True) @@ -72,11 +88,12 @@ def test_grub2_iso_legacy_smoke(mocked_copytree, tmp_path, stage_module): "timeout": 10, } } + options.update(test_data) stage_module.main(treedir, options) assert os.path.exists(confpath) - assert confpath.read_text() == expected_grub_cfg + assert confpath.read_text() == expected_conf assert mocked_copytree.call_args_list == [ call("/usr/lib/grub/i386-pc", os.fspath(treedir / "boot/grub2/i386-pc"), dirs_exist_ok=True), @@ -120,6 +137,20 @@ def test_grub2_iso_legacy_smoke(mocked_copytree, tmp_path, stage_module): }, }, "", ), + # good + fips + ( + { + "isolabel": "an-isolabel", + "product": { + "name": "a-name", + "version": "a-version", + }, + "kernel": { + "dir": "/path/to", + }, + "fips": True, + }, "", + ), ]) def test_schema_validation(stage_schema, test_data, expected_err): test_input = {