From: Daan De Meyer Date: Mon, 6 May 2024 07:05:40 +0000 (+0200) Subject: Don't use scopes for virtiofs when using older unshare X-Git-Tag: v23.1~72 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=769bf2a5ec9b7c284a84aedbc1d499daf8c27038;p=thirdparty%2Fmkosi.git Don't use scopes for virtiofs when using older unshare unshare 2.37 is still shipped in Ubuntu Jammy and CentOS Stream 9 which doesn't have --map-users= and --map-groups=. In this case, let's not use scopes for virtiofsd to make sure that booting using virtiofsd still works. Also add a missing preexec_fn to become root if we're not using a scope. To make this work we have to move all the logic to decide whether we use a scope or not outside of run() as we need to conditionalize other arguments we provide to run() based on whether we use a scope or not. --- diff --git a/mkosi/qemu.py b/mkosi/qemu.py index 3402b53d4..13b725d4b 100644 --- a/mkosi/qemu.py +++ b/mkosi/qemu.py @@ -264,6 +264,7 @@ def start_swtpm(config: Config) -> Iterator[Path]: name=f"mkosi-swtpm-{config.machine_or_name()}", description=f"swtpm for {config.machine_or_name()}", ), + env=scope_env(), stdout=None if ARG_DEBUG.get() else subprocess.DEVNULL, ) @@ -300,6 +301,10 @@ def find_virtiofsd(*, root: Path = Path("/"), extra: Sequence[Path] = ()) -> Opt return None +def unshare_version() -> str: + return run(["unshare", "--version"], stdout=subprocess.PIPE).stdout.strip().split()[-1] + + @contextlib.contextmanager def start_virtiofsd(config: Config, directory: PathString, *, name: str, selinux: bool = False) -> Iterator[Path]: uidmap = Path(directory).stat().st_uid == INVOKING_USER.uid @@ -341,13 +346,18 @@ def start_virtiofsd(config: Config, directory: PathString, *, name: str, selinux cmdline += ["--fd", str(SD_LISTEN_FDS_START)] + name = f"mkosi-virtiofsd-{name}" + description = f"virtiofsd for {directory}" uid = gid = None runas = [] - if uidmap and os.getuid() != INVOKING_USER.uid: - uid = INVOKING_USER.uid - gid = INVOKING_USER.gid - elif not uidmap and os.getuid() != 0: + scope = [] + if uidmap: + uid = INVOKING_USER.uid if os.getuid() != INVOKING_USER.uid else None + gid = INVOKING_USER.gid if os.getuid() != INVOKING_USER.uid else None + scope = scope_cmd(name=name, description=description, user=uid, group=gid) + elif not uidmap and (os.getuid() == 0 or unshare_version() >= "2.38"): runas = become_root_cmd() + scope = scope_cmd(name=name, description=description) with spawn( cmdline, @@ -356,20 +366,17 @@ def start_virtiofsd(config: Config, directory: PathString, *, name: str, selinux # in the user namespace it spawns, so by specifying --uid 0 --gid 0 we'll get a userns with the current # uid/gid mapped to root in the userns. --cap-add=all is required to make virtiofsd work. Since it drops # capabilities itself, we don't bother figuring out the exact set of capabilities it needs. - user=uid, - group=gid, + user=uid if not scope else None, + group=gid if not scope else None, + preexec_fn=become_root if not scope else None, + env=scope_env() if scope else {}, sandbox=config.sandbox( binary=virtiofsd, mounts=[Mount(directory, directory)], options=["--uid", "0", "--gid", "0", "--cap-add", "all"], setup=runas, ), - scope=scope_cmd( - name=f"mkosi-virtiofsd-{name}", - description=f"virtiofsd for {directory}", - user=uid, - group=gid, - ), + scope=scope, ) as (proc, innerpid): yield path kill(proc, innerpid, signal.SIGTERM) @@ -479,6 +486,7 @@ def start_journal_remote(config: Config, sockfd: int) -> Iterator[None]: user=config.forward_journal.parent.stat().st_uid if INVOKING_USER.invoked_as_root else None, group=config.forward_journal.parent.stat().st_gid if INVOKING_USER.invoked_as_root else None, ), + env=scope_env(), foreground=False, ) as (proc, innerpid): yield @@ -685,6 +693,20 @@ def finalize_state(config: Config, cid: int) -> Iterator[None]: p.unlink(missing_ok=True) +def scope_env() -> dict[str, str]: + if not find_binary("systemd-run"): + return {} + elif os.getuid() != 0 and "DBUS_SESSION_BUS_ADDRESS" in os.environ and "XDG_RUNTIME_DIR" in os.environ: + return { + "DBUS_SESSION_BUS_ADDRESS": os.environ["DBUS_SESSION_BUS_ADDRESS"], + "XDG_RUNTIME_DIR": os.environ["XDG_RUNTIME_DIR"] + } + elif os.getuid() == 0 and "DBUS_SYSTEM_ADDRESS" in os.environ: + return {"DBUS_SYSTEM_ADDRESS" : os.environ["DBUS_SYSTEM_ADDRESS"]} + else: + return {} + + def scope_cmd( name: str, description: str, @@ -692,6 +714,9 @@ def scope_cmd( group: Optional[int] = None, properties: Sequence[str] = (), ) -> list[str]: + if not scope_env(): + return [] + return [ "systemd-run", "--system" if os.getuid() == 0 else "--user", diff --git a/mkosi/run.py b/mkosi/run.py index 2a4bf3ae5..76d55209e 100644 --- a/mkosi/run.py +++ b/mkosi/run.py @@ -218,20 +218,6 @@ def spawn( if "TMPDIR" in os.environ: env["TMPDIR"] = os.environ["TMPDIR"] - if scope: - if not find_binary("systemd-run"): - scope = [] - elif os.getuid() != 0 and "DBUS_SESSION_BUS_ADDRESS" in os.environ and "XDG_RUNTIME_DIR" in os.environ: - env["DBUS_SESSION_BUS_ADDRESS"] = os.environ["DBUS_SESSION_BUS_ADDRESS"] - env["XDG_RUNTIME_DIR"] = os.environ["XDG_RUNTIME_DIR"] - elif os.getuid() == 0 and "DBUS_SYSTEM_ADDRESS" in os.environ: - env["DBUS_SYSTEM_ADDRESS"] = os.environ["DBUS_SYSTEM_ADDRESS"] - else: - scope = [] - - if scope: - user = group = None - for e in ("SYSTEMD_LOG_LEVEL", "SYSTEMD_LOG_LOCATION"): if e in os.environ: env[e] = os.environ[e]