diff --git a/contrib/testing/runtime-path-check.sh b/contrib/testing/runtime-path-check.sh new file mode 100755 index 0000000..155dab7 --- /dev/null +++ b/contrib/testing/runtime-path-check.sh @@ -0,0 +1,122 @@ +#!/bin/sh +set -eu + +SERVICE=${BONGO_TEST_SERVICE:-bongo.service} +PACKAGE=${BONGO_TEST_PACKAGE:-mail-mta/bongo} +RUNTIME_DIR=${BONGO_TEST_RUNTIME_DIR:-/run/bongo} +STATE_DIR=${BONGO_TEST_STATE_DIR:-/var/lib/bongo} +TMPFILES_NAME=${BONGO_TEST_TMPFILES_NAME:-bongo.conf} + +if [ "${BONGO_ALLOW_LIVE_RUNTIME_TEST:-}" != 1 ]; then + echo "set BONGO_ALLOW_LIVE_RUNTIME_TEST=1 for the disposable host" >&2 + exit 2 +fi + +case "$RUNTIME_DIR" in + /run/bongo) ;; + *) + echo "refusing to remove unexpected runtime directory $RUNTIME_DIR" >&2 + exit 2 + ;; +esac + +systemctl_cmd() +{ + sudo -n /usr/bin/systemctl "$@" +} + +runtime_pid_ready() +{ + size=$(sudo -n /usr/bin/stat -c '%s' \ + "$RUNTIME_DIR/bongomanager.pid" 2>/dev/null || true) + [ -n "$size" ] && [ "$size" -gt 0 ] 2>/dev/null +} + +restore_service() +{ + systemctl_cmd reset-failed "$SERVICE" >/dev/null 2>&1 || true + systemctl_cmd start "$SERVICE" >/dev/null 2>&1 || true +} +trap restore_service EXIT HUP INT TERM + +package_paths=$(qlist -e "$PACKAGE") +if printf '%s\n' "$package_paths" | + grep -Eq '^(/run|/var/run)(/|$)|/bongomanager\.pid$|/[[:alnum:]_-]+\.pid$'; then + echo "$PACKAGE contains a runtime directory or PID file" >&2 + printf '%s\n' "$package_paths" | + grep -E '^(/run|/var/run)(/|$)|/bongomanager\.pid$|/[[:alnum:]_-]+\.pid$' >&2 + exit 1 +fi +echo "PASS package image contains no runtime directory or PID file" + +tmpfiles_path=/usr/lib/tmpfiles.d/$TMPFILES_NAME +if [ ! -f "$tmpfiles_path" ] || + ! grep -Eq "^d[[:space:]]+$RUNTIME_DIR[[:space:]]+0750[[:space:]]+bongo[[:space:]]+bongo" "$tmpfiles_path"; then + echo "$tmpfiles_path does not declare $RUNTIME_DIR correctly" >&2 + exit 1 +fi +echo "PASS installed tmpfiles declaration owns $RUNTIME_DIR" + +systemctl_cmd stop "$SERVICE" +remaining=30 +while [ "$remaining" -gt 0 ]; do + if ! /usr/bin/pgrep -x bongo-manager >/dev/null 2>&1; then + break + fi + sleep 1 + remaining=$((remaining - 1)) +done +if /usr/bin/pgrep -x bongo-manager >/dev/null 2>&1; then + echo "$SERVICE did not stop cleanly" >&2 + exit 1 +fi + +sudo -n /usr/bin/rm -rf -- "$RUNTIME_DIR" +if [ -e "$RUNTIME_DIR" ]; then + echo "could not remove $RUNTIME_DIR before recreation test" >&2 + exit 1 +fi + +sudo -n /usr/bin/systemd-tmpfiles --create "$TMPFILES_NAME" + +assert_path() +{ + path=$1 + expected_mode=$2 + expected_owner=$3 + actual=$(sudo -n /usr/bin/stat -c '%a %U:%G' "$path") + if [ "$actual" != "$expected_mode $expected_owner" ]; then + echo "$path has $actual, expected $expected_mode $expected_owner" >&2 + exit 1 + fi +} + +assert_path "$RUNTIME_DIR" 750 bongo:bongo +assert_path "$STATE_DIR" 750 bongo:bongo +assert_path "$STATE_DIR/dbf" 750 bongo:bongo +assert_path "$STATE_DIR/dbf/cookies" 700 bongo:bongo +assert_path /etc/bongo/ssl.d 750 root:bongo +echo "PASS tmpfiles recreated volatile and persistent directory ownership" + +systemctl_cmd start "$SERVICE" +remaining=30 +while [ "$remaining" -gt 0 ]; do + if systemctl_cmd is-active "$SERVICE" >/dev/null 2>&1; then + manager=$(systemctl_cmd show "$SERVICE" -p MainPID --value) + if [ -n "$manager" ] && [ "$manager" -gt 1 ] 2>/dev/null && + runtime_pid_ready; then + break + fi + fi + sleep 1 + remaining=$((remaining - 1)) +done +if ! systemctl_cmd is-active "$SERVICE" >/dev/null 2>&1 || + ! runtime_pid_ready; then + echo "$SERVICE did not create its runtime PID file after recreation" >&2 + exit 1 +fi + +trap - EXIT HUP INT TERM +echo "PASS manager PID $manager uses recreated $RUNTIME_DIR" +echo "PASS runtime path lifecycle; service remains active" diff --git a/docs/release-testing-0.7.md b/docs/release-testing-0.7.md index 2bf360b..ede4862 100644 --- a/docs/release-testing-0.7.md +++ b/docs/release-testing-0.7.md @@ -98,7 +98,7 @@ inputs and outputs is absent from the ZIP. | CFG-11 | DKIM/SPF/DMARC discovery, key import/generation, DNS hints, and task | [PASS](test-evidence/0.7-r1.md#cfg-11) | | | | CFG-12 | Manager start, reload, restart, clean stop, stale PID, and crash recovery | [PASS](test-evidence/0.7-r1.md#cfg-12) | | | | CFG-13 | Every agent starts as intended; permanent/effective privilege drops and supervisor exceptions are bounded | [PASS](test-evidence/0.7-r1.md#cfg-13) | | | -| CFG-14 | tmpfiles creates runtime paths; package contains no runtime PID directories | | | | +| CFG-14 | tmpfiles creates runtime paths; package contains no runtime PID directories | [PASS](test-evidence/0.7-r1.md#cfg-14) | | | | CFG-15 | systemd ordering, hardening, scanner timer, and worker scheduling | | | | | CFG-16 | Timezone cache rebuild uses the installed checked-in snapshot | | | | diff --git a/docs/test-evidence/0.7-r1.md b/docs/test-evidence/0.7-r1.md index b584ee3..2fb704d 100644 --- a/docs/test-evidence/0.7-r1.md +++ b/docs/test-evidence/0.7-r1.md @@ -1231,3 +1231,49 @@ BONGO_ALLOW_LIVE_PRIVILEGE_TEST=1 \ Runtime configuration: the installed Gentoo live package and existing disposable Bongo configuration were used. The check was read-only and did not alter process, configuration, mailbox, Queue, or credential state. + +## CFG-14 + +Result: **PASS** + +The Gentoo live package was rebuilt and reinstalled after overlay commit +`0ee4d1e1`. The ebuild now passes the versioned Gentoo documentation path to +CMake, so the installed documentation lives below +`/usr/share/doc/bongo-9999`; the former unversioned +`/usr/share/doc/bongo` staging path and its QA notice are gone. The installed +package contains 1,436 paths, none below `/run` or `/var/run`, and no runtime +PID file. + +`contrib/testing/runtime-path-check.sh` then stopped Bongo cleanly, removed +only the guarded volatile `/run/bongo` directory, and invoked the installed +`bongo.conf` tmpfiles declaration. It recreated `/run/bongo`, the persistent +state roots, and the TLS directory with the declared ownership and modes: + +```text +750 bongo:bongo /run/bongo +750 bongo:bongo /var/lib/bongo +750 bongo:bongo /var/lib/bongo/dbf +700 bongo:bongo /var/lib/bongo/dbf/cookies +750 root:bongo /etc/bongo/ssl.d +``` + +After recreation the service started normally, placed its manager PID file +below `/run/bongo`, and remained active/running as manager PID 1054117 with +systemd result `success`. + +```sh +BONGO_ALLOW_LIVE_RUNTIME_TEST=1 \ + ./contrib/testing/runtime-path-check.sh +``` + +- Gentoo packaging source: overlay commit `0ee4d1e1`. +- Complete rebuilt CTest result: 82 of 82 tests passed in 6.38 seconds. +- Live result: package-image, tmpfiles declaration, directory recreation, + ownership/mode, PID placement, and service recovery checks all passed. + +Runtime configuration: the installed Gentoo live package and existing +disposable Bongo configuration were used. The test intentionally removed only +the volatile `/run/bongo` tree while the service was stopped, recreated it +through systemd-tmpfiles, and left the service active. Persistent mail, +configuration, Queue, credential, and certificate contents were not removed +or modified.