#!/usr/bin/env bash # # Мониторинг живости трёх инбаундов + алерт в Telegram. # Установка в cron: */15 * * * * /opt/vpn-setup/monitor.sh >> /var/log/vpn-monitor.log 2>&1 # set -euo pipefail WORKDIR="/opt/vpn-setup" source "$WORKDIR/params.env" STATE_FILE="$WORKDIR/monitor_state" touch "$STATE_FILE" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" } send_alert() { local message="$1" log "ALERT: $message" if [[ -n "${TELEGRAM_BOT_TOKEN:-}" && -n "${TELEGRAM_CHAT_ID:-}" ]]; then curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \ -d chat_id="${TELEGRAM_CHAT_ID}" \ -d text="🚨 VPN Monitor: ${message}" > /dev/null || true fi } was_down() { grep -q "^$1:down$" "$STATE_FILE" 2>/dev/null } mark_state() { local name="$1" local state="$2" grep -v "^$name:" "$STATE_FILE" > "${STATE_FILE}.tmp" 2>/dev/null || true echo "$name:$state" >> "${STATE_FILE}.tmp" mv "${STATE_FILE}.tmp" "$STATE_FILE" } check_tcp_port() { local name="$1" local port="$2" if timeout 5 bash -c "cat < /dev/null > /dev/tcp/127.0.0.1/${port}" 2>/dev/null; then if was_down "$name"; then send_alert "${name} восстановлен (порт ${port} снова отвечает)" fi mark_state "$name" "up" log "OK: $name (tcp/$port)" else if ! was_down "$name"; then send_alert "${name} НЕ отвечает (порт ${port}). Возможна блокировка." fi mark_state "$name" "down" log "FAIL: $name (tcp/$port)" fi } check_systemd_service() { local name="$1" local service="$2" if systemctl is-active --quiet "$service"; then if was_down "$name-svc"; then send_alert "Сервис ${service} снова запущен" fi mark_state "$name-svc" "up" else if ! was_down "$name-svc"; then send_alert "Сервис ${service} УПАЛ (systemd). Пробую перезапустить." fi mark_state "$name-svc" "down" systemctl restart "$service" || true fi } log "=== Проверка начата ===" check_systemd_service "xray" "xray" check_systemd_service "hysteria" "hysteria-server" check_tcp_port "VLESS-XHTTP-443" 443 check_tcp_port "VLESS-TCP-8443" 8443 if [[ -n "${DOMAIN:-}" ]]; then CERT_FILE="/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" if [[ -f "$CERT_FILE" ]]; then EXPIRY_DATE=$(openssl x509 -enddate -noout -in "$CERT_FILE" | cut -d= -f2) EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s) NOW_EPOCH=$(date +%s) DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 )) if [[ $DAYS_LEFT -lt 14 ]]; then send_alert "Сертификат ${DOMAIN} истекает через ${DAYS_LEFT} дней. Проверьте certbot renew." fi log "Сертификат действителен ещё ${DAYS_LEFT} дней" fi fi log "=== Проверка завершена ==="