- Dockerfile: Containerized Asterisk PBX with Ubuntu 24.04 base, all dependencies pre-installed, health checks, and volume persistence - docker-compose.yml: Asterisk service with host networking (required for RTP port range) + optional self-hosted coturn STUN server via --profile stun - docker/entrypoint.sh: Auto-generates configs, certs, and starts Asterisk in foreground with web admin in background - scripts/vpn-diagnostics.sh: Detects VPN interfaces, checks PJSIP transport config, tests STUN reachability, analyzes NAT type, and provides STUN/TURN recommendations for third-party VPNs - scripts/dns-whitelist.sh: Documents all domains needed per network mode (LAN/VPN vs FQDN), per component (server, Sipnetic, Linphone), with --check mode to test DNS resolution and reachability - easy-asterisk script: Added VPN STUN/ICE menu (option 12 in Server Settings) with self-hosted coturn, Google STUN, or custom STUN server options. LAN/VPN devices now get ice_support=yes when VPN ICE is enabled. Web admin Python code also respects VPN_ICE_ENABLED config. Self-hosted coturn in STUN-only mode eliminates all external DNS dependencies - everything operates by IP address, ideal for DNS-filtered environments. https://claude.ai/code/session_01Vm6NLaQuzM4VosAotqS1q8
307 lines
11 KiB
Bash
307 lines
11 KiB
Bash
#!/bin/bash
|
|
# ================================================================
|
|
# VPN Diagnostics for Easy Asterisk
|
|
#
|
|
# Tests whether your third-party VPN setup needs STUN/TURN
|
|
# and validates connectivity between Asterisk and VPN clients.
|
|
#
|
|
# Usage: vpn-diagnostics [--auto] [--client-ip <ip>]
|
|
# ================================================================
|
|
|
|
set -e
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
CYAN='\033[0;36m'
|
|
BOLD='\033[1m'
|
|
NC='\033[0m'
|
|
|
|
CONFIG_FILE="/etc/easy-asterisk/config"
|
|
RESULTS=()
|
|
WARNINGS=()
|
|
CLIENT_IP=""
|
|
AUTO_MODE=false
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--auto) AUTO_MODE=true; shift ;;
|
|
--client-ip) CLIENT_IP="$2"; shift 2 ;;
|
|
--help|-h)
|
|
echo "Usage: vpn-diagnostics [OPTIONS]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --auto Non-interactive mode"
|
|
echo " --client-ip <ip> Test connectivity to specific VPN client"
|
|
echo " --help Show this help"
|
|
exit 0
|
|
;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
|
|
print_header() {
|
|
echo ""
|
|
echo -e "${CYAN}╔══════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${CYAN} $1${NC}"
|
|
echo -e "${CYAN}╚══════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
}
|
|
|
|
pass() { echo -e " ${GREEN}✓${NC} $1"; RESULTS+=("PASS: $1"); }
|
|
fail() { echo -e " ${RED}✗${NC} $1"; RESULTS+=("FAIL: $1"); }
|
|
warn() { echo -e " ${YELLOW}!${NC} $1"; WARNINGS+=("$1"); }
|
|
info() { echo -e " ${CYAN}→${NC} $1"; }
|
|
|
|
# ── Test 1: Detect network interfaces ────────────────────────
|
|
print_header "VPN Diagnostics for Easy Asterisk"
|
|
|
|
echo -e "${BOLD}1. Network Interface Detection${NC}"
|
|
echo ""
|
|
|
|
# Detect primary LAN interface
|
|
primary_ip=$(hostname -I | awk '{print $1}')
|
|
info "Primary IP: ${primary_ip}"
|
|
|
|
# Detect VPN interfaces (tun, tap, wg, tailscale, utun, ppp)
|
|
vpn_found=false
|
|
vpn_ips=()
|
|
vpn_ifaces=()
|
|
|
|
while IFS= read -r line; do
|
|
iface=$(echo "$line" | awk '{print $2}' | tr -d ':')
|
|
ip_addr=$(echo "$line" | awk '{print $4}' | cut -d'/' -f1)
|
|
|
|
# Check for VPN interface patterns
|
|
if [[ "$iface" =~ ^(tun|tap|wg|tailscale|utun|ppp|nordlynx|proton|mullvad) ]] || \
|
|
[[ "$ip_addr" =~ ^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|100\.64\.|100\.96\.|100\.100\.) ]]; then
|
|
vpn_found=true
|
|
vpn_ips+=("$ip_addr")
|
|
vpn_ifaces+=("$iface")
|
|
pass "VPN interface detected: ${iface} (${ip_addr})"
|
|
fi
|
|
done < <(ip -o -f inet addr show scope global 2>/dev/null)
|
|
|
|
if ! $vpn_found; then
|
|
warn "No VPN interface detected on server"
|
|
info "If your VPN runs on the router (not this server), that's expected"
|
|
info "The VPN subnet should be added via VLAN/VPN subnet configuration"
|
|
fi
|
|
|
|
# ── Test 2: Check Asterisk PJSIP transport configuration ─────
|
|
echo ""
|
|
echo -e "${BOLD}2. Asterisk Transport Configuration${NC}"
|
|
echo ""
|
|
|
|
if [[ -f /etc/asterisk/pjsip.conf ]]; then
|
|
# Check local_net entries
|
|
local_nets=$(grep "^local_net=" /etc/asterisk/pjsip.conf 2>/dev/null | sort -u)
|
|
if [[ -n "$local_nets" ]]; then
|
|
while IFS= read -r net; do
|
|
info "Transport local_net: ${net#local_net=}"
|
|
done <<< "$local_nets"
|
|
|
|
# Check if VPN subnets are included
|
|
for vpn_ip in "${vpn_ips[@]}"; do
|
|
vpn_subnet=$(echo "$vpn_ip" | sed 's/\.[0-9]*$/.0\/24/')
|
|
if echo "$local_nets" | grep -q "$vpn_subnet"; then
|
|
pass "VPN subnet ${vpn_subnet} included in transport"
|
|
else
|
|
fail "VPN subnet ${vpn_subnet} NOT in transport local_net"
|
|
warn "Add via: Server Settings → Configure VLAN/VPN Subnets"
|
|
fi
|
|
done
|
|
else
|
|
warn "No local_net entries found in transport (basic LAN mode)"
|
|
fi
|
|
|
|
# Check transport types
|
|
if grep -q "transport=transport-udp" /etc/asterisk/pjsip.conf; then
|
|
pass "UDP transport configured for LAN/VPN devices"
|
|
fi
|
|
if grep -q "transport=transport-tls" /etc/asterisk/pjsip.conf; then
|
|
pass "TLS transport configured for FQDN devices"
|
|
fi
|
|
else
|
|
fail "pjsip.conf not found"
|
|
fi
|
|
|
|
# ── Test 3: Check RTP and ICE/STUN configuration ─────────────
|
|
echo ""
|
|
echo -e "${BOLD}3. RTP / ICE / STUN Configuration${NC}"
|
|
echo ""
|
|
|
|
if [[ -f /etc/asterisk/rtp.conf ]]; then
|
|
rtp_start=$(grep "^rtpstart=" /etc/asterisk/rtp.conf | cut -d= -f2)
|
|
rtp_end=$(grep "^rtpend=" /etc/asterisk/rtp.conf | cut -d= -f2)
|
|
info "RTP port range: ${rtp_start:-10000}-${rtp_end:-20000}"
|
|
|
|
if grep -q "^icesupport=yes" /etc/asterisk/rtp.conf; then
|
|
pass "ICE support enabled"
|
|
stun_addr=$(grep "^stunaddr=" /etc/asterisk/rtp.conf | cut -d= -f2)
|
|
if [[ -n "$stun_addr" ]]; then
|
|
info "STUN server: ${stun_addr}"
|
|
|
|
# Test STUN server reachability
|
|
stun_host=$(echo "$stun_addr" | cut -d: -f1)
|
|
stun_port=$(echo "$stun_addr" | cut -d: -f2)
|
|
stun_port="${stun_port:-3478}"
|
|
|
|
if command -v nslookup &>/dev/null && nslookup "$stun_host" >/dev/null 2>&1; then
|
|
pass "STUN server DNS resolves: ${stun_host}"
|
|
else
|
|
fail "Cannot resolve STUN server: ${stun_host}"
|
|
warn "Add ${stun_host} to DNS whitelist"
|
|
fi
|
|
fi
|
|
else
|
|
info "ICE support disabled (standard for LAN/VPN mode)"
|
|
warn "If audio fails over VPN, enable ICE via: Server Settings → VPN STUN/ICE"
|
|
fi
|
|
else
|
|
warn "rtp.conf not found"
|
|
fi
|
|
|
|
# ── Test 4: Check endpoint ICE settings ───────────────────────
|
|
echo ""
|
|
echo -e "${BOLD}4. Per-Device ICE Configuration${NC}"
|
|
echo ""
|
|
|
|
if [[ -f /etc/asterisk/pjsip.conf ]]; then
|
|
device_count=$(grep -c "^; === Device:" /etc/asterisk/pjsip.conf 2>/dev/null || echo 0)
|
|
ice_device_count=$(grep -c "^ice_support=yes" /etc/asterisk/pjsip.conf 2>/dev/null || echo 0)
|
|
info "Total devices: ${device_count}"
|
|
info "Devices with ICE: ${ice_device_count}"
|
|
|
|
if [[ "$device_count" -gt 0 && "$ice_device_count" -eq 0 ]]; then
|
|
warn "No devices have ICE enabled"
|
|
info "For third-party VPNs with NAT, enable ICE via VPN STUN/ICE menu"
|
|
fi
|
|
fi
|
|
|
|
# ── Test 5: VPN client connectivity ──────────────────────────
|
|
echo ""
|
|
echo -e "${BOLD}5. VPN Client Connectivity${NC}"
|
|
echo ""
|
|
|
|
if [[ -z "$CLIENT_IP" ]] && ! $AUTO_MODE; then
|
|
echo " Enter a VPN client IP to test connectivity (or press Enter to skip):"
|
|
read -p " Client VPN IP: " CLIENT_IP
|
|
fi
|
|
|
|
if [[ -n "$CLIENT_IP" ]]; then
|
|
# Ping test
|
|
if ping -c 2 -W 3 "$CLIENT_IP" >/dev/null 2>&1; then
|
|
pass "Ping to ${CLIENT_IP} succeeded"
|
|
else
|
|
fail "Ping to ${CLIENT_IP} failed"
|
|
warn "VPN routing issue - client may not be reachable"
|
|
fi
|
|
|
|
# SIP port test (UDP 5060)
|
|
if command -v nc &>/dev/null; then
|
|
if nc -z -u -w 3 "$CLIENT_IP" 5060 2>/dev/null; then
|
|
pass "UDP 5060 reachable on ${CLIENT_IP}"
|
|
else
|
|
info "UDP 5060 probe inconclusive (normal for filtered VPNs)"
|
|
fi
|
|
fi
|
|
else
|
|
info "Skipping client connectivity test (no IP provided)"
|
|
fi
|
|
|
|
# ── Test 6: NAT type detection ───────────────────────────────
|
|
echo ""
|
|
echo -e "${BOLD}6. NAT Type Analysis${NC}"
|
|
echo ""
|
|
|
|
# Check if server is behind NAT
|
|
if [[ -n "$primary_ip" ]]; then
|
|
public_ip=$(curl -s -4 --connect-timeout 5 ifconfig.me 2>/dev/null || echo "")
|
|
if [[ -n "$public_ip" ]]; then
|
|
if [[ "$primary_ip" == "$public_ip" ]]; then
|
|
pass "Server has public IP (no NAT)"
|
|
else
|
|
info "Server behind NAT: ${primary_ip} → ${public_ip}"
|
|
info "This is normal for VPN setups where traffic stays on VPN"
|
|
fi
|
|
else
|
|
info "Cannot detect public IP (DNS filtering or no internet)"
|
|
info "Not needed for LAN/VPN mode"
|
|
fi
|
|
fi
|
|
|
|
# ── Test 7: Asterisk registration status ─────────────────────
|
|
echo ""
|
|
echo -e "${BOLD}7. Asterisk Registration Status${NC}"
|
|
echo ""
|
|
|
|
if command -v asterisk &>/dev/null; then
|
|
reg_output=$(asterisk -rx "pjsip show endpoints" 2>/dev/null || echo "")
|
|
if [[ -n "$reg_output" ]]; then
|
|
online_count=$(echo "$reg_output" | grep -c "Avail" 2>/dev/null || echo 0)
|
|
offline_count=$(echo "$reg_output" | grep -c "Unavail" 2>/dev/null || echo 0)
|
|
info "Endpoints online: ${online_count}"
|
|
info "Endpoints offline: ${offline_count}"
|
|
|
|
if [[ "$offline_count" -gt 0 ]]; then
|
|
warn "Some endpoints are offline - check VPN connectivity"
|
|
echo "$reg_output" | grep "Unavail" | while IFS= read -r line; do
|
|
info " Offline: $line"
|
|
done
|
|
fi
|
|
else
|
|
info "Asterisk not running or no endpoints configured"
|
|
fi
|
|
else
|
|
info "Asterisk CLI not available"
|
|
fi
|
|
|
|
# ── Summary ──────────────────────────────────────────────────
|
|
print_header "Diagnostic Summary"
|
|
|
|
fail_count=0
|
|
pass_count=0
|
|
for result in "${RESULTS[@]}"; do
|
|
if [[ "$result" == FAIL* ]]; then
|
|
((fail_count++))
|
|
elif [[ "$result" == PASS* ]]; then
|
|
((pass_count++))
|
|
fi
|
|
done
|
|
|
|
echo -e " Passed: ${GREEN}${pass_count}${NC}"
|
|
echo -e " Failed: ${RED}${fail_count}${NC}"
|
|
echo -e " Warnings: ${YELLOW}${#WARNINGS[@]}${NC}"
|
|
|
|
if [[ ${#WARNINGS[@]} -gt 0 ]]; then
|
|
echo ""
|
|
echo -e "${BOLD}Recommendations:${NC}"
|
|
for w in "${WARNINGS[@]}"; do
|
|
echo -e " ${YELLOW}→${NC} $w"
|
|
done
|
|
fi
|
|
|
|
# ── STUN Recommendation ─────────────────────────────────────
|
|
echo ""
|
|
echo -e "${BOLD}Do you need STUN?${NC}"
|
|
echo ""
|
|
|
|
if $vpn_found; then
|
|
echo -e " VPN detected on this server."
|
|
echo -e " ${GREEN}If your VPN provides direct routing (both sides get VPN IPs),${NC}"
|
|
echo -e " ${GREEN}STUN is likely NOT needed.${NC}"
|
|
echo ""
|
|
echo -e " ${YELLOW}If audio works one-way or not at all, enable STUN:${NC}"
|
|
echo -e " 1. docker compose --profile stun up -d (self-hosted STUN)"
|
|
echo -e " 2. Or via easy-asterisk: Server Settings → VPN STUN/ICE"
|
|
else
|
|
echo -e " No VPN interface found on server."
|
|
echo -e " ${YELLOW}If VPN runs on router/firewall:${NC}"
|
|
echo -e " - Add VPN subnet via: Server Settings → VLAN/VPN Subnets"
|
|
echo -e " - If audio still fails, enable STUN for NAT traversal"
|
|
fi
|
|
|
|
echo ""
|