From: Daan De Meyer Date: Mon, 25 Sep 2023 11:57:29 +0000 (+0200) Subject: Mount over /etc/passwd when building an image and running build script X-Git-Tag: v18~39^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F1923%2Fhead;p=thirdparty%2Fmkosi.git Mount over /etc/passwd when building an image and running build script Various tools parse /etc/passwd, so let's make sure it contains the expected lines when building an image. Also, when running the build script, mount over /etc/passwd in the image as various build tools will also query the current user in /etc/passwd (e.g. the kernel's build script runs "whoami"). Also add a root entry to the /etc/passwd we generate so both the user running mkosi and root are covered. --- diff --git a/mkosi/__init__.py b/mkosi/__init__.py index 757974d54..6561994d1 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -349,7 +349,9 @@ def run_build_script(state: MkosiState) -> None: ], ) - with complete_step("Running build script…"), mount_build_overlay(state): + with complete_step("Running build script…"),\ + mount_build_overlay(state),\ + mount_passwd(state.name, state.uid, state.gid, state.root): bwrap( [state.config.build_script], network=state.config.with_network, @@ -986,7 +988,7 @@ def build_initrd(state: MkosiState) -> Path: with complete_step("Building initrd"): args, [config] = parse_config(cmdline) unlink_output(args, config) - build_image(args, config, state.uid, state.gid) + build_image(args, config, state.name, state.uid, state.gid) symlink.symlink_to(config.output_dir / config.output) @@ -1854,12 +1856,12 @@ def normalize_mtime(root: Path, mtime: Optional[int], directory: Optional[Path] os.utime(p, (mtime, mtime), follow_symlinks=False) -def build_image(args: MkosiArgs, config: MkosiConfig, uid: int, gid: int) -> None: +def build_image(args: MkosiArgs, config: MkosiConfig, name: str, uid: int, gid: int) -> None: manifest = Manifest(config) if config.manifest_format else None workspace = tempfile.TemporaryDirectory(dir=config.workspace_dir, prefix=".mkosi-tmp") with workspace, scopedenv({"TMPDIR" : workspace.name}): - state = MkosiState(args, config, Path(workspace.name), uid, gid) + state = MkosiState(args, config, Path(workspace.name), name, uid, gid) install_package_manager_trees(state) with mount_base_trees(state): @@ -2389,6 +2391,7 @@ def run_verb(args: MkosiArgs, presets: Sequence[MkosiConfig]) -> None: with complete_step(f"Building {config.preset or 'default'} image"),\ mount_tools(config.tools_tree),\ + mount_passwd(name, uid, gid),\ prepend_to_environ_path(config): # Create these as the invoking user to make sure they're owned by the user running mkosi. @@ -2402,7 +2405,7 @@ def run_verb(args: MkosiArgs, presets: Sequence[MkosiConfig]) -> None: run(["mkdir", "--parents", p], user=uid, group=gid) with acl_toggle_build(config, uid): - build_image(args, config, uid, gid) + build_image(args, config, name, uid, gid) # Make sure all build outputs that are not directories are owned by the user running mkosi. for p in config.output_dir.iterdir(): diff --git a/mkosi/mounts.py b/mkosi/mounts.py index f400f29ef..2ef6efc9f 100644 --- a/mkosi/mounts.py +++ b/mkosi/mounts.py @@ -132,15 +132,17 @@ def mount_usr(tree: Optional[Path], umount: bool = True) -> Iterator[None]: @contextlib.contextmanager -def mount_passwd(name: str, uid: int, gid: int, umount: bool = True) -> Iterator[None]: +def mount_passwd(name: str, uid: int, gid: int, root: Path = Path("/"), umount: bool = True) -> Iterator[None]: """ ssh looks up the running user in /etc/passwd and fails if it can't find the running user. To trick it, we mount over /etc/passwd with our own file containing our user in the user namespace. """ with tempfile.NamedTemporaryFile(prefix="mkosi.passwd", mode="w") as passwd: - passwd.write(f"{name}:x:{uid}:{gid}:{name}:/bin/sh\n") + passwd.write("root:x:0:0:root:/root:/bin/sh\n") + if uid != 0: + passwd.write(f"{name}:x:{uid}:{gid}:{name}:/home/{name}:/bin/sh\n") + passwd.flush() os.fchown(passwd.file.fileno(), uid, gid) - with mount(passwd.name, Path("/etc/passwd"), operation="--bind", umount=umount): - passwd.close() # Don't need the file anymore after it's mounted. + with mount(passwd.name, root / "etc/passwd", operation="--bind", umount=umount): yield diff --git a/mkosi/state.py b/mkosi/state.py index 949a3f33e..9492fbc39 100644 --- a/mkosi/state.py +++ b/mkosi/state.py @@ -10,10 +10,11 @@ from mkosi.util import umask class MkosiState: """State related properties.""" - def __init__(self, args: MkosiArgs, config: MkosiConfig, workspace: Path, uid: int, gid: int) -> None: + def __init__(self, args: MkosiArgs, config: MkosiConfig, workspace: Path, name: str, uid: int, gid: int) -> None: self.args = args self.config = config self.workspace = workspace + self.name = name self.uid = uid self.gid = gid