- Updated .gitlab-ci.yml with complete build, test, and deploy stages
- Added authentication redirect fix in Program.cs (302 redirect for admin routes)
- Fixed Cookie vs Bearer authentication conflict for admin panel
- Configure pipeline to build from .NET 9.0 source
- Deploy to Hostinger VPS with proper environment variables
- Include rollback capability for production deployments
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
347 lines
9.6 KiB
Bash
347 lines
9.6 KiB
Bash
#!/bin/bash
|
|
|
|
################################################################################
|
|
# TOR Health Monitoring Script
|
|
#
|
|
# Purpose: Continuous monitoring of TOR connectivity and TeleBot TOR usage
|
|
# Usage: ./tor-health-monitor.sh [--daemon] [--interval=60]
|
|
# Output: Health reports and alerts
|
|
#
|
|
# Features:
|
|
# - Real-time TOR connectivity monitoring
|
|
# - Circuit health tracking
|
|
# - IP leak detection
|
|
# - Automated alerting
|
|
# - Historical logging
|
|
#
|
|
# Author: Mr Tickles, Security Consultant
|
|
# Date: 2025-10-01
|
|
################################################################################
|
|
|
|
set -euo pipefail
|
|
|
|
# Configuration
|
|
INTERVAL=60 # Check interval in seconds
|
|
DAEMON_MODE=false
|
|
LOG_DIR="/var/log/telebot"
|
|
HEALTH_LOG="$LOG_DIR/tor-health.log"
|
|
ALERT_LOG="$LOG_DIR/tor-alerts.log"
|
|
STATE_DIR="/var/lib/telebot"
|
|
TOR_SOCKS_PORT=9050
|
|
EMAIL_ALERTS=false
|
|
ALERT_EMAIL="admin@example.com"
|
|
|
|
# Parse arguments
|
|
for arg in "$@"; do
|
|
case $arg in
|
|
--daemon)
|
|
DAEMON_MODE=true
|
|
shift
|
|
;;
|
|
--interval=*)
|
|
INTERVAL="${arg#*=}"
|
|
shift
|
|
;;
|
|
--email=*)
|
|
ALERT_EMAIL="${arg#*=}"
|
|
EMAIL_ALERTS=true
|
|
shift
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
# Create directories
|
|
mkdir -p "$LOG_DIR" "$STATE_DIR"
|
|
|
|
################################################################################
|
|
# Logging Functions
|
|
################################################################################
|
|
|
|
log() {
|
|
local level=$1
|
|
shift
|
|
local message="$@"
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
|
|
echo "[$timestamp] [$level] $message" >> "$HEALTH_LOG"
|
|
|
|
if [ "$DAEMON_MODE" = false ]; then
|
|
case $level in
|
|
INFO)
|
|
echo -e "${BLUE}[INFO]${NC} $message"
|
|
;;
|
|
SUCCESS)
|
|
echo -e "${GREEN}[✓]${NC} $message"
|
|
;;
|
|
WARNING)
|
|
echo -e "${YELLOW}[⚠]${NC} $message"
|
|
;;
|
|
ERROR)
|
|
echo -e "${RED}[✗]${NC} $message"
|
|
;;
|
|
ALERT)
|
|
echo -e "${RED}[ALERT]${NC} $message"
|
|
echo "[$timestamp] [ALERT] $message" >> "$ALERT_LOG"
|
|
;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
send_alert() {
|
|
local subject="$1"
|
|
local message="$2"
|
|
|
|
log ALERT "$subject: $message"
|
|
|
|
if [ "$EMAIL_ALERTS" = true ]; then
|
|
echo "$message" | mail -s "TeleBot TOR Alert: $subject" "$ALERT_EMAIL" 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
################################################################################
|
|
# Health Check Functions
|
|
################################################################################
|
|
|
|
check_tor_service() {
|
|
if systemctl is-active --quiet tor 2>/dev/null; then
|
|
log SUCCESS "TOR service is running"
|
|
return 0
|
|
else
|
|
log ERROR "TOR service is not running"
|
|
send_alert "TOR Service Down" "TOR service is not running. TeleBot location is EXPOSED!"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_tor_socks() {
|
|
if netstat -tln 2>/dev/null | grep -q ":${TOR_SOCKS_PORT} "; then
|
|
log SUCCESS "TOR SOCKS5 proxy is listening on port ${TOR_SOCKS_PORT}"
|
|
return 0
|
|
else
|
|
log ERROR "TOR SOCKS5 proxy is not listening"
|
|
send_alert "TOR SOCKS5 Down" "TOR SOCKS5 proxy not available. Traffic cannot be routed through TOR!"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_tor_circuits() {
|
|
local bootstrap_status=$(journalctl -u tor -n 100 --no-pager 2>/dev/null | \
|
|
grep -i "Bootstrapped" | tail -1)
|
|
|
|
if echo "$bootstrap_status" | grep -q "100%"; then
|
|
log SUCCESS "TOR circuits are established (100%)"
|
|
return 0
|
|
else
|
|
log WARNING "TOR circuits may not be fully established"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_tor_ip() {
|
|
local tor_ip=""
|
|
local direct_ip=""
|
|
|
|
# Get IP through TOR
|
|
tor_ip=$(timeout 15 curl --socks5 127.0.0.1:${TOR_SOCKS_PORT} -s https://api.ipify.org 2>/dev/null || echo "")
|
|
|
|
if [ -z "$tor_ip" ]; then
|
|
log ERROR "Failed to get IP through TOR"
|
|
return 1
|
|
fi
|
|
|
|
# Get direct IP
|
|
direct_ip=$(timeout 10 curl -s https://api.ipify.org 2>/dev/null || echo "")
|
|
|
|
if [ -z "$direct_ip" ]; then
|
|
log WARNING "Failed to get direct IP (network issue?)"
|
|
return 0
|
|
fi
|
|
|
|
# Compare IPs
|
|
if [ "$tor_ip" != "$direct_ip" ]; then
|
|
log SUCCESS "TOR IP ($tor_ip) is different from direct IP ($direct_ip)"
|
|
|
|
# Save IPs for tracking
|
|
echo "$tor_ip" > "$STATE_DIR/current_tor_ip"
|
|
echo "$direct_ip" > "$STATE_DIR/real_ip"
|
|
|
|
return 0
|
|
else
|
|
log ERROR "TOR IP matches direct IP - TOR may not be working!"
|
|
send_alert "TOR IP Mismatch" "TOR IP ($tor_ip) matches direct IP! TOR may be bypassed!"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_telebot_process() {
|
|
if pgrep -f "TeleBot" > /dev/null; then
|
|
local pid=$(pgrep -f "TeleBot" | head -1)
|
|
log SUCCESS "TeleBot is running (PID: $pid)"
|
|
|
|
# Check TOR connections
|
|
local tor_conns=$(lsof -p "$pid" -i TCP 2>/dev/null | grep -c ":${TOR_SOCKS_PORT}" || echo 0)
|
|
|
|
if [ "$tor_conns" -gt 0 ]; then
|
|
log SUCCESS "TeleBot has $tor_conns active TOR connections"
|
|
else
|
|
log WARNING "TeleBot has no active TOR connections"
|
|
fi
|
|
|
|
return 0
|
|
else
|
|
log WARNING "TeleBot is not running"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_ip_leaks() {
|
|
if ! pgrep -f "TeleBot" > /dev/null; then
|
|
return 0 # Can't check if not running
|
|
fi
|
|
|
|
local pid=$(pgrep -f "TeleBot" | head -1)
|
|
|
|
# Check for direct external connections
|
|
local external_conns=$(ss -tnp 2>/dev/null | grep "$pid" | \
|
|
grep -v "127.0.0.1" | \
|
|
grep -v "::1" | \
|
|
grep -v ":${TOR_SOCKS_PORT}" | \
|
|
wc -l)
|
|
|
|
if [ "$external_conns" -eq 0 ]; then
|
|
log SUCCESS "No IP leaks detected (all connections through TOR)"
|
|
return 0
|
|
else
|
|
log ERROR "Detected $external_conns direct external connections - IP LEAK!"
|
|
send_alert "IP Leak Detected" "TeleBot has $external_conns direct external connections not through TOR!"
|
|
|
|
# Log the suspicious connections
|
|
ss -tnp 2>/dev/null | grep "$pid" | \
|
|
grep -v "127.0.0.1" | \
|
|
grep -v "::1" | \
|
|
grep -v ":${TOR_SOCKS_PORT}" >> "$ALERT_LOG"
|
|
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_dns_leaks() {
|
|
# Monitor for DNS queries not through TOR
|
|
local dns_count=$(timeout 5 tcpdump -i any -c 10 'port 53' 2>/dev/null | wc -l || echo 0)
|
|
|
|
if [ "$dns_count" -eq 0 ]; then
|
|
log SUCCESS "No DNS leaks detected"
|
|
return 0
|
|
else
|
|
log WARNING "Detected DNS queries - potential DNS leak"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
################################################################################
|
|
# Performance Metrics
|
|
################################################################################
|
|
|
|
measure_tor_latency() {
|
|
local start_time=$(date +%s%N)
|
|
local test_result=$(timeout 10 curl --socks5 127.0.0.1:${TOR_SOCKS_PORT} -s -o /dev/null -w "%{http_code}" https://check.torproject.org 2>/dev/null || echo "0")
|
|
local end_time=$(date +%s%N)
|
|
|
|
if [ "$test_result" = "200" ]; then
|
|
local latency=$(( (end_time - start_time) / 1000000 )) # Convert to milliseconds
|
|
log INFO "TOR latency: ${latency}ms"
|
|
echo "$latency" > "$STATE_DIR/tor_latency"
|
|
|
|
if [ "$latency" -gt 5000 ]; then
|
|
log WARNING "TOR latency is high (${latency}ms)"
|
|
fi
|
|
|
|
return 0
|
|
else
|
|
log ERROR "Failed to measure TOR latency"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
################################################################################
|
|
# Main Health Check
|
|
################################################################################
|
|
|
|
run_health_check() {
|
|
local check_id=$(date +%Y%m%d_%H%M%S)
|
|
log INFO "==================== Health Check $check_id ===================="
|
|
|
|
local total_checks=0
|
|
local passed_checks=0
|
|
|
|
# Run all checks
|
|
for check in check_tor_service check_tor_socks check_tor_circuits \
|
|
check_tor_ip check_telebot_process check_ip_leaks \
|
|
check_dns_leaks measure_tor_latency; do
|
|
total_checks=$((total_checks + 1))
|
|
|
|
if $check; then
|
|
passed_checks=$((passed_checks + 1))
|
|
fi
|
|
done
|
|
|
|
# Calculate health score
|
|
local health_score=$((passed_checks * 100 / total_checks))
|
|
|
|
log INFO "Health Score: $health_score% ($passed_checks/$total_checks checks passed)"
|
|
|
|
# Save health score
|
|
echo "$health_score" > "$STATE_DIR/health_score"
|
|
|
|
# Alert if health is poor
|
|
if [ "$health_score" -lt 80 ]; then
|
|
send_alert "Poor Health Score" "TOR health score is $health_score%. Review logs: $HEALTH_LOG"
|
|
fi
|
|
|
|
log INFO "================================================================"
|
|
echo ""
|
|
}
|
|
|
|
################################################################################
|
|
# Daemon Mode
|
|
################################################################################
|
|
|
|
run_daemon() {
|
|
log INFO "Starting TOR health monitor daemon (interval: ${INTERVAL}s)"
|
|
|
|
# Create PID file
|
|
echo $$ > "$STATE_DIR/monitor.pid"
|
|
|
|
# Trap signals
|
|
trap 'log INFO "Stopping TOR health monitor daemon"; rm -f "$STATE_DIR/monitor.pid"; exit 0' SIGTERM SIGINT
|
|
|
|
while true; do
|
|
run_health_check
|
|
sleep "$INTERVAL"
|
|
done
|
|
}
|
|
|
|
################################################################################
|
|
# Main
|
|
################################################################################
|
|
|
|
main() {
|
|
if [ "$DAEMON_MODE" = true ]; then
|
|
run_daemon
|
|
else
|
|
run_health_check
|
|
fi
|
|
}
|
|
|
|
# Execute
|
|
main "$@"
|