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.
This commit is contained in:
Christian Kellner 2021-11-18 13:48:09 +01:00 committed by Achilleas Koutsou
parent 4c54f7e4a6
commit 36356342b0
2 changed files with 52 additions and 0 deletions

View file

@ -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(":")

View file

@ -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()