#!/usr/bin/env bash # ============================================================================= # WireGuard Watchdog — оптимизирован для Starlink # Мониторит состояние туннеля и перезапускает при обрыве связи. # Использует экспоненциальную отсрочку (backoff) при повторных сбоях. # Запускается внутри Docker-контейнера клиента. # ============================================================================= set -euo pipefail # ── Настройки ─────────────────────────────────────────────────────────────── VPS_PUBLIC_IP="${VPS_PUBLIC_IP:-}" SERVER_WG_IP="${SERVER_WG_IP:-10.0.0.1}" WG_INTERFACE="wg0" # ── Параметры экспоненциальной отсрочки ───────────────────────────────────── INITIAL_BACKOFF=10 MAX_BACKOFF=120 BACKOFF_MULTIPLIER=2 STABLE_THRESHOLD=3 # ── Состояние ─────────────────────────────────────────────────────────────── consecutive_failures=0 consecutive_successes=0 current_backoff=$INITIAL_BACKOFF # ── Логирование ───────────────────────────────────────────────────────────── log() { local level="$1" shift echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $*" } # ── Проверка интерфейса WireGuard ────────────────────────────────────────── check_wg_interface() { if ip link show "$WG_INTERFACE" &>/dev/null; then return 0 fi return 1 } # ── Проверка доступности VPS через туннель ────────────────────────────────── check_vps_tunnel() { if [[ -z "$SERVER_WG_IP" ]]; then return 1 fi if ping -c 3 -W 2 "$SERVER_WG_IP" &>/dev/null; then return 0 fi return 1 } # ── Проверка доступности VPS через интернет ───────────────────────────────── check_vps_internet() { if [[ -z "$VPS_PUBLIC_IP" ]]; then return 1 fi if ping -c 2 -W 3 "$VPS_PUBLIC_IP" &>/dev/null; then return 0 fi return 1 } # ── Перезапуск WireGuard ──────────────────────────────────────────────────── restart_wireguard() { log "WARN" "Перезапуск wg-quick@${WG_INTERFACE}..." wg-quick down "$WG_INTERFACE" 2>/dev/null || true sleep 2 wg-quick up "$WG_INTERFACE" sleep 3 } # ── Сброс backoff после стабильной работы ────────────────────────────────── reset_backoff() { consecutive_failures=0 current_backoff=$INITIAL_BACKOFF consecutive_successes=$((consecutive_successes + 1)) if [[ $consecutive_successes -eq $STABLE_THRESHOLD ]]; then log "INFO" "Туннель стабилен (${STABLE_THRESHOLD} успешных проверок подряд)" fi } # ── Увеличение backoff при сбоях ─────────────────────────────────────────── increase_backoff() { consecutive_successes=0 consecutive_failures=$((consecutive_failures + 1)) current_backoff=$((current_backoff * BACKOFF_MULTIPLIER)) if [[ $current_backoff -gt $MAX_BACKOFF ]]; then current_backoff=$MAX_BACKOFF fi } # ── Основной цикл ────────────────────────────────────────────────────────── main_loop() { log "INFO" "Watchdog запущен. VPS=${VPS_PUBLIC_IP}, VPS_WG=${SERVER_WG_IP}, интерфейс=${WG_INTERFACE}" log "INFO" "Параметры backoff: начальный=${INITIAL_BACKOFF}с, макс=${MAX_BACKOFF}с, множитель=${BACKOFF_MULTIPLIER}" while true; do # Проверка 1: интерфейс поднят? if ! check_wg_interface; then log "ERROR" "Интерфейс ${WG_INTERFACE} не найден" increase_backoff log "INFO" "Ожидание ${current_backoff}с перед следующей проверкой..." sleep "$current_backoff" continue fi # Проверка 2: VPS доступен через туннель? if check_vps_tunnel; then reset_backoff sleep 10 continue fi # Туннель не работает — проверяем интернет log "WARN" "VPS ${SERVER_WG_IP} недоступен через туннель" if ! check_vps_internet; then log "WARN" "VPS ${VPS_PUBLIC_IP} недоступен через интернет. Возможно, проблемы с сетью." increase_backoff log "INFO" "Ожидание ${current_backoff}с..." sleep "$current_backoff" continue fi # Интернет работает, туннель нет — перезапускаем log "ERROR" "Туннель не работает, но VPS доступен через интернет. Перезапуск WireGuard..." restart_wireguard sleep 3 if check_vps_tunnel; then log "INFO" "Туннель восстановлен после перезапуска" reset_backoff else log "ERROR" "Туннель не восстановился после перезапуска" increase_backoff fi log "INFO" "Ожидание ${current_backoff}с перед следующей проверкой..." sleep "$current_backoff" done } # ── Точка входа ───────────────────────────────────────────────────────────── if [[ -z "$VPS_PUBLIC_IP" ]]; then log "ERROR" "VPS_PUBLIC_IP не задан. Watchdog не может работать." exit 1 fi main_loop