diff --git a/.github/workflows/genesis-openembedded.yml b/.github/workflows/genesis-openembedded.yml index f3ffdf6..9a008c5 100644 --- a/.github/workflows/genesis-openembedded.yml +++ b/.github/workflows/genesis-openembedded.yml @@ -78,6 +78,7 @@ jobs: sudo -E prove -v -It/lib t/common-repo-gate.t prove -v t/openeuler.t prove -v t/openeuler_mock_template_include.t + prove -v t/openeuler_native_result_permissions.t prove -v -Ilib t/openeuler_native_trust_dbpath.t prove -v -Ilib t/openeuler_native_cpio_scan.t bats t/verify-xcatsn-install.bats diff --git a/MockBuildUtils.pm b/MockBuildUtils.pm index 2dca2e3..ae71337 100644 --- a/MockBuildUtils.pm +++ b/MockBuildUtils.pm @@ -25,6 +25,7 @@ our @EXPORT_OK = qw( cross_copy_genesis finalize_xcat_dep bump_dep_release_suffix createrepo_c_cmd sign_and_index_repo build_mock_uniqueext rpm_in_cell resolve_mock_cfg + native_owner_command NATIVE_BUILD_GID openeuler_build_target openeuler_repo_subdir recover_common_repository ); @@ -101,6 +102,25 @@ sub sh_quote { return "'$s'"; } +# The host group the native openEuler builds run as: xcatnative, gid 1000. The native mock overlays +# set chrootgid to it and chrootuid to the catalog's build_uid, so mock creates --resultdir and opens +# state.log, build.log and root.log in it as that uid and group. +use constant NATIVE_BUILD_GID => 1000; + +# native_owner_command: wrap a native build step so it runs against $overlay instead of the target's +# own mock configuration, and with the identity mock will write results as. +# +# A dep builder makes its own result directories before mock starts -- ipxe-xcat/mockbuild.pl calls +# make_path on $work_dir/srpm and $work_dir/rpm and passes each one to mock as --resultdir. The +# script runs as root, so without the gid and the umask below those directories are root root 0755 +# and mock, reloaded to chrootuid 1000, cannot open its logs in them. +sub native_owner_command { + my ($overlay, $mock_cfg, $command) = @_; + my $script = 'mount --bind ' . sh_quote($overlay) . ' ' . sh_quote($mock_cfg) + . ' && exec sh -c ' . sh_quote($command); + return 'unshare --mount --propagation private -- sh -c ' . sh_quote($script); +} + # print_step: print a step banner. sub print_step { my ($msg) = @_; diff --git a/mockbuild-all.pl b/mockbuild-all.pl index 176999a..54b3e27 100755 --- a/mockbuild-all.pl +++ b/mockbuild-all.pl @@ -1247,9 +1247,7 @@ sub source_rpm_build_command { sub native_owner_command { my ($plan, $target, $command, $uid) = @_; my $overlay = $plan->{overlays}{$uid} // die "Missing native mock overlay\n"; - my $script = 'mount --bind ' . sh_quote($overlay) . ' ' . sh_quote("/etc/mock/$target.cfg") - . ' && exec sh -c ' . sh_quote($command); - return 'unshare --mount --propagation private -- sh -c ' . sh_quote($script); + return MockBuildUtils::native_owner_command($overlay, "/etc/mock/$target.cfg", $command); } sub native_overlay { diff --git a/t/openeuler_native_result_permissions.t b/t/openeuler_native_result_permissions.t new file mode 100644 index 0000000..0e0418b --- /dev/null +++ b/t/openeuler_native_result_permissions.t @@ -0,0 +1,39 @@ +#!/usr/bin/perl +# A dep builder creates its own result directories before mock starts. ipxe-xcat/mockbuild.pl calls +# make_path on $work_dir/srpm and $work_dir/rpm and gives each one to mock as --resultdir, and it +# runs as root. The native openEuler overlay keeps chrootuid at the catalog's build_uid and +# chrootgid at the native build gid, so mock opens state.log, build.log and root.log in those +# directories as that uid. They are writable only if the step itself carries the native build gid +# and a group-writable umask. native_owner_command is where the step gets both. +use strict; +use warnings; +use Test::More; +use FindBin qw($RealBin); +use lib "$RealBin/.."; +use MockBuildUtils qw(native_owner_command NATIVE_BUILD_GID); + +my $gid = NATIVE_BUILD_GID; +my $overlay = '/work/native-1000.cfg'; +my $cfg = '/etc/mock/openeuler-24.03-ppc64le.cfg'; +my $step = "perl ipxe-xcat/mockbuild.pl --work-dir /tmp/ipxe-xcat --result-dir /results/ipxe-xcat"; +my $cmd = native_owner_command($overlay, $cfg, $step); + +like($cmd, qr/\bsetpriv\b[^'"]*--regid\s+\Q$gid\E\b/, + "the step runs under setpriv --regid $gid, so it creates its result directories in the native build group"); +like($cmd, qr/\bsetpriv\b[^'"]*--clear-groups\b/, + 'setpriv --clear-groups drops the supplementary groups root started with'); +like($cmd, qr/\bumask\s+0002\b/, + 'the step runs with umask 0002, so a directory it creates stays group writable'); + +my $umask = index($cmd, 'umask 0002'); +my $setpriv = index($cmd, 'setpriv'); +my $builder = index($cmd, 'ipxe-xcat/mockbuild.pl'); +ok($setpriv >= 0 && $setpriv < $umask, + 'setpriv comes before umask 0002, so the umask applies under the native build gid'); +ok($umask >= 0 && $umask < $builder, + 'umask 0002 comes before the builder, so make_path in the builder inherits it'); + +like($cmd, qr/mount --bind \S*\Q$overlay\E\S*\s+\S*\Q$cfg\E/, + 'the wrapper binds the overlay it was given over the target config, so chrootuid stays as the catalog declared it'); + +done_testing();