From 8a44bbc31ec210e34627dadf95fedba583e07be7 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Fri, 27 Sep 2024 12:27:21 +0200 Subject: [PATCH] Don't use overlayfs for /etc and /opt in sandbox Unprivileged overlayfs isn't available everywhere (see #3054). So let's try to accomodate this a little by not using overlayfs for /etc and /opt from the sandbox tree and instead mounting them read-only into the sandbox. If required, scripts can still mount an overlayfs onto these if needed, we just don't do it by default anymore. This does mean we need to set up /etc with mountpoints and symlinks beforehand in install_sandbox_trees(), but this shouldn't be a huge problem. --- mkosi/__init__.py | 49 +++++++++++++++++++++++++++++++++++++---------- mkosi/mounts.py | 6 +----- mkosi/run.py | 9 +++++++-- 3 files changed, 47 insertions(+), 17 deletions(-) diff --git a/mkosi/__init__.py b/mkosi/__init__.py index ba5d31d2e..a0d2cb96c 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -1067,11 +1067,6 @@ def install_sandbox_trees(config: Config, dst: Path) -> None: # Ensure /etc exists in the sandbox (dst / "etc").mkdir(exist_ok=True) - if Path("/etc/passwd").exists(): - shutil.copy("/etc/passwd", dst / "etc/passwd") - if Path("/etc/group").exists(): - shutil.copy("/etc/passwd", dst / "etc/group") - if (p := config.tools() / "etc/crypto-policies").exists(): copy_tree( p, @@ -1081,12 +1076,46 @@ def install_sandbox_trees(config: Config, dst: Path) -> None: sandbox=config.sandbox, ) # fmt: skip - if not config.sandbox_trees: - return + if config.sandbox_trees: + with complete_step("Copying in sandbox trees…"): + for tree in config.sandbox_trees: + install_tree(config, tree.source, dst, target=tree.target, preserve=False) - with complete_step("Copying in sandbox trees…"): - for tree in config.sandbox_trees: - install_tree(config, tree.source, dst, target=tree.target, preserve=False) + if Path("/etc/passwd").exists(): + shutil.copy("/etc/passwd", dst / "etc/passwd") + if Path("/etc/group").exists(): + shutil.copy("/etc/passwd", dst / "etc/group") + + if not (dst / "etc/mtab").is_symlink(): + (dst / "etc/mtab").symlink_to("../proc/self/mounts") + + Path(dst / "etc/resolv.conf").unlink(missing_ok=True) + Path(dst / "etc/resolv.conf").touch() + + Path(dst / "etc/static").unlink(missing_ok=True) + if (config.tools() / "etc/static").is_symlink(): + (dst / "etc/static").symlink_to((config.tools() / "etc/static").readlink()) + + # Create various mountpoints in /etc as /etc from the sandbox tree is mounted read-only into the sandbox. + + for d in ( + "etc/pki", + "etc/ssl", + "etc/ca-certificates", + "var/lib/ca-certificates", + "etc/pacman.d/gnupg", + "etc/alternatives", + ): + (dst / d).mkdir(parents=True, exist_ok=True) + + for f in ( + "etc/passwd", + "etc/group", + "etc/shadow", + "etc/gshadow", + "etc/ld.so.cache", + ): + (dst / f).touch(exist_ok=True) def install_package_directories(context: Context, directories: Sequence[Path]) -> None: diff --git a/mkosi/mounts.py b/mkosi/mounts.py index 3c843db30..966f0aa2f 100644 --- a/mkosi/mounts.py +++ b/mkosi/mounts.py @@ -95,7 +95,6 @@ def finalize_crypto_mounts(config: Config) -> list[PathString]: Path("etc/pki"), Path("etc/ssl"), Path("etc/ca-certificates"), - Path("etc/static"), Path("var/lib/ca-certificates"), ) if (root / subdir).exists() @@ -105,7 +104,4 @@ def finalize_crypto_mounts(config: Config) -> list[PathString]: if (config.tools() / "etc/pacman.d/gnupg").exists(): mounts += [(config.tools() / "etc/pacman.d/gnupg", Path("/etc/pacman.d/gnupg"))] - return flatten( - ("--symlink", src.readlink(), target) if src.is_symlink() else ("--ro-bind", src, target) - for src, target in sorted(set(mounts), key=lambda s: s[1]) - ) + return flatten(("--ro-bind", src, target) for src, target in sorted(set(mounts), key=lambda s: s[1])) diff --git a/mkosi/run.py b/mkosi/run.py index ff0afcea1..ca39ce36a 100644 --- a/mkosi/run.py +++ b/mkosi/run.py @@ -538,7 +538,6 @@ def sandbox_cmd( "--dir", "/var/tmp", "--dir", "/var/log", "--unshare-ipc", - "--symlink", "../proc/self/mounts", "/etc/mtab", ] # fmt: skip if devices: @@ -566,7 +565,13 @@ def sandbox_cmd( yield [*cmdline, "--bind", tmp, "/var/tmp", *options, "--"] return - for d in ("etc", "opt", "srv", "media", "mnt", "var", "run", "tmp"): + for d in ("etc", "opt"): + if overlay and (overlay / d).exists(): + cmdline += ["--ro-bind", overlay / d, Path("/") / d] + else: + cmdline += ["--dir", Path("/") / d] + + for d in ("srv", "media", "mnt", "var", "run", "tmp"): tmp = None if d not in ("run", "tmp"): with umask(~0o755):