feat(backup): add audited backup listing and restoration

This commit is contained in:
2026-10-13 22:23:50 +03:00
parent 67a9e3fe03
commit 72c0d21977
5 changed files with 596 additions and 28 deletions
+227 -3
View File
@@ -11,7 +11,7 @@ source "${PROJECT_DIR}/wg-manager.sh"
case $# in
0) ;;
1) case $1 in config|parser|status|list|list-redirected|list-deterministic|transaction|rollback|locking|backups|mutation-preserves-source|secret-modes|add-client|address-allocation|add-collisions|add-rollback|no-secret-logs|add-golden-config|add-invalid-names|show-client|config-export|qr|path-safety|no-secret-default-output|remove-client|remove-cancel|remove-exact-block|remove-rollback|archive-client|archive-path-safety|remove-preserves-neighbors|diagnostics|diagnostics-systemd|diagnostics-firewall|diagnostics-forwarding|diagnostics-runtime-diff|diagnostics-read-only|diagnostics-exit-codes) ;; *) printf 'Ошибка: неизвестная группа тестов или лишние аргументы\n' >&2; exit 64 ;; esac ;;
1) case $1 in config|parser|status|list|list-redirected|list-deterministic|transaction|rollback|locking|backups|mutation-preserves-source|secret-modes|add-client|address-allocation|add-collisions|add-rollback|no-secret-logs|add-golden-config|add-invalid-names|show-client|config-export|qr|path-safety|no-secret-default-output|remove-client|remove-cancel|remove-exact-block|remove-rollback|archive-client|archive-path-safety|remove-preserves-neighbors|diagnostics|diagnostics-systemd|diagnostics-firewall|diagnostics-forwarding|diagnostics-runtime-diff|diagnostics-read-only|diagnostics-exit-codes|audit|audit-no-secrets|backup-list|restore|restore-invalid|restore-rollback|backup-path-safety|runtime-file-modes) ;; *) printf 'Ошибка: неизвестная группа тестов или лишние аргументы\n' >&2; exit 64 ;; esac ;;
*) printf 'Ошибка: неизвестная группа тестов или лишние аргументы\n' >&2; exit 64 ;;
esac
test_group=${1-}
@@ -913,7 +913,7 @@ run_m4_tests() {
assert_contains '192.0.2.3/32' "$(<"${CLIENT_DIR}/alice.conf")" 'allocated address'
assert_contains 'PresharedKey = GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG=' "$(<"$fixture")" 'server peer preshared key'
assert_equal '' "$(grep -E 'EEEE|FFFF|GGGG|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' "$TEST_MOCK_LOG" || true)" 'mock log without key material'
local -a add_log_lines=(); mapfile -t add_log_lines <"$TEST_MOCK_LOG"
local -a add_log_lines=(); mapfile -t add_log_lines < <(grep -E '^(flock -n [0-9]+$|wg show wg0 (public-key|dump)$|wg genkey$|wg pubkey$|wg genpsk$|wg-quick strip |wg syncconf )' "$TEST_MOCK_LOG")
assert_equal 9 "${#add_log_lines[@]}" 'полный порядок add внешних вызовов'
assert_equal 1 "$(grep -c '^wg show wg0 public-key$' "$TEST_MOCK_LOG" || true)" 'ровно один server public-key'
assert_equal 1 "$(grep -c '^wg genkey$' "$TEST_MOCK_LOG" || true)" 'ровно один genkey'
@@ -2029,7 +2029,7 @@ run_m3_tests() {
assert_contains $'wg-quick strip ' "$transaction_log" 'argv strip'
assert_contains $'wg syncconf wg0 ' "$transaction_log" 'argv syncconf'
assert_contains $'wg show wg0 dump' "$transaction_log" 'argv verification dump'
mapfile -t transaction_lines <"$TEST_MOCK_LOG"
mapfile -t transaction_lines < <(grep -E '^(flock -n [0-9]+$|wg-quick strip |wg syncconf |wg show )' "$TEST_MOCK_LOG")
assert_equal 5 "${#transaction_lines[@]}" 'точно пять внешних transaction вызовов'
assert_equal 1 "$([[ ${transaction_lines[0]} =~ ^flock\ -n\ [0-9]+$ ]] && printf 1 || printf 0)" 'первый вызов flock'
assert_equal 1 "$([[ ${transaction_lines[1]} == 'wg-quick strip '* && ${transaction_lines[2]} == 'wg-quick strip '* ]] && printf 1 || printf 0)" 'два strip подряд'
@@ -2788,6 +2788,223 @@ EOF
fi
}
run_m8_tests() {
local config_file=$TEST_ROOT/m8.conf fixture=$TEST_ROOT/m8-wg.conf output status backup audit_content audit_text first_backup second_backup replacement restore_before valid_count manual_now newest_stamp manifest_replacement retained manifest_before_hash
cp -- "$PROJECT_DIR/tests/fixtures/m3/source.conf" "$fixture"; chmod 600 -- "$fixture"
write_m2_config "$config_file" "$fixture"
replace_config_line "$config_file" 'CLIENT_DIR=' "CLIENT_DIR=\"$TEST_ROOT/m8-clients\""
export WG_MANAGER_TEST_MODE=1 WG_MANAGER_CONFIG=$config_file WG_MANAGER_BACKUP_DIR=$TEST_ROOT/m8-backups WG_MANAGER_LOG_DIR=$TEST_ROOT/m8-logs WG_MANAGER_METADATA_DIR=$TEST_ROOT/m8-meta WG_MANAGER_ARCHIVE_DIR=$TEST_ROOT/m8-archive WG_MANAGER_LOCK_FILE=$TEST_ROOT/m8.lock WG_MANAGER_NOW=1700000100 WG_MOCK_INTERFACE=wg0
mkdir -p -- "$TEST_ROOT/m8-backups" "$TEST_ROOT/m8-logs" "$TEST_ROOT/m8-meta" "$TEST_ROOT/m8-clients" "$TEST_ROOT/m8-archive"
chmod 700 -- "$TEST_ROOT/m8-backups" "$TEST_ROOT/m8-logs" "$TEST_ROOT/m8-meta" "$TEST_ROOT/m8-clients" "$TEST_ROOT/m8-archive"
set_runtime_paths; load_config
transaction_builder_copy() { cp -- "$WG_CONF" "$1"; }
transaction_builder_fail() { return "$EXIT_VALIDATION"; }
case $test_group in
audit)
begin_test 'audit covers transaction backup apply and rollback outcomes'
export WG_MOCK_DUMP_FILE="$PROJECT_DIR/tests/fixtures/m3/runtime.dump"
: >"$TEST_MOCK_LOG"
set +e; run_transaction update audit transaction_builder_copy >/dev/null 2>&1; status=$?; set -e
assert_equal 0 "$status" 'successful transaction is audited'
audit_text=$(<"$LOG_DIR/wg-manager.log")
assert_equal 1 "$(grep -c '^.*operation=backup .*outcome=success ' <<<"$audit_text")" 'backup event is emitted once'
assert_equal 1 "$(grep -c '^.*operation=apply .*outcome=success ' <<<"$audit_text")" 'apply event is emitted once'
export WG_SYNC_FAIL_ONCE_FILE="$TEST_ROOT/m8-sync-fail"
set +e; run_transaction update audit transaction_builder_copy >/dev/null 2>&1; status=$?; set -e
unset WG_SYNC_FAIL_ONCE_FILE
assert_equal "$EXIT_OPERATION" "$status" 'failed apply with successful rollback keeps operation status'
assert_contains 'operation=rollback' "$(<"$LOG_DIR/wg-manager.log")" 'successful rollback is audited'
export WG_SYNC_FAIL_ONCE_FILE="$TEST_ROOT/m8-sync-fail-critical" TRANSACTION_ROLLBACK_REPLACE_FAIL=1
set +e; run_transaction update audit transaction_builder_copy >/dev/null 2>&1; status=$?; set -e
unset WG_SYNC_FAIL_ONCE_FILE TRANSACTION_ROLLBACK_REPLACE_FAIL
assert_equal "$EXIT_ROLLBACK" "$status" 'rollback failure keeps critical status'
assert_contains 'operation=rollback' "$(<"$LOG_DIR/wg-manager.log")" 'critical rollback is audited'
assert_equal '' "$(grep -E 'PrivateKey|PresharedKey|EEEE|GGGG|BBBBBBBBBBBBBBBB' "$LOG_DIR/wg-manager.log" || true)" 'transaction audit contains no secrets'
: >"$LOG_DIR/wg-manager.log"
set +e; run_transaction update preflight transaction_builder_fail >/dev/null 2>&1; status=$?; set -e
assert_equal "$EXIT_VALIDATION" "$status" 'pre-apply builder failure retains validation status'
assert_equal 0 "$(grep -Ec 'operation=(apply|rollback) ' "$LOG_DIR/wg-manager.log" || true)" 'pre-apply failure emits no apply or rollback event'
set +e; output=$(list_audit 1 2>&1); status=$?; set -e
assert_equal 0 "$status" 'audit listing accepts limit one'
assert_equal 1 "$(grep -c '^timestamp=' <<<"$output")" 'audit listing limit one returns one record'
set +e; list_audit 0 >/dev/null 2>&1; status=$?; set -e
assert_equal "$EXIT_USAGE" "$status" 'audit listing rejects zero limit'
set +e; list_audit 1001 >/dev/null 2>&1; status=$?; set -e
assert_equal "$EXIT_USAGE" "$status" 'audit listing rejects excessive limit'
: >"$LOG_DIR/wg-manager.log"
local audit_index audit_pid audit_wait_status
local -a audit_pids=()
for audit_index in {1..10}; do
( AUDIT_CLIENT=worker write_audit_event audit "worker${audit_index}" success success ) & audit_pids+=($!)
done
audit_wait_status=0
for audit_pid in "${audit_pids[@]}"; do wait "$audit_pid" || audit_wait_status=1; done
assert_equal 0 "$audit_wait_status" 'concurrent audit writers all complete'
assert_equal 10 "$(grep -Ec '^timestamp=.* operation=audit target=worker[0-9]+ ' "$LOG_DIR/wg-manager.log" || true)" 'concurrent audit records remain complete'
assert_equal 8 "$(awk 'length($0)>0 {print NF}' "$LOG_DIR/wg-manager.log" | sort -u | tr -d '\n')" 'concurrent audit records have stable field counts'
;;
audit-no-secrets)
begin_test 'audit rejects unsafe fields and filters hostile records'
set +e; write_audit_event 'bad operation' alice success success >/dev/null 2>&1; status=$?; set -e
assert_equal "$EXIT_VALIDATION" "$status" 'unsafe operation is rejected'
audit_content=''; [[ -e $LOG_DIR/wg-manager.log ]] && audit_content=$(grep -E 'PrivateKey|PresharedKey|\[Interface\]|\[Peer\]' "$LOG_DIR/wg-manager.log" || true)
assert_equal '' "$audit_content" 'audit output has no config content'
printf '%s\n' 'PrivateKey=EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE=' 'malformed hostile line' >"$LOG_DIR/wg-manager.log"
set +e; output=$(list_audit 2>&1); status=$?; set -e
assert_equal 0 "$status" 'malformed audit input is handled safely'
assert_equal '' "$(grep -E 'PrivateKey|EEEE|malformed' <<<"$output" || true)" 'hostile audit input is never emitted'
assert_equal '' "$(grep -E 'PrivateKey|PresharedKey|EEEE|GGGG|BBBBBBBBBBBBBBBB' <<<"$output" || true)" 'filtered audit output is secret-free'
;;
backup-list)
begin_test 'backup listing returns valid newest entries and ignores hostile entries'
backup=$(create_backup "$WG_CONF" manual "$WG_INTERFACE")
first_backup=${backup##*/}
export WG_MANAGER_NOW=1700000200
backup=$(create_backup "$WG_CONF" manual "$WG_INTERFACE")
second_backup=${backup##*/}
export WG_MANAGER_NOW=1700000100
mkdir -- "$BACKUP_DIR/20231114T221500Z-wg0-777"; chmod 700 -- "$BACKUP_DIR/20231114T221500Z-wg0-777"
printf 'INTERFACE=wg0\nTIMESTAMP=20231114T221500Z\nOPERATION=manual\nTARGET=bad\nSOURCE_MODE=600\nSOURCE_UID=0\nSOURCE_GID=0\nCONFIG=wg.conf\nEXEC=touch %s\n' "$TEST_ROOT/m8-hostile-sentinel" >"$BACKUP_DIR/20231114T221500Z-wg0-777/manifest"; chmod 600 -- "$BACKUP_DIR/20231114T221500Z-wg0-777/manifest"
set +e; output=$(list_backups 2>&1); status=$?; set -e
assert_equal 0 "$status" 'backup list succeeds'
assert_contains "$first_backup" "$output" 'first valid backup appears in list'
assert_contains "$second_backup" "$output" 'newer valid backup appears in list'
assert_equal 0 "$(grep -c '20231114T221500Z-wg0-777' <<<"$output")" 'invalid backup name is not listed'
assert_file_absent "$TEST_ROOT/m8-hostile-sentinel" 'manifest payload is never executed'
assert_equal 0 "$(grep -c 'Резервные копии отсутствуют' <<<"$output")" 'valid list is not reported empty'
mv -- "$BACKUP_DIR" "$TEST_ROOT/m8-backups-missing"
set +e; output=$(list_backups 2>&1); status=$?; set -e
assert_equal 0 "$status" 'missing backup root is an empty list'
assert_contains 'Резервные копии отсутствуют' "$output" 'missing backup root is reported safely'
mv -- "$TEST_ROOT/m8-backups-missing" "$BACKUP_DIR"
manifest_replacement=$TEST_ROOT/m8-manifest-replacement
manifest_before_hash=$(sha256sum -- "$BACKUP_DIR/$first_backup/manifest" | awk '{print $1}')
cp -- "$BACKUP_DIR/$first_backup/manifest" "$manifest_replacement"
replace_config_line "$manifest_replacement" 'TARGET=' 'TARGET=swapped'
export LIST_BACKUPS_REPLACE_MANIFEST=$manifest_replacement
set +e; output=$(list_backups 2>&1); status=$?; set -e
unset LIST_BACKUPS_REPLACE_MANIFEST LIST_BACKUPS_REPLACE_DONE
assert_equal 0 "$status" 'backup list rejects a swapped manifest safely'
assert_equal 0 "$(grep -Ec '(^|[[:space:]])swapped([[:space:]]|$)' <<<"$output")" 'swapped manifest fields are never displayed'
assert_equal "$manifest_before_hash" "$(sha256sum -- "$BACKUP_DIR/$first_backup/manifest" | awk '{print $1}')" 'swapped manifest is restored byte-for-byte'
;;
restore)
begin_test 'restore applies pinned backup and creates current-state backup'
local old_config restore_id restored_hash current_backup_count
old_config=$TEST_ROOT/m8-old.conf; cp -- "$fixture" "$old_config"
backup=$(create_backup "$fixture" manual "$WG_INTERFACE"); restore_id=${backup##*/}
printf '# changed-live-state\n' >>"$fixture"
export WG_MOCK_DUMP_FILE="$PROJECT_DIR/tests/fixtures/m3/runtime.dump"
set +e; output=$(restore_backup "$restore_id" --yes 2>&1); status=$?; set -e
assert_equal 0 "$status" 'restore succeeds'
assert_equal "$(sha256sum -- "$old_config" | awk '{print $1}')" "$(sha256sum -- "$fixture" | awk '{print $1}')" 'restore replaces live config with backup bytes'
assert_contains "operation=restore" "$(<"$LOG_DIR/wg-manager.log")" 'restore audit is emitted'
current_backup_count=$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d -name '*-wg0-*' | wc -l)
assert_equal 1 "$(( current_backup_count >= 2 ? 1 : 0 ))" 'restore keeps source and current-state backups'
assert_contains 'Резервная копия:' "$output" 'restore preview identifies timestamp'
;;
restore-invalid)
begin_test 'restore rejects unsafe identifiers and noninteractive confirmation'
local invalid_restore_status restore_id
for invalid_id in '../unsafe' '-bad' '2023X114T221500Z-wg0-001' 'nested/item'; do
set +e; restore_backup "$invalid_id" --yes >/dev/null 2>&1; invalid_restore_status=$?; set -e
assert_equal "$EXIT_VALIDATION" "$invalid_restore_status" "restore rejects $invalid_id"
done
backup=$(create_backup "$fixture" manual "$WG_INTERFACE"); restore_id=${backup##*/}
set +e; restore_backup "$restore_id" >/dev/null 2>&1; invalid_restore_status=$?; set -e
assert_equal "$EXIT_USAGE" "$invalid_restore_status" 'restore requires --yes without a terminal'
cp -- "$fixture" "$TEST_ROOT/m8-invalid-live.conf"
replace_config_line "$backup/manifest" 'INTERFACE=' 'INTERFACE=wg9'
set +e; restore_backup "$restore_id" --yes >/dev/null 2>&1; invalid_restore_status=$?; set -e
assert_equal "$EXIT_VALIDATION" "$invalid_restore_status" 'restore rejects a backup for another interface'
cp -- "$backup/manifest" "$TEST_ROOT/m8-invalid-manifest"
replace_config_line "$TEST_ROOT/m8-invalid-manifest" 'INTERFACE=' "INTERFACE=$WG_INTERFACE"
cp -- "$TEST_ROOT/m8-invalid-manifest" "$backup/manifest"
printf '[Broken]\n' >>"$backup/wg.conf"
set +e; restore_backup "$restore_id" --yes >/dev/null 2>&1; invalid_restore_status=$?; set -e
assert_equal "$EXIT_VALIDATION" "$invalid_restore_status" 'restore rejects a malformed backup config'
assert_equal "$(sha256sum -- "$TEST_ROOT/m8-invalid-live.conf" | awk '{print $1}')" "$(sha256sum -- "$fixture" | awk '{print $1}')" 'invalid restore leaves live config unchanged'
;;
restore-rollback)
begin_test 'restore apply failure rolls back disk and reports audit outcome'
local restore_source restore_id rollback_before
restore_source=$TEST_ROOT/m8-rollback.conf; cp -- "$fixture" "$restore_source"
backup=$(create_backup "$fixture" manual "$WG_INTERFACE"); restore_id=${backup##*/}
printf '# restore-candidate\n' >>"$fixture"; rollback_before=$(sha256sum -- "$fixture")
export WG_SYNC_FAIL_ONCE_FILE="$TEST_ROOT/m8-restore-sync-fail" WG_MOCK_DUMP_FILE="$PROJECT_DIR/tests/fixtures/m3/runtime.dump"
set +e; output=$(restore_backup "$restore_id" --yes 2>&1); status=$?; set -e
unset WG_SYNC_FAIL_ONCE_FILE
assert_equal "$EXIT_OPERATION" "$status" 'restore apply failure returns operation status'
assert_equal "$rollback_before" "$(sha256sum -- "$fixture")" 'failed restore restores live disk'
assert_contains 'operation=rollback' "$(<"$LOG_DIR/wg-manager.log")" 'restore rollback is audited'
backup=$(create_backup "$fixture" manual "$WG_INTERFACE"); restore_id=${backup##*/}
printf '# restore-critical-candidate\n' >>"$fixture"
export WG_SYNC_FAIL_ONCE_FILE="$TEST_ROOT/m8-restore-sync-fail-critical" TRANSACTION_ROLLBACK_REPLACE_FAIL=1
set +e; output=$(restore_backup "$restore_id" --yes 2>&1); status=$?; set -e
unset WG_SYNC_FAIL_ONCE_FILE TRANSACTION_ROLLBACK_REPLACE_FAIL
assert_equal "$EXIT_ROLLBACK" "$status" 'restore rollback failure is critical'
assert_contains 'КРИТИЧНО' "$output" 'critical restore rollback is diagnosed'
;;
backup-path-safety)
begin_test 'backup path safety rejects symlinked root'
mv -- "$BACKUP_DIR" "$TEST_ROOT/m8-backups-real"; ln -s -- "$TEST_ROOT/m8-backups-real" "$BACKUP_DIR"
set +e; output=$(list_backups 2>&1); status=$?; set -e
assert_failure "$status" 'symlinked backup directory is rejected'
rm -f -- "$BACKUP_DIR"; mv -- "$TEST_ROOT/m8-backups-real" "$BACKUP_DIR"
export MOCK_FLOCK_BUSY=1
set +e; output=$(manual_backup 2>&1); status=$?; set -e
unset MOCK_FLOCK_BUSY
assert_equal "$EXIT_OPERATION" "$status" 'manual backup respects lock contention'
backup=$(create_backup "$WG_CONF" manual "$WG_INTERFACE"); restore_id=${backup##*/}
replacement=$TEST_ROOT/m8-replacement.conf; cp -- "$WG_CONF" "$replacement"; printf '# replacement-source\n' >>"$replacement"
restore_before=$(sha256sum -- "$WG_CONF" | awk '{print $1}')
export RESTORE_PREOPEN_REPLACEMENT=$replacement
set +e; output=$(restore_backup "$restore_id" --yes 2>&1); status=$?; set -e
unset RESTORE_PREOPEN_REPLACEMENT
assert_equal "$EXIT_VALIDATION" "$status" 'restore rejects source replacement before open'
assert_equal "$restore_before" "$(sha256sum -- "$WG_CONF" | awk '{print $1}')" 'pre-open replacement does not alter live config'
replacement=$TEST_ROOT/m8-lock-replacement; printf 'lock replacement\n' >"$replacement"; chmod 600 -- "$replacement"
export MANUAL_BACKUP_PREOPEN_REPLACEMENT=$replacement
set +e; output=$(manual_backup 2>&1); status=$?; set -e
unset MANUAL_BACKUP_PREOPEN_REPLACEMENT
assert_equal "$EXIT_VALIDATION" "$status" 'manual backup rejects lock replacement before open'
replacement=$TEST_ROOT/m8-config-replacement; cp -- "$WG_CONF" "$replacement"; printf '# config replacement\n' >>"$replacement"
restore_before=$(sha256sum -- "$WG_CONF" | awk '{print $1}')
export MANUAL_BACKUP_PRECOPY_REPLACEMENT=$replacement
set +e; output=$(manual_backup 2>&1); status=$?; set -e
unset MANUAL_BACKUP_PRECOPY_REPLACEMENT
assert_equal "$EXIT_VALIDATION" "$status" 'manual backup rejects config replacement before copy'
assert_equal "$restore_before" "$(sha256sum -- "$WG_CONF" | awk '{print $1}')" 'config replacement restores original source'
;;
runtime-file-modes)
begin_test 'manual backup runtime files are restrictive'
set +e; backup=$(manual_backup 2>"$TEST_ROOT/m8-manual-backup.err"); status=$?; set -e
assert_equal 0 "$status" 'manual backup succeeds through locked path'
assert_file_exists "$backup/manifest" 'manual backup manifest exists'
assert_equal 700 "$(stat -c '%a' -- "$BACKUP_DIR")" 'backup root is 0700'
assert_equal 700 "$(stat -c '%a' -- "$backup")" 'backup directory is 0700'
assert_equal 600 "$(stat -c '%a' -- "$backup/manifest")" 'manifest is 0600'
assert_equal 600 "$(stat -c '%a' -- "$backup/wg.conf")" 'backup config is 0600'
assert_contains 'operation=manual-backup' "$(<"$LOG_DIR/wg-manager.log")" 'manual backup is audited'
replace_config_line "$config_file" 'BACKUP_RETENTION=' 'BACKUP_RETENTION="2"'
for manual_now in 1700000300 1700000400 1700000500; do
export WG_MANAGER_NOW=$manual_now
set +e; manual_backup >/dev/null 2>&1; status=$?; set -e
assert_equal 0 "$status" "manual backup succeeds at timestamp $manual_now"
done
valid_count=0
for retained in "$BACKUP_DIR"/*; do
validate_backup_manifest "$retained" && ((valid_count += 1))
done
assert_equal 2 "$valid_count" 'manual backup retention keeps exactly two valid backups'
newest_stamp=$(date -u -d '@1700000500' '+%Y%m%dT%H%M%SZ')
assert_equal 1 "$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d -name "${newest_stamp}-wg0-*" | wc -l)" 'retention keeps newest manual backup'
assert_equal 700 "$(stat -c '%a' -- "$LOG_DIR")" 'log directory remains 0700'
assert_equal 600 "$(stat -c '%a' -- "$LOCK_FILE")" 'manual lock remains 0600'
;;
esac
}
run_m7_tests() {
local config_file=$TEST_ROOT/diag.conf fixture=$TEST_ROOT/diag-wg0.conf output status saved_test_root
rm -rf -- "$TEST_ROOT/diag-meta" "$TEST_ROOT/diag-clients" "$TEST_ROOT/diag-backups" "$TEST_ROOT/diag-logs" "$TEST_ROOT/diag-archive" "$TEST_ROOT/diag.lock" "$TEST_ROOT/ipv4-forward" "$TEST_ROOT/runtime-diff.dump"
@@ -3107,6 +3324,8 @@ elif [[ $test_group == add-client || $test_group == address-allocation || $test_
run_m4_tests
elif [[ $test_group == remove-client || $test_group == remove-cancel || $test_group == remove-exact-block || $test_group == remove-rollback || $test_group == archive-client || $test_group == archive-path-safety || $test_group == remove-preserves-neighbors ]]; then
run_m6_tests
elif [[ $test_group == audit || $test_group == audit-no-secrets || $test_group == backup-list || $test_group == restore || $test_group == restore-invalid || $test_group == restore-rollback || $test_group == backup-path-safety || $test_group == runtime-file-modes ]]; then
run_m8_tests
elif [[ $test_group == diagnostics || $test_group == diagnostics-systemd || $test_group == diagnostics-firewall || $test_group == diagnostics-forwarding || $test_group == diagnostics-runtime-diff || $test_group == diagnostics-read-only || $test_group == diagnostics-exit-codes ]]; then
run_m7_tests
else
@@ -3126,6 +3345,11 @@ else
run_m7_tests
done
unset m7_group
for m8_group in audit audit-no-secrets backup-list restore restore-invalid restore-rollback backup-path-safety runtime-file-modes; do
test_group=$m8_group
run_m8_tests
done
unset m8_group
test_group=''
fi