From a992d4ab1414d8a20c8b83bf8accf21ccb97bc92 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Mon, 25 Sep 2023 10:40:46 +0200 Subject: [PATCH] Set MKOSI_UID and MKOSI_GID when running scripts These point to the UID/GID of the user running mkosi and can be used to run commands as that user in scripts by using setpriv. --- mkosi/__init__.py | 16 ++++++++++++---- mkosi/resources/mkosi.md | 19 +++++++++++++------ mkosi/state.py | 4 +++- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/mkosi/__init__.py b/mkosi/__init__.py index b89a74ea5..88150c82e 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -265,6 +265,8 @@ def run_prepare_script(state: MkosiState, build: bool) -> None: SRCDIR=str(Path.cwd()), CHROOT_SRCDIR="/work/src", BUILDROOT=str(state.root), + MKOSI_UID=str(state.uid), + MKOSI_GID=str(state.gid), ) chroot: list[PathString] = chroot_cmd( @@ -319,6 +321,8 @@ def run_build_script(state: MkosiState) -> None: OUTPUTDIR=str(state.staging), CHROOT_OUTPUTDIR="/work/out", BUILDROOT=str(state.root), + MKOSI_UID=str(state.uid), + MKOSI_GID=str(state.gid), ) if state.config.build_dir is not None: @@ -369,6 +373,8 @@ def run_postinst_script(state: MkosiState) -> None: OUTPUTDIR=str(state.staging), CHROOT_OUTPUTDIR="/work/out", BUILDROOT=str(state.root), + MKOSI_UID=str(state.uid), + MKOSI_GID=str(state.gid), ) chroot = chroot_cmd( @@ -408,6 +414,8 @@ def run_finalize_script(state: MkosiState) -> None: OUTPUTDIR=str(state.staging), CHROOT_OUTPUTDIR="/work/out", BUILDROOT=str(state.root), + MKOSI_UID=str(state.uid), + MKOSI_GID=str(state.gid), ) chroot = chroot_cmd( @@ -978,7 +986,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) + build_image(args, config, state.uid, state.gid) symlink.symlink_to(config.output_dir / config.output) @@ -1842,12 +1850,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) -> None: +def build_image(args: MkosiArgs, config: MkosiConfig, 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)) + state = MkosiState(args, config, Path(workspace.name), uid, gid) install_package_manager_trees(state) with mount_base_trees(state): @@ -2390,7 +2398,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) + build_image(args, config, 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/resources/mkosi.md b/mkosi/resources/mkosi.md index 85092a4a6..8b88c28a4 100644 --- a/mkosi/resources/mkosi.md +++ b/mkosi/resources/mkosi.md @@ -1270,12 +1270,13 @@ Then, for each preset, we execute the following steps: To allow for image customization that cannot be implemented using mkosi's builtin features, mkosi supports running scripts at various points during the image build process that can customize the image as -needed. Scripts are executed on the host system with a customized -environment to simplify modifying the image. For each script, the -configured build sources (`BuildSources=`) are mounted into the current -working directory before running the script and `$SRCDIR` is set to -point to the current working directory. The following scripts are -supported: +needed. Scripts are executed on the host system as root (either real +root or root within the user namespace that mkosi created when running +unprivileged) with a customized environment to simplify modifying the +image. For each script, the configured build sources (`BuildSources=`) +are mounted into the current working directory before running the script +and `$SRCDIR` is set to point to the current working directory. The +following scripts are supported: * If **`mkosi.prepare`** (`PrepareScript=`) exists, it is first called with the `final` argument, right after the software packages are @@ -1363,6 +1364,12 @@ Scripts executed by mkosi receive the following environment variables: [SOURCE_DATE_EPOCH](https://reproducible-builds.org/specs/source-date-epoch/) for more information. +* `$MKOSI_UID` and `$MKOSI_GID` are the respectively the uid, gid of the + user that invoked mkosi, potentially translated to a uid in the user + namespace that mkosi is running in. These can be used in combination + with `setpriv` to run commands as the user that invoked mkosi (e.g. + `setpriv --reuid=$MKOSI_UID --regid=$MKOSI_GID --clear-groups `) + Additionally, when a script is executed, a few scripts are made available via `$PATH` to simplify common usecases. diff --git a/mkosi/state.py b/mkosi/state.py index 3b3b6d200..949a3f33e 100644 --- a/mkosi/state.py +++ b/mkosi/state.py @@ -10,10 +10,12 @@ from mkosi.util import umask class MkosiState: """State related properties.""" - def __init__(self, args: MkosiArgs, config: MkosiConfig, workspace: Path) -> None: + def __init__(self, args: MkosiArgs, config: MkosiConfig, workspace: Path, uid: int, gid: int) -> None: self.args = args self.config = config self.workspace = workspace + self.uid = uid + self.gid = gid with umask(~0o755): # Using a btrfs subvolume as the upperdir in an overlayfs results in EXDEV so make sure we create -- 2.47.2