#!/bin/bash # Proxy-Core - Advanced VPN & Proxy Panel Auto-Installer # Version: 2.0.0 # Author: houseassassin # GitHub: https://github.com/houseassassin/proxy-core # Supports: WireGuard, 3x-ui, Remnawave, Hysteria2 # Based on: 3dp-manager, remnawave-reverse-proxy, remnawave-scripts, RemnaSetup set -euo pipefail SCRIPT_VERSION="2.0.0" PROJECT_DIR="/opt/proxy-core" LANG_FILE="${PROJECT_DIR}/.selected_language" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' MAGENTA='\033[0;35m' CYAN='\033[0;36m' GRAY='\033[0;90m' NC='\033[0m' # Logging LOGFILE="${PROJECT_DIR}/installer.log" log() { echo -e "${GREEN}[INFO]${NC} $1" | tee -a "$LOGFILE"; } warn() { echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$LOGFILE"; } error() { echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOGFILE"; exit 1; } success() { echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOGFILE"; } # Spinner animation spinner() { local pid=$1 local text=$2 local spinstr='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏' while kill -0 "$pid" 2>/dev/null; do for (( i=0; i<${#spinstr}; i++ )); do printf "\r${GREEN}[%s]${NC} %s" "${spinstr:$i:1}" "$text" sleep 0.1 done done printf "\r\033[K" } # 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" [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" [ALL]="Установить всё" ) MENU_LANG="en" load_language() { if [ -f "$LANG_FILE" ]; then MENU_LANG=$(cat "$LANG_FILE" 2>/dev/null | tr -d '[:space:]') fi } save_language() { mkdir -p "$(dirname "$LANG_FILE")" echo "$1" > "$LANG_FILE" MENU_LANG="$1" } L() { local key="$1" local var_name="LANG_${MENU_LANG^^}_${key}" echo "${!var_name:-$key}" } # System checks check_root() { if [[ $EUID -ne 0 ]]; then error "Этот скрипт должен быть запущен с правами root" fi } detect_os() { if [ -f /etc/os-release ]; then . /etc/os-release OS=$ID VER=$VERSION_ID else error "Не удалось определить операционную систему" fi log "Обнаружена ОС: $OS $VER" } check_memory() { local mem_mb=$(free -m | grep Mem: | awk '{print $2}') if [ $mem_mb -lt 2000 ]; then local swap_mb=$(free -m | grep Swap: | awk '{print $2}') if [ $swap_mb -eq 0 ]; then warn "Мало RAM ($mem_mb MB), создаём swap 2GB..." fallocate -l 2G /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=2048 chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo '/swapfile none swap sw 0 0' >> /etc/fstab success "Swap создан" fi fi } # Network utilities get_server_ip() { curl -s -4 ifconfig.me || curl -s -4 api.ipify.org || curl -s -4 ipinfo.io/ip } get_random_port() { local MIN=${1:-3000} local MAX=${2:-6999} while :; do PORT=$(shuf -i "$MIN-$MAX" -n 1) if ! ss -ltun | awk '{print $4}' | grep -q ":$PORT\$"; then echo "$PORT" return fi done } check_domain() { local domain="$1" local domain_ip=$(dig +short A "$domain" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1) local server_ip=$(get_server_ip) if [ -z "$domain_ip" ] || [ -z "$server_ip" ]; then warn "Не удалось проверить DNS домена $domain" return 1 fi if [ "$domain_ip" = "$server_ip" ]; then return 0 fi warn "IP домена ($domain_ip) не совпадает с IP сервера ($server_ip)" return 1 } # Docker management resolve_compose_cmd() { if docker compose version >/dev/null 2>&1; then COMPOSE_CMD=("docker" "compose") return 0 fi if command -v docker-compose >/dev/null 2>&1; then COMPOSE_CMD=("docker-compose") return 0 fi warn "Docker Compose не найден, устанавливаем..." apt-get update apt-get install -y docker-compose-plugin || apt-get install -y docker-compose if docker compose version >/dev/null 2>&1; then COMPOSE_CMD=("docker" "compose") return 0 fi error "Не удалось установить Docker Compose" } install_docker() { if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then log "Docker уже установлен" resolve_compose_cmd return 0 fi log "Установка Docker..." curl -fsSL https://get.docker.com -o /tmp/get-docker.sh sh /tmp/get-docker.sh rm /tmp/get-docker.sh systemctl enable docker systemctl start docker resolve_compose_cmd success "Docker установлен" } # System packages install_packages() { log "Установка системных пакетов..." case $OS in ubuntu|debian) apt-get update -y apt-get install -y curl wget git sudo ufw certbot \ ca-certificates jq openssl tar net-tools \ qrencode python3-certbot-dns-cloudflare \ unattended-upgrades locales dnsutils coreutils \ grep gawk python3-pip cron ;; centos|rhel|fedora) yum update -y yum install -y curl wget git sudo firewalld certbot \ ca-certificates jq openssl tar net-tools qrencode ;; *) error "Неподдерживаемая ОС: $OS" ;; esac # BBR if ! grep -q "net.core.default_qdisc = fq" /etc/sysctl.conf; then echo "net.core.default_qdisc = fq" >> /etc/sysctl.conf fi if ! grep -q "net.ipv4.tcp_congestion_control = bbr" /etc/sysctl.conf; then echo "net.ipv4.tcp_congestion_control = bbr" >> /etc/sysctl.conf fi sysctl -p >/dev/null # UFW if command -v ufw >/dev/null 2>&1; then ufw allow 22/tcp comment 'SSH' >/dev/null 2>&1 ufw allow 443/tcp comment 'HTTPS' >/dev/null 2>&1 ufw --force enable >/dev/null 2>&1 fi success "Системные пакеты установлены" } # SSL Certificates get_ssl_certificate() { local domain="$1" local email="$2" local method="${3:-standalone}" log "Получение SSL сертификата для $domain..." case $method in standalone) ufw allow 80/tcp >/dev/null 2>&1 certbot certonly --standalone \ -d "$domain" \ --email "$email" \ --agree-tos \ --non-interactive \ --http-01-port 80 \ --key-type ecdsa \ --elliptic-curve secp384r1 ufw delete allow 80/tcp >/dev/null 2>&1 ;; cloudflare) if [ -z "$CLOUDFLARE_API_KEY" ]; then read -p "Введите Cloudflare API Token: " CLOUDFLARE_API_KEY fi mkdir -p ~/.secrets/certbot cat > ~/.secrets/certbot/cloudflare.ini < server_public.key chmod 600 server_private.key fi read -p "Введите порт WireGuard (по умолчанию 51820): " WG_PORT WG_PORT=${WG_PORT:-51820} SERVER_PRIVATE_KEY=$(cat server_private.key) SERVER_IP=$(get_server_ip) cat > /etc/wireguard/wg0.conf <> /etc/sysctl.conf sysctl -p systemctl enable wg-quick@wg0 systemctl start wg-quick@wg0 ufw allow $WG_PORT/udp 2>/dev/null || firewall-cmd --permanent --add-port=$WG_PORT/udp 2>/dev/null success "WireGuard установлен на порту $WG_PORT" log "Конфигурация: /etc/wireguard/wg0.conf" } # 3x-ui installation install_3xui() { log "Установка 3x-ui..." bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) success "3x-ui установлен" log "Панель доступна: http://$(get_server_ip):2053" log "Логин: admin | Пароль: admin (СМЕНИТЕ!)" } # Hysteria2 installation install_hysteria2() { log "Установка Hysteria2..." if systemctl cat hysteria-server.service &> /dev/null; then log "Hysteria2 уже установлен" return 0 fi bash <(curl -fsSL https://get.hy2.sh/) local HY2_PORT=$(get_random_port 10000 20000) local HY2_PASSWORD=$(openssl rand -base64 16 | tr -dc 'A-Za-z0-9' | cut -c1-16) local HY2_OBFS=$(openssl rand -base64 16 | tr -dc 'A-Za-z0-9' | cut -c1-16) read -p "Введите домен для Hysteria2: " HY2_DOMAIN read -p "Введите email для Let's Encrypt: " HY2_EMAIL cat > /etc/hysteria/config.yaml < .env < docker-compose.yml < nginx.conf <