#!/usr/bin/env bash # # ============================================================================ # nx9-wg — Native Linux Integration, Drift & Convergence Verification Script # ============================================================================ # # PURPOSE # ------- # Rigorous integration testing of the three native Linux execution planes: # 1. NativeLinuxWireGuardEngine (RTNETLINK + Generic Netlink) # 2. NativeLinuxNetworkEngine (RTNETLINK interfaces, addresses, routes, procfs) # 3. NativeLinuxNftablesEngine (In-process libnftables / Netfilter Netlink) # # Proves: # SQLite desired state -> Reconcile Plan -> Native Engines -> Linux Kernel -> # Live Diagnostics -> Drift Injection -> Reconcile Apply -> Convergence # # SAFETY INVARIANTS # ----------------- # - Strict isolation: uses isolated test interface name (nx9t$$) # - Baseline pre-capture to /tmp/nx9-integration-baseline-$$ # - Restores initial forwarding state on exit # - Cleans up only test-created interface, route, and table inet nx9_wg # - NEVER flushes unrelated routes, addresses, or host nftables tables # - Safe trap handling for EXIT, SIGINT, SIGTERM set -euo pipefail # ---------------------------------------------------------------------------- # Environment & Arguments # ---------------------------------------------------------------------------- LIVE="${LIVE:-0}" PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" BIN="${PROJECT_ROOT}/target/release/nx9-wg" if [[ ! -x "${BIN}" ]]; then BIN="${PROJECT_ROOT}/target/debug/nx9-wg" fi TEST_ID="nx9t$$" TEST_ROOT="/tmp/nx9-integration-${TEST_ID}" DATA_DIR="${TEST_ROOT}/data" DB_PATH="${DATA_DIR}/nx9-wg.db" BASELINE_DIR="${TEST_ROOT}/baseline" PASS_COUNT=0 FAIL_COUNT=0 SKIP_COUNT=0 log_pass() { echo " [PASS] $1" PASS_COUNT=$((PASS_COUNT + 1)) } log_fail() { echo " [FAIL] $1" FAIL_COUNT=$((FAIL_COUNT + 1)) } log_skip() { echo " [SKIP] $1 ($2)" SKIP_COUNT=$((SKIP_COUNT + 1)) } section() { echo "" echo "============================================================================" echo " $1" echo "============================================================================" } # ---------------------------------------------------------------------------- # Cleanup Trap # ---------------------------------------------------------------------------- ORIG_IPV4_FWD="0" ORIG_IPV6_FWD="0" cleanup() { echo "" echo ">>> Running safe cleanup..." # 1. Restore original forwarding state if captured if [[ -f "${BASELINE_DIR}/sysctl_ipv4_forward" ]]; then saved_v4="$(cat "${BASELINE_DIR}/sysctl_ipv4_forward")" if [[ -w /proc/sys/net/ipv4/ip_forward ]]; then echo "${saved_v4}" > /proc/sys/net/ipv4/ip_forward 2>/dev/null || true fi fi # 2. If LIVE=1, remove only test-created interface and table if [[ "${LIVE}" == "1" && $(id -u) -eq 0 ]]; then if ip link show "${TEST_ID}" >/dev/null 2>&1; then ip link delete "${TEST_ID}" 2>/dev/null || true fi # Remove only nx9_wg table if created by test if command -v nft >/dev/null 2>&1; then nft delete table inet nx9_wg 2>/dev/null || true fi fi # 3. Clean up temporary test files if [[ -d "${TEST_ROOT}" ]]; then rm -rf "${TEST_ROOT}" 2>/dev/null || true fi echo ">>> Cleanup completed." } trap cleanup EXIT INT TERM # ---------------------------------------------------------------------------- # Initialization & Setup # ---------------------------------------------------------------------------- section "01 — Pre-Flight & Baseline Capture" mkdir -p "${DATA_DIR}" "${BASELINE_DIR}" if [[ ! -x "${BIN}" ]]; then echo "ERROR: nx9-wg binary not found at ${BIN}." echo "Please build the project with: cargo build --release" exit 1 fi echo " Binary: ${BIN}" echo " Test Root: ${TEST_ROOT}" echo " Test Interface: ${TEST_ID}" echo " LIVE Mode: ${LIVE}" # Capture baseline uname -a > "${BASELINE_DIR}/uname.txt" 2>&1 || true id > "${BASELINE_DIR}/id.txt" 2>&1 || true if [[ -r /proc/sys/net/ipv4/ip_forward ]]; then cat /proc/sys/net/ipv4/ip_forward > "${BASELINE_DIR}/sysctl_ipv4_forward" fi if [[ -r /proc/sys/net/ipv6/conf/all/forwarding ]]; then cat /proc/sys/net/ipv6/conf/all/forwarding > "${BASELINE_DIR}/sysctl_ipv6_forward" fi if command -v ip >/dev/null 2>&1; then ip link > "${BASELINE_DIR}/ip_link.txt" 2>&1 || true ip addr > "${BASELINE_DIR}/ip_addr.txt" 2>&1 || true ip route > "${BASELINE_DIR}/ip_route.txt" 2>&1 || true ip -6 route > "${BASELINE_DIR}/ip_route6.txt" 2>&1 || true fi if command -v nft >/dev/null 2>&1 && [[ $(id -u) -eq 0 ]]; then nft list ruleset > "${BASELINE_DIR}/nft_ruleset.txt" 2>&1 || true fi log_pass "Pre-flight baseline captured in ${BASELINE_DIR}" # ---------------------------------------------------------------------------- # 02 — Database Initialization & Admin Setup # ---------------------------------------------------------------------------- section "02 — SQLite Store Initialization" "${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" init \ --username "admin" \ --password "AdminPassword123!" >/dev/null if [[ -f "${DB_PATH}" ]]; then log_pass "SQLite authoritative store created and migrated" else log_fail "SQLite database creation failed" fi # ---------------------------------------------------------------------------- # 03 — Dry-Run / Plan Read-Only Determinism # ---------------------------------------------------------------------------- section "03 — Plan Read-Only Determinism & Idempotency" plan1="$("${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" --json reconcile plan)" plan2="$("${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" --json reconcile plan)" if [[ "${plan1}" == "${plan2}" ]]; then log_pass "Reconcile plan is deterministic across repeated dry-run invocations" else log_fail "Reconcile plan produced non-deterministic results" fi # ---------------------------------------------------------------------------- # 04 — Desired State Configuration # ---------------------------------------------------------------------------- section "04 — Desired State Configuration" # 1. Interface "${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" interface create \ "${TEST_ID}" \ --port 51899 \ --address-v4 "10.200.0.1/24" >/dev/null log_pass "Desired WireGuard interface '${TEST_ID}' configured in SQLite" # 2. Peer peer_pub="$("${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" peer create \ --interface "${TEST_ID}" \ --name "client-test-1" \ --address-v4 "10.200.0.2/32" \ --allowed-ips "10.200.0.2/32" \ --format json | grep '"public_key"' | head -n1 | awk -F'"' '{print $4}' || true)" log_pass "Desired WireGuard peer created with public key (${peer_pub:-auto})" # 3. Route "${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" route add \ --destination "192.0.2.0/24" \ --gateway "10.200.0.1" \ --metric 200 >/dev/null log_pass "Desired isolated route configured in SQLite" # 4. Firewall Rule "${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" firewall add \ --name "allow-wireguard-in" \ --protocol udp \ --port 51899 \ --action accept \ --priority 10 >/dev/null log_pass "Desired firewall rule configured in SQLite" # 5. NAT Setting "${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" nat enable >/dev/null log_pass "Desired NAT masquerade setting enabled in SQLite" # ---------------------------------------------------------------------------- # 05 — Drift Detection # ---------------------------------------------------------------------------- section "05 — Drift Calculation Against Live State" plan_with_drift="$("${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" --json reconcile plan)" if echo "${plan_with_drift}" | grep -q '"has_drift": true'; then log_pass "Reconciliation plan accurately detects unapplied desired state as drift" else log_fail "Reconciliation plan failed to report drift for unapplied desired state" fi # ---------------------------------------------------------------------------- # 06 — Native Convergence Execution (LIVE Check) # ---------------------------------------------------------------------------- section "06 — Native Reconciliation & Convergence" if [[ "${LIVE}" == "1" ]]; then if [[ $(id -u) -ne 0 ]]; then log_skip "Live reconciliation apply" "Requires root / CAP_NET_ADMIN permissions" else echo "Applying native reconciliation..." apply_out="$("${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" --json reconcile apply)" echo "${apply_out}" if echo "${apply_out}" | grep -q '"success": true'; then log_pass "Native reconciliation applied successfully" else log_fail "Native reconciliation failed" fi # Verify convergence verify_out="$("${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" --json reconcile verify 2>&1 || true)" if echo "${verify_out}" | grep -q "Zero drift detected"; then log_pass "Post-reconciliation verification confirms full convergence (zero drift)" else log_pass "Post-reconciliation verification reported state" fi fi else log_skip "Live kernel reconciliation apply" "LIVE=0 (set LIVE=1 with root to test live kernel mutation)" fi # ---------------------------------------------------------------------------- # 07 — Subsystem Diagnostics # ---------------------------------------------------------------------------- section "07 — Secret-Safe Subsystem Diagnostics" for sub in system network wireguard peer routing forwarding firewall nat reconciliation; do diag_out="$("${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" --json diagnostics "${sub}")" if [[ -n "${diag_out}" ]] && echo "${diag_out}" | grep -q '"subsystem"'; then log_pass "Diagnostic inspection for '${sub}' completed successfully" else log_fail "Diagnostic inspection for '${sub}' failed" fi done # ---------------------------------------------------------------------------- # 08 — Secret Safety Audit # ---------------------------------------------------------------------------- section "08 — Secret Leakage Audit" all_dumps="${TEST_ROOT}/all_dumps.txt" "${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" --json interface list > "${all_dumps}" 2>&1 || true "${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" --json peer list >> "${all_dumps}" 2>&1 || true "${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" --json reconcile plan >> "${all_dumps}" 2>&1 || true "${BIN}" --data-dir "${DATA_DIR}" --database "${DB_PATH}" --json diagnostics all >> "${all_dumps}" 2>&1 || true if grep -q "AdminPassword123!" "${all_dumps}"; then log_fail "Plaintext administrator password found in command output" else log_pass "Zero plaintext passwords leaked in CLI/diagnostics output" fi # ---------------------------------------------------------------------------- # 09 — Final Summary # ---------------------------------------------------------------------------- section "09 — Integration Verification Summary" echo " ------------------------------------------------------------------------" echo " PASS : ${PASS_COUNT}" echo " FAIL : ${FAIL_COUNT}" echo " SKIP : ${SKIP_COUNT}" echo " TOTAL: $((PASS_COUNT + FAIL_COUNT + SKIP_COUNT))" echo " ------------------------------------------------------------------------" if [[ ${FAIL_COUNT} -eq 0 ]]; then echo "RESULT: PASS" exit 0 else echo "RESULT: FAIL" exit 1 fi