From 1c183496cad3ee942bec6985f8b87535e5699566 Mon Sep 17 00:00:00 2001 From: houseassassin <3976100+houseassassin@users.noreply.github.com> Date: Sun, 17 May 2026 14:46:56 +0300 Subject: [PATCH] fix: stabilize installer flows, managers, and terminal output --- README.md | 2 +- banner.sh | 2 +- install.sh | 427 ++++++++++++++++++++++++++++++--------- lib/wireguard.sh | 16 +- manage.sh | 66 +++++- mtproxy-manager.sh | 65 +++++- reverse-proxy-manager.sh | 93 ++++++--- update.sh | 14 +- wg-manager.sh | 68 ++++++- 9 files changed, 605 insertions(+), 148 deletions(-) diff --git a/README.md b/README.md index c0db062..2b29121 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ ### Одна команда (рекомендуется) ```bash -bash <(curl -Ls https://raw.githubusercontent.com/houseassassin/proxy-core/main/install.sh) +sudo bash <(curl -Ls https://raw.githubusercontent.com/houseassassin/proxy-core/main/install.sh) ``` ### Альтернативный метод diff --git a/banner.sh b/banner.sh index 39bc452..6936059 100755 --- a/banner.sh +++ b/banner.sh @@ -27,7 +27,7 @@ show_gradient_banner() { echo -e "${CYAN}║${CYAN} ██║ ██║ ██║╚██████╔╝██╔╝ ██╗ ██║ ╚██████╗╚██████╔╝ ${CYAN}║${NC}" echo -e "${CYAN}║${CYAN} ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ${CYAN}║${NC}" echo -e "${CYAN}║ ║${NC}" - echo -e "${CYAN}║${WHITE} 🚀 Advanced VPN & Proxy Panel Auto-Installer 🚀 ${CYAN}║${NC}" + echo -e "${CYAN}║${WHITE} Advanced VPN & Proxy Panel Auto-Installer ${CYAN}║${NC}" echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════════════╝${NC}" echo "" echo -e " ${GREEN}┌─────────────────────────────────────────────────────────────────┐${NC}" diff --git a/install.sh b/install.sh index 0cb296a..af878d2 100755 --- a/install.sh +++ b/install.sh @@ -14,6 +14,13 @@ set -euo pipefail SCRIPT_VERSION="2.1.1" PROJECT_DIR="/opt/proxy-core" LANG_FILE="${PROJECT_DIR}/.selected_language" +REPO_ARCHIVE_URL="https://github.com/houseassassin/proxy-core/archive/refs/heads/main.tar.gz" +THREEXUI_INSTALL_URL="https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh" +WG_PORT_MIN=51820 +WG_PORT_MAX=51900 +HYSTERIA_PORT_MIN=36712 +HYSTERIA_PORT_MAX=36800 +MTPROXY_PORT_DEFAULT=8443 # Colors and styles RED='\033[0;31m' @@ -39,29 +46,64 @@ BG_BLUE='\033[44m' BG_MAGENTA='\033[45m' BG_CYAN='\033[46m' -# Create project directory first -mkdir -p "$PROJECT_DIR" +USE_UNICODE=1 +if ! locale charmap 2>/dev/null | grep -qi "utf-8"; then + USE_UNICODE=0 +fi + +if [ ! -t 1 ] || [ "${TERM:-}" = "dumb" ] || [ "${NO_COLOR:-}" = "1" ]; then + RED="" + GREEN="" + YELLOW="" + BLUE="" + MAGENTA="" + CYAN="" + GRAY="" + WHITE="" + NC="" + BOLD="" + DIM="" + UNDERLINE="" + BLINK="" + BG_RED="" + BG_GREEN="" + BG_BLUE="" + BG_MAGENTA="" + BG_CYAN="" +fi + +if [ "$USE_UNICODE" -eq 1 ]; then + SYM_INFO="ℹ" + SYM_WARN="⚠" + SYM_ERR="✗" + SYM_OK="✓" +else + SYM_INFO="i" + SYM_WARN="!" + SYM_ERR="x" + SYM_OK="+" +fi # Logging -LOGFILE="${PROJECT_DIR}/installer.log" +LOGFILE="/tmp/proxy-core-installer.log" log() { - echo -e " ${CYAN}ℹ${NC} ${WHITE}$1${NC}" + echo -e " ${CYAN}${SYM_INFO}${NC} ${WHITE}$1${NC}" echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $1" >> "$LOGFILE" 2>/dev/null } warn() { - echo -e " ${YELLOW}⚠${NC} ${YELLOW}$1${NC}" + echo -e " ${YELLOW}${SYM_WARN}${NC} ${YELLOW}$1${NC}" echo "[$(date '+%Y-%m-%d %H:%M:%S')] [WARN] $1" >> "$LOGFILE" 2>/dev/null } error() { - echo -e " ${RED}✗${NC} ${RED}$1${NC}" + echo -e " ${RED}${SYM_ERR}${NC} ${RED}$1${NC}" echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1" >> "$LOGFILE" 2>/dev/null } success() { - echo -e " ${GREEN}✓${NC} ${GREEN}$1${NC}" + echo -e " ${GREEN}${SYM_OK}${NC} ${GREEN}$1${NC}" echo "[$(date '+%Y-%m-%d %H:%M:%S')] [SUCCESS] $1" >> "$LOGFILE" 2>/dev/null } @@ -69,7 +111,10 @@ success() { spinner() { local pid=$1 local text=$2 - local frames=("⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏") + local frames=("|" "/" "-" "\\") + if [ "$USE_UNICODE" -eq 1 ]; then + frames=("⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏") + fi while kill -0 "$pid" 2>/dev/null; do for frame in "${frames[@]}"; do @@ -77,52 +122,9 @@ spinner() { sleep 0.1 done done - printf "\r ${GREEN}✓${NC} ${WHITE}%s${NC}\n" "$text" + printf "\r ${GREEN}${SYM_OK}${NC} ${WHITE}%s${NC}\n" "$text" } -# Language support -declare -A LANG_EN=( - [MENU_TITLE]="VPN Panel Installer" - [MENU_INSTALL]="Install Panel" - [MENU_MANAGE]="Manage Services" - [MENU_BACKUP]="Backup & Restore" - [MENU_UPDATE]="Update Components" - [MENU_UNINSTALL]="Uninstall" - [MENU_EXIT]="Exit" - [SELECT_PANEL]="Select panel to install" - [WIREGUARD]="WireGuard VPN" - [3XUI]="3x-ui Panel" - [REMNAWAVE]="Remnawave Panel" - [HYSTERIA2]="Hysteria2 Proxy" - [MTPROXY]="MTProxy (Telegram)" - [SELECT_REVERSE_PROXY]="Select Reverse Proxy" - [NGINX]="Nginx" - [CADDY]="Caddy" - [SELFSTEAL]="Install Selfsteal Template" - [ALL]="Install All" -) - -declare -A LANG_RU=( - [MENU_TITLE]="Установщик VPN панелей" - [MENU_INSTALL]="Установить панель" - [MENU_MANAGE]="Управление сервисами" - [MENU_BACKUP]="Резервное копирование" - [MENU_UPDATE]="Обновить компоненты" - [MENU_UNINSTALL]="Удалить" - [MENU_EXIT]="Выход" - [SELECT_PANEL]="Выберите панель для установки" - [WIREGUARD]="WireGuard VPN" - [3XUI]="Панель 3x-ui" - [REMNAWAVE]="Панель Remnawave" - [HYSTERIA2]="Прокси Hysteria2" - [MTPROXY]="MTProxy (Telegram)" - [SELECT_REVERSE_PROXY]="Выберите Reverse Proxy" - [NGINX]="Nginx" - [CADDY]="Caddy" - [SELFSTEAL]="Установить Selfsteal шаблон" - [ALL]="Установить всё" -) - MENU_LANG="en" load_language() { @@ -139,14 +141,22 @@ save_language() { L() { local key="$1" - local var_name="LANG_${MENU_LANG^^}_${key}" - echo "${!var_name:-$key}" + case "$MENU_LANG:$key" in + "ru:SELECT_REVERSE_PROXY") echo "Выберите Reverse Proxy" ;; + "ru:NGINX") echo "Nginx" ;; + "ru:CADDY") echo "Caddy" ;; + "en:SELECT_REVERSE_PROXY") echo "Select Reverse Proxy" ;; + "en:NGINX") echo "Nginx" ;; + "en:CADDY") echo "Caddy" ;; + *) echo "$key" ;; + esac } # System checks check_root() { if [[ $EUID -ne 0 ]]; then error "Этот скрипт должен быть запущен с правами root" + exit 1 fi } @@ -157,10 +167,112 @@ detect_os() { VER=$VERSION_ID else error "Не удалось определить операционную систему" + exit 1 fi log "Обнаружена ОС: $OS $VER" } +reload_firewall() { + if command -v firewall-cmd >/dev/null 2>&1; then + firewall-cmd --reload >/dev/null 2>&1 || true + fi +} + +open_firewall_port() { + local port=$1 + local protocol=$2 + + if command -v ufw >/dev/null 2>&1; then + ufw allow "${port}/${protocol}" >/dev/null 2>&1 || true + return 0 + fi + + if command -v firewall-cmd >/dev/null 2>&1; then + firewall-cmd --permanent --add-port="${port}/${protocol}" >/dev/null 2>&1 || true + reload_firewall + fi +} + +run_remote_script() { + local url=$1 + local tmp_script + tmp_script="$(mktemp /tmp/proxy-core-remote.XXXXXX.sh)" + curl -fsSL "$url" -o "$tmp_script" + chmod 700 "$tmp_script" + bash "$tmp_script" + rm -f "$tmp_script" +} + +ensure_project_files() { + mkdir -p "$PROJECT_DIR" + LOGFILE="${PROJECT_DIR}/installer.log" + + local need_bootstrap=0 + local required_files=( + "manage.sh" + "wg-manager.sh" + "update.sh" + "mtproxy-manager.sh" + "reverse-proxy-manager.sh" + "banner.sh" + ) + + for file in "${required_files[@]}"; do + if [ ! -f "$PROJECT_DIR/$file" ]; then + need_bootstrap=1 + break + fi + done + + if [ "$need_bootstrap" -eq 0 ] && [ -d "$PROJECT_DIR/lib" ] && [ -d "$PROJECT_DIR/config" ]; then + return 0 + fi + + local script_dir + script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + if [ -f "$script_dir/manage.sh" ] && [ -d "$script_dir/lib" ] && [ -d "$script_dir/config" ]; then + cp -f "$script_dir/"*.sh "$PROJECT_DIR/" 2>/dev/null || true + cp -rf "$script_dir/lib" "$PROJECT_DIR/" + cp -rf "$script_dir/config" "$PROJECT_DIR/" + chmod +x "$PROJECT_DIR/"*.sh 2>/dev/null || true + return 0 + fi + + log "Загружаем файлы проекта в $PROJECT_DIR..." + local archive + archive="$(mktemp /tmp/proxy-core-src.XXXXXX.tar.gz)" + curl -fsSL "$REPO_ARCHIVE_URL" -o "$archive" + tar -xzf "$archive" --strip-components=1 -C "$PROJECT_DIR" + rm -f "$archive" + chmod +x "$PROJECT_DIR/"*.sh 2>/dev/null || true +} + +run_project_script() { + local script_name=$1 + if [ -f "$PROJECT_DIR/$script_name" ]; then + bash "$PROJECT_DIR/$script_name" + else + error "$script_name не найден в $PROJECT_DIR" + return 1 + fi +} + +load_defaults() { + local defaults_path="" + local script_dir + script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + if [ -f "$PROJECT_DIR/config/defaults.conf" ]; then + defaults_path="$PROJECT_DIR/config/defaults.conf" + elif [ -f "$script_dir/config/defaults.conf" ]; then + defaults_path="$script_dir/config/defaults.conf" + fi + + if [ -n "$defaults_path" ]; then + source "$defaults_path" + log "Загружены настройки по умолчанию: $defaults_path" + fi +} + check_memory() { local mem_mb=$(free -m | grep Mem: | awk '{print $2}') if [ $mem_mb -lt 2000 ]; then @@ -203,9 +315,11 @@ install_docker() { fi log "Установка Docker..." - curl -fsSL https://get.docker.com -o /tmp/get-docker.sh - sh /tmp/get-docker.sh - rm /tmp/get-docker.sh + local docker_installer + docker_installer="$(mktemp /tmp/get-docker.XXXXXX.sh)" + curl -fsSL https://get.docker.com -o "$docker_installer" + sh "$docker_installer" + rm -f "$docker_installer" systemctl enable docker systemctl start docker @@ -249,6 +363,14 @@ install_packages() { ufw --force enable >/dev/null 2>&1 fi + if command -v firewall-cmd >/dev/null 2>&1; then + systemctl enable firewalld >/dev/null 2>&1 || true + systemctl start firewalld >/dev/null 2>&1 || true + firewall-cmd --permanent --add-service=ssh >/dev/null 2>&1 || true + firewall-cmd --permanent --add-service=https >/dev/null 2>&1 || true + reload_firewall + fi + success "Системные пакеты установлены" } @@ -273,7 +395,7 @@ install_wireguard() { SERVER_PRIVATE_KEY=$(cat server_private.key) SERVER_PUBLIC_KEY=$(cat server_public.key) SERVER_IP=$(get_server_ip) - WG_PORT=$(get_random_port 51820 51900) + WG_PORT=$(get_random_port "$WG_PORT_MIN" "$WG_PORT_MAX") cat > /etc/wireguard/wg0.conf </dev/null || firewall-cmd --permanent --add-port=$WG_PORT/udp 2>/dev/null + open_firewall_port "$WG_PORT" "udp" success "WireGuard установлен на порту $WG_PORT" @@ -303,7 +425,7 @@ EOF # 3x-ui installation install_3xui() { log "Установка 3x-ui..." - bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) + run_remote_script "$THREEXUI_INSTALL_URL" success "3x-ui установлен" } @@ -339,8 +461,8 @@ EOF success "Remnawave установлен" echo "" - log "Админ логин: admin" - log "Админ пароль: $ADMIN_PASSWORD" + echo -e " ${CYAN}${SYM_INFO}${NC} ${WHITE}Админ логин: admin${NC}" + echo -e " ${CYAN}${SYM_INFO}${NC} ${WHITE}Админ пароль: ${ADMIN_PASSWORD}${NC}" warn "Сохраните эти данные!" } @@ -349,14 +471,16 @@ install_hysteria2() { log "Установка Hysteria2..." HYSTERIA_VERSION=$(curl -s https://api.github.com/repos/apernet/hysteria/releases/latest | grep tag_name | cut -d '"' -f 4) - wget -O /tmp/hysteria "https://github.com/apernet/hysteria/releases/download/${HYSTERIA_VERSION}/hysteria-linux-amd64" - chmod +x /tmp/hysteria - mv /tmp/hysteria /usr/local/bin/hysteria + local hysteria_bin + hysteria_bin="$(mktemp /tmp/hysteria.XXXXXX)" + wget -O "$hysteria_bin" "https://github.com/apernet/hysteria/releases/download/${HYSTERIA_VERSION}/hysteria-linux-amd64" + chmod +x "$hysteria_bin" + mv "$hysteria_bin" /usr/local/bin/hysteria mkdir -p /etc/hysteria HYSTERIA_PASSWORD=$(openssl rand -base64 32) - HYSTERIA_PORT=$(get_random_port 36712 36800) + HYSTERIA_PORT=$(get_random_port "$HYSTERIA_PORT_MIN" "$HYSTERIA_PORT_MAX") cat > /etc/hysteria/config.yaml </dev/null || firewall-cmd --permanent --add-port=$HYSTERIA_PORT/udp 2>/dev/null + open_firewall_port "$HYSTERIA_PORT" "udp" success "Hysteria2 установлен" echo "" log "Порт: $HYSTERIA_PORT" - log "Пароль: $HYSTERIA_PASSWORD" + echo -e " ${CYAN}${SYM_INFO}${NC} ${WHITE}Пароль: ${HYSTERIA_PASSWORD}${NC}" warn "Сохраните эти данные!" } @@ -433,8 +557,12 @@ install_mtproxy() { SECRET=$(head -c 16 /dev/urandom | xxd -ps) - read -p "Введите порт для MTProxy (по умолчанию 8443): " MTPROXY_PORT - MTPROXY_PORT=${MTPROXY_PORT:-8443} + read -p "Введите порт для MTProxy (по умолчанию ${MTPROXY_PORT_DEFAULT}): " MTPROXY_PORT + MTPROXY_PORT=${MTPROXY_PORT:-$MTPROXY_PORT_DEFAULT} + if ! [[ "$MTPROXY_PORT" =~ ^[0-9]+$ ]] || [ "$MTPROXY_PORT" -lt 1 ] || [ "$MTPROXY_PORT" -gt 65535 ]; then + error "Неверный порт: $MTPROXY_PORT" + return 1 + fi cat > /etc/systemd/system/mtproxy.service </dev/null || firewall-cmd --permanent --add-port=$MTPROXY_PORT/tcp 2>/dev/null + open_firewall_port "$MTPROXY_PORT" "tcp" cat > /opt/MTProxy/.config </dev/null || true + [ -f "/etc/systemd/system/mtproxy.service" ] && cp /etc/systemd/system/mtproxy.service "$backup_dir/" + [ -f "/etc/systemd/system/hysteria-server.service" ] && cp /etc/systemd/system/hysteria-server.service "$backup_dir/" + [ -d "/opt/remnawave" ] && cp -r /opt/remnawave "$backup_dir/remnawave" 2>/dev/null || true + + success "Резервная копия создана: $backup_dir" +} + +uninstall_panels_menu() { + echo "" + echo -e "${YELLOW}Выберите компонент для удаления:${NC}" + echo "1) WireGuard" + echo "2) 3x-ui" + echo "3) Remnawave" + echo "4) Hysteria2" + echo "5) MTProxy" + echo "6) Удалить всё" + echo "0) Назад" + echo "" + read -p "Ваш выбор: " uninstall_choice + + case $uninstall_choice in + 1) + systemctl disable --now wg-quick@wg0 >/dev/null 2>&1 || true + rm -rf /etc/wireguard + success "WireGuard удален" + ;; + 2) + if command -v x-ui >/dev/null 2>&1; then + x-ui uninstall || warn "Не удалось выполнить x-ui uninstall, проверьте вручную" + else + warn "x-ui не найден, возможно уже удален" + fi + ;; + 3) + if [ -d "/opt/remnawave" ]; then + cd /opt/remnawave + docker compose down >/dev/null 2>&1 || true + cd /opt + rm -rf /opt/remnawave + success "Remnawave удален" + else + warn "Remnawave не найден" + fi + ;; + 4) + systemctl disable --now hysteria-server >/dev/null 2>&1 || true + rm -f /etc/systemd/system/hysteria-server.service + systemctl daemon-reload + rm -rf /etc/hysteria + rm -f /usr/local/bin/hysteria + success "Hysteria2 удален" + ;; + 5) + local mtproxy_port="" + if [ -f "/opt/MTProxy/.config" ]; then + mtproxy_port="$(grep '^MTPROXY_PORT=' /opt/MTProxy/.config | cut -d= -f2)" + fi + systemctl disable --now mtproxy >/dev/null 2>&1 || true + rm -f /etc/systemd/system/mtproxy.service + systemctl daemon-reload + rm -rf /opt/MTProxy + if [ -n "$mtproxy_port" ] && command -v ufw >/dev/null 2>&1; then + ufw delete allow "${mtproxy_port}/tcp" >/dev/null 2>&1 || true + fi + success "MTProxy удален" + ;; + 6) + systemctl disable --now wg-quick@wg0 >/dev/null 2>&1 || true + systemctl disable --now hysteria-server >/dev/null 2>&1 || true + systemctl disable --now mtproxy >/dev/null 2>&1 || true + rm -f /etc/systemd/system/hysteria-server.service /etc/systemd/system/mtproxy.service + systemctl daemon-reload + rm -rf /etc/wireguard /etc/hysteria /opt/MTProxy /opt/remnawave + if command -v x-ui >/dev/null 2>&1; then + x-ui uninstall >/dev/null 2>&1 || true + fi + success "Основные компоненты удалены" + ;; + 0) + return 0 + ;; + *) + warn "Неверный выбор" + return 1 + ;; + esac +} + # Main menu show_main_menu() { SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Source banner - if [ -f "$SCRIPT_DIR/banner.sh" ]; then + if [ "$USE_UNICODE" -eq 1 ] && [ -f "$SCRIPT_DIR/banner.sh" ]; then source "$SCRIPT_DIR/banner.sh" show_gradient_banner else clear echo -e "${MAGENTA}╔════════════════════════════════════════╗${NC}" - echo -e "${MAGENTA}║ 🚀 Proxy-Core Installer ║${NC}" + echo -e "${MAGENTA}║ Proxy-Core Installer ║${NC}" echo -e "${MAGENTA}║ Version: ${SCRIPT_VERSION} ║${NC}" echo -e "${MAGENTA}╚════════════════════════════════════════╝${NC}" echo "" fi echo -e " ${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}" - echo -e " ${CYAN}║${NC} ${WHITE}📦 УСТАНОВКА${NC} ${CYAN}║${NC}" + echo -e " ${CYAN}║${NC} ${WHITE}УСТАНОВКА${NC} ${CYAN}║${NC}" echo -e " ${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}" echo "" - echo -e " ${YELLOW}1.${NC} 🔧 ${WHITE}Установить панель${NC}" + echo -e " ${YELLOW}1.${NC} ${WHITE}Установить панель${NC}" echo -e " ${GRAY}WireGuard, 3x-ui, Remnawave, Hysteria2, MTProxy${NC}" echo "" echo -e " ${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}" - echo -e " ${CYAN}║${NC} ${WHITE}⚙️ УПРАВЛЕНИЕ${NC} ${CYAN}║${NC}" + echo -e " ${CYAN}║${NC} ${WHITE}УПРАВЛЕНИЕ${NC} ${CYAN}║${NC}" echo -e " ${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}" echo "" - echo -e " ${YELLOW}2.${NC} 🎮 ${WHITE}Управление сервисами${NC}" + echo -e " ${YELLOW}2.${NC} ${WHITE}Управление сервисами${NC}" echo -e " ${GRAY}Запуск, остановка, перезапуск панелей${NC}" echo "" - echo -e " ${YELLOW}3.${NC} 💾 ${WHITE}Резервное копирование${NC}" + echo -e " ${YELLOW}3.${NC} ${WHITE}Резервное копирование${NC}" echo -e " ${GRAY}Создание и восстановление бэкапов${NC}" echo "" - echo -e " ${YELLOW}4.${NC} 🔄 ${WHITE}Обновить компоненты${NC}" + echo -e " ${YELLOW}4.${NC} ${WHITE}Обновить компоненты${NC}" echo -e " ${GRAY}Обновление установленных панелей${NC}" echo "" echo -e " ${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}" - echo -e " ${CYAN}║${NC} ${WHITE}🌐 ДОПОЛНИТЕЛЬНО${NC} ${CYAN}║${NC}" + echo -e " ${CYAN}║${NC} ${WHITE}ДОПОЛНИТЕЛЬНО${NC} ${CYAN}║${NC}" echo -e " ${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}" echo "" - echo -e " ${YELLOW}5.${NC} 🗑️ ${WHITE}Удалить панель${NC}" + echo -e " ${YELLOW}5.${NC} ${WHITE}Удалить панель${NC}" echo -e " ${GRAY}Полное удаление установленных компонентов${NC}" echo "" - echo -e " ${YELLOW}6.${NC} 🎭 ${WHITE}Установить Selfsteal${NC}" + echo -e " ${YELLOW}6.${NC} ${WHITE}Установить Selfsteal${NC}" echo -e " ${GRAY}Маскировка трафика под обычный сайт${NC}" echo "" - echo -e " ${YELLOW}7.${NC} 🌐 ${WHITE}Настроить Reverse Proxy${NC}" + echo -e " ${YELLOW}7.${NC} ${WHITE}Настроить Reverse Proxy${NC}" echo -e " ${GRAY}Nginx, Caddy, HAProxy - установка и настройка${NC}" echo "" echo -e " ${GRAY}─────────────────────────────────────────────────────────────────${NC}" echo "" - echo -e " ${YELLOW}9.${NC} 🌍 ${WHITE}Сменить язык / Change language${NC}" - echo -e " ${YELLOW}0.${NC} 🚪 ${WHITE}Выход / Exit${NC}" + echo -e " ${YELLOW}9.${NC} ${WHITE}Сменить язык / Change language${NC}" + echo -e " ${YELLOW}0.${NC} ${WHITE}Выход / Exit${NC}" echo "" echo -e " ${GRAY}─────────────────────────────────────────────────────────────────${NC}" echo "" @@ -724,39 +947,39 @@ show_install_menu() { clear echo "" echo -e " ${GREEN}╔═══════════════════════════════════════════════════════════════╗${NC}" - echo -e " ${GREEN}║${NC} ${WHITE}📦 Выберите панель для установки${NC} ${GREEN}║${NC}" + echo -e " ${GREEN}║${NC} ${WHITE}Выберите панель для установки${NC} ${GREEN}║${NC}" echo -e " ${GREEN}╚═══════════════════════════════════════════════════════════════╝${NC}" echo "" - echo -e " ${YELLOW}1.${NC} 🔐 ${WHITE}WireGuard VPN${NC}" + echo -e " ${YELLOW}1.${NC} ${WHITE}WireGuard VPN${NC}" echo -e " ${GRAY}Современный, быстрый и безопасный VPN${NC}" echo "" - echo -e " ${YELLOW}2.${NC} 🌐 ${WHITE}3x-ui Panel${NC}" + echo -e " ${YELLOW}2.${NC} ${WHITE}3x-ui Panel${NC}" echo -e " ${GRAY}VLESS, VMess, Trojan, Shadowsocks${NC}" echo "" - echo -e " ${YELLOW}3.${NC} 🐳 ${WHITE}Remnawave Panel${NC}" + echo -e " ${YELLOW}3.${NC} ${WHITE}Remnawave Panel${NC}" echo -e " ${GRAY}Современная панель с Docker${NC}" echo "" - echo -e " ${YELLOW}4.${NC} ⚡ ${WHITE}Hysteria2 Proxy${NC}" + echo -e " ${YELLOW}4.${NC} ${WHITE}Hysteria2 Proxy${NC}" echo -e " ${GRAY}Высокоскоростной прокси с маскировкой${NC}" echo "" - echo -e " ${YELLOW}5.${NC} 📱 ${WHITE}MTProxy (Telegram)${NC}" + echo -e " ${YELLOW}5.${NC} ${WHITE}MTProxy (Telegram)${NC}" echo -e " ${GRAY}Официальный прокси для Telegram${NC}" echo "" echo -e " ${GRAY}─────────────────────────────────────────────────────────────────${NC}" echo "" - echo -e " ${YELLOW}6.${NC} 🚀 ${WHITE}Установить всё${NC}" + echo -e " ${YELLOW}6.${NC} ${WHITE}Установить всё${NC}" echo -e " ${GRAY}Последовательная установка всех панелей${NC}" echo "" - echo -e " ${YELLOW}0.${NC} ⬅️ ${WHITE}Назад / Back${NC}" + echo -e " ${YELLOW}0.${NC} ${WHITE}Назад / Back${NC}" echo "" } main() { check_root + ensure_project_files + load_defaults detect_os check_memory - - mkdir -p "$PROJECT_DIR" load_language while true; do @@ -813,6 +1036,20 @@ main() { read -p "Нажмите Enter / Press Enter..." fi ;; + 2) + run_project_script "manage.sh" + ;; + 3) + backup_configs + read -p "Нажмите Enter / Press Enter..." + ;; + 4) + run_project_script "update.sh" + ;; + 5) + uninstall_panels_menu + read -p "Нажмите Enter / Press Enter..." + ;; 7) # Запуск Reverse Proxy Manager if [ -f "$PROJECT_DIR/reverse-proxy-manager.sh" ]; then diff --git a/lib/wireguard.sh b/lib/wireguard.sh index cf6e3e4..4f3ba12 100755 --- a/lib/wireguard.sh +++ b/lib/wireguard.sh @@ -23,8 +23,12 @@ get_next_wg_ip() { if [ -z "$LAST_IP" ]; then echo "10.0.0.2" else - LAST_NUM=$(echo $LAST_IP | cut -d. -f4) + LAST_NUM=$(echo "$LAST_IP" | cut -d. -f4) NEXT_NUM=$((LAST_NUM + 1)) + if [ "$NEXT_NUM" -gt 254 ]; then + error "Пул адресов WireGuard исчерпан (10.0.0.2-10.0.0.254)" + return 1 + fi echo "10.0.0.$NEXT_NUM" fi } @@ -147,7 +151,15 @@ remove_wireguard_client() { return 0 fi - sed -i "/# $client_name/,+2d" $WG_CONF + local tmp_conf + tmp_conf="$(mktemp)" + awk -v marker="# ${client_name}" ' + BEGIN {skip=0} + $0 == marker {skip=1; next} + skip && /^\[Peer\]/ {skip=0} + !skip {print} + ' "$WG_CONF" > "$tmp_conf" + mv "$tmp_conf" "$WG_CONF" rm -rf "$CLIENT_DIR" systemctl restart wg-quick@wg0 diff --git a/manage.sh b/manage.sh index 80d45f8..9581576 100755 --- a/manage.sh +++ b/manage.sh @@ -19,6 +19,38 @@ warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } error() { echo -e "${RED}[ERROR]${NC} $1"; } success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } +get_server_ip() { + curl -s -4 ifconfig.me || curl -s -4 api.ipify.org || curl -s -4 ipinfo.io/ip +} + +validate_client_name() { + local client_name=$1 + [[ "$client_name" =~ ^[a-zA-Z0-9_-]+$ ]] +} + +remove_wg_peer_block() { + local client_name=$1 + local tmp_conf + tmp_conf="$(mktemp)" + awk -v marker="# ${client_name}" ' + BEGIN {skip=0} + $0 == marker {skip=1; next} + skip && /^\[Peer\]/ {skip=0} + !skip {print} + ' /etc/wireguard/wg0.conf > "$tmp_conf" + mv "$tmp_conf" /etc/wireguard/wg0.conf +} + +run_remote_script() { + local url=$1 + local tmp_script + tmp_script="$(mktemp /tmp/proxy-core-remote.XXXXXX.sh)" + curl -fsSL "$url" -o "$tmp_script" + chmod 700 "$tmp_script" + bash "$tmp_script" + rm -f "$tmp_script" +} + # WireGuard Management manage_wireguard() { if ! systemctl is-active --quiet wg-quick@wg0; then @@ -84,6 +116,10 @@ add_wireguard_client() { error "Имя не может быть пустым" return 1 fi + if ! validate_client_name "$CLIENT_NAME"; then + error "Неверное имя: только буквы, цифры, _ и -" + return 1 + fi CLIENT_DIR="/etc/wireguard/clients/$CLIENT_NAME" @@ -102,7 +138,7 @@ add_wireguard_client() { CLIENT_PRIVATE_KEY=$(cat private.key) CLIENT_PUBLIC_KEY=$(cat public.key) SERVER_PUBLIC_KEY=$(cat /etc/wireguard/server_public.key) - SERVER_IP=$(curl -s ifconfig.me) + SERVER_IP=$(get_server_ip) SERVER_PORT=$(grep ListenPort /etc/wireguard/wg0.conf | awk '{print $3}') # Get next available IP @@ -112,6 +148,10 @@ add_wireguard_client() { else LAST_NUM=$(echo $LAST_IP | cut -d. -f4) NEXT_NUM=$((LAST_NUM + 1)) + if [ "$NEXT_NUM" -gt 254 ]; then + error "Пул адресов WireGuard исчерпан (10.0.0.2-10.0.0.254)" + return 1 + fi CLIENT_IP="10.0.0.$NEXT_NUM" fi @@ -146,7 +186,11 @@ EOF echo "" log "QR-код для мобильных устройств:" - qrencode -t ansiutf8 < client.conf + if command -v qrencode >/dev/null 2>&1; then + qrencode -t ansiutf8 < client.conf + else + warn "qrencode не установлен, пропускаем QR-код" + fi echo "" read -p "Нажмите Enter..." @@ -182,6 +226,10 @@ remove_wireguard_client() { error "Имя не может быть пустым" return 1 fi + if ! validate_client_name "$CLIENT_NAME"; then + error "Неверное имя: только буквы, цифры, _ и -" + return 1 + fi CLIENT_DIR="/etc/wireguard/clients/$CLIENT_NAME" @@ -190,7 +238,7 @@ remove_wireguard_client() { return 1 fi - sed -i "/# $CLIENT_NAME/,+2d" /etc/wireguard/wg0.conf + remove_wg_peer_block "$CLIENT_NAME" rm -rf "$CLIENT_DIR" systemctl restart wg-quick@wg0 @@ -206,6 +254,10 @@ show_wireguard_qr() { error "Имя не может быть пустым" return 1 fi + if ! validate_client_name "$CLIENT_NAME"; then + error "Неверное имя: только буквы, цифры, _ и -" + return 1 + fi CLIENT_DIR="/etc/wireguard/clients/$CLIENT_NAME" @@ -220,7 +272,11 @@ show_wireguard_qr() { echo "" log "QR-код:" - qrencode -t ansiutf8 < "$CLIENT_DIR/client.conf" + if command -v qrencode >/dev/null 2>&1; then + qrencode -t ansiutf8 < "$CLIENT_DIR/client.conf" + else + warn "qrencode не установлен, пропускаем QR-код" + fi echo "" read -p "Нажмите Enter..." @@ -274,7 +330,7 @@ manage_3xui() { x-ui log ;; 6) - bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) + run_remote_script "https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh" success "3x-ui обновлен" sleep 2 ;; diff --git a/mtproxy-manager.sh b/mtproxy-manager.sh index 95a6d55..f9d1dfb 100755 --- a/mtproxy-manager.sh +++ b/mtproxy-manager.sh @@ -3,10 +3,13 @@ # MTProxy Manager # Author: houseassassin +set -euo pipefail + RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' +CYAN='\033[0;36m' NC='\033[0m' log() { echo -e "${GREEN}[INFO]${NC} $1"; } @@ -16,6 +19,18 @@ success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } MTPROXY_DIR="/opt/MTProxy" +get_server_ip() { + curl -s -4 ifconfig.me || curl -s -4 api.ipify.org || curl -s -4 ipinfo.io/ip +} + +validate_port() { + local port=$1 + if ! [[ "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then + return 1 + fi + return 0 +} + check_installed() { if [ ! -d "$MTPROXY_DIR" ]; then error "MTProxy не установлен" @@ -30,7 +45,7 @@ get_connection_link() { fi source "$MTPROXY_DIR/.config" - SERVER_IP=$(curl -s ifconfig.me) + SERVER_IP=$(get_server_ip) echo "" log "Ссылка для подключения:" @@ -60,12 +75,29 @@ regenerate_secret() { return 0 fi + source "$MTPROXY_DIR/.config" NEW_SECRET=$(head -c 16 /dev/urandom | xxd -ps) # Обновляем конфигурацию sed -i "s/SECRET=.*/SECRET=$NEW_SECRET/" "$MTPROXY_DIR/.config" + cat > /etc/systemd/system/mtproxy.service </dev/null - ufw allow $NEW_PORT/tcp 2>/dev/null + if command -v ufw >/dev/null 2>&1; then + ufw delete allow $old_port/tcp 2>/dev/null || true + ufw allow $NEW_PORT/tcp 2>/dev/null || true + elif command -v firewall-cmd >/dev/null 2>&1; then + firewall-cmd --permanent --remove-port="${old_port}/tcp" >/dev/null 2>&1 || true + firewall-cmd --permanent --add-port="${NEW_PORT}/tcp" >/dev/null 2>&1 || true + firewall-cmd --reload >/dev/null 2>&1 || true + fi success "Порт изменен на $NEW_PORT" get_connection_link @@ -156,7 +201,7 @@ show_stats() { # Количество подключений echo "" log "Активные подключения:" - ss -tn | grep -c ":$(source $MTPROXY_DIR/.config && echo $MTPROXY_PORT) " || echo "0" + ss -tn | grep -c ":$MTPROXY_PORT " || echo "0" echo "" } @@ -183,6 +228,9 @@ uninstall_mtproxy() { return 0 fi + source "$MTPROXY_DIR/.config" 2>/dev/null || true + local mtproxy_port="${MTPROXY_PORT:-}" + # Остановка и удаление сервиса systemctl stop mtproxy systemctl disable mtproxy @@ -193,8 +241,12 @@ uninstall_mtproxy() { rm -rf "$MTPROXY_DIR" # Удаление правила firewall - source "$MTPROXY_DIR/.config" 2>/dev/null - ufw delete allow $MTPROXY_PORT/tcp 2>/dev/null + if [ -n "$mtproxy_port" ] && command -v ufw >/dev/null 2>&1; then + ufw delete allow $mtproxy_port/tcp 2>/dev/null || true + elif [ -n "$mtproxy_port" ] && command -v firewall-cmd >/dev/null 2>&1; then + firewall-cmd --permanent --remove-port="${mtproxy_port}/tcp" >/dev/null 2>&1 || true + firewall-cmd --reload >/dev/null 2>&1 || true + fi success "MTProxy удален" exit 0 @@ -270,3 +322,4 @@ if [[ $EUID -ne 0 ]]; then fi main_menu + source "$MTPROXY_DIR/.config" diff --git a/reverse-proxy-manager.sh b/reverse-proxy-manager.sh index 6a3b3f4..05783f5 100755 --- a/reverse-proxy-manager.sh +++ b/reverse-proxy-manager.sh @@ -17,6 +17,7 @@ YELLOW='\033[1;33m' BLUE='\033[0;34m' MAGENTA='\033[0;35m' CYAN='\033[0;36m' +WHITE='\033[1;37m' NC='\033[0m' # Configuration @@ -115,6 +116,49 @@ configure_nginx_3xui() { read -p "Введите домен (например, vpn.example.com): " DOMAIN read -p "Введите порт 3x-ui (по умолчанию 2053): " XRAY_PORT XRAY_PORT=${XRAY_PORT:-2053} + if ! [[ "$XRAY_PORT" =~ ^[0-9]+$ ]] || [ "$XRAY_PORT" -lt 1 ] || [ "$XRAY_PORT" -gt 65535 ]; then + error "Неверный порт: $XRAY_PORT" + return 1 + fi + + cat > "$NGINX_DIR/sites-available/3xui" < "$NGINX_DIR/sites-available/3xui" < "$tmp_conf" + mv "$tmp_conf" "$WG_CONF" +} + get_next_ip() { if [ ! -f "$WG_CONF" ]; then echo "10.0.0.2" @@ -41,8 +63,12 @@ get_next_ip() { if [ -z "$LAST_IP" ]; then echo "10.0.0.2" else - LAST_NUM=$(echo $LAST_IP | cut -d. -f4) + LAST_NUM=$(echo "$LAST_IP" | cut -d. -f4) NEXT_NUM=$((LAST_NUM + 1)) + if [ "$NEXT_NUM" -gt 254 ]; then + print_error "Пул адресов WireGuard исчерпан (10.0.0.2-10.0.0.254)" + return 1 + fi echo "10.0.0.$NEXT_NUM" fi } @@ -54,6 +80,10 @@ add_client() { print_error "Имя клиента не может быть пустым" exit 1 fi + if ! validate_client_name "$CLIENT_NAME"; then + print_error "Неверное имя: только буквы, цифры, _ и -" + exit 1 + fi CLIENT_DIR="$WG_DIR/clients/$CLIENT_NAME" @@ -70,12 +100,12 @@ add_client() { CLIENT_PRIVATE_KEY=$(cat private.key) CLIENT_PUBLIC_KEY=$(cat public.key) - SERVER_PUBLIC_KEY=$(cat $WG_DIR/server_public.key) - SERVER_IP=$(curl -s ifconfig.me) - SERVER_PORT=$(grep ListenPort $WG_CONF | awk '{print $3}') + SERVER_PUBLIC_KEY=$(cat "$WG_DIR/server_public.key") + SERVER_IP=$(get_server_ip) + SERVER_PORT=$(grep ListenPort "$WG_CONF" | awk '{print $3}') CLIENT_IP=$(get_next_ip) - cat >> $WG_CONF <> "$WG_CONF" </dev/null 2>&1; then + qrencode -t ansiutf8 < client.conf + else + print_error "qrencode не установлен, пропускаем QR-код" + fi echo "" print_msg "Конфигурация клиента:" @@ -120,10 +154,10 @@ list_clients() { return fi - for client in $WG_DIR/clients/*; do + for client in "$WG_DIR"/clients/*; do if [ -d "$client" ]; then CLIENT_NAME=$(basename "$client") - CLIENT_IP=$(grep -A 2 "# $CLIENT_NAME" $WG_CONF | grep AllowedIPs | awk '{print $3}' | cut -d/ -f1) + CLIENT_IP=$(grep -A 2 "# $CLIENT_NAME" "$WG_CONF" | grep AllowedIPs | awk '{print $3}' | cut -d/ -f1) echo "- $CLIENT_NAME ($CLIENT_IP)" fi done @@ -136,6 +170,10 @@ remove_client() { print_error "Имя клиента не может быть пустым" exit 1 fi + if ! validate_client_name "$CLIENT_NAME"; then + print_error "Неверное имя: только буквы, цифры, _ и -" + exit 1 + fi CLIENT_DIR="$WG_DIR/clients/$CLIENT_NAME" @@ -144,7 +182,7 @@ remove_client() { exit 1 fi - sed -i "/# $CLIENT_NAME/,+2d" $WG_CONF + remove_peer_block "$CLIENT_NAME" rm -rf "$CLIENT_DIR" systemctl restart wg-quick@wg0 @@ -159,6 +197,10 @@ show_client() { print_error "Имя клиента не может быть пустым" exit 1 fi + if ! validate_client_name "$CLIENT_NAME"; then + print_error "Неверное имя: только буквы, цифры, _ и -" + exit 1 + fi CLIENT_DIR="$WG_DIR/clients/$CLIENT_NAME" @@ -172,7 +214,11 @@ show_client() { echo "" print_msg "QR-код:" - qrencode -t ansiutf8 < "$CLIENT_DIR/client.conf" + if command -v qrencode >/dev/null 2>&1; then + qrencode -t ansiutf8 < "$CLIENT_DIR/client.conf" + else + print_error "qrencode не установлен, пропускаем QR-код" + fi } show_menu() {