############################################################# # scripts/security-monitor.sh # ############################################################# cat > scripts/security-monitor.sh << 'EOFSECMON' #!/bin/bash # VPN Security Monitor # Continuous monitoring of killswitch and VPN status # Version: 1.0.0 # Configuration CHECK_INTERVAL=10 # seconds ALERT_EMAIL="" # Set email for alerts LOG_FILE="/var/log/vpn-security-monitor.log" STATE_FILE="/var/run/vpn-monitor.state" # Source network config if [ -f /opt/vpn-gateway/network.conf ]; then source /opt/vpn-gateway/network.conf fi # Logging functions log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE" } alert() { local message="$1" log "ALERT: $message" # Send email alert if configured if [ -n "$ALERT_EMAIL" ]; then echo "$message" | mail -s "VPN Security Alert" "$ALERT_EMAIL" 2>/dev/null || true fi # System notification (if available) if command -v notify-send >/dev/null 2>&1; then notify-send "VPN Security Alert" "$message" -u critical fi # Log to syslog logger -t "vpn-security" -p auth.crit "$message" } # Check functions check_killswitch() { # Verify DROP policies are active local policies_ok=true for chain in INPUT OUTPUT FORWARD; do if ! iptables -L $chain -n | grep -q "policy DROP"; then alert "Killswitch policy missing for chain $chain!" policies_ok=false fi done if [ "$policies_ok" = false ]; then log "Reactivating killswitch..." /usr/local/bin/vpn-killswitch.sh enable return 1 fi return 0 } check_vpn_connection() { if wg show wg0 >/dev/null 2>&1; then # VPN interface exists, check if it's actually working local endpoint=$(wg show wg0 endpoints | awk '{print $2}') if [ -z "$endpoint" ]; then log "WARNING: VPN interface exists but no endpoint configured" return 1 fi # Check last handshake local last_handshake=$(wg show wg0 latest-handshakes | awk '{print $2}') local current_time=$(date +%s) if [ -n "$last_handshake" ] && [ "$last_handshake" -ne 0 ]; then local time_diff=$((current_time - last_handshake)) # If last handshake was more than 3 minutes ago, connection might be dead if [ $time_diff -gt 180 ]; then log "WARNING: Last WireGuard handshake was ${time_diff} seconds ago" return 2 fi fi return 0 else return 1 fi } check_dns_leaks() { # Check if DNS is going through VPN local dns_servers=$(cat /etc/resolv.conf | grep "^nameserver" | awk '{print $2}') for dns in $dns_servers; do # Check if DNS server is in private range or VPN provider's DNS case "$dns" in 10.*|172.16.*|172.17.*|172.18.*|172.19.*|172.2*|172.30.*|172.31.*|192.168.*|100.64.*) # Private IP, likely VPN DNS ;; 127.*) # Localhost, check if it's a DNS proxy if systemctl is-active systemd-resolved >/dev/null 2>&1; then log "WARNING: systemd-resolved is active, checking for leaks" # Additional checks for systemd-resolved fi ;; *) alert "Potential DNS leak detected! Public DNS server: $dns" return 1 ;; esac done return 0 } check_leak_test() { # Try to reach internet without VPN local vpn_active=$(check_vpn_connection; echo $?) if [ $vpn_active -eq 1 ]; then # VPN not active, internet should be blocked if ping -c 1 -W 1 8.8.8.8 >/dev/null 2>&1; then alert "CRITICAL: Internet accessible without VPN! Leak detected!" # Immediately re-enable killswitch /usr/local/bin/vpn-killswitch.sh enable return 1 fi fi return 0 } check_processes() { # Check for processes that might bypass VPN local suspicious_processes=$(netstat -tunp 2>/dev/null | grep -v "127.0.0.1\|::1\|$LAN_NET" | grep ESTABLISHED) if [ -n "$suspicious_processes" ]; then log "WARNING: Detected network connections that might bypass VPN:" echo "$suspicious_processes" >> "$LOG_FILE" fi } monitor_bandwidth() { # Monitor bandwidth to detect unusual activity if command -v vnstat >/dev/null 2>&1; then local current_tx=$(vnstat --oneline | cut -d';' -f9) local current_rx=$(vnstat --oneline | cut -d';' -f10) if [ -f "$STATE_FILE" ]; then source "$STATE_FILE" # Compare with previous values # Alert if sudden spike in traffic when VPN is down if ! check_vpn_connection && [ -n "$LAST_TX" ]; then # Calculate difference # If significant traffic when VPN is down, alert log "Bandwidth check: TX=$current_tx RX=$current_rx" fi fi # Save current state echo "LAST_TX='$current_tx'" > "$STATE_FILE" echo "LAST_RX='$current_rx'" >> "$STATE_FILE" fi } auto_recovery() { local vpn_status=$(check_vpn_connection; echo $?) if [ $vpn_status -eq 2 ]; then # Connection stale, try to reconnect log "Attempting auto-recovery of VPN connection..." # Get last used server from config if [ -f /etc/wireguard/wg0.conf ]; then systemctl restart wg-quick@wg0 2>/dev/null || \ wg-quick down wg0 2>/dev/null && wg-quick up wg0 2>/dev/null sleep 5 if check_vpn_connection; then log "Auto-recovery successful" return 0 else alert "Auto-recovery failed - manual intervention required" return 1 fi fi fi return 0 } # Main monitoring loop main() { log "VPN Security Monitor started" log "Check interval: ${CHECK_INTERVAL}s" # Initial checks check_killswitch check_vpn_connection check_dns_leaks local error_count=0 while true; do # Run all checks local all_ok=true if ! check_killswitch; then all_ok=false ((error_count++)) fi if ! check_leak_test; then all_ok=false ((error_count++)) fi if ! check_dns_leaks; then all_ok=false ((error_count++)) fi check_processes monitor_bandwidth # Auto-recovery if needed if [ $error_count -gt 3 ]; then auto_recovery error_count=0 fi # Status indicator if [ "$all_ok" = true ]; then echo -n "." error_count=0 else echo -n "!" fi sleep "$CHECK_INTERVAL" done } # Signal handlers trap 'log "Security monitor stopped"; exit 0' SIGTERM SIGINT # Run main loop main