#!/bin/sh # ============================================================ # Podkop + AmneziaWG + Tailscale Watchdog — Universal Installer # # Сам определяет интерфейс AmneziaWG, сам подбирает рабочий # тестовый домен (через тоннель), создаёт все нужные скрипты, # выдаёт права, настраивает crontab и проверяет результат. # # Запуск: # sh /root/install-watchdog-universal.sh # ============================================================ set -e LOG="/root/podkop_update.log" CRON="/etc/crontabs/root" BACKUP="/root/crontab.backup-$(date +%Y%m%d-%H%M%S)" echo "============================================================" echo " Podkop Watchdog — универсальная установка" echo "============================================================" echo "" # ------------------------------------------------------------ # 1. Определяем интерфейс AmneziaWG # ------------------------------------------------------------ echo "[1/6] Определяем интерфейс AmneziaWG..." IFACE="$(uci -q get podkop.main.interface || true)" if [ -z "$IFACE" ]; then echo " [!] Не удалось получить интерфейс из uci podkop.main.interface." echo " Доступные интерфейсы:" ip -o link show | awk -F': ' '{print " - "$2}' printf " Введите имя интерфейса AmneziaWG вручную: " read IFACE fi if ! ip link show "$IFACE" >/dev/null 2>&1; then echo " [!] Интерфейс '$IFACE' не найден в системе (ip link show)." echo " Доступные интерфейсы:" ip -o link show | awk -F': ' '{print " - "$2}' printf " Введите корректное имя интерфейса: " read IFACE if ! ip link show "$IFACE" >/dev/null 2>&1; then echo " [X] Интерфейс '$IFACE' всё ещё не найден. Прерываю установку." exit 1 fi fi echo " [OK] Используем интерфейс: $IFACE" echo "" # ------------------------------------------------------------ # 2. Подбираем рабочий тестовый домен через тоннель # ------------------------------------------------------------ echo "[2/6] Проверяем, какой домен стабильно открывается через $IFACE..." test_domain_via_tunnel() { DOMAIN="$1" IP="$( nslookup "$DOMAIN" 1.1.1.1 2>/dev/null | awk '/^Address: [0-9]+\./ {print $2; exit}' )" [ -n "$IP" ] || return 1 curl -4 \ --interface "$IFACE" \ --resolve "$DOMAIN:443:$IP" \ --connect-timeout 8 \ --max-time 12 \ -sS \ -o /dev/null \ -w '%{http_code}' \ "https://$DOMAIN" 2>/dev/null | grep -q '^2' } TEST_DOMAIN="" for CANDIDATE in www.google.com www.cloudflare.com www.microsoft.com www.wikipedia.org www.apple.com; do printf " Проверяю %-24s ... " "$CANDIDATE" if test_domain_via_tunnel "$CANDIDATE"; then echo "OK" TEST_DOMAIN="$CANDIDATE" break else echo "не отвечает" fi done if [ -z "$TEST_DOMAIN" ]; then echo "" echo " [!] Ни один из стандартных доменов не открылся через $IFACE." echo " Это может означать, что тоннель сейчас не работает," echo " либо все проверенные домены заблокированы именно у вас." echo "" while [ -z "$TEST_DOMAIN" ]; do printf " Введите домен для проверки (например example.com): " read CUSTOM [ -n "$CUSTOM" ] || continue printf " Проверяю %-24s ... " "$CUSTOM" if test_domain_via_tunnel "$CUSTOM"; then echo "OK" TEST_DOMAIN="$CUSTOM" else echo "не отвечает" printf " Попробовать другой домен? [Y/n] " read ANSWER case "$ANSWER" in n|N) echo " Использую $CUSTOM как есть (без подтверждения) — может давать ложные срабатывания." TEST_DOMAIN="$CUSTOM" ;; esac fi done fi echo "" echo " [OK] Тестовый домен для watchdog: $TEST_DOMAIN" echo "" # ------------------------------------------------------------ # 3. Резервная копия старого crontab # ------------------------------------------------------------ echo "[3/6] Резервное копирование crontab..." if [ -f "$CRON" ]; then cp "$CRON" "$BACKUP" echo " [OK] Старый crontab сохранён: $BACKUP" else echo " [INFO] Старого crontab нет." fi echo "" # ------------------------------------------------------------ # 4. Создаём скрипты # ------------------------------------------------------------ echo "[4/6] Создаём скрипты..." cat > /root/podkop-watchdog.sh <<'SCRIPT_EOF' #!/bin/sh LOG="/root/podkop_update.log" LOCK="/tmp/podkop-watchdog.lock" FLAG="/tmp/podkop-tunnel-down.flag" IFACE="__IFACE__" TEST_DOMAIN="__TEST_DOMAIN__" log() { echo "$(date '+%Y-%m-%d %H:%M:%S') [WATCHDOG] $*" >> "$LOG" } if ! mkdir "$LOCK" 2>/dev/null; then log "Другой экземпляр watchdog уже работает." exit 0 fi trap 'rmdir "$LOCK" 2>/dev/null' EXIT if [ -f "$FLAG" ]; then log "Тоннель ещё не восстановлен (флаг установлен). Восстановлением занимается tunnel-recovery.sh. Пропускаем." exit 0 fi podkop_ok() { STATUS="$(podkop get_sing_box_status 2>/dev/null)" echo "$STATUS" | grep -q '"running":1' || return 1 echo "$STATUS" | grep -q '"dns_configured":1' || return 1 podkop check_fakeip >/dev/null 2>&1 || return 1 return 0 } vpn_tunnel_ok() { IP="$( nslookup "$TEST_DOMAIN" 1.1.1.1 2>/dev/null | awk '/^Address: [0-9]+\./ {print $2; exit}' )" [ -n "$IP" ] || return 1 curl -4 \ --interface "$IFACE" \ --resolve "$TEST_DOMAIN:443:$IP" \ --connect-timeout 8 \ --max-time 12 \ -sS \ -o /dev/null \ -w '%{http_code}' \ "https://$TEST_DOMAIN" 2>/dev/null | grep -q '^2' } log "=== Проверка Podkop ===" if ! vpn_tunnel_ok; then log "Тоннель $IFACE → $TEST_DOMAIN НЕ отвечает. Начинаем 3 проверки тоннеля." TUNNEL_FAILS=0 while [ "$TUNNEL_FAILS" -lt 3 ]; do if vpn_tunnel_ok; then log "Тоннель $IFACE восстановился сам, во время проверки." break fi TUNNEL_FAILS=$((TUNNEL_FAILS + 1)) log "Тоннель: попытка $TUNNEL_FAILS/3 неудачна." if [ "$TUNNEL_FAILS" -lt 3 ]; then sleep 60 fi done if [ "$TUNNEL_FAILS" -ge 3 ]; then log "3 проверки тоннеля подряд неудачны. Перезапускаем интерфейс $IFACE." ifdown "$IFACE" >> "$LOG" 2>&1 sleep 3 ifup "$IFACE" >> "$LOG" 2>&1 log "Ждём 1 минуту после перезапуска интерфейса." sleep 60 if vpn_tunnel_ok; then log "Тоннель $IFACE успешно восстановлен после перезапуска интерфейса." else log "КРИТИЧНО: тоннель $IFACE не поднялся даже после перезапуска интерфейса." log "Останавливаем Podkop немедленно, чтобы не блокировать интернет без тоннеля." podkop stop >> "$LOG" 2>&1 touch "$FLAG" log "Podkop остановлен. Флаг восстановления установлен." log "Дальнейший мониторинг тоннеля каждые 5 минут передан tunnel-recovery.sh." exit 0 fi fi fi log "Тоннель $IFACE → $TEST_DOMAIN работает." if podkop_ok; then log "Podkop работает: sing-box + DNS/FakeIP OK." exit 0 fi log "Podkop не прошёл проверку. Начинаем 3 проверки." FAILS=0 while [ "$FAILS" -lt 3 ]; do if vpn_tunnel_ok && podkop_ok; then log "Podkop восстановился во время проверки." exit 0 fi FAILS=$((FAILS + 1)) log "Попытка $FAILS/3: Podkop/$TEST_DOMAIN не прошли проверку." if [ "$FAILS" -lt 3 ]; then sleep 60 fi done log "3 проверки подряд неудачны. Перезапускаем Podkop." podkop restart >> "$LOG" 2>&1 sleep 15 if vpn_tunnel_ok && podkop_ok; then log "Podkop успешно восстановлен после restart." exit 0 fi log "После restart Podkop всё ещё не работает." log "Останавливаем Podkop." podkop stop >> "$LOG" 2>&1 sleep 3 if vpn_tunnel_ok; then log "$IFACE → $TEST_DOMAIN работает. Запускаем Podkop обратно." podkop start >> "$LOG" 2>&1 sleep 15 if vpn_tunnel_ok && podkop_ok; then log "Podkop успешно запущен после проверки $IFACE." exit 0 fi log "Podkop после повторного запуска снова не прошёл проверку." log "Останавливаем Podkop и оставляем выключенным." podkop stop >> "$LOG" 2>&1 else log "$IFACE → $TEST_DOMAIN НЕ работает." log "Останавливаем Podkop и передаём мониторинг tunnel-recovery.sh." podkop stop >> "$LOG" 2>&1 touch "$FLAG" fi exit 0 SCRIPT_EOF sed -i "s|__IFACE__|$IFACE|g; s|__TEST_DOMAIN__|$TEST_DOMAIN|g" /root/podkop-watchdog.sh chmod +x /root/podkop-watchdog.sh echo " [OK] /root/podkop-watchdog.sh" cat > /root/tunnel-recovery.sh <<'SCRIPT_EOF' #!/bin/sh LOG="/root/podkop_update.log" LOCK="/tmp/tunnel-recovery.lock" FLAG="/tmp/podkop-tunnel-down.flag" IFACE="__IFACE__" TEST_DOMAIN="__TEST_DOMAIN__" log() { echo "$(date '+%Y-%m-%d %H:%M:%S') [TUNNEL-RECOVERY] $*" >> "$LOG" } if [ ! -f "$FLAG" ]; then exit 0 fi if ! mkdir "$LOCK" 2>/dev/null; then exit 0 fi trap 'rmdir "$LOCK" 2>/dev/null' EXIT vpn_tunnel_ok() { IP="$( nslookup "$TEST_DOMAIN" 1.1.1.1 2>/dev/null | awk '/^Address: [0-9]+\./ {print $2; exit}' )" [ -n "$IP" ] || return 1 curl -4 \ --interface "$IFACE" \ --resolve "$TEST_DOMAIN:443:$IP" \ --connect-timeout 8 \ --max-time 12 \ -sS \ -o /dev/null \ -w '%{http_code}' \ "https://$TEST_DOMAIN" 2>/dev/null | grep -q '^2' } log "Podkop остановлен, тоннель $IFACE ещё не восстановлен. Проверяем." if vpn_tunnel_ok; then log "Тоннель $IFACE ожил. Запускаем Podkop обратно." podkop start >> "$LOG" 2>&1 sleep 15 if vpn_tunnel_ok; then log "Podkop успешно запущен, тоннель подтверждён рабочим." else log "ВНИМАНИЕ: Podkop запущен, но повторная проверка тоннеля не прошла. Проверьте вручную." fi rm -f "$FLAG" log "Флаг восстановления снят." else log "Тоннель $IFACE всё ещё не отвечает. Podkop остаётся выключенным. Следующая проверка через 5 минут." fi exit 0 SCRIPT_EOF sed -i "s|__IFACE__|$IFACE|g; s|__TEST_DOMAIN__|$TEST_DOMAIN|g" /root/tunnel-recovery.sh chmod +x /root/tunnel-recovery.sh echo " [OK] /root/tunnel-recovery.sh" cat > /root/tailscale-dns-check.sh <<'SCRIPT_EOF' #!/bin/sh LOG="/root/podkop_update.log" TAILSCALE="/usr/sbin/tailscale" log() { echo "$(date '+%Y-%m-%d %H:%M:%S') [TAILSCALE-DNS] $*" >> "$LOG" } SERVER="$( nslookup fakeip.podkop.fyi 2>/dev/null | awk '/^Server:/{print $2; exit}' )" case "$SERVER" in 100.100.100.100) log "Обнаружен DNS Tailscale 100.100.100.100." log "Выполняем: tailscale up --accept-dns=false --accept-routes" "$TAILSCALE" up \ --accept-dns=false \ --accept-routes \ >> "$LOG" 2>&1 if [ "$?" -eq 0 ]; then log "Tailscale DNS исправлен." else log "ОШИБКА при выполнении tailscale up." fi ;; 127.0.0.1) log "DNS 127.0.0.1 — всё нормально." ;; *) log "Неожиданный DNS-сервер: ${SERVER:-не определён}. Ничего не меняем." ;; esac exit 0 SCRIPT_EOF chmod +x /root/tailscale-dns-check.sh echo " [OK] /root/tailscale-dns-check.sh" cat > /root/podkop-update.sh <<'SCRIPT_EOF' #!/bin/sh LOG="/root/podkop_update.log" TMP="/tmp/podkop-install.sh.$$" URL="https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh" echo "$(date '+%Y-%m-%d %H:%M:%S') [UPDATE] Начало обновления Podkop." >> "$LOG" if wget -q -O "$TMP" "$URL"; then echo "$(date '+%Y-%m-%d %H:%M:%S') [UPDATE] install.sh скачан." >> "$LOG" if sh "$TMP" >> "$LOG" 2>&1; then echo "$(date '+%Y-%m-%d %H:%M:%S') [UPDATE] Podkop успешно обновлён." >> "$LOG" else echo "$(date '+%Y-%m-%d %H:%M:%S') [UPDATE] ОШИБКА обновления Podkop." >> "$LOG" fi else echo "$(date '+%Y-%m-%d %H:%M:%S') [UPDATE] Не удалось скачать install.sh." >> "$LOG" fi rm -f "$TMP" exit 0 SCRIPT_EOF chmod +x /root/podkop-update.sh echo " [OK] /root/podkop-update.sh" echo "" # ------------------------------------------------------------ # 5. Проверка синтаксиса всех скриптов # ------------------------------------------------------------ echo "[5/6] Проверка синтаксиса..." for SCRIPT in podkop-watchdog.sh tunnel-recovery.sh tailscale-dns-check.sh podkop-update.sh; do if sh -n "/root/$SCRIPT"; then echo " [OK] $SCRIPT" else echo " [X] $SCRIPT — ОШИБКА СИНТАКСИСА, прерываю установку." exit 1 fi done echo "" # ------------------------------------------------------------ # 6. Настройка crontab # ------------------------------------------------------------ echo "[6/6] Настройка crontab..." cat > "$CRON" <> /root/podkop_update.log 2>&1 # Перезапуск Podkop понедельник-суббота 0 3 * * 1-6 /etc/init.d/podkop restart >> /root/podkop_update.log 2>&1 # Обновление Podkop в воскресенье 0 3 * * 0 /root/podkop-update.sh >> /root/podkop_update.log 2>&1 # Watchdog Podkop + тоннель (каждые 20 минут) */20 * * * * /root/podkop-watchdog.sh # Восстановление после падения тоннеля (каждые 5 минут, работает только если флаг установлен) */5 * * * * /root/tunnel-recovery.sh # Проверка DNS Tailscale */30 * * * * /root/tailscale-dns-check.sh # Очистка лога раз в неделю 0 5 * * 1 : > /root/podkop_update.log EOF echo " [OK] crontab настроен." /etc/init.d/cron restart echo " [OK] cron перезапущен." echo "" # ------------------------------------------------------------ # Финальная проверка # ------------------------------------------------------------ echo "============================================================" echo " УСТАНОВКА ЗАВЕРШЕНА" echo "============================================================" echo "" echo "Интерфейс: $IFACE" echo "Тестовый домен: $TEST_DOMAIN" echo "Бэкап crontab: $BACKUP" echo "" echo "Текущий crontab:" echo "------------------------------------------------------------" cat "$CRON" echo "------------------------------------------------------------" echo "" echo "Пробный запуск watchdog прямо сейчас..." echo "------------------------------------------------------------" /root/podkop-watchdog.sh tail -10 "$LOG" echo "------------------------------------------------------------" echo "" echo "Готово."