From e1df8cea8f24fd4bbfb37f60ac659a98eb75e1b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hozza?= Date: Wed, 14 Aug 2024 16:49:13 +0200 Subject: [PATCH] Stages/dracut: prepare environment for running dracut in chroot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dracut expects the environment, in which it is run, to have properly mounted /proc, /dev and /sys. Otherwise, some of its modules don't work properly. E.g. dracut fails to embed the CA cert bundle into the initram disk, which means that HTTPS won't work in it. dracut also prints a lot of errors and warnings about this, but we used to ignore them until now. The buildroot environment in which the stage runs is OK, but we actually run dracut using 'chroot', which is the core of the problem. The runtime environment in such case lacks the necessary mounts. Add a context manager for setting up and cleaning up all the necessary mounts in the image FS tree when running dracut. This change is related to: https://bugzilla.redhat.com/show_bug.cgi?id=1962975 And the implementation has been inspired by the fix in lorax: https://github.com/weldr/lorax/pull/1151 Signed-off-by: Tomáš Hozza --- stages/org.osbuild.dracut | 42 +++++++++++++++++++++++++++++++++----- stages/test/test_dracut.py | 2 +- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/stages/org.osbuild.dracut b/stages/org.osbuild.dracut index 16bc379a..36f853fe 100755 --- a/stages/org.osbuild.dracut +++ b/stages/org.osbuild.dracut @@ -1,4 +1,5 @@ #!/usr/bin/python3 +import os import subprocess import sys @@ -10,6 +11,36 @@ def yesno(name: str, value: bool) -> str: return f"--{prefix}{name}" +class DracutMounts: + """ + Setup the mounts inside a chroot root directory, which will be used + for running dracut inside it. Cleanup mounts when done. + This mounts /proc, /dev, and /sys. + """ + + def __init__(self, root: str): + self.root = root + + def __enter__(self): + for d in ["/proc", "/dev", "/sys"]: + if not os.path.exists(self.root + d): + print(f"Making missing dracut chroot directory: {d}") + os.makedirs(self.root + d) + + subprocess.check_call(["/usr/bin/mount", "-t", "proc", "-o", + "nosuid,noexec,nodev", "proc", f"{self.root}/proc"]) + subprocess.check_call(["/usr/bin/mount", "-t", "devtmpfs", "-o", + "mode=0755,noexec,nosuid,strictatime", "devtmpfs", f"{self.root}/dev"]) + subprocess.check_call(["/usr/bin/mount", "-t", "sysfs", "-o", + "nosuid,noexec,nodev", "sysfs", f"{self.root}/sys"]) + + return self + + def __exit__(self, exc_type, exc_value, tracebk): + for d in ["/proc", "/dev", "/sys"]: + subprocess.check_call(["/usr/bin/umount", self.root + d]) + + # pylint: disable=too-many-branches def main(tree, options): kernels = options["kernel"] @@ -82,11 +113,12 @@ def main(tree, options): if initoverlayfs: initfs_bin = "/usr/bin/initoverlayfs-install" - subprocess.run(["/usr/sbin/chroot", tree, initfs_bin, - "--no-hostonly", - "--kver", kver] - + opts, - check=True) + with DracutMounts(tree): + subprocess.run(["/usr/sbin/chroot", tree, initfs_bin, + "--no-hostonly", + "--kver", kver] + + opts, + check=True) return 0 diff --git a/stages/test/test_dracut.py b/stages/test/test_dracut.py index c1e0f6a2..7e019f8b 100644 --- a/stages/test/test_dracut.py +++ b/stages/test/test_dracut.py @@ -20,7 +20,7 @@ def test_dracut_with_initoverlayfs(mocked_run, tmp_path, stage_module, with_init "initoverlayfs": with_initoverlayfs, } - stage_module.main(tmp_path, options) + stage_module.main(str(tmp_path), options) assert len(mocked_run.call_args_list) == 1 args, kwargs = mocked_run.call_args_list[0]