#!/bin/bash # # SSH Tunnel Manager - Профессиональное решение для управления обратными SSH-туннелями # Использует autossh и systemd для обеспечения стабильного и постоянного соединения. # # Использование: # 1. Установка: sudo bash tunnel-manager.sh install # 2. Запуск: sudo bash tunnel-manager.sh menu # # --- Глобальные переменные и конфигурация --- CONFIG_DIR="/etc/tunnel-manager" TUNNELS_DIR="$CONFIG_DIR/tunnels" LOG_DIR="/var/log/tunnel-manager" SERVICE_DIR="/etc/systemd/system" SCRIPT_PATH="/usr/local/bin/tunnel-manager" VPS_CONFIG="$CONFIG_DIR/vps_config" # --- Цвета для вывода --- RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' BOLD='\033[1m' # --- Вспомогательные функции --- # Функция логирования и вывода сообщений log_message() { local message="$1" local color="${2:-$NC}" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') # Вывод в консоль и запись в лог echo -e "${color}[$timestamp] $message${NC}" | tee -a "$LOG_DIR/manager.log" } # Функция проверки ошибок check_error() { local exit_code=$? if [ $exit_code -ne 0 ]; then log_message "ОШИБКА (код $exit_code): $1" "$RED" log_message "Подробности в логе: $LOG_DIR/manager.log" "$YELLOW" echo -e "${YELLOW}Произошла ошибка: $1${NC}" read -p "Продолжить выполнение скрипта? (y/n): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1 fi return 1 fi return 0 } # Функция проверки прав root check_root() { if [[ $EUID -ne 0 ]]; then log_message "ОШИБКА: Этот скрипт требует прав root (sudo)." "$RED" log_message "Пожалуйста, запустите: sudo bash $0 $1" "$BOLD" exit 1 fi } # Функция безопасного ввода с ограничением попыток safe_read() { local prompt="$1" local default="$2" local var_name="$3" local max_attempts="${4:-3}" # По умолчанию 3 попытки local attempt=0 local input="" while [[ $attempt -lt $max_attempts ]]; do attempt=$((attempt + 1)) read -p "$prompt" input input="${input:-$default}" # Если есть ввод или значение по умолчанию if [ -n "$input" ]; then eval "$var_name=\"$input\"" return 0 # Если нет ввода и нет значения по умолчанию, и это не последняя попытка elif [ -z "$default" ] && [[ $attempt -lt $max_attempts ]]; then echo -e "${YELLOW}Поле не может быть пустым! Попытка $attempt из $max_attempts${NC}" # Если нет ввода, но есть значение по умолчанию, и это не последняя попытка (должно быть обработано выше, но для надежности) elif [ -n "$default" ]; then eval "$var_name=\"$default\"" return 0 fi done # Если превышено количество попыток echo -e "${RED}Превышено максимальное количество попыток ввода. Выход.${NC}" exit 1 } # Функция загрузки настроек VPS load_vps_settings() { if [ -f "$VPS_CONFIG" ]; then source "$VPS_CONFIG" return 0 fi return 1 } # --- Основные функции --- # 1. Установка зависимостей и настройка системы install_manager() { check_root "install" log_message "Начало установки SSH Tunnel Manager..." "$CYAN" # Создание директорий log_message "Создание системных директорий..." "$BLUE" mkdir -p "$CONFIG_DIR" "$TUNNELS_DIR" "$LOG_DIR" chmod 700 "$CONFIG_DIR" "$TUNNELS_DIR" chmod 777 "$LOG_DIR" # Для удобства логирования # Установка зависимостей log_message "Установка необходимых зависимостей (autossh, openssh-client)..." "$BLUE" # Определение дистрибутива local OS if [ -f /etc/os-release ]; then . /etc/os-release OS=$ID fi log_message "Обнаружен дистрибутив: ${OS:-Unknown}" "$BLUE" if [[ "$OS" == "ubuntu" || "$OS" == "debian" ]]; then log_message "Обновление списка пакетов..." "$BLUE" apt-get update 2>&1 | tee -a "$LOG_DIR/manager.log" check_error "Не удалось обновить список пакетов" log_message "Установка autossh, openssh-client и sshpass..." "$BLUE" apt-get install -y autossh openssh-client sshpass 2>&1 | tee -a "$LOG_DIR/manager.log" check_error "Не удалось установить пакеты (apt)" elif [[ "$OS" == "centos" || "$OS" == "rhel" || "$OS" == "fedora" || "$OS" == "rocky" || "$OS" == "almalinux" ]]; then log_message "Установка autossh, openssh-clients и sshpass..." "$BLUE" if command -v dnf &> /dev/null; then dnf install -y autossh openssh-clients sshpass 2>&1 | tee -a "$LOG_DIR/manager.log" check_error "Не удалось установить пакеты (dnf)" else yum install -y autossh openssh-clients sshpass 2>&1 | tee -a "$LOG_DIR/manager.log" check_error "Не удалось установить пакеты (yum)" fi else log_message "ОШИБКА: Не удалось определить менеджер пакетов (apt, dnf, yum)." "$RED" log_message "Пожалуйста, установите autossh и openssh-client вручную." "$YELLOW" return 1 fi if ! command -v autossh &> /dev/null; then log_message "ОШИБКА: autossh не установлен. Проверьте вывод установки." "$RED" return 1 fi log_message "Зависимости успешно установлены." "$GREEN" # Копирование скрипта в системный путь log_message "Копирование скрипта в $SCRIPT_PATH..." "$BLUE" cp "$0" "$SCRIPT_PATH" chmod +x "$SCRIPT_PATH" log_message "Установка завершена. Теперь можно использовать команду: sudo tunnel-manager menu" "$GREEN" # Генерация SSH ключа if [ ! -f "/root/.ssh/id_rsa" ]; then log_message "Генерация SSH ключа RSA 4096 бит для root..." "$BLUE" mkdir -p /root/.ssh ssh-keygen -t rsa -b 4096 -N "" -f /root/.ssh/id_rsa -q log_message "SSH ключ сгенерирован: /root/.ssh/id_rsa.pub" "$GREEN" else log_message "SSH ключ уже существует. Используем существующий." "$YELLOW" fi # Настройка SSH конфига для стабильности log_message "Настройка SSH клиента для стабильности..." "$BLUE" cat > /root/.ssh/config << EOFCONFIG Host * StrictHostKeyChecking no UserKnownHostsFile /dev/null ServerAliveInterval 30 ServerAliveCountMax 3 ConnectTimeout 30 TCPKeepAlive yes IdentitiesOnly yes ExitOnForwardFailure yes ControlMaster auto ControlPath ~/.ssh/control-%r@%h:%p ControlPersist 10m # Дополнительные настройки стабильности IPQoS throughput Compression yes GSSAPIAuthentication no AddressFamily inet Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256 EOFCONFIG chmod 600 /root/.ssh/config log_message "Конфигурация SSH клиента обновлена." "$GREEN" # Настройка локального SSHD configure_local_sshd } # Функция настройки локального SSH-демона для приема туннелей configure_local_sshd() { log_message "Настройка локального SSH-демона (sshd) для приема туннелей..." "$BLUE" local SSHD_CONFIG="/etc/ssh/sshd_config" if [ ! -f "$SSHD_CONFIG" ]; then log_message "ОШИБКА: Файл конфигурации SSHD не найден: $SSHD_CONFIG" "$RED" return 1 fi # Создание резервной копии cp "$SSHD_CONFIG" "${SSHD_CONFIG}.backup.$(date +%Y%m%d%H%M%S)" log_message "Создана резервная копия: ${SSHD_CONFIG}.backup.$(date +%Y%m%d%H%M%S)" "$YELLOW" # Настройки для приема обратных туннелей local SETTINGS=( "GatewayPorts yes" "AllowTcpForwarding yes" ) for setting in "${SETTINGS[@]}"; do key=$(echo "$setting" | cut -d' ' -f1) if grep -q "^#\?\s*$key" "$SSHD_CONFIG"; then # Заменяем или раскомментируем sed -i "s/^#\?\s*$key.*/$setting/" "$SSHD_CONFIG" else # Добавляем в конец файла echo "$setting" >> "$SSHD_CONFIG" fi done # Перезапуск SSHD if systemctl restart sshd 2>/dev/null || service ssh restart 2>/dev/null; then log_message "SSHD перезапущен с новыми настройками (GatewayPorts, AllowTcpForwarding)." "$GREEN" else log_message "ПРЕДУПРЕЖДЕНИЕ: Не удалось перезапустить SSHD. Возможно, потребуется ручной перезапуск." "$YELLOW" fi } # 2. Настройка VPS (сервера) setup_vps() { check_root "setup_vps" log_message "Настройка подключения к удаленному VPS (серверу)..." "$CYAN" local current_host="" local current_port="22" local current_user="" if load_vps_settings; then current_host="$VPS_HOST" current_port="$VPS_PORT" current_user="$VPS_USER" log_message "Текущая конфигурация: $current_user@$current_host:$current_port" "$YELLOW" fi echo -e "${BOLD}${CYAN}--- Настройка VPS ---${NC}" local VPS_HOST_TEMP local VPS_PORT_TEMP local VPS_USER_TEMP local USER_CHOICE # Ввод IP и Порта safe_read "Введите IP адрес или доменное имя VPS (текущий: $current_host): " "$current_host" VPS_HOST_TEMP safe_read "Введите порт SSH (по умолчанию 22, текущий: $current_port): " "$current_port" VPS_PORT_TEMP # Выбор пользователя echo -e "${CYAN}Выберите пользователя для подключения:${NC}" echo "1) root (рекомендуется для полного доступа)" echo "2) Другой пользователь" read -p "Ваш выбор [1-2]: " USER_CHOICE if [ "$USER_CHOICE" == "1" ]; then VPS_USER_TEMP="root" echo -e "${YELLOW}Будет использоваться пользователь: root${NC}" else safe_read "Введите имя пользователя на VPS (текущий: $current_user): " "$current_user" VPS_USER_TEMP fi VPS_HOST="$VPS_HOST_TEMP" VPS_PORT="$VPS_PORT_TEMP" VPS_USER="$VPS_USER_TEMP" if [ -z "$VPS_HOST" ] || [ -z "$VPS_USER" ]; then log_message "ОШИБКА: IP адрес и имя пользователя обязательны." "$RED" return 1 fi # Дополнительная проверка, если пользователь выбрал "Другой пользователь", но не ввел имя if [ "$USER_CHOICE" == "2" ] && [ -z "$VPS_USER" ]; then log_message "ОШИБКА: Имя пользователя обязательно." "$RED" return 1 fi # Сохранение настроек cat > "$VPS_CONFIG" << EOFSETTINGS # Настройки VPS для автономных туннелей VPS_HOST="$VPS_HOST" VPS_PORT="$VPS_PORT" VPS_USER="$VPS_USER" CONFIGURED_ON="$(date '+%Y-%m-%d %H:%M:%S')" EOFSETTINGS chmod 600 "$VPS_CONFIG" log_message "Настройки VPS сохранены в $VPS_CONFIG" "$GREEN" # Автоматическое копирование ключа log_message "Попытка автоматического копирования SSH-ключа на VPS..." "$CYAN" # Запрос пароля для sshpass с безопасным вводом local VPS_PASSWORD echo -e "${YELLOW}Для автоматического копирования ключа потребуется пароль от $VPS_USER на VPS.${NC}" # Используем read -s для скрытого ввода read -s -p "Введите пароль для $VPS_USER@$VPS_HOST: " VPS_PASSWORD echo if [ -n "$VPS_PASSWORD" ]; then if command -v sshpass &> /dev/null; then # Используем sshpass для автоматического копирования ключа if sshpass -p "$VPS_PASSWORD" ssh-copy-id -p "$VPS_PORT" -i /root/.ssh/id_rsa.pub -o StrictHostKeyChecking=no "$VPS_USER@$VPS_HOST" 2>&1 | tee -a "$LOG_DIR/manager.log"; then log_message "SSH ключ успешно скопирован на VPS!" "$GREEN" else log_message "ОШИБКА: Не удалось скопировать ключ через sshpass." "$RED" log_message "Возможно, пароль неверен, или на VPS не установлен ssh-copy-id." "$YELLOW" log_message "Вам может потребоваться скопировать ключ вручную:" "$YELLOW" cat /root/.ssh/id_rsa.pub fi else log_message "ПРЕДУПРЕЖДЕНИЕ: sshpass не установлен. Невозможно скопировать ключ автоматически." "$YELLOW" log_message "Вам потребуется скопировать ключ вручную:" "$YELLOW" cat /root/.ssh/id_rsa.pub fi else log_message "Пароль не введен. Вам потребуется скопировать ключ вручную:" "$YELLOW" cat /root/.ssh/id_rsa.pub fi # Тестовое подключение log_message "Проверка подключения к $VPS_USER@$VPS_HOST:$VPS_PORT..." "$BLUE" if timeout 10 ssh -p "$VPS_PORT" -o BatchMode=yes "$VPS_USER@$VPS_HOST" "echo 'Connection OK'" 2>/dev/null; then log_message "Тестовое подключение успешно! Ключ работает." "$GREEN" else log_message "ОШИБКА: Тестовое подключение не удалось." "$RED" log_message "Убедитесь, что публичный ключ скопирован, порт $VPS_PORT открыт, и SSH-сервер на VPS запущен." "$YELLOW" fi } # 3. Добавление нового туннеля add_tunnel() { check_root "add_tunnel" if ! load_vps_settings; then log_message "Сначала необходимо настроить VPS (опция 1)." "$RED" return 1 fi echo -e "${BOLD}${CYAN}--- Добавление нового обратного туннеля (Local -> VPS) ---${NC}" log_message "VPS: $VPS_USER@$VPS_HOST:$VPS_PORT" "$YELLOW" local LOCAL_PORT="" local REMOTE_PORT="" local TUNNEL_NAME="" safe_read "Введите локальный порт, который нужно пробросить (например, 22 для SSH): " "" LOCAL_PORT safe_read "Введите удаленный порт на VPS, через который будет доступен локальный порт (например, 10022): " "" REMOTE_PORT safe_read "Введите имя туннеля (например, 'ssh_access'): " "" TUNNEL_NAME if [ -z "$LOCAL_PORT" ] || [ -z "$REMOTE_PORT" ] || [ -z "$TUNNEL_NAME" ]; then log_message "ОШИБКА: Все поля обязательны." "$RED" return 1 fi # Проверка имени туннеля TUNNEL_ID=$(echo "$TUNNEL_NAME" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]_') if [ -f "$TUNNELS_DIR/$TUNNEL_ID.conf" ]; then log_message "ОШИБКА: Туннель с именем '$TUNNEL_ID' уже существует." "$RED" return 1 fi # Создание конфигурационного файла туннеля local CONFIG_FILE="$TUNNELS_DIR/$TUNNEL_ID.conf" cat > "$CONFIG_FILE" << EOFCONF # Конфигурация туннеля: $TUNNEL_NAME TUNNEL_ID="$TUNNEL_ID" LOCAL_PORT="$LOCAL_PORT" REMOTE_PORT="$REMOTE_PORT" VPS_HOST="$VPS_HOST" VPS_PORT="$VPS_PORT" VPS_USER="$VPS_USER" CREATED="$(date '+%Y-%m-%d %H:%M:%S')" EOFCONF # Создание systemd unit-файла local SERVICE_NAME="tunnel-$TUNNEL_ID" local SERVICE_FILE="$SERVICE_DIR/$SERVICE_NAME.service" cat > "$SERVICE_FILE" << EOFSERVICE [Unit] Description=SSH Reverse Tunnel: $TUNNEL_NAME ($LOCAL_PORT -> $REMOTE_PORT) After=network-online.target Wants=network-online.target [Service] Type=simple EnvironmentFile=$CONFIG_FILE ExecStart=/usr/bin/autossh -M 0 -N \ -o "ExitOnForwardFailure=yes" \ -o "ServerAliveInterval=30" \ -o "ServerAliveCountMax=3" \ -o "StrictHostKeyChecking=no" \ -o "UserKnownHostsFile=/dev/null" \ -o "TCPKeepAlive=yes" \ -o "ConnectTimeout=30" \ -o "IdentityFile=/root/.ssh/id_rsa" \ -R $REMOTE_PORT:localhost:$LOCAL_PORT \ $VPS_USER@$VPS_HOST -p $VPS_PORT Restart=always RestartSec=10 User=root StandardOutput=append:$LOG_DIR/$TUNNEL_ID.log StandardError=append:$LOG_DIR/$TUNNEL_ID.log [Install] WantedBy=multi-user.target EOFSERVICE log_message "Конфигурация туннеля '$TUNNEL_NAME' создана." "$GREEN" # Запуск и включение службы systemctl daemon-reload systemctl enable "$SERVICE_NAME.service" > /dev/null 2>&1 systemctl start "$SERVICE_NAME.service" if systemctl is-active --quiet "$SERVICE_NAME.service"; then log_message "Туннель '$TUNNEL_NAME' успешно запущен и включен в автозагрузку." "$GREEN" log_message "Для доступа к локальному порту $LOCAL_PORT через VPS используйте:" "$YELLOW" log_message "ssh -p $REMOTE_PORT $VPS_USER@$VPS_HOST" "$BOLD" else log_message "ОШИБКА: Не удалось запустить туннель '$TUNNEL_NAME'." "$RED" log_message "Проверьте лог: tail -f $LOG_DIR/$TUNNEL_ID.log" "$YELLOW" fi } # 4. Управление туннелем (запуск, остановка, перезапуск, удаление) manage_tunnel() { local TUNNEL_ID="$1" local ACTION="$2" local SERVICE_NAME="tunnel-$TUNNEL_ID" if [ ! -f "$TUNNELS_DIR/$TUNNEL_ID.conf" ]; then log_message "ОШИБКА: Туннель с ID '$TUNNEL_ID' не найден." "$RED" return 1 fi case "$ACTION" in start|stop|restart) systemctl "$ACTION" "$SERVICE_NAME.service" log_message "Служба '$SERVICE_NAME' выполнила команду '$ACTION'." "$GREEN" ;; status) systemctl status "$SERVICE_NAME.service" ;; remove) log_message "Остановка и удаление туннеля '$TUNNEL_ID'..." "$YELLOW" systemctl stop "$SERVICE_NAME.service" systemctl disable "$SERVICE_NAME.service" > /dev/null 2>&1 rm -f "$SERVICE_DIR/$SERVICE_NAME.service" rm -f "$TUNNELS_DIR/$TUNNEL_ID.conf" rm -f "$LOG_DIR/$TUNNEL_ID.log" systemctl daemon-reload log_message "Туннель '$TUNNEL_ID' полностью удален." "$GREEN" ;; *) log_message "Неизвестное действие: $ACTION" "$RED" return 1 ;; esac } # 5. Список туннелей list_tunnels() { echo -e "${BOLD}${CYAN}--- Список настроенных туннелей ---${NC}" local count=0 local header="%-20s | %-10s | %-10s | %-20s | %-10s" local separator="---------------------|------------|------------|----------------------|------------" printf "$header\n" "ИМЯ ТУННЕЛЯ" "ЛОК. ПОРТ" "УД. ПОРТ" "СТАТУС" "АВТОЗАГРУЗКА" echo "$separator" for config_file in "$TUNNELS_DIR"/*.conf; do if [ -f "$config_file" ]; then source "$config_file" local SERVICE_NAME="tunnel-$TUNNEL_ID" local STATUS=$(systemctl is-active "$SERVICE_NAME.service" 2>/dev/null || echo "inactive") local ENABLED=$(systemctl is-enabled "$SERVICE_NAME.service" 2>/dev/null || echo "disabled") local STATUS_COLOR="$RED" if [ "$STATUS" == "active" ]; then STATUS_COLOR="$GREEN" elif [ "$STATUS" == "inactive" ]; then STATUS_COLOR="$YELLOW" fi printf "%-20s | %-10s | %-10s | ${STATUS_COLOR}%-20s${NC} | %-10s\n" \ "$TUNNEL_ID" "$LOCAL_PORT" "$REMOTE_PORT" "$STATUS" "$ENABLED" count=$((count + 1)) fi done if [ "$count" -eq 0 ]; then echo -e "${YELLOW}Туннели не найдены. Используйте опцию 2 для добавления нового.${NC}" fi echo "" } # Функция просмотра логов view_logs() { while true; do clear echo -e "${BOLD}${CYAN}Просмотр логов${NC}" echo -e "${YELLOW}=============${NC}" echo "" echo -e "${CYAN}[1]${NC} Логи конкретного туннеля" echo -e "${CYAN}[2]${NC} Все логи туннелей (последние 5 строк)" echo -e "${CYAN}[3]${NC} Системные логи (journalctl)" echo -e "${CYAN}[4]${NC} Назад" echo "" read -p "Выберите действие: " choice case $choice in 1) local LOG_DIR="/var/log/tunnel-manager" echo -e "${CYAN}Доступные логи:${NC}" find "$LOG_DIR" -maxdepth 1 -type f -name "tunnel-*.log" -printf " %f\n" echo "" read -p "Введите имя лог-файла (например, tunnel-ssh_access.log): " log_file if [ -f "$LOG_DIR/$log_file" ]; then less "$LOG_DIR/$log_file" else log_message "Файл не найден" "$RED" sleep 1 fi ;; 2) local LOG_DIR="/var/log/tunnel-manager" echo -e "${CYAN}Все логи туннелей (последние 5 строк):${NC}" for log in "$LOG_DIR"/tunnel-*.log; do if [ -f "$log" ]; then echo -e "\n${YELLOW}=== $(basename "$log") ===${NC}" tail -5 "$log" fi done read -p "Нажмите Enter для продолжения..." ;; 3) echo -e "${CYAN}Системные логи туннелей (последний час):${NC}" journalctl -u tunnel-* --since "1 hour ago" -n 20 2>/dev/null || log_message "Системные логи отсутствуют" "$YELLOW" read -p "Нажмите Enter для продолжения..." ;; 4) return ;; *) log_message "Неверный выбор" "$RED" sleep 1 ;; esac done } # Функция тестирования подключений test_connections() { while true; do clear echo -e "${BOLD}${CYAN}Тестирование подключений${NC}" echo -e "${YELLOW}========================${NC}" echo "1) Тест подключения к VPS" echo "2) Тест локальных портов туннелей" echo "3) Назад" echo "" read -p "Выберите тест: " test_choice case $test_choice in 1) check_ssh_connection read -p "Нажмите Enter для продолжения..." ;; 2) echo -e "${CYAN}Тест локальных портов туннелей:${NC}" local TUNNEL_DIR="/etc/tunnel-manager/tunnels" local NC_CMD if command -v nc &> /dev/null; then NC_CMD="nc -z localhost" elif command -v ncat &> /dev/null; then NC_CMD="ncat -z localhost" else log_message "ОШИБКА: Утилита 'nc' или 'ncat' не найдена. Невозможно проверить порты." "$RED" read -p "Нажмите Enter для продолжения..." continue fi for conf in "$TUNNEL_DIR"/*.conf; do if [ -f "$conf" ]; then local LOCAL_PORT LOCAL_PORT=$(grep '^LOCAL_PORT=' "$conf" | cut -d'=' -f2 | tr -d '"') local TUNNEL_NAME TUNNEL_NAME=$(basename "$conf" .conf) echo -n " $TUNNEL_NAME (локальный порт $LOCAL_PORT): " if timeout 2 $NC_CMD "$LOCAL_PORT" 2>/dev/null; then echo -e "${GREEN}✓ Открыт${NC}" else echo -e "${RED}✗ Закрыт${NC}" fi fi done read -p "Нажмите Enter для продолжения..." ;; 3) return ;; *) log_message "Неверный выбор" "$RED" sleep 1 ;; esac done } # Функция настройки маршрутизации (iptables) setup_routing() { if ! command -v iptables &> /dev/null; then log_message "ОШИБКА: Утилита 'iptables' не найдена. Установите ее для использования этой функции." "$RED" read -p "Нажмите Enter для продолжения..." return fi while true; do clear echo -e "${BOLD}${CYAN}Настройка маршрутизации (iptables)${NC}" echo -e "${YELLOW}==================================${NC}" echo "1) Показать текущие правила NAT (PREROUTING)" echo "2) Добавить правило перенаправления порта (DNAT)" echo "3) Сохранить правила (требует iptables-persistent/netfilter-persistent)" echo "4) Назад" echo "" read -p "Выберите действие: " routing_choice case $routing_choice in 1) echo -e "${CYAN}Текущие правила iptables (NAT PREROUTING):${NC}" sudo iptables -t nat -L PREROUTING -n -v read -p "Нажмите Enter для продолжения..." ;; 2) local IN_PORT DEST_IP DEST_PORT read -p "Входной порт (напр. 80): " IN_PORT read -p "IP назначения (напр. 192.168.1.10): " DEST_IP read -p "Порт назначения (напр. 8080): " DEST_PORT if [ -n "$IN_PORT" ] && [ -n "$DEST_IP" ] && [ -n "$DEST_PORT" ]; then sudo iptables -t nat -A PREROUTING -p tcp --dport "$IN_PORT" -j DNAT --to-destination "$DEST_IP:$DEST_PORT" log_message "Правило DNAT добавлено: $IN_PORT -> $DEST_IP:$DEST_PORT" "$GREEN" else log_message "Не все поля заполнены." "$RED" fi read -p "Нажмите Enter для продолжения..." ;; 3) if command -v netfilter-persistent &> /dev/null; then sudo netfilter-persistent save log_message "Правила iptables сохранены с помощью netfilter-persistent." "$GREEN" elif command -v iptables-save &> /dev/null; then sudo mkdir -p /etc/iptables sudo iptables-save > /etc/iptables/rules.v4 log_message "Правила iptables сохранены в /etc/iptables/rules.v4." "$GREEN" else log_message "ОШИБКА: Не найдена утилита для сохранения правил (netfilter-persistent или iptables-save)." "$RED" fi read -p "Нажмите Enter для продолжения..." ;; 4) return ;; *) log_message "Неверный выбор" "$RED" sleep 1 ;; esac done } # Функция настройки SSH демона setup_ssh_config() { while true; do clear echo -e "${BOLD}${CYAN}Настройки SSH Демона (sshd)${NC}" echo -e "${YELLOW}=============================${NC}" echo "1) Показать текущие настройки sshd (для туннелей)" echo "2) Перезапустить SSH демон" echo "3) Проверить конфигурацию sshd" echo "4) Назад" echo "" read -p "Выберите действие: " ssh_choice case $ssh_choice in 1) echo -e "${CYAN}Текущие настройки sshd (GatewayPorts, AllowTcpForwarding):${NC}" grep -E "^(GatewayPorts|AllowTcpForwarding)" /etc/ssh/sshd_config 2>/dev/null || log_message "Настройки не найдены" "$YELLOW" read -p "Нажмите Enter для продолжения..." ;; 2) if sudo systemctl restart sshd 2>/dev/null || sudo service ssh restart 2>/dev/null; then log_message "SSH демон перезапущен." "$GREEN" else log_message "ОШИБКА: Не удалось перезапустить SSH демон." "$RED" fi read -p "Нажмите Enter для продолжения..." ;; 3) if sudo sshd -t 2>/dev/null; then log_message "Конфигурация SSH корректна." "$GREEN" else log_message "ОШИБКА: Ошибка в конфигурации SSH." "$RED" fi read -p "Нажмите Enter для продолжения..." ;; 4) return ;; *) log_message "Неверный выбор" "$RED" sleep 1 ;; esac done } # 6. Меню управления туннелями manage_tunnels_menu() { while true; do clear echo -e "${BOLD}${CYAN}--- Управление существующими туннелями ---${NC}" echo -e "${YELLOW}===========================================${NC}" local count=0 local TUNNEL_IDS=() for config_file in "$TUNNELS_DIR"/*.conf; do if [ -f "$config_file" ]; then local TUNNEL_ID=$(basename "$config_file" .conf) TUNNEL_IDS+=("$TUNNEL_ID") count=$((count + 1)) fi done if [ "$count" -eq 0 ]; then log_message "Туннели не найдены. Возврат в главное меню." "$YELLOW" sleep 2 return fi list_tunnels echo -e "${CYAN}Введите ID туннеля для управления (или 'b' для назад):${NC}" read -p "ID туннеля: " TUNNEL_ID_MANAGE if [ "$TUNNEL_ID_MANAGE" == "b" ]; then return fi if [[ " ${TUNNEL_IDS[@]} " =~ " ${TUNNEL_ID_MANAGE} " ]]; then while true; do clear echo -e "${BOLD}${CYAN}--- Управление туннелем $TUNNEL_ID_MANAGE ---${NC}" echo -e "${YELLOW}===========================================${NC}" # Показать текущий статус manage_tunnel "$TUNNEL_ID_MANAGE" "status" echo -e "\n${CYAN}Выберите действие:${NC}" echo " s) Статус (обновить)" echo " t) Старт" echo " p) Стоп" echo " r) Перезапуск" echo " d) Удалить" echo " b) Назад" read -p "Действие: " ACTION_CHOICE case "$ACTION_CHOICE" in s) ;; # Статус уже показан t) manage_tunnel "$TUNNEL_ID_MANAGE" "start";; p) manage_tunnel "$TUNNEL_ID_MANAGE" "stop";; r) manage_tunnel "$TUNNEL_ID_MANAGE" "restart";; d) read -p "Вы уверены, что хотите удалить туннель $TUNNEL_ID_MANAGE? (y/N): " CONFIRM_REMOVE if [[ "$CONFIRM_REMOVE" =~ ^[Yy]$ ]]; then manage_tunnel "$TUNNEL_ID_MANAGE" "remove" return # Возврат в меню туннелей после удаления fi ;; b) break;; *) log_message "Неизвестная опция." "$RED";; esac read -p "Нажмите Enter для продолжения..."; done else log_message "Туннель с ID '$TUNNEL_ID_MANAGE' не найден." "$RED" sleep 1 fi done } # 7. Главное меню main_menu() { check_root "menu" while true; do clear echo -e "${BOLD}${CYAN}========================================================${NC}" echo -e "${BOLD}${CYAN} SSH Tunnel Manager v1.0 (by Manus) ${NC}" echo -e "${BOLD}${CYAN}========================================================${NC}" if load_vps_settings; then echo -e "${GREEN}✓ VPS настроен: $VPS_USER@$VPS_HOST:$VPS_PORT${NC}" else echo -e "${RED}✗ VPS не настроен. Начните с опции 1.${NC}" fi echo "" list_tunnels echo -e "${BOLD}${YELLOW}--- МЕНЮ УПРАВЛЕНИЯ ---${NC}" echo -e "1) ${BOLD}Настроить/сменить VPS${NC} (Указать адрес, пользователя, порт)" echo -e "2) ${BOLD}Добавить новый туннель${NC} (Local:Port -> VPS:Port)" echo -e "3) ${BOLD}Управление туннелем${NC} (Статус, Старт, Стоп, Перезапуск, Удаление)" echo -e "4) ${BOLD}Просмотр логов и мониторинг${NC}" echo -e "5) ${BOLD}Тестирование подключений${NC}" echo -e "6) ${BOLD}Настройка маршрутизации (iptables)${NC}" echo -e "7) ${BOLD}Настройки SSH Демона (sshd)${NC}" echo -e "8) ${BOLD}Показать публичный ключ${NC} (Для копирования на VPS)" echo -e "9) ${BOLD}Выход${NC}" echo "" read -p "Выберите опцию [1-9]: " choice case $choice in 1) setup_vps ;; 2) add_tunnel ;; 3) manage_tunnels_menu ;; 4) view_logs ;; 5) test_connections ;; 6) setup_routing ;; 7) setup_ssh_config ;; 8) show_public_key ;; 9) log_message "Выход из менеджера." "$GREEN" exit 0 ;; *) log_message "Неверный выбор. Попробуйте еще раз." "$RED" sleep 1 ;; esac done } # --- Точка входа --- case "$1" in install) install_manager ;; menu) main_menu ;; *) echo -e "${BOLD}${CYAN}SSH Tunnel Manager v1.0${NC}" echo -e "Использование: sudo bash $0 {install|menu}" echo -e " ${BOLD}install${NC} - Устанавливает зависимости и копирует скрипт в /usr/local/bin/tunnel-manager" echo -e " ${BOLD}menu${NC} - Запускает интерактивное меню управления туннелями" echo "" echo -e "Рекомендуемый первый запуск: ${BOLD}sudo bash $0 install${NC}" ;; esac exit 0