From eba43f034c5c19a478249ba50fc1b97faffda75c Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 19 Dec 2024 12:35:47 +0100 Subject: [PATCH] Run binaries from ExtraSearchPaths= within tools tree Until now, there was always the implicit assumption that any paths configured with ExtraSearchPaths= contained binaries built against the host's /usr. With that assumption, it made sense to execute binaries found in these paths outside of the tools tree as otherwise you might end up with missing or out of date libraries if these are not available within the tools tree. However, with the introduction of "mkosi sandbox", what I want to do in systemd is to have contributors build systemd within the sandbox and then use those binaries in mkosi to build the image. This means that the build directory configured with ExtrasSearchPaths= suddenly contains binaries built against the tools tree's /usr (if one is configured) instead of the host's /usr. Given this new use case, let's get rid of the logic to not use the tools tree for binaries in ExtraSearchPaths=, instead, users of ExtraSearchPaths= using a tools tree will have to make sure to use a tools tree that mostly matches their host's /usr. --- mkosi/__init__.py | 62 +++++++++------------------------ mkosi/archive.py | 3 -- mkosi/bootloader.py | 27 +++----------- mkosi/burn.py | 1 - mkosi/config.py | 25 ++++++------- mkosi/context.py | 2 -- mkosi/curl.py | 1 - mkosi/distributions/debian.py | 6 ++-- mkosi/distributions/opensuse.py | 1 - mkosi/installer/__init__.py | 1 - mkosi/installer/apt.py | 1 - mkosi/installer/dnf.py | 4 +-- mkosi/installer/pacman.py | 5 +-- mkosi/installer/rpm.py | 2 +- mkosi/installer/zypper.py | 5 +-- mkosi/manifest.py | 12 ++----- mkosi/partition.py | 2 +- mkosi/qemu.py | 52 +++++++++------------------ mkosi/resources/man/mkosi.1.md | 8 +++-- mkosi/run.py | 2 -- mkosi/sysupdate.py | 1 - mkosi/tree.py | 10 +++--- mkosi/vmspawn.py | 1 - 23 files changed, 71 insertions(+), 163 deletions(-) diff --git a/mkosi/__init__.py b/mkosi/__init__.py index 669fb0b00..e7201548f 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -498,10 +498,7 @@ def finalize_host_scripts( if context.config.find_binary(binary): scripts[binary] = (binary, "--root", "/buildroot") if ukify := context.config.find_binary("ukify"): - # A script will always run with the tools tree mounted, so we pass binary=None to disable - # the conditional search logic of python_binary() depending on whether the binary is in an - # extra search path or not. - scripts["ukify"] = (python_binary(context.config, binary=None), ukify) + scripts["ukify"] = (python_binary(context.config), ukify) return finalize_scripts(context.config, scripts | dict(helpers)) @@ -542,7 +539,6 @@ def run_configure_scripts(config: Config) -> Config: ["/work/configure"], env=env | config.environment, sandbox=config.sandbox( - binary=None, options=[ "--dir", "/work/src", "--chdir", "/work/src", @@ -617,7 +613,6 @@ def run_sync_scripts(config: Config) -> None: env=env | config.environment, stdin=sys.stdin, sandbox=config.sandbox( - binary=None, network=True, options=options, overlay=Path(sandbox_tree), @@ -656,7 +651,6 @@ def script_maybe_chroot_sandbox( with finalize_host_scripts(context, helpers) as hd: if script.suffix != ".chroot": with context.sandbox( - binary=None, network=network, options=[ *options, @@ -981,7 +975,6 @@ def run_postoutput_scripts(context: Context) -> None: ["/work/postoutput"], env=env | context.config.environment, sandbox=context.sandbox( - binary=None, # postoutput scripts should run as (fake) root so that file ownership is # always recorded as if owned by root. options=[ @@ -1034,7 +1027,6 @@ def install_tree( ["systemd-dissect", "--copy-from", workdir(src), "/", workdir(t)], env=dict(SYSTEMD_DISSECT_VERITY_EMBEDDED="no", SYSTEMD_DISSECT_VERITY_SIDECAR="no"), sandbox=config.sandbox( - binary="systemd-dissect", devices=True, network=True, options=[ @@ -1495,7 +1487,7 @@ def run_ukify( (context.workspace / "cmdline").write_text(f"{' '.join(cmdline)}\x00") cmd = [ - python_binary(context.config, binary=ukify), + python_binary(context.config), ukify, "build", *arguments, @@ -1570,7 +1562,6 @@ def run_ukify( ), env=context.config.environment, sandbox=context.sandbox( - binary=ukify, options=[*opt, *options], devices=context.config.secure_boot_key_source.type != KeySourceType.file, ), @@ -1658,7 +1649,7 @@ def build_uki( # new .ucode section support? if ( systemd_tool_version( - python_binary(context.config, binary=ukify), + python_binary(context.config), ukify, sandbox=context.sandbox, ) @@ -1731,7 +1722,7 @@ def find_entry_token(context: Context) -> str: not in run( ["kernel-install", "--help"], stdout=subprocess.PIPE, - sandbox=context.sandbox(binary="kernel-install"), + sandbox=context.sandbox(), ).stdout ) or systemd_tool_version("kernel-install", sandbox=context.sandbox) < "255.1" @@ -1741,9 +1732,7 @@ def find_entry_token(context: Context) -> str: output = json.loads( run( ["kernel-install", "--root=/buildroot", "--json=pretty", "inspect"], - sandbox=context.sandbox( - binary="kernel-install", options=["--ro-bind", context.root, "/buildroot"] - ), + sandbox=context.sandbox(options=["--ro-bind", context.root, "/buildroot"]), stdout=subprocess.PIPE, env={"BOOT_ROOT": "/boot"}, ).stdout @@ -2186,7 +2175,7 @@ def maybe_compress( src.unlink() with dst.open("wb") as o: - run(cmd, stdin=i, stdout=o, sandbox=context.sandbox(binary=cmd[0])) + run(cmd, stdin=i, stdout=o, sandbox=context.sandbox()) def copy_nspawn_settings(context: Context) -> None: @@ -2336,10 +2325,7 @@ def calculate_signature_gpg(context: Context) -> None: run( cmdline, env=env, - sandbox=context.sandbox( - binary="gpg", - options=options, - ), + sandbox=context.sandbox(options=options), ) @@ -2358,7 +2344,6 @@ def calculate_signature_sop(context: Context) -> None: stdin=i, stdout=o, sandbox=context.sandbox( - binary=context.config.openpgp_tool, options=[ "--bind", context.config.key, "/signing-key.pgp", "--bind", context.staging, workdir(context.staging), @@ -2563,7 +2548,7 @@ def check_ukify( ) -> None: ukify = check_tool(config, "ukify", "/usr/lib/systemd/ukify", reason=reason, hint=hint) - v = systemd_tool_version(python_binary(config, binary=ukify), ukify, sandbox=config.sandbox) + v = systemd_tool_version(python_binary(config), ukify, sandbox=config.sandbox) if v < version: die( f"Found '{ukify}' with version {v} but version {version} or newer is required to {reason}.", @@ -2794,9 +2779,7 @@ def run_sysusers(context: Context) -> None: with complete_step("Generating system users"): run( ["systemd-sysusers", "--root=/buildroot"], - sandbox=context.sandbox( - binary="systemd-sysusers", options=["--bind", context.root, "/buildroot"] - ), + sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]), ) @@ -2827,7 +2810,6 @@ def run_tmpfiles(context: Context) -> None: # as success by the systemd-tmpfiles service so we handle those as success as well. success_exit_status=(0, 65, 73), sandbox=context.sandbox( - binary="systemd-tmpfiles", options=[ "--bind", context.root, "/buildroot", # systemd uses acl.h to parse ACLs in tmpfiles snippets which uses the host's @@ -2853,11 +2835,11 @@ def run_preset(context: Context) -> None: with complete_step("Applying presets…"): run( ["systemctl", "--root=/buildroot", "preset-all"], - sandbox=context.sandbox(binary="systemctl", options=["--bind", context.root, "/buildroot"]), + sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]), ) run( ["systemctl", "--root=/buildroot", "--global", "preset-all"], - sandbox=context.sandbox(binary="systemctl", options=["--bind", context.root, "/buildroot"]), + sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]), ) @@ -2872,7 +2854,7 @@ def run_hwdb(context: Context) -> None: with complete_step("Generating hardware database"): run( ["systemd-hwdb", "--root=/buildroot", "--usr", "--strict", "update"], - sandbox=context.sandbox(binary="systemd-hwdb", options=["--bind", context.root, "/buildroot"]), + sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]), ) # Remove any existing hwdb in /etc in favor of the one we just put in /usr. @@ -2891,7 +2873,7 @@ def run_firstboot(context: Context) -> None: if password and not hashed: password = run( ["openssl", "passwd", "-stdin", "-6"], - sandbox=context.sandbox(binary="openssl"), + sandbox=context.sandbox(), input=password, stdout=subprocess.PIPE, ).stdout.strip() @@ -2925,9 +2907,7 @@ def run_firstboot(context: Context) -> None: with complete_step("Applying first boot settings"): run( ["systemd-firstboot", "--root=/buildroot", "--force", *options], - sandbox=context.sandbox( - binary="systemd-firstboot", options=["--bind", context.root, "/buildroot"] - ), + sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]), ) # Initrds generally don't ship with only /usr so there's not much point in putting the @@ -2952,7 +2932,7 @@ def run_selinux_relabel(context: Context) -> None: with complete_step(f"Relabeling files using {policy} policy"): run( [setfiles, "-mFr", "/buildroot", "-c", binpolicy, fc, "/buildroot"], - sandbox=context.sandbox(binary=setfiles, options=["--bind", context.root, "/buildroot"]), + sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]), check=context.config.selinux_relabel == ConfigFeature.enabled, ) @@ -3017,7 +2997,6 @@ def have_cache(config: Config) -> bool: input=new, check=False, sandbox=config.sandbox( - binary="diff", tools=False, options=["--bind", manifest, workdir(manifest)], ), @@ -3575,10 +3554,9 @@ def copy_repository_metadata(config: Config, dst: Path) -> None: def sandbox( *, - binary: Optional[PathString], options: Sequence[PathString] = (), ) -> AbstractContextManager[list[PathString]]: - return config.sandbox(binary=binary, options=[*options, *exclude]) + return config.sandbox(options=[*options, *exclude]) copy_tree(src, subdst, sandbox=sandbox) @@ -3796,7 +3774,6 @@ def run_sandbox(args: Args, config: Config) -> None: env=os.environ | {"MKOSI_IN_SANDBOX": "1"}, log=False, sandbox=config.sandbox( - binary=cmdline[0], devices=True, network=True, relaxed=True, @@ -3876,7 +3853,6 @@ def run_shell(args: Args, config: Config) -> None: stdin=sys.stdin, env=config.environment, sandbox=config.sandbox( - binary="systemd-repart", network=True, devices=True, options=["--bind", fname, workdir(fname)], @@ -3987,7 +3963,6 @@ def run_shell(args: Args, config: Config) -> None: env=os.environ | config.environment, log=False, sandbox=config.sandbox( - binary="systemd-nspawn", devices=True, network=True, relaxed=True, @@ -4029,7 +4004,6 @@ def run_systemd_tool(tool: str, args: Args, config: Config) -> None: env=os.environ | config.environment, log=False, sandbox=config.sandbox( - binary=tool_path, network=True, devices=config.output_format == OutputFormat.disk, relaxed=True, @@ -4050,11 +4024,10 @@ def run_serve(args: Args, config: Config) -> None: """Serve the output directory via a tiny HTTP server""" run( - [python_binary(config, binary=None), "-m", "http.server", "8081"], + [python_binary(config), "-m", "http.server", "8081"], stdin=sys.stdin, stdout=sys.stdout, sandbox=config.sandbox( - binary=python_binary(config, binary=None), network=True, relaxed=True, options=["--chdir", config.output_dir_or_cwd()], @@ -4244,7 +4217,6 @@ def run_clean_scripts(config: Config) -> None: ["/work/clean"], env=env | config.environment, sandbox=config.sandbox( - binary=None, tools=False, options=[ "--dir", "/work/src", diff --git a/mkosi/archive.py b/mkosi/archive.py index 5ab9ed3fe..be323ec78 100644 --- a/mkosi/archive.py +++ b/mkosi/archive.py @@ -50,7 +50,6 @@ def make_tar(src: Path, dst: Path, *, sandbox: SandboxProtocol = nosandbox) -> N stdout=f, # Make sure tar uses user/group information from the root directory instead of the host. sandbox=sandbox( - binary="tar", options=[ "--ro-bind", src, workdir(src, sandbox), *finalize_passwd_symlinks(workdir(src, sandbox)), @@ -96,7 +95,6 @@ def extract_tar( *options, ], sandbox=sandbox( - binary="tar", # Make sure tar uses user/group information from the root directory instead of the host. options=[ "--ro-bind", src, workdir(src, sandbox), @@ -138,7 +136,6 @@ def make_cpio( input="\0".join(os.fspath(f) for f in files), stdout=f, sandbox=sandbox( - binary="cpio", options=[ "--ro-bind", src, workdir(src, sandbox), *finalize_passwd_symlinks(workdir(src, sandbox)) diff --git a/mkosi/bootloader.py b/mkosi/bootloader.py index d80aa86b6..163837ca1 100644 --- a/mkosi/bootloader.py +++ b/mkosi/bootloader.py @@ -232,7 +232,6 @@ def grub_mkimage( *modules, ], sandbox=context.sandbox( - binary=mkimage, options=[ "--bind", directory, "/grub", "--ro-bind", earlyconfig.name, workdir(Path(earlyconfig.name)), @@ -262,17 +261,11 @@ def find_signed_grub_image(context: Context) -> Optional[Path]: return None -def python_binary(config: Config, *, binary: Optional[PathString]) -> PathString: - tools = ( - not binary - or not (path := config.find_binary(binary)) - or not any(path.is_relative_to(d) for d in config.extra_search_paths) - ) - +def python_binary(config: Config) -> PathString: # If there's no tools tree, prefer the interpreter from MKOSI_INTERPRETER. If there is a tools # tree, just use the default python3 interpreter. exe = Path(sys.executable) - return "python3" if (tools and config.tools_tree) or not exe.is_relative_to("/usr") else exe + return "python3" if config.tools_tree or not exe.is_relative_to("/usr") else exe def extract_pe_section(context: Context, binary: Path, section: str, output: Path) -> Path: @@ -297,11 +290,10 @@ def extract_pe_section(context: Context, binary: Path, section: str, output: Pat with open(output, "wb") as f: result = run( - [python_binary(context.config, binary=None)], + [python_binary(context.config)], input=pefile, stdout=f, sandbox=context.sandbox( - binary=python_binary(context.config, binary=None), options=["--ro-bind", binary, workdir(binary)], ), success_exit_status=(0, 67), @@ -392,7 +384,6 @@ def grub_bios_setup(context: Context, partitions: Sequence[Partition]) -> None: workdir(context.staging / context.config.output_with_format), ], sandbox=context.sandbox( - binary=setup, options=[ "--bind", directory, "/grub", "--bind", context.staging, workdir(context.staging), @@ -428,7 +419,7 @@ def certificate_common_name(context: Context, certificate: Path) -> str: "-in", workdir(certificate), ], stdout=subprocess.PIPE, - sandbox=context.sandbox(binary="openssl", options=["--ro-bind", certificate, workdir(certificate)]), + sandbox=context.sandbox(options=["--ro-bind", certificate, workdir(certificate)]), ).stdout # fmt: skip for line in output.splitlines(): @@ -462,7 +453,7 @@ def run_systemd_sign_tool( cmdline, stdout=stdout, env={**config.environment, **env}, - sandbox=config.sandbox(binary=cmdline[0], options=options, devices=devices), + sandbox=config.sandbox(options=options, devices=devices), ) assert certificate @@ -498,7 +489,6 @@ def run_systemd_sign_tool( stdout=stdout, env={**config.environment, **env}, sandbox=config.sandbox( - binary=cmd[0], options=opt, devices=( devices @@ -537,7 +527,6 @@ def pesign_prepare(context: Context) -> None: ], stdout=f, sandbox=context.sandbox( - binary="openssl", options=[ "--ro-bind", context.config.secure_boot_key, workdir(context.config.secure_boot_key), "--ro-bind", context.config.secure_boot_certificate, workdir(context.config.secure_boot_certificate), # noqa: E501 @@ -556,7 +545,6 @@ def pesign_prepare(context: Context) -> None: "-d", workdir(context.workspace / "pesign"), ], sandbox=context.sandbox( - binary="pk12util", options=[ "--ro-bind", context.workspace / "secure-boot.p12", workdir(context.workspace / "secure-boot.p12"), # noqa: E501 "--ro-bind", context.workspace / "pesign", workdir(context.workspace / "pesign"), @@ -626,7 +614,6 @@ def sign_efi_binary(context: Context, input: Path, output: Path) -> Path: ), env=context.config.environment, sandbox=context.sandbox( - binary="sbsign", options=options, devices=context.config.secure_boot_key_source.type != KeySourceType.file, ), @@ -657,7 +644,6 @@ def sign_efi_binary(context: Context, input: Path, output: Path) -> Path: ), env=context.config.environment, sandbox=context.sandbox( - binary="pesign", options=[ "--ro-bind", context.workspace / "pesign", workdir(context.workspace / "pesign"), "--ro-bind", input, workdir(input), @@ -834,7 +820,6 @@ def install_systemd_boot(context: Context) -> None: "-out", workdir(context.workspace / "mkosi.der"), ], sandbox=context.sandbox( - binary="openssl", options=[ "--ro-bind", context.config.secure_boot_certificate, @@ -854,7 +839,6 @@ def install_systemd_boot(context: Context) -> None: workdir(context.workspace / "mkosi.der"), ], sandbox=context.sandbox( - binary="sbsiglist", options=[ "--bind", context.workspace, workdir(context.workspace), "--ro-bind", context.workspace / "mkosi.der", workdir(context.workspace / "mkosi.der"), # noqa: E501 @@ -898,7 +882,6 @@ def install_systemd_boot(context: Context) -> None: else subprocess.DEVNULL ), sandbox=context.sandbox( - binary="sbvarsign", options=options, devices=context.config.secure_boot_key_source.type != KeySourceType.file, ), diff --git a/mkosi/burn.py b/mkosi/burn.py index 09e3abdc6..07aacdc79 100644 --- a/mkosi/burn.py +++ b/mkosi/burn.py @@ -37,7 +37,6 @@ def run_burn(args: Args, config: Config) -> None: env=os.environ | config.environment, log=False, sandbox=config.sandbox( - binary="systemd-repart", devices=True, network=True, relaxed=True, diff --git a/mkosi/config.py b/mkosi/config.py index 958aff477..5fa2507c1 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -571,6 +571,7 @@ def parse_path( secret: bool = False, absolute: bool = False, directory: bool = False, + exclude: Sequence[PathString] = (), constants: Sequence[str] = (), ) -> Path: if value in constants: @@ -594,6 +595,10 @@ def parse_path( if absolute and not path.is_absolute(): die(f"{value} must be an absolute path") + for e in exclude: + if path.is_relative_to(e): + die(f"{path} can not be relative to {e}") + if resolve: path = path.resolve() @@ -1094,6 +1099,7 @@ def make_path_parser( expanduser: bool = True, expandvars: bool = True, secret: bool = False, + exclude: Sequence[PathString] = (), constants: Sequence[str] = (), ) -> Callable[[str], Path]: return functools.partial( @@ -1103,6 +1109,7 @@ def make_path_parser( expanduser=expanduser, expandvars=expandvars, secret=secret, + exclude=exclude, constants=constants, ) @@ -2062,7 +2069,6 @@ class Config: def sandbox( self, *, - binary: Optional[PathString], network: bool = False, devices: bool = False, relaxed: bool = False, @@ -2073,7 +2079,10 @@ class Config: setup: Sequence[PathString] = (), ) -> AbstractContextManager[list[PathString]]: opt: list[PathString] = [*options] + if not relaxed: + opt += flatten(("--ro-bind", d, d) for d in self.extra_search_paths) + if p := self.proxy_peer_certificate: opt += ["--ro-bind", os.fspath(p), "/proxy.cacert"] if p := self.proxy_client_certificate: @@ -2081,14 +2090,6 @@ class Config: if p := self.proxy_client_key: opt += ["--ro-bind", os.fspath(p), "/proxy.clientkey"] - if ( - binary - and (path := self.find_binary(binary, tools=tools)) - and any(path.is_relative_to(d) for d in self.extra_search_paths) - ): - tools = False - opt += flatten(("--ro-bind", d, d) for d in self.extra_search_paths if not relaxed) - return sandbox_cmd( network=network, devices=devices, @@ -3175,7 +3176,7 @@ SETTINGS: list[ConfigSetting[Any]] = [ long="--extra-search-path", metavar="PATH", section="Build", - parse=config_make_list_parser(delimiter=",", parse=make_path_parser()), + parse=config_make_list_parser(delimiter=",", parse=make_path_parser(exclude=["/usr"])), help="List of comma-separated paths to look for programs before looking in PATH", scope=SettingScope.universal, ), @@ -5102,7 +5103,7 @@ def want_selinux_relabel( policy = run( ["sh", "-c", f". {workdir(selinux)} && echo $SELINUXTYPE"], - sandbox=config.sandbox(binary="sh", options=["--ro-bind", selinux, workdir(selinux)]), + sandbox=config.sandbox(options=["--ro-bind", selinux, workdir(selinux)]), stdout=subprocess.PIPE, ).stdout.strip() if not policy: @@ -5144,7 +5145,7 @@ def systemd_tool_version(*tool: PathString, sandbox: SandboxProtocol = nosandbox run( [*tool, "--version"], stdout=subprocess.PIPE, - sandbox=sandbox(binary=tool[-1]), + sandbox=sandbox(), ) .stdout.split()[2] .strip("()") diff --git a/mkosi/context.py b/mkosi/context.py index a6caed275..2f8d32374 100644 --- a/mkosi/context.py +++ b/mkosi/context.py @@ -63,14 +63,12 @@ class Context: def sandbox( self, *, - binary: Optional[PathString], network: bool = False, devices: bool = False, scripts: Optional[Path] = None, options: Sequence[PathString] = (), ) -> AbstractContextManager[list[PathString]]: return self.config.sandbox( - binary=binary, network=network, devices=devices, scripts=scripts, diff --git a/mkosi/curl.py b/mkosi/curl.py index 2aa91749a..8d39acc97 100644 --- a/mkosi/curl.py +++ b/mkosi/curl.py @@ -24,7 +24,6 @@ def curl(config: Config, url: str, output_dir: Path) -> None: url, ], sandbox=config.sandbox( - binary="curl", network=True, options=["--bind", output_dir, workdir(output_dir), *finalize_crypto_mounts(config)], ), diff --git a/mkosi/distributions/debian.py b/mkosi/distributions/debian.py index b3daf4c92..90ebe5bb6 100644 --- a/mkosi/distributions/debian.py +++ b/mkosi/distributions/debian.py @@ -169,7 +169,7 @@ class Installer(DistributionInstaller): ["dpkg-deb", "--fsys-tarfile", "/dev/stdin"], stdin=i, stdout=o, - sandbox=context.sandbox(binary="dpkg-deb"), + sandbox=context.sandbox(), ) extract_tar( Path(o.name), @@ -298,9 +298,7 @@ def fixup_os_release(context: Context) -> None: f"/{candidate}.dpkg", f"/{candidate}", ], - sandbox=context.sandbox( - binary="dpkg-divert", options=["--bind", context.root, "/buildroot"] - ), + sandbox=context.sandbox(options=["--bind", context.root, "/buildroot"]), ) newosrelease.rename(osrelease) diff --git a/mkosi/distributions/opensuse.py b/mkosi/distributions/opensuse.py index fe8c6f2b4..6b07ef530 100644 --- a/mkosi/distributions/opensuse.py +++ b/mkosi/distributions/opensuse.py @@ -115,7 +115,6 @@ class Installer(DistributionInstaller): *(key.removeprefix("file://") for key in gpgkeys), ], sandbox=context.sandbox( - binary="rpm", options=[ "--bind", context.root, "/buildroot", *finalize_crypto_mounts(context.config), diff --git a/mkosi/installer/__init__.py b/mkosi/installer/__init__.py index fc9713420..ebf858b47 100644 --- a/mkosi/installer/__init__.py +++ b/mkosi/installer/__init__.py @@ -135,7 +135,6 @@ class PackageManager: options: Sequence[PathString] = (), ) -> AbstractContextManager[list[PathString]]: return context.sandbox( - binary=cls.executable(context.config), network=True, options=[ "--bind", context.root, "/buildroot", diff --git a/mkosi/installer/apt.py b/mkosi/installer/apt.py index ae8c4b595..4c0ecafdf 100644 --- a/mkosi/installer/apt.py +++ b/mkosi/installer/apt.py @@ -256,7 +256,6 @@ class Apt(PackageManager): *(d.name for glob in PACKAGE_GLOBS for d in context.repository.glob(glob) if "deb" in glob), ], sandbox=context.sandbox( - binary="reprepro", options=[ "--bind", context.repository, workdir(context.repository), "--chdir", workdir(context.repository), diff --git a/mkosi/installer/dnf.py b/mkosi/installer/dnf.py index aa70f756e..43237877b 100644 --- a/mkosi/installer/dnf.py +++ b/mkosi/installer/dnf.py @@ -218,9 +218,7 @@ class Dnf(PackageManager): def createrepo(cls, context: Context) -> None: run( ["createrepo_c", workdir(context.repository)], - sandbox=context.sandbox( - binary="createrepo_c", options=["--bind", context.repository, workdir(context.repository)] - ), + sandbox=context.sandbox(options=["--bind", context.repository, workdir(context.repository)]), ) (context.sandbox_tree / "etc/yum.repos.d/mkosi-local.repo").write_text( diff --git a/mkosi/installer/pacman.py b/mkosi/installer/pacman.py index e7db0a831..e8b7b81af 100644 --- a/mkosi/installer/pacman.py +++ b/mkosi/installer/pacman.py @@ -191,10 +191,7 @@ class Pacman(PackageManager): key=lambda p: GenericVersion(Path(p).name), ), ], - sandbox=context.sandbox( - binary="repo-add", - options=["--bind", context.repository, workdir(context.repository)], - ), + sandbox=context.sandbox(options=["--bind", context.repository, workdir(context.repository)]), ) (context.sandbox_tree / "etc/mkosi-local.conf").write_text( diff --git a/mkosi/installer/rpm.py b/mkosi/installer/rpm.py index f210db543..a50768a52 100644 --- a/mkosi/installer/rpm.py +++ b/mkosi/installer/rpm.py @@ -83,7 +83,7 @@ def setup_rpm( plugindir = Path( run( ["rpm", "--eval", "%{__plugindir}"], - sandbox=context.sandbox(binary="rpm"), + sandbox=context.sandbox(), stdout=subprocess.PIPE, ).stdout.strip() ) diff --git a/mkosi/installer/zypper.py b/mkosi/installer/zypper.py index 16ad76d49..1215aadbf 100644 --- a/mkosi/installer/zypper.py +++ b/mkosi/installer/zypper.py @@ -139,10 +139,7 @@ class Zypper(PackageManager): def createrepo(cls, context: Context) -> None: run( ["createrepo_c", workdir(context.repository)], - sandbox=context.sandbox( - binary="createrepo_c", - options=["--bind", context.repository, workdir(context.repository)], - ), + sandbox=context.sandbox(options=["--bind", context.repository, workdir(context.repository)]), ) (context.sandbox_tree / "etc/zypp/repos.d/mkosi-local.repo").write_text( diff --git a/mkosi/manifest.py b/mkosi/manifest.py index d27a66200..2dd2fd576 100644 --- a/mkosi/manifest.py +++ b/mkosi/manifest.py @@ -111,7 +111,7 @@ class Manifest: ], stdout=subprocess.PIPE, sandbox=( - self.context.sandbox(binary="rpm", options=["--ro-bind", self.context.root, "/buildroot"]) + self.context.sandbox(options=["--ro-bind", self.context.root, "/buildroot"]) ), ) # fmt: skip @@ -158,10 +158,7 @@ class Manifest: ], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, - sandbox=self.context.sandbox( - binary="rpm", - options=["--ro-bind", self.context.root, "/buildroot"], - ), + sandbox=self.context.sandbox(options=["--ro-bind", self.context.root, "/buildroot"]), ) changelog = c.stdout.strip() source = SourcePackageManifest(srpm, changelog) @@ -178,10 +175,7 @@ class Manifest: "--showformat", r"${Package}\t${source:Package}\t${Version}\t${Architecture}\t${Installed-Size}\t${db-fsys:Last-Modified}\n", # noqa: E501 ], stdout=subprocess.PIPE, - sandbox=self.context.sandbox( - binary="dpkg-query", - options=["--ro-bind", self.context.root, "/buildroot"], - ), + sandbox=self.context.sandbox(options=["--ro-bind", self.context.root, "/buildroot"]), ) # fmt: skip packages = sorted(c.stdout.splitlines()) diff --git a/mkosi/partition.py b/mkosi/partition.py index 282fae37e..e7eb94407 100644 --- a/mkosi/partition.py +++ b/mkosi/partition.py @@ -39,7 +39,7 @@ def find_partitions(image: Path, *, sandbox: SandboxProtocol = nosandbox) -> lis ["systemd-repart", "--json=short", workdir(image, sandbox)], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, - sandbox=sandbox(binary="systemd-repart", options=["--ro-bind", image, workdir(image, sandbox)]), + sandbox=sandbox(options=["--ro-bind", image, workdir(image, sandbox)]), ).stdout ) return [Partition.from_dict(d) for d in output] diff --git a/mkosi/qemu.py b/mkosi/qemu.py index aef731099..512ff32d1 100644 --- a/mkosi/qemu.py +++ b/mkosi/qemu.py @@ -159,7 +159,7 @@ class KernelType(StrEnum): type = run( ["bootctl", "kernel-identify", workdir(path)], stdout=subprocess.PIPE, - sandbox=config.sandbox(binary="bootctl", options=["--ro-bind", path, workdir(path)]), + sandbox=config.sandbox(options=["--ro-bind", path, workdir(path)]), ).stdout.strip() try: @@ -178,15 +178,13 @@ class OvmfConfig: vars_format: str -def find_ovmf_firmware(config: Config, qemu: Path, firmware: QemuFirmware) -> Optional[OvmfConfig]: +def find_ovmf_firmware(config: Config, firmware: QemuFirmware) -> Optional[OvmfConfig]: if not firmware.is_uefi(): return None - tools = Path("/") if any(qemu.is_relative_to(d) for d in config.extra_search_paths) else config.tools() - - desc = list((tools / "usr/share/qemu/firmware").glob("*")) - if tools == Path("/"): - desc += list((tools / "etc/qemu/firmware").glob("*")) + desc = list((config.tools() / "usr/share/qemu/firmware").glob("*")) + if config.tools() == Path("/"): + desc += list((config.tools() / "etc/qemu/firmware").glob("*")) arch = config.architecture.to_qemu() machine = config.architecture.default_qemu_machine() @@ -235,7 +233,7 @@ def find_ovmf_firmware(config: Config, qemu: Path, firmware: QemuFirmware) -> Op logging.debug(f"Using {p.name} firmware description") return OvmfConfig( - description=Path("/") / p.relative_to(tools), + description=Path("/") / p.relative_to(config.tools()), firmware=Path(j["mapping"]["executable"]["filename"]), format=j["mapping"]["executable"]["format"], vars=Path(j["mapping"]["nvram-template"]["filename"]), @@ -258,10 +256,7 @@ def start_swtpm(config: Config) -> Iterator[Path]: "sha256", "--config", "/dev/null", ], - sandbox=config.sandbox( - binary="swtpm_setup", - options=["--bind", state, workdir(Path(state))], - ), + sandbox=config.sandbox(options=["--bind", state, workdir(Path(state))]), stdout=None if ARG_DEBUG.get() else subprocess.DEVNULL, ) # fmt: skip @@ -280,7 +275,6 @@ def start_swtpm(config: Config) -> Iterator[Path]: cmdline, pass_fds=(sock.fileno(),), sandbox=config.sandbox( - binary="swtpm", options=["--bind", state, workdir(Path(state))], setup=scope_cmd( name=f"mkosi-swtpm-{config.machine_or_name()}", @@ -314,9 +308,7 @@ def systemd_escape(config: Config, s: PathString, path: bool = False) -> str: if path: cmdline += ["--path"] - return run( - cmdline, stdout=subprocess.PIPE, sandbox=config.sandbox(binary="systemd-escape") - ).stdout.strip() + return run(cmdline, stdout=subprocess.PIPE, sandbox=config.sandbox()).stdout.strip() @contextlib.contextmanager @@ -404,7 +396,6 @@ def start_virtiofsd( # features. preexec_fn=become_root_in_subuid_range if not scope and not uidmap else None, sandbox=config.sandbox( - binary=virtiofsd, options=[ "--bind", directory, workdir(directory), *(["--become-root"] if uidmap else []), @@ -476,7 +467,7 @@ def make_nocow(config: Config, path: Path) -> None: ["chattr", "+C", workdir(path)], check=False, stderr=subprocess.DEVNULL if not ARG_DEBUG.get() else None, - sandbox=config.sandbox(binary="chattr", options=["--bind", path, workdir(path)]), + sandbox=config.sandbox(options=["--bind", path, workdir(path)]), ) @@ -537,7 +528,6 @@ def start_journal_remote(config: Config, sockfd: int) -> Iterator[None]: ], pass_fds=(sockfd,), sandbox=config.sandbox( - binary=bin, options=[ "--bind", config.forward_journal.parent, workdir(config.forward_journal.parent), "--ro-bind", f.name, "/etc/systemd/journal-remote.conf", @@ -591,7 +581,7 @@ def copy_ephemeral(config: Config, src: Path) -> Iterator[Path]: if config.output_format in (OutputFormat.disk, OutputFormat.esp): attr = run( ["lsattr", "-l", workdir(src)], - sandbox=config.sandbox(binary="lsattr", options=["--ro-bind", src, workdir(src)]), + sandbox=config.sandbox(options=["--ro-bind", src, workdir(src)]), stdout=subprocess.PIPE, ).stdout @@ -629,7 +619,7 @@ def qemu_version(config: Config, binary: Path) -> GenericVersion: run( [binary, "--version"], stdout=subprocess.PIPE, - sandbox=config.sandbox(binary=binary), + sandbox=config.sandbox(), ).stdout.split()[3] ) @@ -650,10 +640,7 @@ def generate_scratch_fs(config: Config) -> Iterator[Path]: run( [f"mkfs.{fs}", "-L", "scratch", *extra.split(), workdir(Path(scratch.name))], stdout=subprocess.DEVNULL, - sandbox=config.sandbox( - binary=f"mkfs.{fs}", - options=["--bind", scratch.name, workdir(Path(scratch.name))], - ), + sandbox=config.sandbox(options=["--bind", scratch.name, workdir(Path(scratch.name))]), ) yield Path(scratch.name) @@ -708,7 +695,6 @@ def finalize_firmware_variables( "--loglevel", "WARNING", ], sandbox=config.sandbox( - binary=qemu, options=[ "--bind", ovmf_vars.name, workdir(Path(ovmf_vars.name)), "--ro-bind", ovmf.vars, workdir(ovmf.vars), @@ -717,11 +703,8 @@ def finalize_firmware_variables( ), ) # fmt: skip else: - tools = ( - Path("/") if any(qemu.is_relative_to(d) for d in config.extra_search_paths) else config.tools() - ) vars = ( - tools / ovmf.vars.relative_to("/") + config.tools() / ovmf.vars.relative_to("/") if config.qemu_firmware_variables == Path("microsoft") or not config.qemu_firmware_variables else config.qemu_firmware_variables ) @@ -745,7 +728,7 @@ def apply_runtime_size(config: Config, image: Path) -> None: "--offline=yes", workdir(image), ], - sandbox=config.sandbox(binary="systemd-repart", options=["--bind", image, workdir(image)]), + sandbox=config.sandbox(options=["--bind", image, workdir(image)]), ) # fmt: skip @@ -949,7 +932,7 @@ def register_machine(config: Config, pid: int, fname: Path) -> None: ], # fmt: skip foreground=False, env=os.environ | config.environment, - sandbox=config.sandbox(binary="busctl", relaxed=True), + sandbox=config.sandbox(relaxed=True), # systemd-machined might not be installed so let's ignore any failures unless running in debug mode. check=ARG_DEBUG.get(), stderr=None if ARG_DEBUG.get() else subprocess.DEVNULL, @@ -1045,7 +1028,7 @@ def run_qemu(args: Args, config: Config) -> None: "or provide a -kernel argument to mkosi qemu" ) - ovmf = find_ovmf_firmware(config, qemu, firmware) + ovmf = find_ovmf_firmware(config, firmware) # A shared memory backend might increase ram usage so only add one if actually necessary for virtiofsd. shm = [] @@ -1176,7 +1159,6 @@ def run_qemu(args: Args, config: Config) -> None: workdir(fname), ], # fmt: skip sandbox=config.sandbox( - binary="systemd-repart", options=[ "--bind", fname.parent, workdir(fname.parent), "--ro-bind", src, workdir(src), @@ -1405,7 +1387,6 @@ def run_qemu(args: Args, config: Config) -> None: env=os.environ | config.environment, foreground=True, sandbox=config.sandbox( - binary=qemu, network=True, devices=True, relaxed=True, @@ -1466,7 +1447,6 @@ def run_ssh(args: Args, config: Config) -> None: env=os.environ | config.environment, log=False, sandbox=config.sandbox( - binary="ssh", network=True, devices=True, relaxed=True, diff --git a/mkosi/resources/man/mkosi.1.md b/mkosi/resources/man/mkosi.1.md index 29f8f7fbb..79f199f7b 100644 --- a/mkosi/resources/man/mkosi.1.md +++ b/mkosi/resources/man/mkosi.1.md @@ -1228,8 +1228,12 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`, but the `mkosi.tools/` directory is found in the local directory it is automatically used for this purpose with the root directory as target. - Note if a binary is found in any of the paths configured with - `ExtraSearchPaths=`, the binary will be executed on the host. + Note that binaries found in any of the paths configured with + `ExtraSearchPaths=` will be executed with `/usr/` from the tools + tree instead of from the host. If the host distribution or release + does not match the tools tree distribution or release respectively, + this might result in failures when trying to execute binaries from + any of the extra search paths. If set to `default`, mkosi will automatically add an extra tools tree image and use it as the tools tree. diff --git a/mkosi/run.py b/mkosi/run.py index b777fffb7..042921b24 100644 --- a/mkosi/run.py +++ b/mkosi/run.py @@ -397,14 +397,12 @@ class SandboxProtocol(Protocol): def __call__( self, *, - binary: Optional[PathString], options: Sequence[PathString] = (), ) -> AbstractContextManager[list[PathString]]: ... def nosandbox( *, - binary: Optional[PathString], options: Sequence[PathString] = (), ) -> AbstractContextManager[list[PathString]]: return contextlib.nullcontext([]) diff --git a/mkosi/sysupdate.py b/mkosi/sysupdate.py index f23c2edfa..9b9a8a00b 100644 --- a/mkosi/sysupdate.py +++ b/mkosi/sysupdate.py @@ -51,7 +51,6 @@ def run_sysupdate(args: Args, config: Config) -> None: env=os.environ | config.environment, log=False, sandbox=config.sandbox( - binary=sysupdate, devices=True, network=True, relaxed=True, diff --git a/mkosi/tree.py b/mkosi/tree.py index df3e8247c..da6806776 100644 --- a/mkosi/tree.py +++ b/mkosi/tree.py @@ -26,7 +26,7 @@ def cp_version(*, sandbox: SandboxProtocol = nosandbox) -> GenericVersion: return GenericVersion( run( ["cp", "--version"], - sandbox=sandbox(binary="cp"), + sandbox=sandbox(), stdout=subprocess.PIPE, ) .stdout.splitlines()[0] @@ -52,7 +52,7 @@ def make_tree( if use_subvolumes != ConfigFeature.disabled: result = run( ["btrfs", "subvolume", "create", workdir(path, sandbox)], - sandbox=sandbox(binary="btrfs", options=["--bind", path.parent, workdir(path.parent, sandbox)]), + sandbox=sandbox(options=["--bind", path.parent, workdir(path.parent, sandbox)]), check=use_subvolumes == ConfigFeature.enabled, ).returncode else: @@ -117,7 +117,7 @@ def copy_tree( if src.is_dir(): cmdline += ["--no-target-directory"] - run(cmdline, sandbox=sandbox(binary="cp", options=options)) + run(cmdline, sandbox=sandbox(options=options)) # Subvolumes always have inode 256 so we can use that to check if a directory is a subvolume. if ( @@ -138,7 +138,7 @@ def copy_tree( result = run( ["btrfs", "subvolume", "snapshot", workdir(src, sandbox), workdir(dst, sandbox)], check=use_subvolumes == ConfigFeature.enabled, - sandbox=sandbox(binary="btrfs", options=options), + sandbox=sandbox(options=options), ).returncode if result != 0: @@ -161,7 +161,6 @@ def rmtree(*paths: Path, sandbox: SandboxProtocol = nosandbox) -> None: ["btrfs", "subvolume", "delete", *(workdir(p, sandbox) for p in subvolumes)], check=False, sandbox=sandbox( - binary="btrfs", options=flatten(("--bind", p.parent, workdir(p.parent, sandbox)) for p in subvolumes), ), stdout=subprocess.DEVNULL if not ARG_DEBUG.get() else None, @@ -173,7 +172,6 @@ def rmtree(*paths: Path, sandbox: SandboxProtocol = nosandbox) -> None: run( ["rm", "-rf", "--", *(workdir(p, sandbox) for p in filtered)], sandbox=sandbox( - binary="rm", options=flatten(("--bind", p.parent, workdir(p.parent, sandbox)) for p in filtered), ), ) diff --git a/mkosi/vmspawn.py b/mkosi/vmspawn.py index 452e119ce..95726168e 100644 --- a/mkosi/vmspawn.py +++ b/mkosi/vmspawn.py @@ -115,7 +115,6 @@ def run_vmspawn(args: Args, config: Config) -> None: env=os.environ | config.environment, log=False, sandbox=config.sandbox( - binary=cmdline[0], network=True, devices=True, relaxed=True, -- 2.47.3