Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
This commit is contained in:
Executable
+587
@@ -0,0 +1,587 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#####################################################################################
|
||||
#### vpn-smoke-tests.sh — Verify VPN tunnels are healthy ####
|
||||
#### Checks WireGuard and OpenVPN interface, handshake, peers, DNS, routes, MTU. ####
|
||||
#### ####
|
||||
#### Author: Phil Connor ####
|
||||
#### Contact: contact@mylinux.work ####
|
||||
#### License: MIT ####
|
||||
#### Version: 1.0 ####
|
||||
#### ####
|
||||
#### Usage: ./vpn-smoke-tests.sh ####
|
||||
#### VPN_TYPE=wireguard VPN_INTERFACE=wg0 ./vpn-smoke-tests.sh ####
|
||||
#### ####
|
||||
#### See --help for all options. ####
|
||||
#####################################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Defaults ──────────────────────────────────────────────────────────
|
||||
VPN_TYPE="${VPN_TYPE:-auto}"
|
||||
VPN_INTERFACE="${VPN_INTERFACE:-}"
|
||||
HANDSHAKE_MAX_SEC="${HANDSHAKE_MAX_SEC:-180}"
|
||||
PING_TARGET="${PING_TARGET:-}"
|
||||
DNS_TEST_DOMAIN="${DNS_TEST_DOMAIN:-}"
|
||||
VPN_DNS_SERVER="${VPN_DNS_SERVER:-}"
|
||||
EXPECTED_ROUTES="${EXPECTED_ROUTES:-}"
|
||||
MGMT_SOCKET="${MGMT_SOCKET:-}"
|
||||
EXPECTED_MTU="${EXPECTED_MTU:-}"
|
||||
VPN_ENDPOINT="${VPN_ENDPOINT:-}"
|
||||
SKIP_HANDSHAKE="${SKIP_HANDSHAKE:-false}"
|
||||
SKIP_DNS="${SKIP_DNS:-false}"
|
||||
SKIP_PING="${SKIP_PING:-false}"
|
||||
OUTPUT_FORMAT="${OUTPUT_FORMAT:-text}"
|
||||
COLOR="${COLOR:-auto}"
|
||||
VERBOSE="${VERBOSE:-false}"
|
||||
|
||||
# ── State ─────────────────────────────────────────────────────────────
|
||||
PASS=0
|
||||
FAIL=0
|
||||
SKIP=0
|
||||
TOTAL=0
|
||||
RESULTS=()
|
||||
START_TIME=""
|
||||
|
||||
# ── Colors ────────────────────────────────────────────────────────────
|
||||
setup_colors() {
|
||||
if [[ "$COLOR" == "never" ]]; then
|
||||
RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET=""
|
||||
return
|
||||
fi
|
||||
if [[ "$COLOR" == "always" ]] || [[ -t 1 ]]; then
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
BOLD='\033[1m'
|
||||
RESET='\033[0m'
|
||||
else
|
||||
RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET=""
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Logging ───────────────────────────────────────────────────────────
|
||||
log() { echo -e "${BLUE}[INFO]${RESET} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; }
|
||||
err() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
|
||||
verbose() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${BLUE}[DEBUG]${RESET} $*"; fi; }
|
||||
|
||||
# ── Test Result Recording ─────────────────────────────────────────────
|
||||
record_pass() {
|
||||
local name="$1" detail="${2:-}"
|
||||
((PASS++)) || true; ((TOTAL++)) || true
|
||||
RESULTS+=("PASS|${name}|${detail}")
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then echo "ok ${TOTAL} - ${name}${detail:+ (${detail})}"
|
||||
else echo -e " ${GREEN}✓${RESET} ${name}${detail:+ — ${detail}}"; fi
|
||||
}
|
||||
|
||||
record_fail() {
|
||||
local name="$1" detail="${2:-}"
|
||||
((FAIL++)) || true; ((TOTAL++)) || true
|
||||
RESULTS+=("FAIL|${name}|${detail}")
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
|
||||
echo "not ok ${TOTAL} - ${name}"
|
||||
[[ -n "$detail" ]] && echo " # ${detail}"
|
||||
else echo -e " ${RED}✗${RESET} ${name}${detail:+ — ${detail}}"; fi
|
||||
}
|
||||
|
||||
record_skip() {
|
||||
local name="$1" reason="${2:-}"
|
||||
((SKIP++)) || true; ((TOTAL++)) || true
|
||||
RESULTS+=("SKIP|${name}|${reason}")
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then echo "ok ${TOTAL} - ${name} # SKIP ${reason}"
|
||||
else echo -e " ${YELLOW}⊘${RESET} ${name}${reason:+ — ${reason}}"; fi
|
||||
}
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────
|
||||
has_cmd() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
section() {
|
||||
if [[ "$OUTPUT_FORMAT" != "tap" ]]; then echo ""; echo -e "${BOLD}$1${RESET}"; fi
|
||||
}
|
||||
|
||||
# ── Cleanup ───────────────────────────────────────────────────────────
|
||||
# shellcheck disable=SC2317
|
||||
cleanup() {
|
||||
verbose "Cleanup complete."
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# ── VPN Type Detection ───────────────────────────────────────────────
|
||||
detect_vpn_type() {
|
||||
if [[ "$VPN_TYPE" != "auto" ]]; then
|
||||
verbose "VPN type set to ${VPN_TYPE}"
|
||||
return
|
||||
fi
|
||||
if has_cmd wg && wg show interfaces 2>/dev/null | grep -q .; then
|
||||
VPN_TYPE="wireguard"
|
||||
verbose "Auto-detected WireGuard"
|
||||
elif pgrep -x openvpn >/dev/null 2>&1; then
|
||||
VPN_TYPE="openvpn"
|
||||
verbose "Auto-detected OpenVPN"
|
||||
elif ip link show wg0 >/dev/null 2>&1; then
|
||||
VPN_TYPE="wireguard"
|
||||
verbose "Auto-detected WireGuard (interface wg0 exists)"
|
||||
elif ip link show tun0 >/dev/null 2>&1; then
|
||||
VPN_TYPE="openvpn"
|
||||
verbose "Auto-detected OpenVPN (interface tun0 exists)"
|
||||
else
|
||||
err "Could not auto-detect VPN type. Set VPN_TYPE=wireguard or VPN_TYPE=openvpn."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
set_defaults() {
|
||||
if [[ -z "$VPN_INTERFACE" ]]; then
|
||||
case "$VPN_TYPE" in
|
||||
wireguard) VPN_INTERFACE="wg0" ;;
|
||||
openvpn) VPN_INTERFACE="tun0" ;;
|
||||
esac
|
||||
verbose "Default interface: ${VPN_INTERFACE}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# TEST FUNCTIONS
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
# ── 1. VPN type detection ────────────────────────────────────────────
|
||||
test_vpn_type() {
|
||||
case "$VPN_TYPE" in
|
||||
wireguard|openvpn) record_pass "VPN type detection" "${VPN_TYPE}" ;;
|
||||
*) record_fail "VPN type detection" "unknown type: ${VPN_TYPE}" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ── 2. Interface up ──────────────────────────────────────────────────
|
||||
test_interface_up() {
|
||||
if ! ip link show "$VPN_INTERFACE" >/dev/null 2>&1; then
|
||||
record_fail "Interface up" "${VPN_INTERFACE} does not exist"
|
||||
return
|
||||
fi
|
||||
local state
|
||||
state=$(ip -o link show "$VPN_INTERFACE" 2>/dev/null | grep -oP 'state \K\S+') || true
|
||||
if [[ "$state" == "UP" || "$state" == "UNKNOWN" ]]; then
|
||||
record_pass "Interface up" "${VPN_INTERFACE} state=${state}"
|
||||
else
|
||||
record_fail "Interface up" "${VPN_INTERFACE} state=${state:-DOWN}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 3. WireGuard handshake ───────────────────────────────────────────
|
||||
test_wg_handshake() {
|
||||
if [[ "$VPN_TYPE" != "wireguard" ]]; then record_skip "WireGuard handshake" "not WireGuard"; return; fi
|
||||
if [[ "$SKIP_HANDSHAKE" == "true" ]]; then record_skip "WireGuard handshake" "SKIP_HANDSHAKE=true"; return; fi
|
||||
if ! has_cmd wg; then record_skip "WireGuard handshake" "wg not installed"; return; fi
|
||||
|
||||
local handshakes oldest_age=0 peer_count=0 now
|
||||
now=$(date +%s)
|
||||
handshakes=$(wg show "$VPN_INTERFACE" latest-handshakes 2>/dev/null) || true
|
||||
|
||||
if [[ -z "$handshakes" ]]; then
|
||||
record_fail "WireGuard handshake" "no handshake data from wg show"
|
||||
return
|
||||
fi
|
||||
|
||||
while IFS=$'\t' read -r _peer ts; do
|
||||
((peer_count++)) || true
|
||||
if [[ "$ts" == "0" ]]; then
|
||||
record_fail "WireGuard handshake" "peer has never completed a handshake"
|
||||
return
|
||||
fi
|
||||
local age=$(( now - ts ))
|
||||
if [[ $age -gt $oldest_age ]]; then oldest_age=$age; fi
|
||||
done <<< "$handshakes"
|
||||
|
||||
if [[ $peer_count -eq 0 ]]; then
|
||||
record_fail "WireGuard handshake" "no peers configured"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ $oldest_age -le $HANDSHAKE_MAX_SEC ]]; then
|
||||
record_pass "WireGuard handshake" "${oldest_age}s ago (<= ${HANDSHAKE_MAX_SEC}s), ${peer_count} peer(s)"
|
||||
else
|
||||
record_fail "WireGuard handshake" "${oldest_age}s ago (> ${HANDSHAKE_MAX_SEC}s)"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 4. WireGuard peer reachable ──────────────────────────────────────
|
||||
test_wg_peer_reachable() {
|
||||
if [[ "$VPN_TYPE" != "wireguard" ]]; then record_skip "WireGuard peer reachable" "not WireGuard"; return; fi
|
||||
if [[ "$SKIP_PING" == "true" ]]; then record_skip "WireGuard peer reachable" "SKIP_PING=true"; return; fi
|
||||
if ! has_cmd wg; then record_skip "WireGuard peer reachable" "wg not installed"; return; fi
|
||||
|
||||
local endpoints peer_count=0 reachable=0
|
||||
endpoints=$(wg show "$VPN_INTERFACE" endpoints 2>/dev/null) || true
|
||||
|
||||
if [[ -z "$endpoints" ]]; then
|
||||
record_skip "WireGuard peer reachable" "no endpoint data"
|
||||
return
|
||||
fi
|
||||
|
||||
while IFS=$'\t' read -r _peer endpoint; do
|
||||
[[ "$endpoint" == "(none)" || -z "$endpoint" ]] && continue
|
||||
((peer_count++)) || true
|
||||
local host="${endpoint%:*}"
|
||||
# Strip brackets from IPv6
|
||||
host="${host#[}"
|
||||
host="${host%]}"
|
||||
if ping -c 1 -W 3 "$host" >/dev/null 2>&1; then
|
||||
((reachable++)) || true
|
||||
fi
|
||||
done <<< "$endpoints"
|
||||
|
||||
if [[ $peer_count -eq 0 ]]; then
|
||||
record_skip "WireGuard peer reachable" "no endpoints configured"
|
||||
elif [[ $reachable -eq $peer_count ]]; then
|
||||
record_pass "WireGuard peer reachable" "${reachable}/${peer_count} endpoints"
|
||||
else
|
||||
record_fail "WireGuard peer reachable" "${reachable}/${peer_count} endpoints reachable"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 5. WireGuard transfer ────────────────────────────────────────────
|
||||
test_wg_transfer() {
|
||||
if [[ "$VPN_TYPE" != "wireguard" ]]; then record_skip "WireGuard transfer" "not WireGuard"; return; fi
|
||||
if ! has_cmd wg; then record_skip "WireGuard transfer" "wg not installed"; return; fi
|
||||
|
||||
local transfer total_rx=0 total_tx=0
|
||||
transfer=$(wg show "$VPN_INTERFACE" transfer 2>/dev/null) || true
|
||||
|
||||
if [[ -z "$transfer" ]]; then
|
||||
record_fail "WireGuard transfer" "no transfer data"
|
||||
return
|
||||
fi
|
||||
|
||||
while IFS=$'\t' read -r _peer rx tx; do
|
||||
total_rx=$((total_rx + rx))
|
||||
total_tx=$((total_tx + tx))
|
||||
done <<< "$transfer"
|
||||
|
||||
if [[ $total_rx -gt 0 && $total_tx -gt 0 ]]; then
|
||||
local rx_h tx_h
|
||||
rx_h=$(numfmt --to=iec "$total_rx" 2>/dev/null) || rx_h="${total_rx}B"
|
||||
tx_h=$(numfmt --to=iec "$total_tx" 2>/dev/null) || tx_h="${total_tx}B"
|
||||
record_pass "WireGuard transfer" "rx=${rx_h} tx=${tx_h}"
|
||||
elif [[ $total_rx -gt 0 || $total_tx -gt 0 ]]; then
|
||||
record_fail "WireGuard transfer" "one-way traffic only (rx=${total_rx} tx=${total_tx})"
|
||||
else
|
||||
record_fail "WireGuard transfer" "no traffic (rx=0 tx=0)"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 6. OpenVPN process ───────────────────────────────────────────────
|
||||
test_ovpn_process() {
|
||||
if [[ "$VPN_TYPE" != "openvpn" ]]; then record_skip "OpenVPN process" "not OpenVPN"; return; fi
|
||||
|
||||
local pid
|
||||
pid=$(pgrep -x openvpn 2>/dev/null | head -1) || true
|
||||
if [[ -n "$pid" ]]; then
|
||||
record_pass "OpenVPN process" "PID ${pid}"
|
||||
else
|
||||
record_fail "OpenVPN process" "openvpn not running"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 7. OpenVPN management ────────────────────────────────────────────
|
||||
test_ovpn_management() {
|
||||
if [[ "$VPN_TYPE" != "openvpn" ]]; then record_skip "OpenVPN management" "not OpenVPN"; return; fi
|
||||
if [[ -z "$MGMT_SOCKET" ]]; then record_skip "OpenVPN management" "MGMT_SOCKET not set"; return; fi
|
||||
|
||||
if [[ -S "$MGMT_SOCKET" ]]; then
|
||||
local status_output
|
||||
status_output=$(echo "status" | socat - "UNIX-CONNECT:${MGMT_SOCKET}" 2>/dev/null) || true
|
||||
if [[ -n "$status_output" ]] && echo "$status_output" | grep -qi "client\|connected\|bytes"; then
|
||||
record_pass "OpenVPN management" "socket responding"
|
||||
elif [[ -n "$status_output" ]]; then
|
||||
record_pass "OpenVPN management" "socket responding"
|
||||
else
|
||||
record_fail "OpenVPN management" "socket not responding"
|
||||
fi
|
||||
elif [[ -e "$MGMT_SOCKET" ]]; then
|
||||
record_fail "OpenVPN management" "${MGMT_SOCKET} exists but is not a socket"
|
||||
else
|
||||
record_fail "OpenVPN management" "${MGMT_SOCKET} not found"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 8. Tunnel IP assigned ────────────────────────────────────────────
|
||||
test_tunnel_ip() {
|
||||
local ip_addr
|
||||
ip_addr=$(ip -o -4 addr show "$VPN_INTERFACE" 2>/dev/null | awk '{print $4}' | head -1) || true
|
||||
|
||||
if [[ -z "$ip_addr" ]]; then
|
||||
# Try IPv6
|
||||
ip_addr=$(ip -o -6 addr show "$VPN_INTERFACE" 2>/dev/null | grep -v "fe80" | awk '{print $4}' | head -1) || true
|
||||
fi
|
||||
|
||||
if [[ -n "$ip_addr" ]]; then
|
||||
record_pass "Tunnel IP assigned" "${VPN_INTERFACE} ${ip_addr}"
|
||||
else
|
||||
record_fail "Tunnel IP assigned" "${VPN_INTERFACE} has no IP address"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 9. DNS over VPN ──────────────────────────────────────────────────
|
||||
test_dns_over_vpn() {
|
||||
if [[ "$SKIP_DNS" == "true" ]]; then record_skip "DNS over VPN" "SKIP_DNS=true"; return; fi
|
||||
if [[ -z "$DNS_TEST_DOMAIN" ]]; then record_skip "DNS over VPN" "DNS_TEST_DOMAIN not set"; return; fi
|
||||
if [[ -z "$VPN_DNS_SERVER" ]]; then record_skip "DNS over VPN" "VPN_DNS_SERVER not set"; return; fi
|
||||
|
||||
local output
|
||||
if has_cmd dig; then
|
||||
output=$(dig +short +time=5 +tries=1 "@${VPN_DNS_SERVER}" "${DNS_TEST_DOMAIN}" A 2>/dev/null) || true
|
||||
elif has_cmd nslookup; then
|
||||
output=$(nslookup "${DNS_TEST_DOMAIN}" "${VPN_DNS_SERVER}" 2>/dev/null | grep -i "address" | tail -1) || true
|
||||
elif has_cmd drill; then
|
||||
output=$(drill "@${VPN_DNS_SERVER}" "${DNS_TEST_DOMAIN}" A 2>/dev/null | grep -A1 "ANSWER SECTION" | tail -1) || true
|
||||
else
|
||||
record_skip "DNS over VPN" "no DNS tool available (dig, nslookup, drill)"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -n "$output" ]] && ! echo "$output" | grep -qi "timed out\|SERVFAIL\|connection refused"; then
|
||||
record_pass "DNS over VPN" "${DNS_TEST_DOMAIN} via ${VPN_DNS_SERVER}"
|
||||
else
|
||||
record_fail "DNS over VPN" "failed to resolve ${DNS_TEST_DOMAIN} via ${VPN_DNS_SERVER}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 10. Route check ─────────────────────────────────────────────────
|
||||
test_route_check() {
|
||||
if [[ -z "$EXPECTED_ROUTES" ]]; then record_skip "Route check" "EXPECTED_ROUTES not set"; return; fi
|
||||
|
||||
local IFS=',' missing=0 checked=0
|
||||
for route in $EXPECTED_ROUTES; do
|
||||
route=$(echo "$route" | xargs)
|
||||
[[ -z "$route" ]] && continue
|
||||
((checked++)) || true
|
||||
if ip route show "$route" 2>/dev/null | grep -q "$VPN_INTERFACE"; then
|
||||
verbose "Route ${route} via ${VPN_INTERFACE} — OK"
|
||||
else
|
||||
verbose "Route ${route} via ${VPN_INTERFACE} — MISSING"
|
||||
((missing++)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $checked -eq 0 ]]; then
|
||||
record_skip "Route check" "no routes to check"
|
||||
elif [[ $missing -eq 0 ]]; then
|
||||
record_pass "Route check" "${checked} route(s) via ${VPN_INTERFACE}"
|
||||
else
|
||||
record_fail "Route check" "${missing}/${checked} route(s) missing from ${VPN_INTERFACE}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 11. Peer connectivity ───────────────────────────────────────────
|
||||
test_peer_connectivity() {
|
||||
if [[ "$SKIP_PING" == "true" ]]; then record_skip "Peer connectivity" "SKIP_PING=true"; return; fi
|
||||
if [[ -z "$PING_TARGET" ]]; then record_skip "Peer connectivity" "PING_TARGET not set"; return; fi
|
||||
|
||||
if ping -c 3 -W 5 -I "$VPN_INTERFACE" "$PING_TARGET" >/dev/null 2>&1; then
|
||||
record_pass "Peer connectivity" "ping ${PING_TARGET} via ${VPN_INTERFACE}"
|
||||
else
|
||||
record_fail "Peer connectivity" "cannot reach ${PING_TARGET} via ${VPN_INTERFACE}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 12. MTU check ────────────────────────────────────────────────────
|
||||
test_mtu_check() {
|
||||
if [[ -z "$EXPECTED_MTU" ]]; then record_skip "MTU check" "EXPECTED_MTU not set"; return; fi
|
||||
|
||||
local actual_mtu
|
||||
actual_mtu=$(ip -o link show "$VPN_INTERFACE" 2>/dev/null | grep -oP 'mtu \K[0-9]+') || true
|
||||
|
||||
if [[ -z "$actual_mtu" ]]; then
|
||||
record_fail "MTU check" "could not read MTU for ${VPN_INTERFACE}"
|
||||
elif [[ "$actual_mtu" == "$EXPECTED_MTU" ]]; then
|
||||
record_pass "MTU check" "${VPN_INTERFACE} MTU=${actual_mtu}"
|
||||
else
|
||||
record_fail "MTU check" "${VPN_INTERFACE} MTU=${actual_mtu} (expected ${EXPECTED_MTU})"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 13. Endpoint reachable ───────────────────────────────────────────
|
||||
test_endpoint_reachable() {
|
||||
if [[ -z "$VPN_ENDPOINT" ]]; then record_skip "Endpoint reachable" "VPN_ENDPOINT not set"; return; fi
|
||||
|
||||
local host port
|
||||
# Handle host:port format
|
||||
if [[ "$VPN_ENDPOINT" =~ ^\[.*\]:[0-9]+$ ]]; then
|
||||
# IPv6 [addr]:port
|
||||
host="${VPN_ENDPOINT%:*}"
|
||||
host="${host#[}"
|
||||
host="${host%]}"
|
||||
port="${VPN_ENDPOINT##*:}"
|
||||
elif [[ "$VPN_ENDPOINT" =~ ^[^:]+:[0-9]+$ ]]; then
|
||||
host="${VPN_ENDPOINT%:*}"
|
||||
port="${VPN_ENDPOINT##*:}"
|
||||
else
|
||||
host="$VPN_ENDPOINT"
|
||||
port=""
|
||||
fi
|
||||
|
||||
if [[ -n "$port" ]]; then
|
||||
# Test TCP/UDP reachability
|
||||
if has_cmd nc; then
|
||||
if nc -z -w 5 "$host" "$port" >/dev/null 2>&1; then
|
||||
record_pass "Endpoint reachable" "${VPN_ENDPOINT}"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
# Fall back to ping if nc fails or unavailable
|
||||
if ping -c 1 -W 5 "$host" >/dev/null 2>&1; then
|
||||
record_pass "Endpoint reachable" "${host} (ping OK, port ${port} not tested)"
|
||||
else
|
||||
record_fail "Endpoint reachable" "${VPN_ENDPOINT} unreachable"
|
||||
fi
|
||||
else
|
||||
if ping -c 1 -W 5 "$host" >/dev/null 2>&1; then
|
||||
record_pass "Endpoint reachable" "${host}"
|
||||
else
|
||||
record_fail "Endpoint reachable" "${host} unreachable"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# OUTPUT
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
print_tap_header() {
|
||||
echo "TAP version 13"
|
||||
}
|
||||
|
||||
print_tap_footer() {
|
||||
echo "1..${TOTAL}"
|
||||
echo "# pass ${PASS}"
|
||||
echo "# fail ${FAIL}"
|
||||
echo "# skip ${SKIP}"
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
local end_time; end_time=$(date +%s)
|
||||
local duration=$(( end_time - START_TIME ))
|
||||
echo ""
|
||||
echo -e "${BOLD}────────────────────────────────────────${RESET}"
|
||||
echo -e "${BOLD}Summary${RESET} VPN Smoke Tests ${VPN_TYPE}/${VPN_INTERFACE}"
|
||||
echo -e " ${GREEN}${PASS} passed${RESET} ${RED}${FAIL} failed${RESET} ${YELLOW}${SKIP} skipped${RESET} (${duration}s)"
|
||||
echo -e "${BOLD}────────────────────────────────────────${RESET}"
|
||||
if [[ $FAIL -eq 0 ]]; then echo -e "${GREEN}${BOLD}All tests passed.${RESET}"
|
||||
else echo -e "${RED}${BOLD}${FAIL} test(s) failed.${RESET}"; fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# MAIN
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") [OPTIONS]
|
||||
|
||||
Smoke-test VPN tunnels. Supports WireGuard and OpenVPN.
|
||||
|
||||
Environment variables (all optional):
|
||||
VPN_TYPE VPN backend: auto, wireguard, openvpn (default: auto)
|
||||
VPN_INTERFACE Network interface (default: wg0 or tun0)
|
||||
HANDSHAKE_MAX_SEC Max seconds since WireGuard handshake (default: 180)
|
||||
PING_TARGET IP to ping through tunnel (skip if empty)
|
||||
DNS_TEST_DOMAIN Domain to resolve via VPN DNS (skip if empty)
|
||||
VPN_DNS_SERVER DNS server for VPN resolution (skip if empty)
|
||||
EXPECTED_ROUTES Comma-separated CIDRs to verify (skip if empty)
|
||||
MGMT_SOCKET OpenVPN management socket path (skip if empty)
|
||||
EXPECTED_MTU Expected MTU value (skip if empty)
|
||||
VPN_ENDPOINT VPN server IP:port to check (skip if empty)
|
||||
SKIP_HANDSHAKE Skip WireGuard handshake check (default: false)
|
||||
SKIP_DNS Skip DNS-over-VPN test (default: false)
|
||||
SKIP_PING Skip ping-based tests (default: false)
|
||||
OUTPUT_FORMAT Output: text (default), tap
|
||||
COLOR Color: auto (default), always, never
|
||||
VERBOSE Show debug output (default: false)
|
||||
|
||||
Options:
|
||||
--type TYPE Set VPN_TYPE
|
||||
--interface IFACE Set VPN_INTERFACE
|
||||
--format FORMAT Output format: text (default), tap
|
||||
--skip-handshake Skip WireGuard handshake check
|
||||
--skip-dns Skip DNS-over-VPN test
|
||||
--skip-ping Skip ping-based tests
|
||||
--verbose Show debug output
|
||||
--no-color Disable colored output
|
||||
--help Show this help
|
||||
|
||||
Examples:
|
||||
./$(basename "$0")
|
||||
VPN_TYPE=wireguard PING_TARGET=10.0.0.1 ./$(basename "$0")
|
||||
VPN_TYPE=openvpn MGMT_SOCKET=/run/openvpn/mgmt.sock ./$(basename "$0")
|
||||
OUTPUT_FORMAT=tap ./$(basename "$0") --skip-ping
|
||||
EOF
|
||||
}
|
||||
|
||||
main() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--type) VPN_TYPE="$2"; shift ;;
|
||||
--interface) VPN_INTERFACE="$2"; shift ;;
|
||||
--format) OUTPUT_FORMAT="$2"; shift ;;
|
||||
--skip-handshake) SKIP_HANDSHAKE=true ;;
|
||||
--skip-dns) SKIP_DNS=true ;;
|
||||
--skip-ping) SKIP_PING=true ;;
|
||||
--verbose) VERBOSE=true ;;
|
||||
--no-color) COLOR=never ;;
|
||||
--help|-h) usage; exit 0 ;;
|
||||
*) err "Unknown option: $1"; usage; exit 1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
setup_colors
|
||||
detect_vpn_type
|
||||
set_defaults
|
||||
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
|
||||
print_tap_header
|
||||
else
|
||||
echo ""
|
||||
echo -e "${BOLD}VPN Smoke Tests${RESET}"
|
||||
echo -e "Type: ${VPN_TYPE} Interface: ${VPN_INTERFACE}"
|
||||
echo -e "Time: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
fi
|
||||
|
||||
section "Detection"
|
||||
test_vpn_type
|
||||
|
||||
section "Interface"
|
||||
test_interface_up
|
||||
test_tunnel_ip
|
||||
test_mtu_check
|
||||
|
||||
section "WireGuard"
|
||||
test_wg_handshake
|
||||
test_wg_peer_reachable
|
||||
test_wg_transfer
|
||||
|
||||
section "OpenVPN"
|
||||
test_ovpn_process
|
||||
test_ovpn_management
|
||||
|
||||
section "Connectivity"
|
||||
test_endpoint_reachable
|
||||
test_peer_connectivity
|
||||
test_dns_over_vpn
|
||||
test_route_check
|
||||
|
||||
# ── Results ──
|
||||
if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
|
||||
print_tap_footer
|
||||
else
|
||||
print_summary
|
||||
fi
|
||||
|
||||
[[ $FAIL -eq 0 ]] && exit 0 || exit 1
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user