diff --git a/.github/workflows/xcat_test.yml b/.github/workflows/xcat_test.yml index 6949eaa8b..14b63787d 100644 --- a/.github/workflows/xcat_test.yml +++ b/.github/workflows/xcat_test.yml @@ -26,6 +26,9 @@ jobs: - name: Exercise image package policy if: ${{ !cancelled() }} run: prove xCAT-test/native/el10_image_package_policy.t + - name: Check Debian probe payload permissions + if: ${{ !cancelled() }} + run: bats xCAT-test/native/probe_debian_permissions.bats - name: Prepare xCAT syslog directory if: ${{ !cancelled() }} run: sudo install -d -o syslog -g adm -m 0750 /var/log/xcat diff --git a/xCAT-probe/debian/postinst b/xCAT-probe/debian/postinst index 5ced18096..19af47964 100644 --- a/xCAT-probe/debian/postinst +++ b/xCAT-probe/debian/postinst @@ -20,6 +20,12 @@ set -e case "$1" in configure) + probe_lib=/opt/xcat/probe/lib/perl/xCAT + if [ -d "$probe_lib" ] && [ ! -L "$probe_lib" ] && + [ "$(stat -c %a "$probe_lib")" = 644 ] && + ! dpkg-statoverride --list "$probe_lib" >/dev/null; then + chmod 755 "$probe_lib" + fi ;; abort-upgrade|abort-remove|abort-deconfigure) diff --git a/xCAT-probe/debian/rules b/xCAT-probe/debian/rules index bb1842ebe..7f1a3c4ba 100755 --- a/xCAT-probe/debian/rules +++ b/xCAT-probe/debian/rules @@ -42,8 +42,8 @@ binary-arch: dh_install -X".svn" chmod 755 `pwd`/debian/xcat-probe/opt/xcat/bin/* chmod 755 `pwd`/debian/xcat-probe/opt/xcat/probe/subcmds/* - chmod 644 `pwd`/debian/xcat-probe/opt/xcat/probe/lib/perl/* - chmod 644 `pwd`/debian/xcat-probe/opt/xcat/probe/lib/perl/xCAT/* + find `pwd`/debian/xcat-probe/opt/xcat/probe/lib -type d -exec chmod 755 {} + + find `pwd`/debian/xcat-probe/opt/xcat/probe/lib -type f -exec chmod 644 {} + # dh_installmenu # dh_installdebconf # dh_installlogrotate diff --git a/xCAT-test/README.md b/xCAT-test/README.md index 115fe5553..8582d7d73 100644 --- a/xCAT-test/README.md +++ b/xCAT-test/README.md @@ -8,7 +8,7 @@ language: | Perl unit tests | `xCAT-test/unit/*.t` | `prove -r xCAT-test/unit` | | Shell unit tests | `xCAT-test/bats/*.bats` | `bats -r xCAT-test/bats` | | Black-box service tests | `xCAT-test/blackboxtest/` | `python3 -m unittest discover -s tests`, then `blackboxtest run ` on a management node | -| Native host tests | `xCAT-test/native/*.t` | Explicit lab runs; see [native/README.md](native/README.md) | +| Native host tests | `xCAT-test/native/*.t`, `xCAT-test/native/*.bats` | Explicit lab runs; see [native/README.md](native/README.md) | | CLI functional tests | `xCAT-test/autotest/testcase/` and `xCAT-test/autotest/bundle/` | `xcattest -f -t ` or `xcattest -f -b ` | Use Perl `.t` tests for Perl modules, Perl scripts, templates, and repository diff --git a/xCAT-test/native/README.md b/xCAT-test/native/README.md index 58d3240c4..677dad2be 100644 --- a/xCAT-test/native/README.md +++ b/xCAT-test/native/README.md @@ -61,6 +61,15 @@ agent. These checks inspect real packages and repository output; they do not qualify package installation on every target architecture. On EL8, install both `python36` and `python3-rpm` to provide the interpreter and its RPM bindings. +The Debian probe permission test has a separate pull-request CI step. It needs +Debian build tools, BATS, bubblewrap, fakeroot, reprepro and +libfile-slurper-perl. Run it as an ordinary user on a Debian or Ubuntu host +with unprivileged namespaces enabled: + +``` +bats xCAT-test/native/probe_debian_permissions.bats +``` + Run the package and unprivileged namespace cases as an ordinary user: ``` diff --git a/xCAT-test/native/probe_debian_permissions.bats b/xCAT-test/native/probe_debian_permissions.bats new file mode 100644 index 000000000..846030036 --- /dev/null +++ b/xCAT-test/native/probe_debian_permissions.bats @@ -0,0 +1,140 @@ +#!/usr/bin/env bats + +load '../bats/helpers/shell_source' + +setup() +{ + if [ "$(uname -s)" != Linux ] || [ ! -f /etc/debian_version ]; then + skip 'requires Debian package tools' + fi + if [ "$(id -u)" -eq 0 ]; then + echo 'Run this test as an unprivileged user' >&2 + return 1 + fi + for tool in perl dpkg-buildpackage dpkg-deb fakeroot reprepro bwrap; do + command -v "$tool" >/dev/null || { + echo "Required utility is unavailable: $tool" >&2 + return 1 + } + done + fixture="$(mktemp -d "${BATS_TMPDIR:-${TMPDIR:-/tmp}}/probe-permissions.XXXXXX")" + checkout="$fixture/checkout" + destination="$fixture/repository" + payload="$fixture/payload" + mkdir -p "$checkout" "$payload" "$fixture/home" + for entry in builddebs.pl build-utils Version Release xCAT-probe perl-xCAT; do + cp -a "$(repo_path "$entry")" "$checkout/" + done + mkdir -p "$checkout/xCAT-probe/lib/perl/xCAT" + chmod 700 "$checkout/xCAT-probe/lib" "$checkout/xCAT-probe/lib/perl" \ + "$checkout/xCAT-probe/lib/perl/xCAT" + chmod 600 "$checkout/xCAT-probe/lib/perl/"*.pm + printf '1600000000\n' > "$checkout/Gitepoch" + printf '%040d\n' 0 > "$checkout/Gitinfo" +} + +teardown() +{ + if [ -n "${fixture:-}" ] && [ -d "$fixture" ]; then + chmod -R u+rwX "$fixture" + rm -rf "$fixture" + fi +} + +build_probe() +{ + cd "$checkout" || return 1 + run env -i HOME="$fixture/home" PATH=/usr/bin:/bin LC_ALL=C \ + perl builddebs.pl --package xCAT-probe --dist noble --release permissiontest \ + --dest "$destination" + [ "$status" -eq 0 ] || { echo "$output"; return 1; } + + packages=("$destination"/debs/xcat-probe_*_all.deb) + [ "${#packages[@]}" -eq 1 ] + [ -f "${packages[0]}" ] + run dpkg-deb -x "${packages[0]}" "$payload" + [ "$status" -eq 0 ] || { echo "$output"; return 1; } + + prefix="$payload/opt/xcat" + library_root="$prefix/probe/lib" + library="$library_root/perl" +} + +prepare_postinst() +{ + mkdir -p "$fixture/control" "$fixture/dpkg" + run dpkg-deb -e "${packages[0]}" "$fixture/control" + [ "$status" -eq 0 ] || { echo "$output"; return 1; } + sandbox=(env -i PATH=/usr/bin:/bin LC_ALL=C bwrap + --unshare-all --die-with-parent --new-session --ro-bind / / + --dev /dev + --tmpfs /opt --bind "$prefix" /opt/xcat + --bind "$fixture/dpkg" /var/lib/dpkg) +} + +run_postinst() +{ + run "${sandbox[@]}" /bin/sh "$fixture/control/postinst" "$@" + [ "$status" -eq 0 ] || { echo "$output"; return 1; } +} + +@test "the Debian probe payload preserves library modes and contents" { + build_probe + [ "$(stat -c %a "$library_root")" = 755 ] + [ "$(stat -c %a "$library")" = 755 ] + [ "$(stat -c %a "$library/xCAT")" = 755 ] + run find "$library_root" -type d ! -perm 0755 -print + [ "$status" -eq 0 ] + [ -z "$output" ] + run find "$library_root" -type f ! -perm 0644 -print + [ "$status" -eq 0 ] + [ -z "$output" ] + for helper in CommandUtils GlobalDef NetworkUtils PacketCapture ServiceNodeUtils; do + cmp "$(repo_path "perl-xCAT/xCAT/$helper.pm")" "$library/xCAT/$helper.pm" + done + cmp "$(repo_path xCAT-probe/lib/perl/probe_utils.pm)" "$library/probe_utils.pm" +} + +@test "packaged probe commands load their helpers without root privileges" { + build_probe + for subcommand in code_template detect_dhcpd discovery osdeploy xcatmn; do + [ "$(stat -c %a "$prefix/probe/subcmds/$subcommand")" = 755 ] + run env -i PATH=/usr/bin:/bin XCATROOT="$prefix" \ + "$prefix/probe/subcmds/$subcommand" -T + [ "$status" -eq 0 ] || { echo "$output"; return 1; } + [[ "$output" =~ ^\[ok\][[:space:]]*: ]] || { echo "$output"; return 1; } + done +} + +@test "package configuration repairs the old directory mode idempotently" { + build_probe + prepare_postinst + chmod 644 "$library/xCAT" + run_postinst configure 2.19.0 + [ "$(stat -c %a "$library/xCAT")" = 755 ] + run_postinst configure 2.19.0 + [ "$(stat -c %a "$library/xCAT")" = 755 ] +} + +@test "package configuration preserves overrides and unrelated modes" { + build_probe + prepare_postinst + chmod 644 "$library/xCAT" + run "${sandbox[@]}" dpkg-statoverride --add root root 644 /opt/xcat/probe/lib/perl/xCAT + [ "$status" -eq 0 ] || { echo "$output"; return 1; } + run "${sandbox[@]}" dpkg-statoverride --list /opt/xcat/probe/lib/perl/xCAT + [ "$status" -eq 0 ] || { echo "$output"; return 1; } + [ "$output" = 'root root 644 /opt/xcat/probe/lib/perl/xCAT' ] + run_postinst configure 2.19.0 + [ "$(stat -c %a "$library/xCAT")" = 644 ] + run "${sandbox[@]}" dpkg-statoverride --remove /opt/xcat/probe/lib/perl/xCAT + [ "$status" -eq 0 ] || { echo "$output"; return 1; } + chmod 700 "$library/xCAT" + run_postinst configure 2.19.0 + [ "$(stat -c %a "$library/xCAT")" = 700 ] + mv "$library/xCAT" "$library/private" + chmod 644 "$library/private" + ln -s private "$library/xCAT" + run_postinst configure 2.19.0 + [ "$(stat -c %a "$library/private")" = 644 ] +}