#!/bin/bash export LANG=ru_RU.UTF-8 # Цвета для вывода RED="\033[31m" GREEN="\033[32m" YELLOW="\033[33m" BLUE="\033[34m" PURPLE="\033[35m" CYAN="\033[36m" PLAIN="\033[0m" red(){ echo -e "\033[31m\033[01m$1\033[0m" } green(){ echo -e "\033[32m\033[01m$1\033[0m" } yellow(){ echo -e "\033[33m\033[01m$1\033[0m" } blue(){ echo -e "\033[34m\033[01m$1\033[0m" } purple(){ echo -e "\033[35m\033[01m$1\033[0m" } cyan(){ echo -e "\033[36m\033[01m$1\033[0m" } # Проверка прав root if [[ $EUID -ne 0 ]]; then red "Внимание: Запустите скрипт от имени root пользователя" exit 1 fi # Параметры по умолчанию CUSTOM_SNI="" CONFIG_DIR="/etc/hysteria" USERS_FILE="$CONFIG_DIR/users.txt" HYSTERIA_SERVICE="hysteria-server" # Обработка аргументов командной строки for arg in "$@"; do case $arg in --custom-sni=*) CUSTOM_SNI="${arg#*=}" shift ;; --help) echo "Использование: $0 [--custom-sni=example.com]" echo "" echo "Опции:" echo " --custom-sni=HOSTNAME Указать свой SNI хост (по умолчанию: web.max.ru)" echo " --help Показать эту справку" exit 0 ;; esac done # Определение ОС REGEX=("debian" "ubuntu" "centos|red hat|kernel|oracle linux|alma|rocky" "'amazon linux'" "fedora" "alpine") RELEASE=("Debian" "Ubuntu" "CentOS" "CentOS" "Fedora" "Alpine") PACKAGE_UPDATE=("apt-get update" "apt-get update" "yum -y update" "yum -y update" "yum -y update" "apk update -f") PACKAGE_INSTALL=("apt -y install" "apt -y install" "yum -y install" "yum -y install" "yum -y install" "apk add -f") CMD=("$(grep -i pretty_name /etc/os-release 2>/dev/null | cut -d \" -f2)" "$(hostnamectl 2>/dev/null | grep -i system | cut -d : -f2)" "$(lsb_release -sd 2>/dev/null)" "$(grep -i description /etc/lsb-release 2>/dev/null | cut -d \" -f2)" "$(grep . /etc/redhat-release 2>/dev/null)" "$(grep . /etc/issue 2>/dev/null | cut -d \\ -f1 | sed '/^[ ]*$/d')") for i in "${CMD[@]}"; do SYS="$i" && [[ -n $SYS ]] && break done for ((int = 0; int < ${#REGEX[@]}; int++)); do [[ $(echo "$SYS" | tr '[:upper:]' '[:lower:]') =~ ${REGEX[int]} ]] && SYSTEM="${RELEASE[int]}" && [[ -n $SYSTEM ]] && break done [[ -z $SYSTEM ]] && red "Текущая система VPS не поддерживается, используйте основную операционную систему" && exit 1 # Функция для установки необходимых пакетов install_dependencies() { if [[ -z $(type -P curl) ]]; then if [[ ! $SYSTEM == "CentOS" ]]; then ${PACKAGE_UPDATE[int]} fi ${PACKAGE_INSTALL[int]} curl fi if [[ -z $(type -P openssl) ]]; then ${PACKAGE_INSTALL[int]} openssl fi if [[ -z $(type -P qrencode) ]]; then ${PACKAGE_INSTALL[int]} qrencode fi if [[ -z $(type -P md5sum) ]]; then ${PACKAGE_INSTALL[int]} coreutils fi } # Функция для получения IP адреса get_ip() { local ip=$(curl -s4m8 ip.sb -k 2>/dev/null) || ip=$(curl -s6m8 ip.sb -k 2>/dev/null) echo "$ip" } # Функция для генерации пароля generate_password() { date +%s%N | md5sum | cut -c 1-16 } # Функция для получения obfs пароля из конфига get_obfs_password() { if [[ -f "$CONFIG_DIR/config.yaml" ]]; then grep -A 2 "obfs:" "$CONFIG_DIR/config.yaml" | grep "password:" | head -1 | awk '{print $2}' else echo "" fi } # Функция для получения SNI из конфига get_sni() { if [[ -n "$CUSTOM_SNI" ]]; then echo "$CUSTOM_SNI" elif [[ -f "$CONFIG_DIR/config.yaml" ]]; then grep "url: https://" "$CONFIG_DIR/config.yaml" | sed 's|.*https://\([^/]*\).*|\1|' else echo "web.max.ru" fi } # Функция для генерации ссылки пользователя generate_user_link() { local username=$1 local password=$2 local sni_host=$(get_sni) local server_ip=$(get_ip) local port="443" local obfs_pwd=$(get_obfs_password) if [[ -z "$obfs_pwd" ]]; then obfs_pwd="unknown" fi local user_link="hy2://$password@$server_ip:$port?mport&security=tls&sni=$sni_host&allowInsecure=true&alpn&obfs=salamander&obfs-password=$obfs_pwd#$username" echo "$user_link" } # Функция для сохранения ссылки пользователя в файл save_user_link() { local username=$1 local password=$2 local user_link=$(generate_user_link "$username" "$password") echo "$user_link" > "/root/hysteria2_${username}.txt" echo "$user_link" } # Функция для обновления конфигурации Hysteria с пользователями update_hysteria_config() { if [[ ! -f "$CONFIG_DIR/config.yaml" ]]; then red "Конфигурационный файл не найден!" return 1 fi if [[ ! -f "$USERS_FILE" ]]; then # Если нет файла пользователей, создаем временный с заглушкой local temp_users=$(mktemp) echo " userpass:" > "$temp_users" echo " _placeholder: \"_placeholder\"" >> "$temp_users" else local temp_users=$(mktemp) echo " userpass:" > "$temp_users" while IFS=: read -r username password; do if [[ -n "$username" && -n "$password" ]]; then echo " $username: \"$password\"" >> "$temp_users" fi done < "$USERS_FILE" fi # Создаем резервную копию cp "$CONFIG_DIR/config.yaml" "$CONFIG_DIR/config.yaml.backup" # Обновляем конфиг if grep -q "userpass:" "$CONFIG_DIR/config.yaml"; then # Удаляем старую секцию userpass sed -i '/userpass:/,/^[^ ]/ { /userpass:/! { /^[^ ]/! d } }' "$CONFIG_DIR/config.yaml" # Вставляем новую секцию после auth: sed -i "/auth:/r $temp_users" "$CONFIG_DIR/config.yaml" else red "Не удалось найти секцию auth в конфигурации" rm -f "$temp_users" return 1 fi rm -f "$temp_users" # Перезапускаем сервис systemctl restart "$HYSTERIA_SERVICE" return 0 } # Функция установки Hysteria2 install_hysteria() { yellow "Начинаем установку Hysteria2..." # Проверяем, установлен ли уже Hysteria if [[ -f /usr/local/bin/hysteria ]]; then red "Hysteria2 уже установлен!" read -p "Хотите переустановить? (y/n): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then return fi uninstall_hysteria fi install_dependencies # Определяем архитектуру case "$(uname -m)" in 'i386' | 'i686') ARCH='386' ;; 'amd64' | 'x86_64') ARCH='amd64' ;; 'armv5tel' | 'armv6l' | 'armv7' | 'armv7l') ARCH='arm' ;; 'armv8' | 'aarch64') ARCH='arm64' ;; 'mips' | 'mipsle' | 'mips64' | 'mips64le') ARCH='mipsle' ;; 's390x') ARCH='s390x' ;; *) red "Архитектура не поддерживается!" exit 1 ;; esac # Скачиваем последнюю версию green "Загружаем последнюю версию Hysteria2..." LATEST_VERSION=$(curl -s https://api.github.com/repos/apernet/hysteria/releases/latest | grep tag_name | cut -d '"' -f 4) if [[ -z "$LATEST_VERSION" ]]; then red "Не удалось получить последнюю версию" exit 1 fi DOWNLOAD_URL="https://github.com/apernet/hysteria/releases/download/app/$LATEST_VERSION/hysteria-linux-$ARCH" curl -L -o /usr/local/bin/hysteria "$DOWNLOAD_URL" chmod +x /usr/local/bin/hysteria # Создаем директорию для конфигурации mkdir -p "$CONFIG_DIR" # Настройка Hysteria configure_hysteria # Создаем systemd сервис cat << 'EOF' > /etc/systemd/system/hysteria-server.service [Unit] Description=Hysteria Server Service After=network.target [Service] Type=simple ExecStart=/usr/local/bin/hysteria server --config /etc/hysteria/config.yaml WorkingDirectory=/etc/hysteria User=root Group=root Environment=HYSTERIA_LOG_LEVEL=info CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW NoNewPrivileges=true Restart=always RestartSec=5 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable hysteria-server systemctl start hysteria-server if systemctl is-active hysteria-server > /dev/null; then green "Hysteria2 успешно установлен и запущен!" # Создаем первого пользователя echo yellow "Создаем первого пользователя..." local admin_user="user1" local admin_pass=$(generate_password) echo "$admin_user:$admin_pass" > "$USERS_FILE" # Обновляем конфиг update_hysteria_config # Сохраняем ссылку local user_link=$(save_user_link "$admin_user" "$admin_pass") green "Первый пользователь создан:" echo "======================================================" echo "Имя пользователя: $admin_user" echo "Пароль: $admin_pass" echo "Ссылка для подключения:" echo "$user_link" echo "======================================================" if command -v qrencode &> /dev/null; then echo "QR код для подключения:" qrencode -t ANSIUTF8 "$user_link" fi echo yellow "Файл со ссылкой сохранен: /root/hysteria2_${admin_user}.txt" else red "Ошибка запуска Hysteria-server" systemctl status hysteria-server exit 1 fi } # Функция настройки Hysteria configure_hysteria() { yellow "Настройка сервера Hysteria2..." if [[ -n "$CUSTOM_SNI" ]]; then local sni_host="$CUSTOM_SNI" yellow "Используется кастомный SNI: $sni_host" else local sni_host="web.max.ru" yellow "Используется SNI по умолчанию: $sni_host" fi local masquerade_url="$sni_host" local port="443" local obfs_pwd=$(generate_password) # Генерируем SSL сертификат openssl ecparam -genkey -name prime256v1 -out "$CONFIG_DIR/private.key" openssl req -new -x509 -days 36500 -key "$CONFIG_DIR/private.key" -out "$CONFIG_DIR/cert.crt" -subj "/CN=$sni_host" chmod 600 "$CONFIG_DIR/cert.crt" chmod 600 "$CONFIG_DIR/private.key" # Создаем базовую конфигурацию cat << EOF > "$CONFIG_DIR/config.yaml" listen: :$port tls: cert: $CONFIG_DIR/cert.crt key: $CONFIG_DIR/private.key obfs: type: salamander salamander: password: $obfs_pwd auth: type: userpass userpass: _placeholder: "_placeholder" masquerade: type: proxy proxy: url: https://$masquerade_url rewriteHost: true quic: initStreamReceiveWindow: 16777216 maxStreamReceiveWindow: 16777216 initConnReceiveWindow: 33554432 maxConnReceiveWindow: 33554432 EOF green "Базовая конфигурация создана" } # Функция добавления пользователя add_user() { if [[ ! -f /usr/local/bin/hysteria ]]; then red "Hysteria2 не установлен! Сначала выполните установку." return fi echo cyan "=== Добавление нового пользователя ===" echo read -p "Введите имя пользователя: " username if [[ -z "$username" ]]; then red "Имя пользователя не может быть пустым!" return fi # Проверяем, существует ли уже пользователь if [[ -f "$USERS_FILE" ]] && grep -q "^$username:" "$USERS_FILE"; then red "Пользователь $username уже существует!" return fi read -p "Введите пароль (оставьте пустым для генерации): " password if [[ -z "$password" ]]; then password=$(generate_password) green "Сгенерирован пароль: $password" fi # Добавляем пользователя echo "$username:$password" >> "$USERS_FILE" # Обновляем конфиг if update_hysteria_config; then green "Конфигурация обновлена" # Сохраняем ссылку local user_link=$(save_user_link "$username" "$password") echo green "Пользователь $username успешно создан!" echo "======================================================" echo "Имя пользователя: $username" echo "Пароль: $password" echo "Ссылка для подключения:" echo "$user_link" echo "======================================================" echo "Ссылка также сохранена в /root/hysteria2_${username}.txt" echo if command -v qrencode &> /dev/null; then echo "QR код для подключения:" qrencode -t ANSIUTF8 "$user_link" fi else red "Ошибка при обновлении конфигурации" # Удаляем пользователя из файла в случае ошибки sed -i "/^$username:/d" "$USERS_FILE" fi } # Функция удаления пользователя delete_user() { if [[ ! -f "$USERS_FILE" ]]; then red "Нет пользователей для удаления" return fi echo cyan "=== Удаление пользователя ===" echo # Показываем список пользователей list_users echo read -p "Введите имя пользователя для удаления: " username if [[ -z "$username" ]]; then red "Имя пользователя не может быть пустым!" return fi # Проверяем, существует ли пользователь if ! grep -q "^$username:" "$USERS_FILE"; then red "Пользователь $username не найден!" return fi # Подтверждение read -p "Вы уверены, что хотите удалить пользователя $username? (y/n): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then yellow "Удаление отменено" return fi # Удаляем пользователя sed -i "/^$username:/d" "$USERS_FILE" # Удаляем файл со ссылкой если существует if [[ -f "/root/hysteria2_${username}.txt" ]]; then rm "/root/hysteria2_${username}.txt" fi # Обновляем конфиг if update_hysteria_config; then green "Пользователь $username успешно удален!" else red "Ошибка при обновлении конфигурации" fi } # Функция показа списка пользователей list_users() { if [[ ! -f "$USERS_FILE" ]]; then yellow "Нет пользователей" return fi echo cyan "=== Список пользователей Hysteria2 ===" echo "----------------------------------------" printf "%-20s | %-30s | %s\n" "Имя пользователя" "Пароль" "Файл ссылки" echo "----------------------------------------" local count=0 while IFS=: read -r username password; do if [[ -n "$username" && "$username" != "_placeholder" ]]; then link_file="/root/hysteria2_${username}.txt" if [[ -f "$link_file" ]]; then file_status="✓ есть" else file_status="✗ нет" fi printf "%-20s | %-30s | %s\n" "$username" "$password" "$file_status" ((count++)) fi done < "$USERS_FILE" echo "----------------------------------------" green "Всего пользователей: $count" echo } # Функция показа ссылок пользователей show_links() { if [[ ! -f "$USERS_FILE" ]]; then red "Нет пользователей" return fi echo cyan "=== Ссылки для подключения ===" echo while IFS=: read -r username password; do if [[ -n "$username" && "$username" != "_placeholder" ]]; then echo "----------------------------------------" yellow "Пользователь: $username" local user_link=$(generate_user_link "$username" "$password") echo "$user_link" echo fi done < "$USERS_FILE" } # Функция показа QR-кодов show_qrcodes() { if [[ ! -f "$USERS_FILE" ]]; then red "Нет пользователей" return fi if ! command -v qrencode &> /dev/null; then red "qrencode не установлен" ${PACKAGE_INSTALL[int]} qrencode fi echo cyan "=== QR-коды для подключения ===" echo while IFS=: read -r username password; do if [[ -n "$username" && "$username" != "_placeholder" ]]; then echo "----------------------------------------" yellow "Пользователь: $username" local user_link=$(generate_user_link "$username" "$password") qrencode -t ANSIUTF8 "$user_link" echo fi done < "$USERS_FILE" } # Функция проверки статуса check_status() { echo cyan "=== Статус Hysteria2 ===" echo if [[ -f /usr/local/bin/hysteria ]]; then green "✓ Hysteria2 установлен" if systemctl is-active hysteria-server > /dev/null; then green "✓ Сервис запущен" else red "✗ Сервис остановлен" fi if systemctl is-enabled hysteria-server > /dev/null 2>&1; then green "✓ Сервис добавлен в автозагрузку" else yellow "✗ Сервис не в автозагрузке" fi # Показываем версию local version=$(/usr/local/bin/hysteria version 2>/dev/null | head -1) if [[ -n "$version" ]]; then green "Версия: $version" fi # Показываем основную информацию echo cyan "Информация о сервере:" echo "----------------------------------------" echo "IP адрес: $(get_ip)" echo "SNI: $(get_sni)" echo "Порт: 443" echo "OBFS пароль: $(get_obfs_password)" echo "Количество пользователей: $(grep -c "^[^_]" "$USERS_FILE" 2>/dev/null || echo 0)" echo "----------------------------------------" else red "✗ Hysteria2 не установлен" fi } # Функция перезапуска сервиса restart_service() { if [[ ! -f /usr/local/bin/hysteria ]]; then red "Hysteria2 не установлен!" return fi yellow "Перезапуск сервиса Hysteria2..." systemctl restart hysteria-server if systemctl is-active hysteria-server > /dev/null; then green "✓ Сервис успешно перезапущен" else red "✗ Ошибка при перезапуске сервиса" fi } # Функция просмотра логов view_logs() { if [[ ! -f /usr/local/bin/hysteria ]]; then red "Hysteria2 не установлен!" return fi yellow "Просмотр логов Hysteria2 (Ctrl+C для выхода):" journalctl -u hysteria-server -f -n 50 } # Функция полного удаления Hysteria2 uninstall_hysteria() { echo cyan "=== Удаление Hysteria2 ===" echo if [[ ! -f /usr/local/bin/hysteria ]]; then red "Hysteria2 не установлен!" return fi # Подтверждение read -p "Вы уверены, что хотите полностью удалить Hysteria2? (y/n): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then yellow "Удаление отменено" return fi yellow "Останавливаем сервис..." systemctl stop hysteria-server systemctl disable hysteria-server yellow "Удаляем файлы..." rm -f /usr/local/bin/hysteria rm -f /etc/systemd/system/hysteria-server.service systemctl daemon-reload read -p "Удалить конфигурационные файлы и данные пользователей? (y/n): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then rm -rf "$CONFIG_DIR" rm -f /root/hysteria2_*.txt green "Конфигурация и данные пользователей удалены" else green "Конфигурация сохранена в $CONFIG_DIR" fi green "Hysteria2 успешно удален!" } # Функция резервного копирования backup_config() { if [[ ! -f "$CONFIG_DIR/config.yaml" ]]; then red "Конфигурация не найдена!" return fi local backup_dir="/root/hysteria_backup_$(date +%Y%m%d_%H%M%S)" mkdir -p "$backup_dir" cp -r "$CONFIG_DIR" "$backup_dir/" if [[ -f "$USERS_FILE" ]]; then cp "$USERS_FILE" "$backup_dir/" fi green "Резервная копия создана в: $backup_dir" } # Функция восстановления из резервной копии restore_backup() { echo cyan "=== Восстановление из резервной копии ===" echo local backups=(/root/hysteria_backup_*) if [[ ${#backups[@]} -eq 0 ]] || [[ ! -d "${backups[0]}" ]]; then red "Резервные копии не найдены" return fi echo "Доступные резервные копии:" for i in "${!backups[@]}"; do echo "$((i+1)). ${backups[$i]}" done echo read -p "Выберите номер резервной копии для восстановления: " choice if [[ ! "$choice" =~ ^[0-9]+$ ]] || [[ "$choice" -lt 1 ]] || [[ "$choice" -gt ${#backups[@]} ]]; then red "Неверный выбор" return fi local selected_backup="${backups[$((choice-1))]}" # Останавливаем сервис systemctl stop hysteria-server # Восстанавливаем файлы cp -r "$selected_backup/hysteria/"* "$CONFIG_DIR/" 2>/dev/null if [[ -f "$selected_backup/users.txt" ]]; then cp "$selected_backup/users.txt" "$USERS_FILE" fi # Запускаем сервис systemctl start hysteria-server green "Восстановление завершено!" } # Функция изменения SNI change_sni() { if [[ ! -f "$CONFIG_DIR/config.yaml" ]]; then red "Конфигурация не найдена!" return fi echo cyan "=== Изменение SNI ===" echo local current_sni=$(get_sni) yellow "Текущий SNI: $current_sni" read -p "Введите новый SNI (например: web.max.ru): " new_sni if [[ -z "$new_sni" ]]; then red "SNI не может быть пустым" return fi # Обновляем конфиг sed -i "s|url: https://[^/]*|url: https://$new_sni|" "$CONFIG_DIR/config.yaml" # Обновляем сертификат openssl req -new -x509 -days 36500 -key "$CONFIG_DIR/private.key" -out "$CONFIG_DIR/cert.crt" -subj "/CN=$new_sni" # Перезапускаем сервис systemctl restart hysteria-server green "SNI изменен на: $new_sni" } # Функция отображения меню show_menu() { clear echo "======================================================" blue " Hysteria2 Management Script" echo "======================================================" echo if [[ -f /usr/local/bin/hysteria ]]; then green "✓ Hysteria2 установлен" if systemctl is-active hysteria-server > /dev/null; then green "✓ Сервис запущен" else red "✗ Сервис остановлен" fi else red "✗ Hysteria2 не установлен" fi echo cyan "Основные операции:" echo "1) 📦 Установить Hysteria2" echo "2) 🗑️ Полностью удалить Hysteria2" echo cyan "Управление пользователями:" echo "3) ➕ Добавить пользователя" echo "4) ❌ Удалить пользователя" echo "5) 📋 Список пользователей" echo "6) 🔗 Показать ссылки" echo "7) 📱 Показать QR-коды" echo cyan "Управление сервисом:" echo "8) 🔄 Перезапустить сервис" echo "9) 📊 Статус и информация" echo "10) 📜 Просмотр логов" echo cyan "Дополнительно:" echo "11) 💾 Создать резервную копию" echo "12) 🔄 Восстановить из резервной копии" echo "13) 🌐 Изменить SNI" echo cyan "Прочее:" echo "0) 🚪 Выход" echo echo "======================================================" } # Основной цикл программы main() { while true; do show_menu read -p "Выберите действие (0-13): " choice echo case $choice in 1) install_hysteria ;; 2) uninstall_hysteria ;; 3) add_user ;; 4) delete_user ;; 5) list_users ;; 6) show_links ;; 7) show_qrcodes ;; 8) restart_service ;; 9) check_status ;; 10) view_logs ;; 11) backup_config ;; 12) restore_backup ;; 13) change_sni ;; 0) green "Выход..." exit 0 ;; *) red "Неверный выбор! Пожалуйста, выберите 0-13" ;; esac echo read -p "Нажмите Enter для продолжения..." done } # Запуск основной программы main