Files
hysteria2/install.sh
2026-02-23 18:39:31 +01:00

901 lines
28 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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