From 36356342b087f19559988d8a113edb3a52c4bf1c Mon Sep 17 00:00:00 2001 From: Christian Kellner Date: Thu, 18 Nov 2021 13:48:09 +0100 Subject: [PATCH] buildroot: mask /proc/cmdline Since we bind `/proc` inside the container, we leak certain information that comes with it. One of this is the kernel command line. None of the decisions done by software running inside the container should depend on the kernel command line on the host, so overwrite the kernel command line by creating a temporary directory and mapping it inside the build- root. For now we default to a simple `root=/dev/osbuild` fake kernel command line. Add a simple check for it as well. --- osbuild/buildroot.py | 33 +++++++++++++++++++++++++++++++++ test/mod/test_buildroot.py | 19 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/osbuild/buildroot.py b/osbuild/buildroot.py index 59e91dd9..e25b988e 100644 --- a/osbuild/buildroot.py +++ b/osbuild/buildroot.py @@ -45,6 +45,25 @@ class CompletedBuild: return self.output +class ProcOverrides: + """Overrides for /proc inside the buildroot""" + + def __init__(self, path) -> None: + self.path = path + self.overrides = set() + + @property + def cmdline(self) -> str: + with open(os.path.join(self.path, "cmdline"), "r") as f: + return f.read().strip() + + @cmdline.setter + def cmdline(self, value) -> None: + with open(os.path.join(self.path, "cmdline"), "w") as f: + f.write(value + "\n") + self.overrides.add("cmdline") + + # pylint: disable=too-many-instance-attributes class BuildRoot(contextlib.AbstractContextManager): """Build Root @@ -69,6 +88,7 @@ class BuildRoot(contextlib.AbstractContextManager): self._apis = [] self.dev = None self.var = None + self.proc = None self.tmp = None self.mount_boot = True @@ -108,6 +128,11 @@ class BuildRoot(contextlib.AbstractContextManager): self.var = os.path.join(self.tmp, "var") os.makedirs(self.var, exist_ok=True) + proc = os.path.join(self.tmp, "proc") + os.makedirs(proc) + self.proc = ProcOverrides(proc) + self.proc.cmdline = "root=/dev/osbuild" + subprocess.run(["mount", "-t", "tmpfs", "-o", "nosuid", "none", self.dev], check=True) self._exitstack.callback(lambda: subprocess.run(["umount", "--lazy", self.dev], check=True)) @@ -209,6 +234,14 @@ class BuildRoot(contextlib.AbstractContextManager): modpath = os.path.dirname(modorigin) mounts += ["--ro-bind", f"{modpath}", "/run/osbuild/lib/osbuild"] + # Setup /proc overrides + for override in self.proc.overrides: + mounts += [ + "--ro-bind", + os.path.join(self.proc.path, override), + os.path.join("/proc", override) + ] + # Make caller-provided mounts available as well. for b in binds or []: mounts += ["--bind"] + b.split(":") diff --git a/test/mod/test_buildroot.py b/test/mod/test_buildroot.py index 2ab3c0b9..67b51982 100644 --- a/test/mod/test_buildroot.py +++ b/test/mod/test_buildroot.py @@ -145,3 +145,22 @@ def test_selinuxfs_ro(tempdir): r = root.run(cmd, monitor, readonly_binds=ro_binds) assert r.returncode == 0 + + +@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only") +def test_proc_overrides(tempdir): + runner = detect_host_runner() + libdir = os.path.abspath(os.curdir) + var = pathlib.Path(tempdir, "var") + var.mkdir() + + cmdline = "is-this-the-real-world" + + monitor = NullMonitor(sys.stderr.fileno()) + with BuildRoot("/", runner, libdir, var) as root: + + root.proc.cmdline = cmdline + + r = root.run(["cat", "/proc/cmdline"], monitor) + assert r.returncode == 0 + assert cmdline in r.output.strip()